18.6 Agent 专项评估框架
本节目标:掌握 Agent 专项评估的前沿方法,包括 Agent-as-Judge 范式、τ-bench / OSWorld / SWE-bench 等基准测试,并能实现完整的 Agent-as-Judge 评估器。
从 LLM-as-Judge 到 Agent-as-Judge
在 19.1 中我们介绍了 LLM-as-Judge——用一个 LLM 来评判另一个 LLM 的输出质量。但 Agent 不同于普通的聊天模型:Agent 会调用工具、执行多步操作、与环境交互。仅仅评估最终输出是不够的,我们需要评估 Agent 的整个行为轨迹。
这就是 Agent-as-Judge 的核心思想:用一个 Agent(而不仅仅是一个 LLM)来评估另一个 Agent 的完整执行过程 [1]。
LLM-as-Judge vs Agent-as-Judge
| 维度 | LLM-as-Judge | Agent-as-Judge |
|---|---|---|
| 评估对象 | 单轮文本输出 | 完整执行轨迹(多步、多工具) |
| 评估方式 | 一次性打分 | 逐步审查 + 交互验证 |
| 上下文理解 | 仅看输入和输出 | 理解工具调用、中间状态、错误恢复 |
| 评估深度 | 语义质量 | 决策质量 + 执行效率 + 错误处理 |
| 成本 | 较低 | 较高(需要多轮推理) |
| 一致性 | 较高 | 中等(评估过程更复杂) |
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
class TrajectoryAspect(Enum):
"""Agent 行为轨迹的评估方面"""
GOAL_ACHIEVEMENT = "目标达成" # 是否完成了用户目标
TOOL_SELECTION = "工具选择" # 选择的工具是否合理
TOOL_USAGE = "工具使用" # 工具参数是否正确
ERROR_RECOVERY = "错误恢复" # 出错后能否自我修正
EFFICIENCY = "执行效率" # 是否走了弯路
REASONING_QUALITY = "推理质量" # 思考过程是否合理
@dataclass
class AgentTrace:
"""Agent 执行轨迹"""
task_id: str
user_query: str
steps: list[dict] = field(default_factory=list) # 每一步的详细记录
final_output: str = ""
success: bool = False
total_tokens: int = 0
total_time: float = 0.0
@dataclass
class TraceEvaluation:
"""轨迹评估结果"""
task_id: str
aspect: TrajectoryAspect
score: float # 0.0 - 1.0
reasoning: str
evidence: list[str] = field(default_factory=list) # 从轨迹中提取的证据
Agent-as-Judge 评估方法论
核心流程
Agent-as-Judge 的评估流程分为三个阶段:
- 轨迹收集:记录被评估 Agent 的完整执行过程
- 逐步审查:评估 Agent 对每一步进行审查
- 综合评判:汇总各步骤评估,给出总体评价
import json
from langchain_openai import ChatOpenAI
class AgentAsJudge:
"""用 Agent 评估 Agent 的完整执行轨迹"""
def __init__(self, model: str = "gpt-4.1"):
self.llm = ChatOpenAI(model=model, temperature=0)
def evaluate_trace(self, trace: AgentTrace) -> dict:
"""评估完整的 Agent 执行轨迹"""
# 阶段 1:格式化轨迹信息
trajectory_text = self._format_trajectory(trace)
# 阶段 2:逐步审查
step_evaluations = self._review_steps(trajectory_text, trace.user_query)
# 阶段 3:综合评判
overall_evaluation = self._synthesize_evaluation(
trace, step_evaluations
)
return {
"task_id": trace.task_id,
"step_evaluations": step_evaluations,
"overall": overall_evaluation
}
def _format_trajectory(self, trace: AgentTrace) -> str:
"""将执行轨迹格式化为可读文本"""
lines = [f"用户请求:{trace.user_query}\n"]
for i, step in enumerate(trace.steps, 1):
lines.append(f"--- 第 {i} 步 ---")
if "thought" in step:
lines.append(f"思考:{step['thought']}")
if "action" in step:
lines.append(f"动作:{step['action']}")
if "tool" in step:
lines.append(f"工具:{step['tool']}")
if "tool_input" in step:
lines.append(f"工具输入:{json.dumps(step['tool_input'], ensure_ascii=False)}")
if "observation" in step:
lines.append(f"观察:{step['observation']}")
lines.append("")
lines.append(f"最终输出:{trace.final_output}")
lines.append(f"执行成功:{'是' if trace.success else '否'}")
return "\n".join(lines)
def _review_steps(self, trajectory: str, query: str) -> list[dict]:
"""逐步审查 Agent 行为"""
prompt = f"""你是一个专业的 Agent 行为评审员。请逐步审查以下 Agent 的执行轨迹。
{trajectory}
请对每一步进行审查,分析:
1. 这一步的思考是否合理?
2. 选择的工具/动作是否恰当?
3. 工具参数是否正确?
4. 对观察结果的理解是否准确?
以 JSON 格式回复:
{{
"steps": [
{{
"step_number": 1,
"thought_quality": "<好/中/差>",
"action_appropriateness": "<好/中/差>",
"parameter_correctness": "<好/中/差>",
"observation_understanding": "<好/中/差>",
"issues": ["问题1", "问题2"],
"improvement": "改进建议"
}}
]
}}"""
response = self.llm.invoke(prompt)
try:
result = json.loads(response.content)
return result.get("steps", [])
except json.JSONDecodeError:
return []
def _synthesize_evaluation(self, trace: AgentTrace, step_evals: list) -> dict:
"""综合评判,给出总体评价"""
prompt = f"""基于以下信息,对 Agent 的整体表现进行综合评判。
任务:{trace.user_query}
执行步骤数:{len(trace.steps)}
是否成功:{'是' if trace.success else '否'}
总耗时:{trace.total_time:.1f}s
总 Token:{trace.total_tokens}
逐步审查结果:
{json.dumps(step_evals, ensure_ascii=False, indent=2)}
请从以下维度评分(0-10)并给出总体评价:
1. 目标达成度:是否完成了用户目标
2. 决策质量:每一步的决策是否合理
3. 执行效率:是否有不必要的步骤
4. 错误处理:出错时的应对能力
5. 输出质量:最终回答的质量
以 JSON 格式回复:
{{
"goal_achievement": <0-10>,
"decision_quality": <0-10>,
"execution_efficiency": <0-10>,
"error_handling": <0-10>,
"output_quality": <0-10>,
"overall_score": <0-10>,
"summary": "总体评价(2-3句话)",
"key_strengths": ["优点1", "优点2"],
"key_weaknesses": ["不足1", "不足2"],
"recommendations": ["建议1", "建议2"]
}}"""
response = self.llm.invoke(prompt)
try:
return json.loads(response.content)
except json.JSONDecodeError:
return {"overall_score": 0, "summary": "评估解析失败"}
实战案例:评估一个搜索 Agent
# 构造一个待评估的 Agent 轨迹
sample_trace = AgentTrace(
task_id="search_eval_001",
user_query="帮我对比 Python 和 Rust 在 Web 后端开发中的优劣势",
steps=[
{
"thought": "用户想要对比两种语言,我需要分别搜索它们的优劣势",
"action": "调用搜索工具",
"tool": "web_search",
"tool_input": {"query": "Python Web 后端开发 优劣势 2025"},
"observation": "搜索到 5 条结果:1. Python 优势:生态丰富... 2. Django/Flask..."
},
{
"thought": "现在搜索 Rust 的信息",
"action": "调用搜索工具",
"tool": "web_search",
"tool_input": {"query": "Rust Web 后端开发 优劣势 2025"},
"observation": "搜索到 5 条结果:1. Rust 优势:高性能... 2. Actix/Axum..."
},
{
"thought": "我已有足够信息,可以综合对比了",
"action": "生成最终回答",
"tool": None,
"tool_input": None,
"observation": None
}
],
final_output="Python 和 Rust 在 Web 后端开发中各有优势...\nPython:生态丰富、开发速度快...\nRust:高性能、内存安全...",
success=True,
total_tokens=3200,
total_time=8.5
)
# 运行 Agent-as-Judge 评估
judge = AgentAsJudge(model="gpt-4.1")
result = judge.evaluate_trace(sample_trace)
print(json.dumps(result["overall"], ensure_ascii=False, indent=2))
注意事项
| 问题 | 说明 | 应对策略 |
|---|---|---|
| 评估偏差 | Judge Agent 可能对特定风格有偏好 | 使用多个 Judge Agent 投票 |
| 评估成本 | 每次评估需要多轮 LLM 调用 | 对简单任务用规则+LLM 混合评估 |
| 一致性问题 | 同一轨迹多次评估结果可能不同 | temperature=0 + 多次评估取平均 |
| 评估能力上限 | Judge Agent 的评估能力受自身模型能力限制 | 使用强于被评估 Agent 的模型做 Judge |
| 轨迹格式化 | 过长的轨迹可能超出上下文窗口 | 对轨迹做摘要或分段评估 |
💡 最佳实践:Agent-as-Judge 的 Judge 模型应当比被评估的 Agent 使用更强的模型。例如,用 gpt-4.1 评估 gpt-4.1-mini 驱动的 Agent,避免"学生给自己打分"的问题。
τ-bench:面向工具使用的基准测试
什么是 τ-bench?
τ-bench(tau-bench)是 2024 年提出的专门评估 LLM Agent 工具使用能力的基准测试 [2]。与传统基准不同,τ-bench 关注 Agent 在真实环境中使用工具的能力,而不仅仅是选择正确的工具。
τ-bench 的核心设计
| 特性 | 说明 |
|---|---|
| 评估维度 | 工具选择、参数填充、多步推理、错误处理 |
| 环境 | 模拟真实 API 环境(航班查询、酒店预订等) |
| 难度级别 | 简单单工具 → 复杂多工具协作 |
| 评估方式 | 端到端结果匹配 + 轨迹审查 |
| 核心创新 | 引入"用户模拟器",模拟真实用户的多轮对话行为 |
τ-bench 评估维度详解
@dataclass
class TauBenchResult:
"""τ-bench 评估结果"""
task_id: str
# 核心指标
tool_selection_accuracy: float # 工具选择准确率
param_fill_accuracy: float # 参数填充准确率
multi_step_success_rate: float # 多步任务成功率
error_recovery_rate: float # 错误恢复率
# 辅助指标
avg_steps_per_task: float # 平均步骤数
avg_redundant_steps: float # 平均冗余步骤数
total_token_usage: int # 总 Token 使用量
class TauBenchEvaluator:
"""τ-bench 风格的评估器"""
def __init__(self, agent_func, user_simulator, env):
self.agent_func = agent_func # 待评估的 Agent
self.user_simulator = user_simulator # 用户模拟器
self.env = env # 模拟环境
def evaluate_task(self, task: dict) -> TauBenchResult:
"""评估单个任务"""
steps = []
tool_calls_correct = 0
tool_calls_total = 0
params_correct = 0
params_total = 0
errors_encountered = 0
errors_recovered = 0
# 模拟多轮对话
conversation = [{"role": "user", "content": task["initial_query"]}]
for step_idx in range(20): # 最多 20 步
agent_response = self.agent_func(conversation)
# 提取工具调用
if hasattr(agent_response, "tool_calls") and agent_response.tool_calls:
for tc in agent_response.tool_calls:
tool_calls_total += 1
params_total += len(tc["args"])
# 检查工具选择是否正确
expected_tools = task.get("expected_tool_sequence", [])
if step_idx < len(expected_tools):
if tc["name"] == expected_tools[step_idx]:
tool_calls_correct += 1
# 检查参数
expected_args = task.get("expected_args", {}).get(step_idx, {})
for key, expected_val in expected_args.items():
if key in tc["args"] and tc["args"][key] == expected_val:
params_correct += 1
# 执行工具并获取结果
try:
observation = self.env.execute(tc["name"], tc["args"])
except Exception as e:
errors_encountered += 1
observation = f"错误:{str(e)}"
steps.append({
"tool": tc["name"],
"args": tc["args"],
"observation": observation,
"is_error": "错误" in str(observation)
})
conversation.append({
"role": "assistant",
"content": None,
"tool_calls": [tc]
})
conversation.append({
"role": "tool",
"content": str(observation)
})
else:
# Agent 给出最终回答
break
# 评估最终结果是否正确
final_success = self._check_final_result(task, steps)
return TauBenchResult(
task_id=task["id"],
tool_selection_accuracy=(
tool_calls_correct / tool_calls_total
if tool_calls_total > 0 else 0.0
),
param_fill_accuracy=(
params_correct / params_total
if params_total > 0 else 0.0
),
multi_step_success_rate=1.0 if final_success else 0.0,
error_recovery_rate=(
errors_recovered / errors_encountered
if errors_encountered > 0 else 1.0
),
avg_steps_per_task=len(steps),
avg_redundant_steps=self._count_redundant_steps(steps),
total_token_usage=sum(
len(str(m["content"]).split()) for m in conversation
)
)
def _check_final_result(self, task: dict, steps: list) -> bool:
"""检查最终结果是否符合预期"""
expected_results = task.get("expected_results", {})
if not expected_results:
return len(steps) > 0
# 简化:检查关键工具是否被调用且成功
for required_tool in expected_results.get("required_tools", []):
found = any(s["tool"] == required_tool and not s["is_error"] for s in steps)
if not found:
return False
return True
def _count_redundant_steps(self, steps: list) -> int:
"""计算冗余步骤数(重复调用同一工具且参数相同)"""
redundant = 0
seen = set()
for step in steps:
key = (step["tool"], json.dumps(step["args"], sort_keys=True))
if key in seen:
redundant += 1
seen.add(key)
return redundant
OSWorld 与 VisualWebArena:多模态 Agent 基准
OSWorld:真实桌面环境中的 Agent 评估
OSWorld [3] 是 2024 年提出的首个在真实操作系统环境中评估多模态 Agent 的基准。与之前基于模拟环境的基准不同,OSWorld 让 Agent 在真实的 Ubuntu / Windows / macOS 桌面环境中完成任务。
| 特性 | 说明 |
|---|---|
| 环境 | 真实 OS(Ubuntu 22.04、Windows 11、macOS) |
| 任务类型 | 文件操作、应用使用、网页浏览、多应用协作 |
| 交互方式 | 截图 + 可访问性树(Accessibility Tree) |
| 任务数量 | 369 个真实任务 |
| 评估方式 | 基于执行结果的函数验证(非字符串匹配) |
VisualWebArena:网页环境中的多模态 Agent 基准
VisualWebArena [4] 专注于网页环境中的多模态 Agent 评估,要求 Agent 通过视觉理解和操作网页:
| 特性 | 说明 |
|---|---|
| 环境 | 自托管的 Web 应用(电商、论坛、CMS) |
| 任务类型 | 信息检索、内容管理、数据操作 |
| 交互方式 | 网页截图 + DOM 操作 |
| 核心挑战 | 需要理解视觉布局、表单填写、多页面导航 |
多模态 Agent 基准对比
| 基准 | 环境类型 | 交互方式 | 任务数量 | 最佳成功率 |
|---|---|---|---|---|
| OSWorld | 真实桌面 OS | 截图 + 键鼠操作 | 369 | ~12.5% (2024) |
| VisualWebArena | 网页应用 | 截图 + DOM 操作 | 910 | ~14.6% (2024) |
| WebArena | 网页应用 | HTML + DOM | 812 | ~35.9% (2024) |
| τ-bench | 模拟 API | 文本 + 工具调用 | 200+ | ~68% (2024) |
⚠️ 注意:OSWorld 和 VisualWebArena 的最佳成功率远低于纯文本基准,说明多模态 Agent 仍有巨大提升空间。
评估多模态 Agent 的关键指标
@dataclass
class MultimodalEvalMetrics:
"""多模态 Agent 评估指标"""
# 基础指标
task_success_rate: float # 任务完成率
partial_success_rate: float # 部分完成率
# 视觉理解指标
screenshot_understanding_acc: float # 截图理解准确率
element_localization_acc: float # 元素定位准确率
ocr_accuracy: float # OCR 准确率
# 操作指标
action_accuracy: float # 动作选择准确率
coordinate_accuracy: float # 坐标定位准确率(点击任务)
typing_accuracy: float # 输入准确率
# 效率指标
avg_steps: int # 平均步骤数
avg_time_per_task: float # 平均每任务耗时
unnecessary_actions_rate: float # 不必要操作比例
class OSWorldStyleEvaluator:
"""OSWorld 风格的多模态 Agent 评估器"""
def __init__(self, agent_func, environment):
self.agent_func = agent_func
self.env = environment
def evaluate(self, task: dict) -> MultimodalEvalMetrics:
"""评估单个多模态任务"""
steps_data = []
action_correct = 0
action_total = 0
coord_errors = []
typing_errors = []
# 重置环境
self.env.reset(task["initial_state"])
for step_idx in range(task.get("max_steps", 15)):
# 获取当前截图和可访问性信息
screenshot = self.env.get_screenshot()
accessibility_tree = self.env.get_accessibility_tree()
# Agent 决策
agent_action = self.agent_func(
task["instruction"],
screenshot,
accessibility_tree,
steps_data # 之前的历史
)
# 记录步骤
step_info = {
"step": step_idx,
"action_type": agent_action.get("type"),
"action_params": agent_action.get("params", {}),
}
# 评估动作准确性
if step_idx < len(task.get("expected_actions", [])):
expected = task["expected_actions"][step_idx]
action_total += 1
if agent_action["type"] == expected["type"]:
action_correct += 1
# 评估坐标/输入准确性
if expected["type"] == "click":
expected_coord = expected.get("coordinates", (0, 0))
actual_coord = agent_action["params"].get(
"coordinates", (0, 0)
)
error = (
(expected_coord[0] - actual_coord[0]) ** 2
+ (expected_coord[1] - actual_coord[1]) ** 2
) ** 0.5
coord_errors.append(error)
elif expected["type"] == "type":
expected_text = expected.get("text", "")
actual_text = agent_action["params"].get("text", "")
typing_errors.append(
self._edit_distance(expected_text, actual_text)
)
# 执行动作
self.env.execute_action(agent_action)
steps_data.append(step_info)
# 检查是否完成
if self.env.is_task_completed():
break
# 计算最终结果
success = self.env.verify_final_state(task["expected_state"])
return MultimodalEvalMetrics(
task_success_rate=1.0 if success else 0.0,
partial_success_rate=self._partial_score(task, steps_data),
screenshot_understanding_acc=0.0, # 需要额外评估
element_localization_acc=0.0, # 需要额外评估
ocr_accuracy=0.0, # 需要额外评估
action_accuracy=(
action_correct / action_total
if action_total > 0 else 0.0
),
coordinate_accuracy=(
1.0 - min(1.0, sum(coord_errors) / len(coord_errors) / 100)
if coord_errors else 1.0
),
typing_accuracy=(
1.0 - min(1.0, sum(typing_errors) / len(typing_errors) / 10)
if typing_errors else 1.0
),
avg_steps=len(steps_data),
avg_time_per_task=0.0, # 需要实际计时
unnecessary_actions_rate=0.0 # 需要人工标注
)
@staticmethod
def _edit_distance(s1: str, s2: str) -> int:
"""计算编辑距离"""
m, n = len(s1), len(s2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
return dp[m][n]
def _partial_score(self, task: dict, steps: list) -> float:
"""计算部分完成分数"""
expected = task.get("expected_actions", [])
if not expected:
return 0.0
completed = min(len(steps), len(expected))
correct = sum(
1 for i in range(completed)
if steps[i].get("action_type") == expected[i].get("type")
)
return correct / len(expected)
SWE-bench Verified:代码 Agent 的黄金基准
SWE-bench 概述
SWE-bench [5] 是评估代码 Agent 解决真实 GitHub Issue 能力的基准。2024 年推出的 SWE-bench Verified 版本通过人工验证,过滤掉了有问题的测试用例,使评估结果更可靠。
| 版本 | Issue 数量 | 说明 |
|---|---|---|
| SWE-bench Full | 2294 | 全量数据集,部分 Issue 描述不清晰 |
| SWE-bench Lite | 300 | 精选子集,但仍有质量问题 |
| SWE-bench Verified | 500 | 人工验证,每个 Issue 都确认可解决 |
SWE-bench Verified 评估方式
@dataclass
class SWEBenchResult:
"""SWE-bench 评估结果"""
instance_id: str
repo: str
resolved: bool # 是否解决了 Issue
patch_applied: bool # Patch 是否能应用
tests_passed: bool # 测试是否通过
fail_to_pass: list[str] # 从失败变为通过的测试
pass_to_pass: list[str] # 始终通过的测试
fail_to_fail: list[str] # 始终失败的测试
class SWEBenchEvaluator:
"""SWE-bench 风格的评估器"""
def __init__(self, agent_func, docker_env=None):
self.agent_func = agent_func
self.docker_env = docker_env
def evaluate_instance(self, instance: dict) -> SWEBenchResult:
"""评估单个 SWE-bench 实例"""
# 1. 准备环境
repo_path = self._setup_repo(instance)
# 2. 让 Agent 分析问题并生成 Patch
agent_patch = self.agent_func(
problem_statement=instance["problem_statement"],
repo_path=repo_path,
hints_text=instance.get("hints_text", "")
)
# 3. 应用 Patch
patch_applied = self._apply_patch(repo_path, agent_patch)
if not patch_applied:
return SWEBenchResult(
instance_id=instance["instance_id"],
repo=instance["repo"],
resolved=False,
patch_applied=False,
tests_passed=False,
fail_to_pass=[],
pass_to_pass=[],
fail_to_fail=[]
)
# 4. 运行测试
test_results = self._run_tests(
repo_path,
instance.get("test_patch", ""),
instance.get("fail_to_pass", []),
instance.get("pass_to_pass", [])
)
# 5. 判定是否解决
resolved = (
len(test_results["fail_to_pass_resolved"])
== len(instance.get("fail_to_pass", []))
and len(test_results["pass_to_pass_failed"]) == 0
)
return SWEBenchResult(
instance_id=instance["instance_id"],
repo=instance["repo"],
resolved=resolved,
patch_applied=True,
tests_passed=resolved,
fail_to_pass=test_results["fail_to_pass_resolved"],
pass_to_pass=test_results["pass_to_pass_passed"],
fail_to_fail=test_results.get("fail_to_fail", [])
)
def _setup_repo(self, instance: dict) -> str:
"""设置 Git 仓库到指定版本"""
import subprocess
repo_dir = f"/tmp/swebench_{instance['instance_id']}"
# 克隆并 checkout 到基础 commit
subprocess.run(
["git", "clone", instance["repo"], repo_dir],
capture_output=True
)
subprocess.run(
["git", "checkout", instance["base_commit"]],
cwd=repo_dir, capture_output=True
)
return repo_dir
def _apply_patch(self, repo_path: str, patch: str) -> bool:
"""尝试应用 Patch"""
import subprocess
try:
result = subprocess.run(
["git", "apply"],
input=patch.encode(),
cwd=repo_path,
capture_output=True
)
return result.returncode == 0
except Exception:
return False
def _run_tests(self, repo_path, test_patch, fail_to_pass, pass_to_pass):
"""运行测试并收集结果"""
import subprocess
# 应用测试 Patch
subprocess.run(
["git", "apply"],
input=test_patch.encode(),
cwd=repo_path,
capture_output=True
)
# 运行测试
result = subprocess.run(
["python", "-m", "pytest", "-x", "--tb=short"],
cwd=repo_path,
capture_output=True,
text=True,
timeout=300
)
# 解析测试结果(简化版)
output = result.stdout + result.stderr
return {
"fail_to_pass_resolved": [], # 需要解析 output
"pass_to_pass_passed": [],
"pass_to_pass_failed": [],
"fail_to_fail": []
}
SWE-bench Verified 最新进展(2025—2026)
| 排名 | 方法 | 解决率 | 说明 |
|---|---|---|---|
| OpenHands + CodeAct | ~53% | 2025 年初 | 开源最佳 |
| Devin | ~50% | 2025 年初 | 商业产品 |
| SWE-Agent + GPT-4.1 | ~48% | 2025 年 | Agent 框架 |
| AutoCodeRover | ~45% | 2024 年 | 谱分析 + LLM |
| Amazon Q Developer | ~42% | 2024 年 | Amazon 出品 |
💡 趋势观察:SWE-bench Verified 的解决率在 2025 年已突破 50%,但仍有近半数 Issue 无法自动解决。核心瓶颈在于长上下文理解、多文件修改和复杂调试推理。
完整实战:构建一个可运行的评估管线
⚠️ 诚实说明:旧版这一节用
TraceCollector/AgentAsJudgeEvaluator两个类"演示"了评估系统的完整实现,但它们的evaluate方法依赖真实 LLM API,且配套给出的"实战案例"是手写的模拟轨迹——也就是说,它从未在任何固定数据集上真正跑通过,属于"看似完整、实则无法复现"的凑数写法。
评估系统真正的难点不在于写出
class Evaluator,而在于:有一份固定、可复现的任务集,和一个能对每个任务给出通过/失败判定的可运行 harness。本节不再重复造轮子,而是直接复用本书统一底座reference-agent/中已经过测试的评估模块。
真实可运行的评估 harness
reference-agent/src/reference_agent/evaluation/harness.py 提供三个函数:
load_samples(path)—— 从 JSONL 读取固定任务集;run_case(agent, sample)—— 跑单个任务并判定passed(基于expect_contains关键词命中);evaluate(agent, samples)—— 批量跑,返回total / passed / pass_rate / results的可复现报告。
# reference-agent 中的真实代码(已随本书提交,可通过 pytest 复现)
from reference_agent.evaluation.harness import evaluate
report = evaluate() # 默认用 FakeProvider,离线即可跑通
print(report["pass_rate"]) # 例如 1.0
任务集 reference-agent/data/eval_samples.jsonl 每行一个 JSON 对象,schema 固定:
{"id": "calc_001", "input": "3加5等于几", "expect_contains": ["8"]}
如何本地复现
cd reference-agent
pip install -e .
pytest # 含 evaluation 测试,离线通过
FakeProvider 默认离线返回确定性回答,因此 pass_rate 是可复现的;接真实模型时设 AGENT_REAL=1 并配置 API Key,harness 会自动切换到 OpenAIProvider。
设计要点(比"写个类"更重要)
- 固定任务集 > 临时轨迹:评估必须基于不随时间漂移的任务集,否则无法做回归对比。每次改动 Prompt / 模型后跑同一份
eval_samples.jsonl,看pass_rate是否下降。 - 判定要可执行:
expect_contains是弱判定(关键词命中),生产环境应替换为更强的判定——结构化输出校验、单元测试、或人工标注。 - 报告要可复现:
pass_rate+ 每条passed标志,可直接作为 CI 质量门禁(见 18.7)。
想看"多维度 Agent-as-Judge 评分"如何落到真实代码:第 16 章
examples/dev_team/的reviewer节点用结构化输出做质量判定,同样遵循"判定可执行、报告可复现"的原则。
Agent 专项评估基准全景对比
单一基准无法覆盖 Agent 的全部能力。生产团队通常会按能力域组合多套基准:工具调用看 τ-bench,网页操作看 WebArena/VisualWebArena,代码修改看 SWE-bench,研究任务看 GAIA/HLE,安全看 AgentDojo/InjecAgent/ASB,记忆系统看 LoCoMo/LongMemEval。
按能力域选择评估基准
| 能力域 | 代表基准 | 主要评估什么 | 适合的 Agent 类型 |
|---|---|---|---|
| 工具调用 | τ-bench、ToolBench、API-Bank | 工具选择、参数填充、多轮工具使用 | 工具型 Agent、客服 Agent |
| 网页操作 | WebArena、VisualWebArena、Mind2Web | 网页导航、DOM/视觉理解、表单操作 | Browser Use / Web Agent |
| 桌面操作 | OSWorld、AndroidWorld | GUI 操作、多应用协作、环境状态验证 | Computer Use Agent |
| 代码任务 | SWE-bench Verified、HumanEval、RepoBench | Issue 修复、代码生成、多文件理解 | Coding Agent |
| 深度研究 | GAIA、HLE、BrowseComp、FRAMES | 多步检索、证据整合、复杂问答 | Deep Research Agent |
| 安全鲁棒性 | AgentDojo、InjecAgent、ASB、PromptBench | 间接提示注入、工具滥用、越权行为 | Web/RAG/Tool Agent |
| 长期记忆 | LoCoMo、LongMemEval、ConvoMem | 长对话记忆、时序推理、用户画像一致性 | Memory Agent、个人助理 |
主流基准横向对比
| 基准 | 领域 | 核心能力 | 最佳表现 | 局限性 |
|---|---|---|---|---|
| τ-bench | 工具使用 | 工具选择与参数填充 | ~68% | 环境模拟,非真实 |
| OSWorld | 桌面操作 | 多应用协作 | ~12.5% | 成功率低,成本高 |
| VisualWebArena | 网页操作 | 视觉理解+DOM操作 | ~14.6% | 仅限网页环境 |
| SWE-bench Verified | 代码修复 | 问题定位+Patch生成 | ~53% | 仅限 Python 项目 |
| WebArena | 网页导航 | 信息检索+操作 | ~35.9% | 无视觉输入版本 |
| GAIA | 通用推理 | 多步推理+工具调用 | ~45% | 任务数量有限 |
| BrowseComp | 浏览研究 | 网页检索+复杂问答 | 快速变化 | 依赖实时网页环境 |
| AgentDojo | Agent 安全 | 工具注入攻防 | 任务相关 | 偏安全专项 |
| InjecAgent | 间接注入 | 工具集成 Agent 的注入攻击 | 任务相关 | 偏攻击评估 |
| LoCoMo | 长期记忆 | 长对话、多跳记忆推理 | 方案差异大 | 主要评估记忆层 |
| LongMemEval | 长期记忆 | 长上下文记忆检索与问答 | 方案差异大 | 与具体记忆架构强相关 |
| AgentBench | 多任务 | 多种 Agent 能力 | ~35% | 评估维度不够细 |
如何组合成生产评估套件?
如果你要评估一个真实 Agent,不建议只报一个 benchmark 分数,而应构建“能力矩阵”:
基础能力:工具调用准确率、格式合法率、任务完成率
+
场景能力:Web / Code / Research / Memory 等专项基准
+
安全能力:间接提示注入、越权工具调用、数据泄露测试
+
生产指标:延迟、成本、人工接管率、回归稳定性
例如,一个 Deep Research Agent 的评估套件可以是:
| 评估层 | 指标 |
|---|---|
| 任务结果 | GAIA / BrowseComp 正确率 |
| 过程质量 | 搜索轮数、来源多样性、引用有效率、冲突处理率 |
| 安全性 | 对恶意网页指令的拒绝率、外链访问审批率 |
| 生产性 | 平均耗时、Token 成本、失败重试率、人工接管率 |
这能避免一个常见误区:Benchmark 高分不等于生产可用。Agent 上线前必须同时满足能力、安全、成本和稳定性四个维度。
小结
| 概念 | 说明 |
|---|---|
| Agent-as-Judge | 用 Agent 评估 Agent 的完整执行轨迹,超越 LLM-as-Judge 的单轮评判 |
| τ-bench | 面向工具使用能力的专项基准,关注工具选择和参数填充 |
| WebArena / VisualWebArena | 浏览器与网页操作 Agent 的核心评估基准 |
| OSWorld | 真实桌面环境中的多模态 Agent 评估,成功率仍很低 |
| SWE-bench Verified | 代码 Agent 的黄金基准,人工验证确保评估可靠性 |
| GAIA / BrowseComp | Deep Research Agent 和多步检索推理的重要参考 |
| AgentDojo / InjecAgent / ASB | Agent 安全与间接提示注入评估基准 |
| LoCoMo / LongMemEval | 长期记忆与 Memory Governance 的评估基准 |
| 评估系统 | 轨迹收集 → 逐步审查 → 综合评判 → 报告生成 |
下一节预告:我们将学习 A/B 测试与回归测试自动化,了解如何在 CI/CD 流水线中持续保障 Agent 质量。
参考文献
[1] ZHUGE M, WANG H, LIU J, et al. Agent-as-Judge: Evaluate Agents with Agents for Long Tasks[J]. arXiv preprint arXiv:2410.10934, 2024.
[2] SIYAN Z, YU G, JIAYI P, et al. τ-bench: A Benchmark for Tool-Using LLMs[J]. arXiv preprint arXiv:2406.12045, 2024.
[3] XUE Y, WU D, ZHENG Z, et al. OSWorld: Benchmarking Multimodal Agents for Open-Ended Tasks in Real Computer Environments[C]//NeurIPS. 2024.
[4] KOH J, LO R, JANG J, et al. VisualWebArena: Evaluating Multimodal Agents on Realistic Visual Web Tasks[J]. arXiv preprint arXiv:2401.13649, 2024.
[5] JIMENEZ C E, YANG J, WETZIG A, et al. SWE-bench: Can Language Models Resolve Real-World GitHub Issues?[C]//ICLR. 2024.