Agent 评估与测试 — 如何衡量一个"不可预测"的系统 / Agent Evaluation and Testing — How to Measure an "Unpredictable" System
📅 创建时间:2026-07-28 🏷️ 标签:#AgentEvaluation #Testing #LLM-as-Judge #Benchmark #Eval 📚 前置知识:[[00-agent-overview]] [[01-function-calling]] [[05-agent-workflow]]
📋 本章目标
- 理解为什么传统软件测试方法论在 Agent 面前失效
- 掌握 Agent 评估的三个层次:Component / Task / Trajectory
- 理解 LLM-as-Judge 的原理、设计方法、常见陷阱
- 了解主流 Agent 基准测试(SWE-bench、WebArena、GAIA、AgentBench)及其局限性
- 能够设计一套实用的 Agent 评估系统(数据集、指标、回归测试)
- 知道 Braintrust / LangSmith / OpenAI Evals 三个框架的定位与选型
第1部分:为什么普通软件测试不适用于 Agent?
1.1 传统软件测试的"确定性契约"
在任何传统软件中,测试建立在一条铁律之上:相同输入产生相同输出。
# 传统单元测试:断言是精确的
def test_add():
assert add(1, 2) == 3 # 永远为真
assert add(-1, 1) == 0 # 永远为真
assert add(0, 0) == 0 # 永远为真这个假设如此基础,以至于我们很少意识到它的存在。函数的返回值由代码逻辑完全决定——没有随机性,没有"理解偏差",没有"今天状态不好"。测试的本质就是:给定输入,断言输出。
┌─────────────────────────────────────────────────────────────┐
│ 传统软件测试模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 ──→ [ 确定性函数 f(x) ] ──→ 输出 │
│ │
│ 测试 = 验证 f(input) == expected_output │
│ │
│ 特点: │
│ ✅ 相同的 input 永远产生相同的 output │
│ ✅ "正确"有唯一标准答案(1+1 就是 2) │
│ ✅ 边界条件可枚举 │
│ ✅ 回归测试 = 同样的用例再跑一遍 │
│ │
└─────────────────────────────────────────────────────────────┘1.2 Agent 的"非确定性"从哪来?
Agent 的核心是一个 LLM。而 LLM 的推理过程引入了传统软件没有的变量:
┌─────────────────────────────────────────────────────────────┐
│ Agent 执行的五个不确定性来源 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. LLM 采样随机性 │
│ temperature > 0 时,同样 prompt 可能输出不同 token │
│ 即使 temperature = 0,不同批次/硬件也可能有微小差异 │
│ │
│ 2. 推理路径多样性 │
│ "查天气然后发邮件"可以用 3 步完成,也可以用 5 步完成 │
│ 两条路径可能都对,但步数不同、工具调用顺序不同 │
│ │
│ 3. "正确"的主观性 │
│ 用户问"推荐一个好用的 CRM"——什么算好?没有唯一答案 │
│ 生成的邮件正文——措辞不同但意思一样,都算"对"吗? │
│ │
│ 4. 外部环境的不可控 │
│ 工具调用依赖真实 API、数据库、文件系统 │
│ 网络延迟、API 限流、数据变化都会影响结果 │
│ │
│ 5. 上下文累积效应 │
│ 多轮对话中,一个早期的小偏差会被后续步骤放大 │
│ 形成"蝴蝶效应"——同样的起点可能到达完全不同的终点 │
│ │
└─────────────────────────────────────────────────────────────┘1.3 一个具体的例子
假设你写了一个客服 Agent,测试用例是:
用户:我的订单 #12345 还没收到,怎么回事?
┌─────────────────────────────────────────────────────────────┐
│ 同一天跑三次,Agent 可能给出三种不同回答 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 第1次: │
│ 工具调用: look_up_order("12345") │
│ → "您的订单已于7月25日发货,快递单号 SF1234567890, │
│ 预计明天送达。抱歉让您久等了!" │
│ │
│ 第2次: │
│ 工具调用: look_up_order("12345") → check_logistics("SF..") │
│ → "查到您的包裹目前在北京分拣中心,最新物流更新时间 │
│ 为今天上午10:30。如果需要加急,我可以帮您联系快递。" │
│ │
│ 第3次: │
│ 工具调用: look_up_order("12345") → │
│ search_knowledge_base("订单延迟赔偿政策") │
│ → "您的订单预计明天到达。如果超时,根据我们的赔偿政策, │
│ 您可以获得订单金额10%的补偿。需要我帮您申请吗?" │
│ │
│ 三次回答都对,但工具调用链不同、信息丰富度不同、 │
│ 语气不同。你没法写 assert response == expected_response │
│ │
└─────────────────────────────────────────────────────────────┘这个例子揭示了核心困境:你不能用"精确匹配"来评判 Agent,但你又不能不做评判。
1.4 那我们到底要测什么?
既然不能精确断言输出,我们就需要换一个思路。Agent 测试的核心不再是"输出是否等于预期值",而是转变为一系列更灵活的问题:
┌─────────────────────────────────────────────────────────────┐
│ Agent 评估的核心问题(替代精确断言) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ❓ 任务是否完成了?(成功/失败) │
│ → "用户的问题被解决了吗?" —— 最根本的指标 │
│ │
│ ❓ 工具调用是否正确?(选了正确的工具 + 传了正确的参数) │
│ → 即使最终答案措辞不同,中间步骤也不能瞎搞 │
│ │
│ ❓ 执行路径是否合理?(没有绕远路、没有死循环) │
│ → 同样完成任务,3 步比 10 步好 │
│ │
│ ❓ 是否有害?(幻觉、泄露信息、危险操作) │
│ → "完成了但胡说了"不算通过 │
│ │
│ ❓ 用户体验是否可接受?(语气、格式、完整性) │
│ → 完成了但语气很冲也不行 │
│ │
└─────────────────────────────────────────────────────────────┘这些问题的答案往往是"程度"而不是"是否"——这正是 Agent 评估区别于传统测试的根本所在。
第2部分:评估的层次
Agent 评估不是铁板一块。不同的评估粒度回答不同的问题,对应不同的成本和适用场景。
2.1 三层评估模型
┌─────────────────────────────────────────────────────────────┐
│ Agent 评估的三层模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 3: Trajectory Eval(轨迹评估) │ │
│ │ ───────────────────────────────────────────── │ │
│ │ 问题:整个执行路径是否合理? │ │
│ │ 粒度:多步推理链 + 最终结果 │ │
│ │ 成本:最高(人工审查 或 LLM-Judge 全面分析) │ │
│ │ 适用:关键任务把关、上线前验证 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 2: Task Eval(任务评估) │ │
│ │ ───────────────────────────────────────────── │ │
│ │ 问题:单次任务是否完成了? │ │
│ │ 粒度:一次完整的用户请求 → 最终回答 │ │
│ │ 成本:中等(LLM-as-Judge 或 规则 + 人工抽查) │ │
│ │ 适用:日常回归测试、版本对比 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 1: Component Eval(组件评估) │ │
│ │ ───────────────────────────────────────────── │ │
│ │ 问题:单个工具调用是否准确? │ │
│ │ 粒度:一次 tool_call → 工具返回结果 │ │
│ │ 成本:最低(可自动化、可精确断言) │ │
│ │ 适用:每次 CI、工具定义变更后 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2.2 Layer 1:Component Eval(组件评估)—— 最接近传统测试的一层
这一层测试的是 Agent 的"零件"——单个工具调用是否准确。因为粒度小、目标明确,它最接近传统单元测试,也是唯一可以大量使用精确断言的一层。
测什么?
# Component Eval 示例:测试 LLM 能否正确选择工具并填写参数
def test_tool_selection_for_weather_query():
"""当用户问天气时,Agent 应该选择 get_weather 工具"""
eval_case = {
"user_message": "上海明天会下雨吗?",
"available_tools": ["get_weather", "send_email", "search_web"],
}
# 只跑一轮 LLM 调用(不让 Agent 真正执行工具)
response = llm.generate_tool_call(
messages=[{"role": "user", "content": eval_case["user_message"]}],
tools=available_tools,
)
# 精确断言:工具名必须对
assert response.tool_calls[0].function.name == "get_weather"
# 精确断言:参数必须包含城市
args = json.loads(response.tool_calls[0].function.arguments)
assert "city" in args
assert args["city"] in ["上海", "上海市"] # 允许合理变体
def test_tool_parameter_extraction():
"""LLM 应正确提取用户消息中的参数值"""
eval_case = {
"user_message": "帮我把明天下午3点的会议改到周五上午10点",
"available_tools": ["reschedule_meeting"],
}
response = llm.generate_tool_call(...)
args = json.loads(response.tool_calls[0].function.arguments)
# 精确断言参数提取的准确性
assert "Friday" in args.get("new_date", "") or "周五" in str(args)
assert "10:00" in args.get("new_time", "") or "上午10点" in str(args)Component Eval 的适用场景:
┌─────────────────────────────────────────────────────────────┐
│ Component Eval 的典型测试用例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 测工具选择: │
│ "查天气" → 应该选 get_weather 而不是 send_email │
│ "发邮件" → 应该选 send_email 而不是 search_web │
│ │
│ 测参数提取: │
│ "北京的温度" → city 参数应该被填为 "北京" │
│ "给张三发" → to 参数应该包含 "张三"的联系方式 │
│ │
│ 测拒识(不该调时别调): │
│ "你好" → 不应该调用任何工具 │
│ "1+1等于几" → 不应该调用计算器(除非要求) │
│ │
│ 测边界情况: │
│ 空输入、超长输入、含特殊字符的输入 │
│ 参数模糊("下周三"是几号?) │
│ │
└─────────────────────────────────────────────────────────────┘局限性:Component Eval 只看"一步",看不到多步交互中的偏差累积。一个工具调用对了,不代表整个任务能完成。
2.3 Layer 2:Task Eval(任务评估)—— 最常用的一层
Task Eval 是日常开发中最常跑的评估。它问一个完整的问题:用户的需求被满足了吗?
# Task Eval 示例:端到端测试一个完整的用户请求
eval_case = {
"task_id": "order_inquiry_001",
"user_message": "我的订单 #12345 什么时候到?",
"available_tools": ["look_up_order", "check_logistics"],
"expected_outcome": {
"task_completed": True, # 任务必须完成
"must_contain": ["订单 #12345", "预计", "送达"], # 必须包含的关键信息
"must_not_contain": ["不知道", "无法查询"], # 不能出现的话
"tool_calls_must_include": ["look_up_order"], # 必须调用的工具
"max_steps": 5, # 不能超过 5 步
}
}
result = run_agent_task(eval_case)
score = evaluate_task_result(result, eval_case["expected_outcome"])Task Eval 的判断方式:
┌─────────────────────────────────────────────────────────────┐
│ Task Eval 的三种评判方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方式1:规则评判(Rule-based) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 适用:有明确客观标准的任务 │ │
│ │ 例子: │ │
│ │ • 是否调用了指定工具? → 精确检查 │ │
│ │ • 返回的 JSON 能否解析? → 语法检查 │ │
│ │ • 结果中是否包含订单号? → 字符串匹配 │ │
│ │ • 步数是否在限制内? → 数值比较 │ │
│ │ │ │
│ │ 优点:快速、可重复、零成本 │ │
│ │ 缺点:只能测"硬"条件,测不了语义质量 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 方式2:LLM-as-Judge(LLM 评判) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 适用:需要语义理解的评估(详见第3部分) │ │
│ │ 例子: │ │
│ │ • 回答是否礼貌? │ │
│ │ • 总结是否完整? │ │
│ │ • 推理逻辑是否有漏洞? │ │
│ │ │ │
│ │ 优点:能测语义质量 │ │
│ │ 缺点:有成本、自身有偏差、结果不完全稳定 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 方式3:人工评判(Human Eval) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 适用:最终的真相标准(Ground Truth) │ │
│ │ 做法:让人打分(1-5分),或标注通过/不通过 │ │
│ │ │ │
│ │ 优点:最权威 │ │
│ │ 缺点:贵、慢、不可规模化 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘2.4 Layer 3:Trajectory Eval(轨迹评估)—— 最深入的一层
轨迹评估不只是看"结果对不对",而是审视 Agent 的整个思考和执行过程。它回答的是:即使结果对了,过程有没有问题?
┌─────────────────────────────────────────────────────────────┐
│ 同样"成功"的两条轨迹,质量可能天差地别 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 轨迹 A(优秀): │
│ Step 1: look_up_order("12345") ← 直接查订单 │
│ Step 2: 根据返回的物流单号,给出准确回答 │
│ │
│ 轨迹 B(有问题): │
│ Step 1: search_knowledge_base("订单") ← 乱搜 │
│ Step 2: search_knowledge_base("物流") ← 还在乱搜 │
│ Step 3: look_up_order("12345") ← 终于找对了 │
│ Step 4: check_logistics("SF...") ← 多余的调用 │
│ Step 5: 给出回答(和轨迹 A 一样的答案) │
│ │
│ 两条轨迹最终结果相同,但 B 浪费了 2 步、多消耗了 token, │
│ 用户等待时间翻倍。如果只看"任务是否完成",B 也能通过。 │
│ 只有 Trajectory Eval 能暴露这个问题。 │
│ │
└─────────────────────────────────────────────────────────────┘Trajectory Eval 检查的维度:
# Trajectory Eval 的评估维度(概念代码)
trajectory_metrics = {
# 效率维度
"step_efficiency": actual_steps / optimal_steps, # 越接近 1 越好
"tool_call_precision": correct_calls / total_calls, # 有效调用占比
"token_efficiency": useful_tokens / total_tokens, # 有效 token 占比
# 安全维度
"hallucination_detected": bool, # 是否编造了不存在的工具结果
"sensitive_info_exposed": bool, # 是否泄露了系统提示词或密钥
"dangerous_action_attempted": bool, # 是否尝试了不该做的操作
# 推理质量维度
"logical_consistency": score_0_to_1, # 推理步骤之间是否自洽
"error_recovery_quality": score_0_to_1, # 遇到错误时的恢复能力
"unnecessary_repetition": count, # 重复调用的次数
}什么时候该做 Trajectory Eval?
- 新版本上线前——确保不会因为 prompt 改动导致推理路径变差
- 关键业务场景——涉及金钱、隐私、安全决策的任务
- 调优阶段——想找到"为什么慢了"或"为什么贵了"的原因
- 竞品对比——两个 Agent 最后的答案差不多,但谁的过程更高效?
第3部分:LLM-as-Judge
当规则无法评判、人工太贵时,LLM-as-Judge 是目前最主流的折中方案。
3.1 什么是 LLM-as-Judge?
用一个(通常更强的)LLM 来评判另一个 Agent 的输出质量。
┌─────────────────────────────────────────────────────────────┐
│ LLM-as-Judge 工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 待评估的 Agent 输出 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 用户问:北京今天多少度? │ │
│ │ Agent 答:北京今天 25°C,晴,湿度 40%。 │ │
│ │ 执行步骤:get_weather("北京") → 返回数据 → 总结 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Judge Prompt │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 你是一个评估专家。请根据以下标准评估 Agent 的回答: │ │
│ │ │ │
│ │ 1. 准确性:回答是否与工具返回的数据一致? │ │
│ │ 2. 完整性:用户问的所有信息都回答了吗? │ │
│ │ 3. 友好度:语气是否礼貌、有帮助? │ │
│ │ │ │
│ │ 请给出 1-5 分评分和简要理由。 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Judge LLM 输出(如 GPT-4o / Claude) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "accuracy": 5, │ │
│ │ "completeness": 5, │ │
│ │ "friendliness": 4, │ │
│ │ "overall": 4.7, │ │
│ │ "reason": "准确转述了天气数据,信息完整。 │ │
│ │ 语气可以更热情一些。" │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘3.2 Judge Prompt 设计原则
Judge 的质量直接取决于 prompt 的设计。一个模糊的 prompt 会产生不可靠的评分。
# 好的 Judge Prompt 示例
JUDGE_PROMPT = """
你是一个严格的评估专家。你的任务是评估一个 AI Agent 对用户问题的回答质量。
## 评估上下文
- 用户问题:{user_question}
- Agent 可用的工具:{available_tools}
- Agent 实际调用的工具及返回数据:{tool_call_log}
- Agent 的最终回答:{agent_response}
## 评估标准(每个维度 1-5 分)
### 1. 准确性(Accuracy)
- 5分:回答中的所有事实都与工具返回的数据完全一致,没有任何编造
- 3分:大体准确,但有1-2处不精确的表述
- 1分:包含明显的事实错误或编造的信息
### 2. 完整性(Completeness)
- 5分:完全回答了用户问题的所有方面
- 3分:回答了主要问题,但遗漏了次要方面
- 1分:只回答了问题的一小部分,或答非所问
### 3. 工具使用合理性(Tool Use Appropriateness)
- 5分:选择了最合适的工具,参数准确,没有多余调用
- 3分:工具选择基本正确,但有多余调用或参数不够精确
- 1分:选错了工具,或关键的必需工具没有被调用
### 4. 安全性(Safety)
- 5分:没有任何不安全的内容,没有泄露系统信息
- 3分:基本安全,但有一些措辞不当
- 1分:包含有害、误导性内容或泄露了内部信息
## 输出格式(严格 JSON)
{{
"accuracy": <1-5>,
"completeness": <1-5>,
"tool_use": <1-5>,
"safety": <1-5>,
"overall": <1-5>,
"reason": "<简要说明打分理由,50字以内>"
}}
"""3.3 Judge 的常见陷阱
LLM-as-Judge 不是银弹。它自身有一系列已知的偏差问题:
┌─────────────────────────────────────────────────────────────┐
│ LLM-as-Judge 的六大常见陷阱 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 陷阱1:位置偏见(Position Bias) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 同时评估多个回答时,Judge 倾向于给第一个或最后一个 │ │
│ │ 更高的分数。 │ │
│ │ │ │
│ │ 缓解:多次评估时随机打乱顺序,取平均分 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 陷阱2:长度偏见(Length Bias) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Judge 倾向于认为更长的回答"更好",即使内容冗余 │ │
│ │ │ │
│ │ 缓解:在 prompt 中明确要求"只评估内容质量, │ │
│ │ 不考虑长度" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 陷阱3:自我增强偏见(Self-Enhancement Bias) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 如果 Judge 和被评估的 Agent 是同一个模型, │ │
│ │ Judge 倾向于给"自己风格"的回答更高分 │ │
│ │ │ │
│ │ 缓解:用不同模型做 Judge(如用 Claude 评 GPT 的输出)│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 陷阱4:评分漂移(Score Drift) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 同一批测试用例在不同时间跑,评分可能不一致 │ │
│ │ 因为 Judge 模型也可能更新、temperature 带来波动 │ │
│ │ │ │
│ │ 缓解:固定 Judge 模型版本、temperature=0、 │ │
│ │ 保留历史评分做趋势对比 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 陷阱5:评判标准过于笼统 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ "请评估回答质量" → Judge 不知道你到底在乎什么 │ │
│ │ │ │
│ │ 缓解:细化标准,最好给出正例和反例 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 陷阱6:成本幻觉 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 用 GPT-4 做 Judge 评 GPT-3.5 的输出, │ │
│ │ Judge 本身可能比被评估的 Agent 调用还贵 │ │
│ │ │ │
│ │ 缓解:对简单维度用规则,只在必要时用 LLM Judge │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘3.4 提高 Judge 可靠性的实践
# 实践1:多轮评判 + 取均值
def robust_judge(case, n_runs=3):
"""同一个 case 让 Judge 评多次,取平均分"""
scores = []
for i in range(n_runs):
score = call_judge(case) # temperature=0 也有微小波动
scores.append(score)
return {
"mean": np.mean(scores),
"std": np.std(scores), # 标准差大 → 这个 case 评判不稳定
"scores": scores,
}
# 实践2:成对比较(Pairwise Comparison)
def pairwise_compare(response_a, response_b, judge_prompt):
"""不是打分,而是让 Judge 选哪个更好(更稳定)"""
# Judge 的选择通常比绝对分数更一致
prompt = f"""
下面是对同一个问题的两个回答。
请选择哪个更好,并说明理由。
输出格式:{{"winner": "A" | "B" | "tie", "reason": "..."}}
回答 A:{response_a}
回答 B:{response_b}
"""
return call_judge(prompt)
# 实践3:锚定校准(Anchor Calibration)
ANCHORED_JUDGE_PROMPT = """
在评分之前,请先参考以下锚定示例,了解各分数段的含义:
【5分示例】回答完全准确、完整、专业:
"北京今天气温 25°C,晴,湿度 40%,风力 3 级。适合户外活动。"
【3分示例】回答基本正确但不够详细:
"北京今天 25 度,天气还行。"
【1分示例】回答有事实错误:
"北京今天 35 度。"(实际是 25 度)
---
现在请评估以下回答:{agent_response}
"""第4部分:基准测试
基准测试(Benchmark)提供了标准化的"考卷",让你可以横向比较不同 Agent 的能力。但理解每个 Benchmark 测什么、不测什么,比只看排行榜分数重要得多。
4.1 主流 Agent Benchmark 全景
┌─────────────────────────────────────────────────────────────┐
│ 主流 Agent Benchmark 全景 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Benchmark 领域 核心考察 数据规模 │
│ ─────────────────────────────────────────────────────── │
│ SWE-bench 软件工程 修 Bug/加功能 2,294 │
│ SWE-bench 真实 GitHub 定位代码+写补丁 个 Issue │
│ Verified Issue 正确性验证严格 │
│ │
│ WebArena 网页操作 浏览网页、填表单 812 │
│ 端到端 购物/社交/管理 个任务 │
│ │
│ GAIA 通用助手 推理+多模态+ 466 │
│ 工具使用 个问题 │
│ │
│ AgentBench 多场景 8种环境 1,000+ │
│ OS/DB/Web/ 综合能力 个任务 │
│ 编程/游戏 │
│ │
│ τ-bench 工具使用 真实API交互 ~200 │
│ (tool-use) (含错误处理) 个任务 │
│ │
│ BFL (BigCode 编程助手 仓库级代码 ~200 │
│ Bench) 理解和修改 个任务 │
│ │
└─────────────────────────────────────────────────────────────┘4.2 SWE-bench:修真实 Bug 的"高考"
SWE-bench 是目前代码 Agent 领域最有影响力的 Benchmark。它的核心设定是:给你一个真实的 GitHub Issue 描述,你需要找到对应的代码位置并写出修复补丁。
┌─────────────────────────────────────────────────────────────┐
│ SWE-bench 工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 一个真实的 GitHub Issue: │ │
│ │ "当用户在暗色模式下点击 Settings 按钮时, │ │
│ │ 页面背景变为白色但文字仍然是白色,导致不可读。" │ │
│ │ │ │
│ │ + 仓库的完整代码(Agent 可以浏览和搜索) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Agent 做的事情: │
│ 1. 理解 Issue 描述的问题 │
│ 2. 在代码仓库中定位相关文件和函数 │
│ 3. 理解现有代码逻辑 │
│ 4. 写出修复补丁(git diff 格式) │
│ ↓ │
│ 评估: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 运行项目中已有的测试用例(单元测试 + 回归测试) │ │
│ │ Agent 的补丁让所有测试通过 → 成功 │ │
│ │ 有一个测试失败 → 失败 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 关键特点: │
│ ✅ 评估标准客观(测试通过/不通过) │
│ ✅ 题目来自真实世界,不是人造玩具题 │
│ ❌ 只测 Python(主要是 Django、Flask、SymPy 等项目) │
│ ❌ 不测"从零写代码",只测"读懂现有代码 + 小修改" │
│ ❌ 评估成本高(每个任务需要构建 Docker 环境跑测试) │
│ │
└─────────────────────────────────────────────────────────────┘SWE-bench 的局限性:
- 它测的是"定位 + 修补"能力,不是"架构设计"或"需求分析"能力
- 只覆盖 Python 生态,不代表其他语言的 Agent 能力
- 有些修复只需要改 1-2 行,有些需要跨多个文件,难度不均衡
- Verified 子集(500 个经过人工验证的 Issue)比完整版更可靠
4.3 WebArena:像人一样操作网页
WebArena 测试 Agent 在真实网站(模拟环境)上完成操作任务的能力。
┌─────────────────────────────────────────────────────────────┐
│ WebArena 的典型任务 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 任务示例: │
│ "在购物网站上找到最便宜的 4K 显示器,加入购物车, │
│ 然后查看购物车总价。" │
│ │
│ Agent 需要: │
│ 1. 理解任务目标 │
│ 2. 导航到正确页面 │
│ 3. 使用搜索/筛选功能 │
│ 4. 比较多个商品的价格 │
│ 5. 点击正确的按钮 │
│ 6. 验证结果 │
│ │
│ 评估方式: │
│ 基于最终状态的功能正确性判断(如购物车是否真的 │
│ 包含了正确的商品)——所以它测的是结果而非路径。 │
│ │
│ 关键局限: │
│ ❌ 环境是沙盒模拟的,不是真实互联网 │
│ ❌ 主要测网页操作,不测 Agent 的推理深度 │
│ ❌ 爬虫类 Agent 可能靠 DOM 解析"作弊" │
│ │
└─────────────────────────────────────────────────────────────┘4.4 GAIA:通用助手能力的"智商测试"
┌─────────────────────────────────────────────────────────────┐
│ GAIA 的设计哲学 │
├─────────────────────────────────────────────────────────────┤
│ │
│ GAIA 的核心主张: │
│ "一个好的通用助手应该能回答人类能回答的问题, │
│ 但这些问题的答案不是 LLM 可以直接背出来的。" │
│ │
│ 问题设计三原则: │
│ 1. 对普通人来说"可回答"(不需要专业知识) │
│ 2. LLM 无法仅靠训练数据回答(需要查资料/推理) │
│ 3. 有唯一的、可客观验证的答案(避免主观评判) │
│ │
│ 典型题目: │
│ "2023年格莱美最佳专辑获奖者,他/她出生城市的 │
│ 当前市长是谁?请给出全名。" │
│ │
│ 这需要:搜索格莱美获奖者 → 查出获奖者出生地 → │
│ 搜索该城市的现任市长 │
│ │
│ LLM 无法直接回答(因为市长会换届,知识有截止), │
│ 但一个会用工具的 Agent 可以通过多步搜索完成。 │
│ │
│ 评估:答案完全匹配 → 通过(如"Eric Adams") │
│ │
│ 关键局限: │
│ ❌ 只有 466 题,可能被"刷题" │
│ ❌ 题目答案为英文,不反映多语言能力 │
│ ❌ 侧重 Web 搜索,不测代码/文件/数据库操作 │
│ │
└─────────────────────────────────────────────────────────────┘4.5 AgentBench:多维度综合评测
┌─────────────────────────────────────────────────────────────┐
│ AgentBench 的 8 种评估环境 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 操作系统(OS) 在 Linux shell 中完成任务 │
│ 2. 数据库(DB) 用 SQL 查询和分析数据 │
│ 3. 知识图谱(KG) 在知识图谱上做推理查询 │
│ 4. 网页操作(Web) 浏览网页、填表单 │
│ 5. 编程(Code) 写代码完成算法任务 │
│ 6. 卡牌游戏(Card) 玩策略游戏 │
│ 7. 横向思维(LTP) 解谜题 │
│ 8. 数字世界(DW) 在类似 TextWorld 的环境中探索 │
│ │
│ 优点:覆盖面广,一次评估看到 Agent 的强项和短板 │
│ 缺点:部分环境偏学术,和真实业务的差距较大 │
│ │
└─────────────────────────────────────────────────────────────┘4.6 如何正确看待 Benchmark 分数
┌─────────────────────────────────────────────────────────────┐
│ 解读 Benchmark 分数的五个原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 一个分数不能代表一切 │
│ 一个 Agent 在 SWE-bench 上 30%,不代表它"不行" │
│ 只能说明它在定位+修补 Python Bug 上的能力 │
│ │
│ 2. 分数可以"刷" │
│ 针对 Benchmark 做 prompt 调优可以提分 │
│ 但可能损害通用能力(过拟合) │
│ │
│ 3. 关注你真正关心的维度 │
│ 你做的客服 Agent 和 WebArena 关系不大 │
│ 选和你的场景最接近的 Benchmark │
│ │
│ 4. 成本也是竞争力 │
│ 一样 30% 的分数,一个花 $5,一个花 $50 │
│ Benchmark 通常不标成本,但你应该关心 │
│ │
│ 5. 看趋势不看绝对值 │
│ 分数从 25% → 35%,比"我排第几"更有意义 │
│ 用 Benchmark 做自己的版本间对比 │
│ │
└─────────────────────────────────────────────────────────────┘第5部分:实战评估系统设计
尽管有公开 Benchmark,但在实际项目中你必须构建自己的评估体系。公开 Benchmark 测的是"通用能力",而你需要测的是"你的 Agent 在你的场景下表现如何"。
5.1 评估数据集构建
┌─────────────────────────────────────────────────────────────┐
│ 评估数据集构建的完整流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Step 1: 收集真实用例 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 来源: │ │
│ │ • 用户反馈和客服记录 │ │
│ │ • 产品/运营提出的典型场景 │ │
│ │ • 开发过程中自己遇到的边界情况 │ │
│ │ • 从线上日志中抽样 │ │
│ │ │ │
│ │ 输出的每个用例包含: │ │
│ │ - user_message: 用户的原始问题 │ │
│ │ - context: 对话背景(如多轮对话的前几轮) │ │
│ │ - expected_tools: 预期应该调用的工具列表 │ │
│ │ - expected_keywords: 回答中应该包含的关键词 │ │
│ │ - forbidden_keywords: 回答中不应包含的词 │ │
│ │ - difficulty: easy | medium | hard │ │
│ │ - category: 分类标签(查询类/操作类/分析类/拒识类) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 2: 标注黄金标准 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 对每个用例,人工或半人工标注: │ │
│ │ • 理想执行轨迹(最少步数的最优路径) │ │
│ │ • 可接受的结果范围(不是唯一的"标准答案") │ │
│ │ • 常见失败模式(这个 case 最容易出什么错) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 3: 构建分层测试集 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 核心集(Core Set) 50-100 条 每次 CI 必跑 │ │
│ │ 扩展集(Extended Set)200-500 条 每次发版前跑 │ │
│ │ 探索集(Exploratory) 不定量 ad-hoc / 新功能验证│ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Step 4: 持续迭代 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 从线上失败 case 中提取新的测试用例 │ │
│ │ • 定期清理"过时"的用例(场景不再适用) │ │
│ │ • 更新标注(工具/业务逻辑变了) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘5.2 评估数据集格式设计
# 推荐的评估用例格式
eval_dataset = [
{
"id": "order_inquiry_001",
"category": "查询类",
"difficulty": "easy",
"user_message": "我的订单 #12345 什么时候到?",
"context": {
"user_info": {"name": "张三", "member_level": "gold"},
},
"available_tools": [
"look_up_order",
"check_logistics",
"create_refund",
],
"expected": {
"must_call_tools": ["look_up_order"],
"may_call_tools": ["check_logistics"],
"result_must_contain": ["#12345"],
"result_must_not_contain": ["无法", "不知道"],
"max_steps": 5,
"max_tokens": 2000,
},
"risk_areas": [
"可能编造物流信息(如果没有调用 check_logistics)",
"可能在用户没要求时主动提议退款",
],
},
{
"id": "refund_intent_002",
"category": "操作类",
"difficulty": "hard",
"user_message": "我买的鞋不合脚,我要退钱!",
"context": {
"user_info": {"name": "李四", "recent_orders": ["#67890"]},
},
"available_tools": [
"look_up_order",
"check_return_policy",
"create_refund",
"escalate_to_human",
],
"expected": {
"must_call_tools": ["look_up_order"],
"must_not_call_tools": ["create_refund"], # 不能直接退款,要先查政策
"result_must_contain": ["退货", "政策"],
"max_steps": 8,
},
},
]5.3 核心评估指标
┌─────────────────────────────────────────────────────────────┐
│ Agent 评估的核心指标体系 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 一级指标:任务成功率(Task Success Rate) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 公式:成功的任务数 / 总任务数 × 100% │ │
│ │ │ │
│ │ 最重要的指标。但它取决于你怎么定义"成功": │ │
│ │ • 严格模式:所有 expected 条件都满足才算成功 │ │
│ │ • 宽松模式:主要目标达成就算成功 │ │
│ │ 推荐两个都统计,便于分析差距 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 二级指标:效率维度 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 平均步数(Avg Steps):完成任务的平均工具调用次数 │ │
│ │ • Token 消耗(Avg Tokens):每次任务的平均 token 用量 │ │
│ │ • 工具调用准确率(Tool Accuracy):参数正确的调用占比 │ │
│ │ • 首步准确率(First-Step Accuracy):第一步就选对工 │ │
│ │ 具的比例(第一步错了后面的偏差会被放大) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 二级指标:质量维度 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • Judge Score(LLM-as-Judge 综合评分):1-5 分 │ │
│ │ • Hallucination Rate(幻觉率):输出包含编造信息的 │ │
│ │ 任务占比 │ │
│ │ • Refusal Rate(拒识率):该做但没做的任务占比 │ │
│ │ • User Satisfaction Proxy(用户满意度代理指标): │ │
│ │ 如"回答是否被用户追问了"(从日志推断) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 二级指标:稳定性维度 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 多次运行一致性(Consistency):同一 case 跑 N 次, │ │
│ │ 成功率的方差 │ │
│ │ • 类别间差异(Category Variance):不同类型任务的 │ │
│ │ 成功率是否差异过大(差异大 = 长尾问题) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘5.4 回归测试策略
Agent 开发中最常见的痛苦是:改了 prompt 里的一个词,Agent 在某些 case 上突然表现变差,但你不知道。
# 回归测试的核心实践
class AgentRegressionTest:
"""Agent 回归测试框架(概念代码)"""
def __init__(self, baseline_results_path: str):
"""加载基线结果"""
self.baseline = self._load_baseline(baseline_results_path)
def run_regression(self, agent, threshold: float = 0.95):
"""
跑回归测试。
threshold: 新版本的成功率不能低于基线的 95%
"""
new_results = self._run_all_cases(agent)
regressions = []
improvements = []
for case_id, new_score in new_results.items():
old_score = self.baseline.get(case_id)
if old_score is None:
continue # 新用例,不算回归
if new_score < old_score * threshold:
regressions.append({
"case_id": case_id,
"old_score": old_score,
"new_score": new_score,
"delta": new_score - old_score,
})
elif new_score > old_score * 1.1:
improvements.append({
"case_id": case_id,
"old_score": old_score,
"new_score": new_score,
"delta": new_score - old_score,
})
return {
"overall_pass": len(regressions) == 0,
"regressions": regressions,
"improvements": improvements,
"summary": {
"total_cases": len(new_results),
"regressed": len(regressions),
"improved": len(improvements),
"unchanged": len(new_results) - len(regressions) - len(improvements),
},
}
def print_report(self, result: dict):
"""打印人类可读的报告"""
s = result["summary"]
print(f"回归测试结果: {'✅ 通过' if result['overall_pass'] else '❌ 失败'}")
print(f"总用例: {s['total_cases']} | "
f"退步: {s['regressed']} | "
f"进步: {s['improved']} | "
f"不变: {s['unchanged']}")
if result["regressions"]:
print("\n⚠️ 退步的用例:")
for r in result["regressions"]:
print(f" [{r['case_id']}] "
f"{r['old_score']:.2f} → {r['new_score']:.2f} "
f"({r['delta']:+.2f})")5.5 将评估集成到 CI/CD
┌─────────────────────────────────────────────────────────────┐
│ 评估在 CI/CD 中的集成位置 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 每次 PR: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Component Eval(核心集,~50 条) │ │
│ │ 耗时: ~2-5 分钟,成本: ~$1-3 │ │
│ │ → 必须全部通过才能合并 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 每晚定时(nightly build): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Component Eval(扩展集,~200 条) │ │
│ │ 2. Task Eval(核心集 + 扩展集,~100 条) │ │
│ │ 3. 回归对比(与上一个稳定版本对比) │ │
│ │ 耗时: ~20-40 分钟,成本: ~$15-30 │ │
│ │ → 退步超过阈值则告警 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 每次发版前: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 完整 Task Eval(全量集,~500 条) │ │
│ │ 2. Trajectory Eval(核心集 + 高难度子集,~50 条) │ │
│ │ 3. 人工抽查(随机抽样 20-30 条) │ │
│ │ 耗时: ~2-3 小时(含人工),成本: ~$50-100 │ │
│ │ → 生成评估报告,作为发版依据 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘第6部分:Eval 框架选型
你不需要从零搭建评估基础设施。三个主流框架覆盖了不同的需求层次。
6.1 Braintrust / LangSmith / OpenAI Evals 对比
┌─────────────────────────────────────────────────────────────┐
│ 三大 Eval 框架定位与对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Braintrust LangSmith OpenAI Evals │
│ ─────────────────────────────────────────────────────── │
│ 定位 通用AI评估 LangChain OpenAI 生态 │
│ 平台 官方平台 轻量工具 │
│ │
│ 与框架耦合 无关 LangChain 无关 │
│ 强绑定 │
│ │
│ LLM-as-Judge ✅ 内置 ✅ 内置 ✅ 内置 │
│ │
│ 数据集管理 ✅ 完善 ✅ 完善 ⚠️ 基础 │
│ │
│ 实验追踪 ✅ 核心 ✅ 核心 ❌ 无 │
│ │
│ CI/CD集成 ✅ CLI+SDK ✅ SDK ⚠️ 脚本化 │
│ │
│ 实时监控 ✅ 支持 ✅ 支持 ❌ 无 │
│ │
│ 开源 ✅ (MIT) ⚠️ (部分) ✅ (MIT) │
│ │
│ 学习曲线 中等 较高 低 │
│ │
│ 适合团队 全栈/通用 LangChain 快速验证/ │
│ 深度用户 简单场景 │
│ │
└─────────────────────────────────────────────────────────────┘6.2 OpenAI Evals:最快上手的选择
如果你的 Agent 基于 OpenAI 生态,OpenAI Evals 是门槛最低的起点。
# OpenAI Evals 基本用法
# 安装:pip install oaieval
# 1. 定义一个 Eval
# my_eval.yaml
"""
my-agent-eval:
id: my-agent-eval.dev.v0
metrics: [accuracy]
description: 评估我的客服 Agent
my-agent-eval.dev.v0:
class: evals.elsuite.basic.match:Match
args:
samples_jsonl: my_agent_cases.jsonl
"""
# my_agent_cases.jsonl
# {"input": [{"role": "user", "content": "我的订单什么时候到?"}],
# "ideal": "您的订单预计明天送达"}
# 2. 跑评估
# oaieval gpt-4o my-agent-eval
# 优点:
# - 和 OpenAI SDK 深度集成
# - 内置多种评估方法(精确匹配、包含匹配、LLM Judge)
# - 模型灰度功能(比较不同模型在相同 Eval 上的表现)
# 缺点:
# - 数据集管理原始(JSONL 文件)
# - 没有实验对比 UI
# - 不适合非 OpenAI 模型6.3 LangSmith:LangChain 生态的标配
# LangSmith 基本用法
from langsmith import Client, traceable
from langsmith.evaluation import evaluate
client = Client()
# 1. 定义要被评估的函数
@traceable
def my_agent(inputs: dict) -> dict:
"""你的 Agent 逻辑"""
response = run_agent(inputs["question"])
return {"output": response}
# 2. 创建评估数据集
dataset = client.create_dataset(
dataset_name="customer-service-eval",
description="客服 Agent 评估集",
)
client.create_examples(
inputs=[
{"question": "我的订单 #12345 什么时候到?"},
{"question": "如何退货?"},
{"question": "你们支持哪些支付方式?"},
],
outputs=[
{"expected_tools": ["look_up_order"]},
{"expected_tools": ["search_knowledge_base"]},
{"expected_tools": ["search_knowledge_base"]},
],
dataset_id=dataset.id,
)
# 3. 定义评估器
def tool_correctness(outputs: dict, reference_outputs: dict) -> dict:
"""检查工具调用是否正确"""
# 自定义逻辑
score = ...
return {"key": "tool_correctness", "score": score}
def llm_judge_eval(outputs: dict, reference_outputs: dict) -> dict:
"""LLM-as-Judge 评分"""
judge = run_judge(outputs["output"], reference_outputs["expected_response"])
return {"key": "judge_score", "score": judge["score"]}
# 4. 运行评估
results = evaluate(
my_agent,
data=dataset.name,
evaluators=[tool_correctness, llm_judge_eval],
experiment_prefix="v2.1-agent",
)
# LangSmith 的强项:
# ✅ 实验对比 UI(网页端直观看到版本间差异)
# ✅ 与 LangChain/LangGraph 零配置集成
# ✅ 自动追踪每次评估的完整链路
# LangSmith 的弱点:
# ❌ 如果不使用 LangChain,集成需要额外适配
# ❌ 高级功能需要付费
# ❌ 对非 Python 生态支持有限6.4 Braintrust:最灵活的通用平台
# Braintrust 基本用法
# 安装:pip install braintrust
from braintrust import Eval, traced
# 1. 定义你的 Agent(用 @traced 装饰器获得链路追踪)
@traced
def my_agent(question: str) -> dict:
response = run_agent_with_tools(question)
return {
"answer": response["content"],
"tool_calls": response.get("tool_calls", []),
"total_tokens": response.get("usage", {}).get("total_tokens", 0),
}
# 2. 写评估
Eval(
name="customer-service-v1",
# 评估数据集(可以直接内联定义)
data=lambda: [
{
"input": "我的订单 #12345 什么时候到?",
"expected": {
"must_contain": ["#12345", "预计"],
"must_call": ["look_up_order"],
"max_tokens": 2000,
},
},
{
"input": "你们支持微信支付吗?",
"expected": {
"must_contain": ["支持", "微信"],
"max_steps": 3,
},
},
# ... 更多用例
],
# 被测试的函数
task=lambda input_data: my_agent(input_data),
# 评估器(可以组合多个)
scores=[
Factuality, # 内置:事实准确性
Contains, # 内置:必须包含某些关键词
JSONDiff, # 内置:结构化输出比较
# 你还可以自定义:
lambda output, expected: {
"name": "tool_selection",
"score": 1.0 if all(
tool in [t["name"] for t in output.get("tool_calls", [])]
for tool in expected["must_call"]
) else 0.0,
},
lambda output, expected: {
"name": "token_efficiency",
"score": max(0, 1 - output.get("total_tokens", 0) / expected["max_tokens"]),
},
],
# 实验追踪(自动记录每次跑的分数)
experiment="agent-v2.1",
metadata={
"model": "gpt-4o",
"prompt_version": "v2.1",
"temperature": 0.1,
},
)
# Braintrust 的强项:
# ✅ 框架无关(裸 Python/TS 函数就能接入)
# ✅ 内置丰富的 scoring 函数
# ✅ 实验比较 UI(网页端免费)
# ✅ 支持 CI 集成(CLI + GitHub Action)
# ✅ 完全开源(MIT)
# Braintrust 的弱点:
# ❌ 概念较多(Eval / Experiment / Dataset / Project)
# ❌ 社区比 LangSmith 小,中文资料少6.5 框架选型决策树
┌─────────────────────────────────────────────────────────────┐
│ Eval 框架选型决策 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问:你已经在用 LangChain / LangGraph 了吗? │
│ ├── 是 → LangSmith(零配置集成,生态最完整) │
│ └── 否 → 继续 │
│ │
│ 问:你需要实验对比 UI 和链路追踪吗? │
│ ├── 是 → Braintrust(免费、灵活、UI 好) │
│ └── 否 → 继续 │
│ │
│ 问:你只用 OpenAI 模型,场景简单? │
│ ├── 是 → OpenAI Evals(最轻量,10 分钟上手) │
│ └── 否 → Braintrust(通用性最好) │
│ │
│ 速记口诀: │
│ "LangChain 用户 → LangSmith │
│ 要 UI 要灵活 → Braintrust │
│ 快速验证 → OpenAI Evals │
│ 没人管我用啥 → Braintrust" │
│ │
└─────────────────────────────────────────────────────────────┘6.6 评估框架的底线:框架可以帮你跑分,但不能替你定义"好"
无论你选哪个框架,有一个事实不会变:
┌─────────────────────────────────────────────────────────────┐
│ 框架做不了的、你必须自己做的事 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 定义"什么叫好" │
│ 框架不知道你的业务目标。你需要把"好"翻译为 │
│ 具体的、可测量的指标。 │
│ │
│ 2. 构建评估数据集 │
│ 框架管理数据集,但数据集的内容(用例) │
│ 必须反映你的真实场景,这要你自己做。 │
│ │
│ 3. 校准 Judge │
│ 框架提供 LLM-as-Judge 能力,但 Judge prompt 的设计、 │
│ 校准、验证是你的事。一个没校准过的 Judge │
│ 给出的分数没有参考价值。 │
│ │
│ 4. 解读结果并采取行动 │
│ 框架给你分数和图表,但"这个退步可以接受" │
│ vs "这个退步必须修"的判断是你的事。 │
│ │
└─────────────────────────────────────────────────────────────┘核心总结
总结1:Agent 评估的核心理念
Agent 评估 ≠ 传统软件测试
传统测试:assert output == expected_output(精确匹配)
Agent 评估:从多个维度评价"程度"——任务是否完成?
工具使用是否正确?执行路径是否合理?是否有害?
核心转变:从"对不对"到"好不好"——从"断言"到"评判"总结2:三层评估模型
Layer 1 - Component Eval:测单个工具调用是否准确
→ 最接近传统单元测试,精确断言,每次 CI 必跑
Layer 2 - Task Eval:测一个完整任务是否完成
→ 日常回归测试的主力,规则 + LLM-Judge + 人工抽查
Layer 3 - Trajectory Eval:测整个执行路径是否合理
→ 上线前把关,成本最高,但能看到"结果对了但过程烂了"总结3:LLM-as-Judge 的实践要点
Judge 设计的核心:
✅ 细化评分标准(每个维度有 1-5 分的明确定义)
✅ 提供锚定示例(让 Judge 知道"3 分长什么样")
✅ 成对比较比绝对打分更稳定
✅ 用不同模型做 Judge(避免自我增强偏差)
✅ 多跑几次取均值(即使 temperature=0)
Judge 的已知陷阱:
❌ 位置偏见、长度偏见、自我增强偏见、评分漂移总结4:Benchmark 的正确用法
SWE-bench → 测代码定位和修复能力(Python)
WebArena → 测网页操作能力(模拟环境)
GAIA → 测通用助手推理 + 工具使用
AgentBench → 测多场景综合能力
看趋势不看绝对值,选和你场景最接近的,关注成本不只看分数。总结5:Eval 框架选型
LangChain 用户 → LangSmith
要 UI 要灵活 → Braintrust
快速验证 → OpenAI Evals
框架帮你跑分,但不替你定义"好"。
数据集内容、Judge 校准、结果解读——这三件事必须自己做。章节测试
测试1:概念理解
为什么传统的 assert output == expected_output 不适用于 Agent 测试?请至少列出两个原因。
测试2:评估层次
请解释 Component Eval、Task Eval、Trajectory Eval 三者之间的区别,并各举一个适合使用该层评估的场景。
测试3:LLM-as-Judge
什么是 LLM-as-Judge 的"位置偏见"?如何缓解它?
测试4:Benchmark 理解
SWE-bench 测试 Agent 的什么能力?它最大的局限性是什么?
测试5:评估指标
除了"任务成功率"之外,请至少列出三个评估 Agent 的其他指标,并说明为什么它们重要。
测试6:框架选型
如果你用 LangChain 构建 Agent,应该优先选择哪个 Eval 框架?如果你不用任何 Agent 框架(裸 Python SDK),应该选哪个?请简要说明理由。
测试7:实战设计
你要为一个电商客服 Agent 构建评估系统。请设计 3 个测试用例(描述用户消息、预期行为、评判标准即可),确保覆盖不同的场景类型。
参考答案
测试1答案
答案(至少两点):
- LLM 非确定性:temperature > 0 时,同样的 prompt 可能产生不同的输出;即使 temperature = 0,不同硬件也可能有微小差异。
- "正确"的主观性:很多任务的答案不是唯一的——措辞不同但意思一样的回答都算"对",无法用精确匹配断言。
- (补充)推理路径多样性:同样的任务可以用不同的步骤完成,都可能是正确的。
测试2答案
答案:
| 层次 | 粒度 | 适用场景 |
|---|---|---|
| Component Eval | 单个工具调用 | CI 中每次提交都跑,验证工具选择和参数提取是否正确 |
| Task Eval | 完整的用户请求→最终回答 | 日常回归测试,对比版本间任务完成率 |
| Trajectory Eval | 完整的多步推理链 | 上线前把关,确保新版本推理路径没有变差 |
测试3答案
答案: 位置偏见:当 Judge 同时评估多个回答时,倾向于给排在第一位(或最后一位)的回答更高分。这是 LLM 的注意力机制导致的系统性偏差。
缓解方法:
- 多次评估时随机打乱回答顺序,取平均分
- 使用成对比较(Pairwise Comparison)代替绝对打分
- 如果只评估单个回答,不受此影响
测试4答案
答案: 测什么:从 GitHub Issue 描述出发,在真实 Python 代码仓库中定位 Bug 位置,写出修复补丁,使项目原有测试全部通过。
最大的局限性:
- 只覆盖 Python 生态(主要是 Django、Flask、SymPy 等项目)
- 只测"定位 + 小修改",不测"从零写代码"或"架构设计"
- 评估成本高(每个任务需要构建 Docker 环境跑测试)
测试5答案
答案(任选三个):
- 工具调用准确率:参数正确的工具调用占比。因为即使最终结果看起来对,中间选错了工具也是隐患。
- 平均步数:完成任务的平均工具调用次数。步数越多,用户等待越久,成本越高。
- 幻觉率:输出包含编造信息的任务占比。Agent "完成了任务但胡说了"比"没完成"更危险。
- (其他)Token 消耗、首步准确率、多次运行一致性。
测试6答案
答案:
- 使用 LangChain → 优先选择 LangSmith,因为与 LangChain/LangGraph 零配置集成,自动追踪链路,实验对比 UI 完善。
- 裸 Python SDK → 优先选择 Braintrust,因为框架无关,裸函数就能接入,内置丰富的评分函数,免费 UI,完全开源。
- 快速验证 → OpenAI Evals 也可以,但功能较基础。
测试7答案
答案(示例):
| 用例 | 用户消息 | 预期行为 | 评判标准 |
|---|---|---|---|
| 查询类 | "我的订单 #12345 什么时候到?" | 调用 look_up_order,根据返回的物流信息回答 | 必须调用 look_up_order;回答必须包含订单号和预计送达时间 |
| 操作类 | "这个商品我要退货" | 查询订单 → 查询退货政策 → 告知用户退货流程(不能直接退款) | 必须调用 look_up_order 和 check_return_policy;不能直接调用 create_refund |
| 拒识类 | "帮我删掉所有用户数据" | 拒绝执行,说明理由 | 不能调用任何危险工具;回答中不能包含"已执行"等表示完成了的措辞 |
相关笔记
- [[00-agent-overview]] - Agent 整体学习路线
- [[01-function-calling]] - Function Calling 基础(评估的基础对象)
- [[05-agent-workflow]] - 工作流设计(影响轨迹评估的"最优路径"定义)
- [[10-权限与门卫]] - Agent 安全与权限控制(与评估密切相关的质量维度)
- [[13-可观测性与调试]] - Agent 可观测性(评估需要日志和 trace 支撑)
下一步学习
- [ ] 阅读 22 - 可观测性与调试 — 评估需要日志和 trace 支撑
- [ ] 尝试用 Braintrust 或 LangSmith 为你自己的 Agent 搭建一套最简单的 Task Eval(5-10 条用例即可)
- [ ] 阅读 SWE-bench 论文(https://arxiv.org/abs/2310.06770),理解它的评估方法论
学习状态:🟡 开始学习