从零搭一个 Agent Eval Harness:Task、Environment、Trial、Trajectory 与 Grader
系列:开源模型评测
来源公众号:智能大时代
发布时间:2026-09-08 22:34
原文链接:阅读公众号原文
这里单独
上一篇 模型能力评测:从 LLM Eval 到 Agent Eval 我们讨论了:
Agent Eval 到底在评什么?
结论是,Agent Eval 测量的不只是一个 Model,而是一个完整的 Agent System。
整体流程可以表示为:
Model × Scaffold × Environment × Task × Protocol ↓ Trial ↓ ┌────────────┴────────────┐ ↓ ↓ Trajectory Outcome │ │ └────────────┬────────────┘ ↓ Grader ↓ Metric ↓ Evaluation
但是,怎么把这套 Evaluation 真正跑起来呢?
一个自然的想法是使用类似下面的代码实现:
for task in tasks: result = agent.run(task) score = evaluate(result)
几十行代码就能快速进行测试。
但是,Agent 的执行不像普通的独立测评任务,随着任务变复杂,会出现各种问题:
环境如何初始化? 不同 Agent 是否从相同状态开始? Agent 失败了,还是 API 挂了? Timeout 后如何处理? Trajectory 怎么保存? 不同模型是否使用了相同资源? 上一个任务会不会污染下一个任务?
这时候会发现:
Agent Eval 需要的不是一个 evaluation script,而是一套 Eval Harness。
什么是 Eval Harness?
Eval Harness 不同于 Agent Harness。
Agent Harness 是被测 Agent System 的一部分,负责:
Agent 怎么完成任务。
而 Eval Harness 负责的是:
怎么管理评测环境、给 Agent 出题、运行 Agent、记录过程、检查结果并生成评测结果。
具体包括:
Task Loading Environment Setup Protocol Control Agent Execution Trajectory Recording Outcome Collection Grading Reporting
Agent Eval 需要哪些组件?
一个最小的 Agent Eval 系统不需要很复杂,核心是下面几个组件:
Task Environment Agent Protocol Trial Trajectory Outcome Grader
下面对几个重要组件进行简要介绍:
Task
Task 描述:
Agent 被要求完成什么?
例如:
根据 Alice 发来的销售数据更新 Q4 forecast。
在系统中可能是这样表示:
Task( id="forecast_001", instruction="根据 Alice 的邮件更新 Q4 forecast", environment="office_env_v1", )
Environment:Agent 所处的世界
Environment 回答的是:
Agent 能看到什么、能做什么,以及动作会产生什么后果?
Coding Agent 的 Environment 可能包含:
Repository Filesystem Shell Python Tests
Office Agent 可能包含:
Email Calendar Spreadsheet Docs
一个 Environment 最基本需要提供 reset() 和 execute();为了评测最终结果,通常还需要通过 snapshot() 获取环境的最终状态。
其中,reset() 负责把世界恢复到 Task 的初始状态,execute(action) 负责执行 Agent 的动作并返回 Observation,snapshot() 则负责获取最终 Environment State。
Agent:Eval Harness 面向被测系统的统一接口
Agent 框架很多,例如不同的 Coding Agent、Browser Agent 或自研 Agent Harness。
为了对多种 Agent 进行统一评测,Eval Harness 不应该绑定某一种 Agent Framework,而应该定义统一接口。
例如:
class Agent: def run(self, task, environment): ...
至于内部怎么完成任务,是 Agent Harness 自己的事情。
Eval Harness 只需要知道:
给定 Task、Environment 和 Protocol,这个 Agent 能不能完成一次 Trial。
Trial:Agent Eval 的基本单位
所谓 Trial,就是:
某个 Agent 在一组确定的评测条件下,对某个 Task 的一次完整尝试。
为什么需要把 Task 和 Trial 区分开?
因为 Agent 通常具有随机性,同一个 Task 需要执行多次,以分析整体成功率,其中每一次具体尝试都是一个 Trial:
Trial 1 → Success Trial 2 → Failure Trial 3 → Success
Trial 和 Task 的关系可以这样理解:
Task A ├── Trial 1 ├── Trial 2 ├── Trial 3 └── Trial 4
这也是后面具体评测指标的基础:
Success Rate Pass@k Pass^k Variance Reliability
Trial 的生命周期
Trial 是评测的基本单位,相对其他组件更为重要,我们在本节单独进行分析。一个完整 Trial 可以简化成:
Prepare ↓ Reset ↓ Execute ↓ Collect ↓ Grade ↓ Persist ↓ Cleanup
可以把它理解成一次受控实验的完整生命周期。
Prepare
加载本次 Trial 需要的:
Task Agent Configuration Environment Version Protocol
同时生成唯一的:
trial_id
Reset
把环境恢复到指定 initial state。
Coding Agent 可能需要:
checkout commit reset filesystem restart container clear temporary files
Office Agent 可能需要:
restore inbox restore calendar restore spreadsheet clear session
这一步非常重要。
如果 Agent B 接着 Agent A 修改后的环境继续跑,那么两个 Agent 已经不在同一个实验条件下。
Execute
Agent 根据 Task 开始与 Environment 交互,不断执行 Action 并接收新的 Observation,直到 Agent 主动结束,或者达到:
max_steps timeout agent_error environment_error
Collect
Agent 停止以后,不能只保存 final_answer,还应该收集真实 Evidence。
例如:
git diff test result final files calendar state email state tool calls token usage latency
因为:
Agent 说“我完成了”,并不等于任务真的完成了。
Agent 最终改变了什么,才是 Outcome。
Grade
Grader 负责具体的评分,根据 Task 和 Evidence 判断任务是否完成。
例如:
代码测试是否通过? 有没有引入 regression? 表格数据是否正确? 有没有修改不该修改的数据? 会议邀请对象是否正确?
最终返回结构化结果。此外,对于固定的结果证据,可以修改 Grader 以得到更“合理”的得分。
例如:
{ "success": true, "constraint_violation": false }
Persist
保存 Trial、Evidence 和 Grade。
这一步会直接决定后面能不能做:
Failure Analysis Regression Analysis Regrading Statistics Training Data Mining
Cleanup
Trial 结束以后,还需要释放本次运行产生的资源,例如容器、临时文件、Session 等,避免本次 Trial 的状态影响后面的运行。
对于一个稳定的 Eval Harness 来说:
Reset 保证每次 Trial 从正确的状态开始,Cleanup 保证它不会污染下一次 Trial。
Trajectory 的意义
Score 只能告诉我们 Agent 成功还是失败,却无法解释为什么失败。为了了解 Agent 究竟做了哪些事情,Eval Harness 至少应该保存可观察的 Trajectory + Outcome + Grade:Trajectory 记录 Agent 做了什么,Outcome 记录环境最终变成什么,Grade 记录我们如何评价这些证据。
同时,如果需要进行模型的后训练,也依赖 Trajectory。
记录好 Version
在 Agent 的持续评测过程中,Model、System Prompt、Tool、Environment、Task、Protocol、Grader 都可能导致分数变化。
为了知道一次结果变化究竟来自哪里,每次 Trial 都应该保存完整的版本和运行配置:
task_version: 3 model: model-x-2026-03-01 scaffold_version: 17 environment_version: 4 max_steps: 30 timeout: 300
这样结果变化时,我们才有可能回答:到底是什么发生了变化? 从这个角度看,Eval Harness 不只是一个 Agent Runner,同时也是一套最小的实验管理系统。
一个贯穿整个系列的 Mini Framework
现在,我们已经可以搭出第一版 mini framework,核心接口不需要复杂:
class Task: id: str instruction: str environment: str class Environment: def reset(self, task): ... def execute(self, action): ... def snapshot(self): ... def cleanup(self): ... class Agent: def run(self, task, env, recorder): ... class Trial: task_id: str trajectory: list outcome: dict execution_status: str class Grader: def grade(self, task, trial): ... class EvalRunner: def run(self, task, agent, env, protocol): ...
执行流程大概是:
def run(task, agent, env, grader, protocol): trial = Trial(task_id=task.id) try: env.reset(task) agent.run( task=task, env=env, recorder=trial ) trial.execution_status = "completed" trial.outcome = env.snapshot() except TimeoutError: trial.execution_status = "timeout" except EnvironmentError: trial.execution_status = "environment_error" finally: env.cleanup() save_trial(trial) grade = grader.grade(task, trial) save_grade(grade) return grade
当然,在 AI Coding 已经非常成熟的今天,实现这套 Mini Framework 本身并不是有难度的事情。上面的代码是为了读者更清楚理解 Eval Harness 的设计原理。