1. Agent 评测体系到底在解决什么问题
1.1 从“跑起来”到“跑得对”的鸿沟
做 Agent 开发的人大概都有过这种体验:demo 阶段一切丝滑,工具调用准确、多轮对话连贯、任务完成率看着也不错。可一旦把同一套 Agent 放到真实业务流量里,问题就像雨后春笋一样冒出来——昨天还能正确查天气的 Agent,今天面对“帮我看看明天适不适合洗车”这种带推理的请求就彻底懵了。更让人头疼的是,你根本说不清它到底是退步了还是只是这次运气不好。
这就是 Agent 评测体系要解决的核心矛盾。传统的软件测试有明确的输入输出断言,单元测试跑一遍绿灯就代表没问题。但 Agent 的输出是自然语言、是工具调用序列、是多步推理链,你没法用assert result == expected来判定对错。一个 Agent 回答“北京明天晴,适合洗车”和“明天北京天气不错,可以洗车”,语义上等价,字符串比对却完全不同。
我见过太多团队在 Agent 上线后靠“用户反馈”来发现问题,这本质上是一种被动的、滞后的质量监控。等用户投诉来了,坏影响已经产生了。评测体系的价值就在于把这种被动变成主动——在 Agent 进入生产环境之前,用一套可重复、可量化、可对比的流程,把它的能力边界摸清楚。
1.2 评测体系的三层结构:Harness、Rubric、LLM-Judge
一套完整的 Agent 评测体系,拆开来看是三个相互咬合的模块。
Harness(评测执行框架)是骨架。它负责把测试用例喂给 Agent、捕获 Agent 的完整执行轨迹(包括思考过程、工具调用、中间结果、最终输出)、然后交给评判模块。你可以把它理解成一个专门为 Agent 设计的“测试跑道”——没有它,你连 Agent 每一步做了什么都不知道,更别提评测了。
Rubric(评分标准)是尺子。它定义了“什么叫好”。比如对于“订机票”这个任务,Rubric 可能包含:是否正确识别了出发地和目的地、是否选择了用户偏好的时间段、是否在价格超出预算时主动提醒、最终是否成功完成预订。每一条都是一个可判定的维度。
LLM-Judge(大模型裁判)是裁判。它用另一个大模型来根据 Rubric 对 Agent 的表现打分。为什么不用规则匹配?因为 Agent 的输出太灵活了,规则覆盖不全。LLM-Judge 能理解语义等价、能判断推理链是否合理,这是传统断言做不到的。
这三者的关系可以这样理解:Harness 负责“跑”,Rubric 负责“量”,LLM-Judge 负责“判”。缺了任何一个,评测都跑不通。
1.3 谁需要这套体系
如果你只是自己玩一玩 Agent,做个玩具项目,那确实不需要这么重的评测。但只要你面临以下任一场景,评测体系就是刚需:
- Agent 要上线给真实用户用,你需要知道它到底靠不靠谱
- 你在迭代 Agent 的 prompt 或工具集,需要判断新版本是不是真的比旧版本好
- 你在多个模型之间做选型,需要客观对比它们在具体任务上的表现
- 你的 Agent 涉及多步工具调用,你需要定位失败到底发生在哪一步
我个人的经验是,Agent 开发到一定复杂度后,没有评测体系就像蒙着眼睛开车——你感觉在前进,但不知道方向对不对,也不知道什么时候会撞墙。
2. 核心组件拆解:Harness、Rubric、LLM-Judge 各自怎么落地
2.1 Harness 工程:评测执行框架的设计要点
Harness 这个词在 Agent 圈子里最近热度很高,但很多人对它的理解还停留在“一个跑测试的脚本”。实际上,一个合格的 Harness 需要处理的问题远比想象中复杂。
第一,它要能捕获完整的执行轨迹。Agent 执行一个任务,中间可能调用了三次搜索、两次计算器、一次代码执行。如果 Harness 只记录最终输出,那评测就只能看到结果对不对,看不到过程合不合理。而 Agent 的很多问题恰恰出在过程里——比如它绕了一大圈才找到答案,或者调用了不该调用的工具。
第二,它要能处理非确定性。同一个 Agent 跑同一个任务,两次结果可能不同。Harness 需要支持多次运行取统计结果,而不是跑一次就下结论。我一般会设置至少 3 次重复运行,对于关键任务会跑到 5 次,然后看通过率而不是单次结果。
第三,它要能隔离环境。Agent 在执行过程中可能会修改文件、调用外部 API、产生副作用。Harness 需要为每个测试用例提供干净的沙盒环境,避免用例之间相互污染。这一点在涉及代码执行的 Agent 上尤其重要。
一个典型的 Harness 执行流程是这样的:
# 伪代码示意,展示 Harness 的核心逻辑 class AgentHarness: def __init__(self, agent, test_cases, rubric, judge): self.agent = agent self.test_cases = test_cases self.rubric = rubric self.judge = judge def run_single_case(self, case, repeat=3): results = [] for i in range(repeat): # 每次运行前重置环境 env = self.setup_sandbox() # 捕获完整执行轨迹 trace = self.agent.execute( task=case.input, env=env, capture_trace=True ) # 用 LLM-Judge 按 Rubric 打分 score = self.judge.evaluate( trace=trace, rubric=self.rubric, expected=case.expected ) results.append(score) return self.aggregate(results) def run_all(self): report = {} for case in self.test_cases: report[case.id] = self.run_single_case(case) return report这段代码的关键点在于capture_trace=True和setup_sandbox()。前者保证你能看到 Agent 的每一步,后者保证用例之间不互相干扰。
实操心得:Harness 的日志格式一定要统一。我踩过的坑是早期用不同格式记录不同 Agent 的轨迹,结果后面想对比两个版本时发现数据对不齐,白白浪费了两天做数据清洗。建议从一开始就定义好 trace 的 schema,包含时间戳、步骤序号、动作类型、输入、输出、耗时这几个字段。
2.2 Rubric 设计:把“好”拆成可判定的维度
Rubric 是评测体系里最需要人工投入的部分,也是最容易被低估的部分。很多人随便写几条“回答准确”“格式正确”就完事了,结果 LLM-Judge 打分时模棱两可,评测结果毫无区分度。
好的 Rubric 应该满足三个条件:可判定、有区分度、覆盖关键维度。
可判定意味着每一条标准都能明确回答“是”或“否”,或者至少能落到一个明确的分数档位上。比如“回答准确”就不可判定,但“回答中提到的天气状况与工具返回结果一致”就可判定。
有区分度意味着这条标准能区分好的 Agent 和差的 Agent。如果所有 Agent 在某个维度上都拿满分,那这个维度就没有存在的意义。
覆盖关键维度意味着 Rubric 要覆盖任务完成度、过程合理性、输出质量这几个层面。我通常会把 Rubric 分成三组:
| 维度组 | 具体条目示例 | 权重建议 |
|---|---|---|
| 任务完成度 | 是否完成了用户请求的核心目标 | 40% |
| 过程合理性 | 工具调用是否必要且顺序合理 | 30% |
| 输出质量 | 语言是否自然、格式是否规范、有无幻觉 | 30% |
权重的分配取决于你的业务场景。如果是客服 Agent,输出质量的权重可能更高;如果是自动化任务 Agent,任务完成度的权重应该占大头。
Rubric 的每一条最好配上正例和反例。比如“工具调用是否必要”这一条,正例是“用户问天气,Agent 调用了天气 API”,反例是“用户问天气,Agent 先调用了计算器再调用天气 API”。有了正反例,LLM-Judge 的判断会稳定很多。
2.3 LLM-Judge 实战:怎么让裁判靠谱
用大模型当裁判,最大的风险是裁判本身不稳定。同一个回答,今天打 8 分明天打 6 分,那评测结果就没有意义了。让 LLM-Judge 靠谱,有几个关键技巧。
第一,给裁判明确的评分锚点。不要只说“1-10 分打分”,要给出每个分数段的具体描述。比如:
- 9-10 分:完全满足所有 Rubric 条目,无任何可改进之处
- 7-8 分:满足核心条目,有轻微瑕疵但不影响使用
- 5-6 分:部分满足,存在明显问题但仍有参考价值
- 3-4 分:大部分不满足,输出基本不可用
- 1-2 分:完全偏离任务目标
第二,让裁判输出推理过程。不要只让 LLM-Judge 给一个分数,要求它先逐条分析 Rubric 的满足情况,再给出总分。这样一方面提高了打分的可解释性,另一方面也迫使裁判更认真地思考。
第三,用多个裁判取共识。如果条件允许,用两个不同的大模型分别打分,然后取平均或取一致结果。这能有效降低单个模型的偏见。我实测下来,双裁判的一致性通常在 80% 左右,比单裁判的稳定性好很多。
第四,定期校准裁判。每隔一段时间,人工抽一批样本打分,和 LLM-Judge 的结果对比。如果发现偏差较大,就需要调整 Rubric 的描述或裁判的 prompt。
# LLM-Judge 的 prompt 模板示例 JUDGE_PROMPT = """ 你是一个 Agent 评测裁判。请根据以下评分标准,对 Agent 的执行轨迹进行打分。 ## 评分标准 {rubric} ## 任务输入 {task_input} ## Agent 执行轨迹 {trace} ## 期望输出 {expected_output} ## 评分要求 1. 逐条分析每个评分标准的满足情况,给出你的判断理由 2. 基于逐条分析,给出总分(1-10 分) 3. 输出格式: 逐条分析: - 标准1:[满足/部分满足/不满足],理由:... - 标准2:... 总分:X 总评:... """注意事项:LLM-Judge 对长轨迹的处理能力有限。如果 Agent 的执行轨迹超过裁判模型的上下文窗口,就需要做摘要或分段评判。我的做法是先用一个轻量模型对轨迹做结构化摘要,再把摘要交给裁判。这样既控制了 token 消耗,又保留了关键信息。
3. 从零搭建一套可用的 Agent 评测流程
3.1 测试用例的设计与组织
测试用例是评测的基石。用例设计得好不好,直接决定了评测结果有没有参考价值。
我一般把测试用例分成四个层次:
基础功能用例覆盖 Agent 的核心能力,比如“查询天气”“预订会议室”“发送邮件”。这些用例的输入明确、期望输出清晰,主要用来验证 Agent 的基本功能是否正常。
边界用例测试 Agent 在极端情况下的表现,比如“查询一个不存在的城市天气”“预订一个已经满员的会议室”。这些用例用来验证 Agent 的错误处理能力。
推理用例需要 Agent 进行多步推理,比如“帮我找一个明天下午三点后有空、且离我办公室步行 10 分钟以内的会议室”。这类用例最能区分 Agent 的智能程度。
对抗用例故意给 Agent 制造陷阱,比如输入中包含误导信息、或者要求 Agent 做它不应该做的事。这类用例用来验证 Agent 的安全性和鲁棒性。
用例的组织建议用 YAML 或 JSON 格式,方便版本管理和批量加载:
# test_cases.yaml - id: weather_001 category: basic input: "北京明天天气怎么样?" expected: must_contain: ["北京", "明天"] tool_calls: ["weather_api"] rubric_overrides: - "必须调用天气工具获取数据,不能凭记忆回答" - id: meeting_003 category: reasoning input: "帮我找一个明天下午三点后有空、离我办公室步行10分钟以内的会议室" expected: must_contain: ["会议室", "时间"] tool_calls: ["calendar_api", "map_api"] rubric_overrides: - "必须同时考虑时间可用性和距离约束"rubric_overrides字段允许你为特定用例追加评分标准,这在处理特殊场景时非常有用。
3.2 评测执行与结果分析
有了 Harness、Rubric 和测试用例,就可以跑评测了。但跑完评测只是开始,真正的价值在于结果分析。
我一般会从三个角度看评测结果:
通过率视角:整体通过率是多少?哪些类别的用例通过率最低?这能帮你快速定位 Agent 的薄弱环节。
趋势视角:和上一个版本相比,通过率是升了还是降了?如果某个维度的分数下降了,说明这次改动可能引入了回归。
失败模式视角:失败的用例有没有共同的模式?比如是不是都在多步推理上出错?是不是都在某个特定工具上失败?找到失败模式,才能有针对性地改进。
下面是一个评测报告的示例结构:
| 用例类别 | 用例数 | 通过率 | 平均分 | 主要失败模式 |
|---|---|---|---|---|
| 基础功能 | 20 | 95% | 8.7 | 无 |
| 边界情况 | 15 | 73% | 6.2 | 错误处理不完善 |
| 多步推理 | 10 | 60% | 5.8 | 中间步骤遗漏 |
| 对抗测试 | 5 | 40% | 4.1 | 被误导信息带偏 |
这张表一眼就能看出,Agent 在基础功能上没问题,但推理和对抗能力是短板。接下来的优化方向就很明确了。
3.3 持续评测:把评测融入开发流程
评测体系建好之后,最重要的是让它持续运转起来。我见过太多团队花大力气搭了评测,结果只跑了一次就放在那里吃灰。
持续评测的关键是自动化。每次 Agent 的 prompt、工具集或模型版本有变更,就自动触发一轮评测。评测结果和上一次对比,如果有明显退步就告警。
# CI 中集成评测的示例 #!/bin/bash # 在 Agent 代码变更后自动运行评测 # 1. 启动 Agent 服务 docker-compose up -d agent-service # 2. 运行评测 python run_evaluation.py \ --test-cases ./test_cases/ \ --rubric ./rubric.yaml \ --output ./reports/latest.json # 3. 对比历史结果 python compare_reports.py \ --current ./reports/latest.json \ --baseline ./reports/baseline.json \ --threshold 0.05 # 4. 如果退步超过阈值,退出码非零,CI 会标记失败这个流程跑通之后,每次代码提交都会自动评测,团队能第一时间发现回归。我自己的项目里,这套流程帮我在一次 prompt 调整中发现了多步推理通过率从 60% 掉到了 45%,及时回滚避免了上线事故。
实操心得:评测用例不要一次性写太多。我一开始贪多写了 200 个用例,结果每次评测要跑半小时,开发迭代时根本等不及。后来精简到 50 个核心用例,评测时间控制在 5 分钟以内,大家才愿意频繁跑。用例的质量比数量重要得多。
4. 常见问题与排查技巧实录
4.1 LLM-Judge 打分不稳定怎么办
这是被问得最多的问题。同一个回答,裁判模型今天打 8 分明天打 6 分,评测结果完全不可信。
排查思路分三步走。第一步,检查 Rubric 是否足够具体。如果 Rubric 里写的是“回答质量高”,那裁判只能凭感觉打分,当然不稳定。改成“回答中不包含与工具返回结果矛盾的信息”就具体多了。
第二步,检查裁判的 prompt 是否给了足够的上下文。裁判需要知道任务是什么、期望是什么、评分标准是什么。如果只给它一个回答让它打分,它只能瞎猜。
第三步,检查温度参数。裁判模型的 temperature 应该设为 0 或接近 0,减少随机性。我一般用 0.1,既能保持一定的灵活性,又不会太随机。
如果以上都做了还是不稳定,那就上多裁判共识机制。两个裁判分别打分,如果分差超过 2 分,就引入第三个裁判做仲裁。
4.2 Agent 执行轨迹太长导致裁判看不全
这个问题在复杂任务上特别常见。Agent 执行了 20 步,轨迹有上万 token,裁判模型的上下文窗口装不下。
我的解决方案是分层摘要。先用一个轻量模型把轨迹压缩成结构化摘要,保留关键决策点和工具调用结果,去掉冗余的中间文本。然后把摘要交给裁判。
摘要的 prompt 可以这样写:
SUMMARIZE_PROMPT = """ 请将以下 Agent 执行轨迹压缩为结构化摘要,保留以下信息: 1. 每一步的工具调用名称和关键参数 2. 每一步的关键返回结果(如果结果很长,只保留与任务相关的部分) 3. Agent 的关键决策点(比如为什么选择这个工具而不是另一个) 4. 最终输出 去掉以下内容: - 重复的思考过程 - 与任务无关的工具返回细节 - 格式化的冗余文本 轨迹: {trace} 请输出摘要: """这样处理之后,摘要通常能压缩到原轨迹的 20%-30%,裁判就能看全了。
4.3 评测通过但线上效果差
这是最让人沮丧的情况——评测全绿,上线后用户投诉不断。根本原因通常是评测用例和真实流量分布不一致。
排查方法:从线上随机抽取一批真实请求,人工标注期望输出,然后把这些请求加入评测集。跑一遍评测,看看通过率是多少。如果明显低于原有评测集的通过率,说明你的评测集偏离了真实场景。
我自己的做法是每个月做一次“评测集刷新”,从线上流量中采样 20-30 条新用例补充进去,同时淘汰一些已经不再有代表性的旧用例。这样评测集始终跟真实场景保持同步。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查动作 | 解决方案 |
|---|---|---|---|
| 裁判打分波动大 | Rubric 不具体 / 温度过高 | 检查 Rubric 描述和 temperature | 细化 Rubric,temperature 设为 0.1 |
| 轨迹超长裁判看不全 | 任务步骤过多 | 统计轨迹 token 数 | 引入分层摘要 |
| 评测通过但线上差 | 用例分布偏离真实流量 | 对比评测集和线上请求分布 | 定期从线上采样补充用例 |
| 评测跑得太慢 | 用例太多 / 重复次数太多 | 统计单次评测耗时 | 精简用例,关键用例才做多次重复 |
| 不同版本无法对比 | 评测集变更 / 评分标准变更 | 检查评测集和 Rubric 版本 | 评测集和 Rubric 都做版本管理 |
| Agent 工具调用失败 | 沙盒环境配置问题 | 检查工具依赖是否安装 | 在 Harness 中预装所有工具依赖 |
4.5 几个容易踩的坑
坑一:用同一个模型既做 Agent 又做裁判。这会导致“自己评自己”的偏见,Agent 的输出风格和裁判的偏好高度一致,评分虚高。尽量用不同的模型做裁判,至少用不同版本的模型。
坑二:忽略工具调用的评测。很多人只评测最终输出,不评测工具调用过程。结果 Agent 可能用错误的方式得到了正确答案,这次对了下次就错了。工具调用的必要性、顺序、参数正确性都应该纳入评测。
坑三:评测集一成不变。Agent 在迭代,用户需求在变化,评测集如果一直不变,很快就会失去参考价值。建议至少每季度更新一次评测集。
坑四:只看总分不看细分。总分 7.5 分看起来不错,但如果任务完成度是 9 分而过程合理性只有 5 分,说明 Agent 虽然能完成任务但过程很笨拙。细分维度的分数才能指导优化方向。
5. 工具选型与规模化实践
5.1 自建还是用现成方案
Agent 评测这个领域,现成的开源方案还不多,而且大多偏向研究用途,工程化程度不够。我的建议是:核心的 Harness 和 Rubric 自己搭,LLM-Judge 可以用现成的 API。
自建 Harness 的好处是灵活,能适配你自己的 Agent 架构和工具集。现成的评测框架往往假设了特定的 Agent 接口,接入成本可能比自己写还高。
Rubric 必须自己设计,因为只有你最清楚你的业务场景里什么叫“好”。
LLM-Judge 可以直接调用大模型 API,不需要自己训练裁判模型。现在主流的大模型在语义理解和推理判断上已经足够好了。
5.2 规模化评测的工程挑战
当评测用例从几十个增长到几百个,评测就从“跑个脚本”变成了“跑一个系统”。这时候会遇到几个工程挑战。
并发控制:同时跑太多用例会打爆 API 限流。需要实现一个带速率限制的并发调度器,控制同时执行的用例数量。
失败重试:网络抖动、API 超时这些临时故障会导致用例假失败。需要实现重试机制,但要注意区分“Agent 真的失败了”和“评测基础设施故障”。
结果存储:评测结果需要持久化存储,方便历史对比。我一般用 SQLite 存结构化结果,用对象存储存原始轨迹。
成本控制:LLM-Judge 的 API 调用是有成本的。几百个用例乘以多次重复乘以多个裁判,token 消耗很快。需要做成本估算和预算控制。
# 带并发控制和重试的评测调度示例 import asyncio from asyncio import Semaphore class EvaluationScheduler: def __init__(self, max_concurrent=5, max_retries=3): self.semaphore = Semaphore(max_concurrent) self.max_retries = max_retries async def run_case_with_retry(self, case): async with self.semaphore: for attempt in range(self.max_retries): try: result = await self.run_single_case(case) return result except InfrastructureError as e: if attempt == self.max_retries - 1: raise await asyncio.sleep(2 ** attempt) # 指数退避 except AgentError as e: # Agent 本身的错误不重试,直接记录 return {"status": "agent_error", "error": str(e)}这段代码的关键是区分了InfrastructureError和AgentError。前者是评测系统的问题,需要重试;后者是 Agent 本身的问题,重试没有意义,直接记录即可。
5.3 评测体系的演进方向
一套评测体系建起来之后,不是一成不变的。随着 Agent 能力的提升和业务场景的变化,评测体系也需要演进。
从静态评测到动态评测:早期的评测用例是固定的,后来可以引入基于模板的用例生成,自动产生变体。比如同一个“订机票”任务,可以自动生成不同的出发地、目的地、时间组合。
从单轮评测到多轮评测:很多 Agent 的实际使用场景是多轮对话,但评测往往只测单轮。多轮评测需要模拟用户的后续追问,这对 Harness 的设计提出了更高要求。
从结果评测到过程评测:不仅看最终输出对不对,还要看 Agent 的推理过程是否合理、工具调用是否高效。这需要更精细的轨迹分析和更复杂的 Rubric。
从离线评测到在线评测:离线评测跑得再好,也不能完全代表线上表现。可以在线上做 A/B 测试,用真实用户的行为数据来补充评测结果。
我个人在实际操作中的体会是,评测体系的价值不在于一开始就建得多完美,而在于持续运转和迭代。哪怕一开始只有 10 个用例、3 条 Rubric,只要坚持每次变更都跑一遍,就能比没有评测的团队少踩很多坑。先跑起来,再慢慢完善,这是最务实的路径。
最后分享一个小技巧:把评测结果做成一个简单的看板,每次评测后自动更新。团队所有人随时能看到当前 Agent 的健康度,这比在群里发评测报告有效得多。可视化带来的透明度,会让整个团队对 Agent 的质量有共同的认知基础。