写Agent测试的时候,我最怕听到的一句话是:“我这边Demo跑得挺好的,怎么放到真实环境就废了”。这种情况我见过太多次,Agent在演示环境里能查天气、能聊文档、能调API,一到真实业务链路里就迷路、忘事、反复调用同一个错误的工具。后来我把自己在Agent评测这块的实践整理成了一套东西,内部代号就叫Agent-Reach,核心就一句话:不看Agent嘴上说什么,只看它能不能“触达”目标状态,以及在多大范围内能稳定触达。这篇文章就是把这套做法拆开讲讲,适合正在做Agent应用开发、接入了各类Agent框架、或者负责企业AI平台质量评估的读者。
1. 为什么需要Agent-Reach:被“Demo完美、线上翻车”逼出来的工具
1.1 从一次让我失眠的翻车说起
今年年初我们接了一个内部场景,让Agent帮业务同学查订单、改地址、申请退款,链路大概是:理解用户意图、查会员信息、查订单状态、调用售后接口、返回结果。Demo环境里跑得丝般顺滑,业务负责人当场点头。结果上线灰度第一天就出状况:用户说“我上周那个订单怎么还没发货”,Agent理解成了“查询所有未发货订单”,然后调错了接口,返回了一堆无关信息,用户又问了一遍,Agent开始道歉循环。
那天晚上我复盘的时候就在想一个问题:我们到底怎么评估一个Agent“行不行”?靠几个演示用例肯定不行,靠人工看对话质量也不行,因为这根本不是一个“聊得好不好”的问题,而是“任务到底有没有被完成、在什么条件下能被完成”的问题。这就是Agent-Reach最早的原型动机。
1.2 Reach在Agent语境里的双重含义
“Reach”这个词在Agent领域有很妙的双重含义。第一层是“触达”,指Agent从初始状态出发,经过一系列动作,最终能否到达一个目标状态;第二层是“覆盖范围”,指它在多大的状态空间内能保持这种触达能力,不是只会在一个精心设计的路径里成功。
打个比方,面试型Agent像是一个口才很好的人,你问他什么他都能答上两句,但上岗之后发现他根本不会干活;Reach要考察的是上岗型能力,安排一件具体的事,看他在真实约束下能不能办成、要花多少步、中间会不会跑偏。所以我一直觉得,Reach评估和传统的模型评测完全是两种逻辑:传统评测考“知识量”,Reach评测考“执行链路”。
1.3 现有评测工具为什么不够用
市面上的Agent评测工具和榜单我也用过不少,大多是这么几类:一类是单轮问答和知识问答评测,测的是模型本身;一类是检索评测,测的是RAG召回质量;还有一类是代码生成、工具调用正确率的评测,这些都有价值,但它们有一个共同盲区——不太关心“整条任务链路”的完成情况。
比如一个多跳任务:查库存、锁库存、生成订单、发起支付、更新库存。中间任何一步出错,最终结果都是错的。但你在单点指标上可能根本看不出来,因为每一步单独看都“挺正常”。Agent-Reach的设计出发点就是补上这个盲区:把任务链路当成整体来观测,对最终状态负责,而不是对每一步的正确率负责。
2. Agent-Reach的核心指标:怎么把“触达”量化
2.1 指标体系总览
这套体系我跑了几个月之后,沉淀出了六个核心指标,先列个总表,后面逐个拆开讲。
| 指标 | 含义 | 计算方式 | 重点关注 | | --- | --- | --- | --- --- | | 任务完成率 | 完整触达目标状态的比例 | 完成的用例数 / 总用例数 | 最终结果是否达标 | | 触达深度 | 任务执行到里程碑的深度 | 已到达里程碑数 / 总里程碑数 | 部分完成时的进度定位 | | 中途脱轨率 | 偏离计划路径的比例 | 脱轨用例数 / 总用例数 | Agent是否“跑题” | | 工具误用率 | 工具调用方式错误的比例 | 错误调用次数 / 总调用次数 | 参数、方法、对象是否准确 | | 扰动敏感度 | 输入微小变化后的指标衰减 | 扰动前完成率 - 扰动后完成率 | 鲁棒性和稳定性 | | 步数冗余率 | 相对最优路径的额外步数 | (实际步数 - 参考步数) / 参考步数 | 效率与成本 |
2.2 任务完成率与触达深度
任务完成率看起来很简单,实际上坑很多。最关键的是“什么算完成”必须定义清楚。不是Agent说了“已完成”就算,而是环境里的状态真的发生了预期变化。比如退款任务,Agent跟用户说“退款已发起”不算完成,退款状态字段从“待处理”变成“已发起”才算完成。我们在环境里给每个任务定义了目标状态的校验函数,最终评定全部交给状态判断,不听Agent自述。
触达深度解决的是另一个问题:Agent没完全成功,但也不是完全失败。比如一个五步任务,它完成了前三步,卡在第四步,你给我一个“失败”就太浪费信息了。我们把任务拆成带顺序的里程碑,记录它最深到过哪里,就可以看到失败主要集中在哪一段。很多模型在那时候表现出的规律是:前两步完成率很高,第三步开始骤降,这是很典型的上下文长度和记忆衰减问题。
2.3 中途脱轨率与工具误用率
脱轨是我自己造的一个词,指Agent在思考链或行为链上离开了可达目标状态的路径。典型表现包括:反复调用同一个失败的接口、在一个工具上纠结超过了三回合、开始问用户一些跟任务无关的问题、甚至自己编造了一个不存在的操作结果。这类行为虽然最后可能也“完成”了,但路径已经不可信了。
工具误用率和单点正确率不同,它看的是调用质量。举例来说,Agent要调用查询订单接口,正确参数是orderId,它传成了orderNo,这种就是典型的参数组装失败;再比如该用“批量查询”接口,它却循环调了五次“单个查询”接口,这是工具选择不当。这两个问题在纯对话评测里很难暴露,但在链路里会直接导致状态无法推进,所以必须单独统计。
2.4 扰动敏感度:测试Agent的“抗干扰能力”
我在实际测试里最大的一个感受是:很多Agent是“玻璃人”,你稍微换一个说法它就懵了。同一句话,“帮我查一下订单”和“看一下我的快递到哪了”,语义一样,但不少Agent的处理路径完全不同,一个走了订单查询,一个走了物流查询,甚至因为物流查询没配置就直接失败。
所以Agent-Reach里我专门加了一组扰动测试:改措辞、调换信息顺序、加入无关背景句、改变上下文里数据表的字段顺序等等。核心观察变量是完成率的衰减幅度。如果一个Agent在原始用例上完成率90%,扰动后掉到40%,那它的真实可用性就有大问题,因为真实用户不会每次都按模板说话。
2.5 效率指标:步数冗余率
效率看起来是锦上添花,实际上直接关系到成本和体验。Agent如果绕了三倍的路才完成一个任务,就算最后成功了,消耗的token、API调用时延、用户等待时间都会变差。步数冗余率的算分母是人工或参考Agent走出的最优步数,分子是实际步数减去参考步数。这个指标不需要卡得太严,但超过一定程度,说明Agent的工具调度和规划能力需要调优。
3. 环境构建与任务生成:让Agent在受控环境里“真跑一趟”
3.1 沙盒业务域的选择
做Agent-Reach评估的第一步,是先搭一个可控的沙盒环境。不能拿生产环境直接测,因为Agent在里面乱调接口,代价是不可控的。我们搭了一套模拟业务域:订单系统、库存系统、CRM系统、可退换货的售后系统,每个系统的状态之间都有联动关系,比如订单创建后扣库存、退款成功后恢复库存。
这套环境很贱,但是极其实用,因为它把Agent放在一个“会联动变化”的世界里,而不是一堆静态接口。状态每次被修改都会被记录成一个事件流,测试完之后能把整个业务域还原回初始状态,保证每个用例都是公平的起点。数据种子也要费心,既要有正常订单,也要有已经取消的、已经退款的、物流异常的,不然Agent永远学不会处理边界场景。
3.2 任务生成器如何设计难度
任务不能手工一个个写,那样样本太少了。我做了个任务生成器,思路是“模板 + 参数化 + 难度控制”。先写任务模板,比如“查询用户A的订单B状态并判断是否可以退款”,然后把用户、订单、状态、条件全部替换成沙盒里的真实数据。模板分成难度档位:
- 单跳任务:查一个信息,调一次接口,返回结果
- 链式任务:需要两到三个连续动作才能完成
- 多分支任务:不同的前置状态导向不同的后续操作
- 约束竞争任务:多个目标之间有冲突,需要权衡取舍
这里我特别提一下“约束竞争”,这是最难也最容易暴露问题的一档。比如“用户想退掉一个已经发货的订单”,这时候Agent必须判断需要先走拦截物流,还是直接拒绝,或者给出替代方案。很多Agent在这一档的表现就是死板地执行退款指令,忽略了状态约束,最后环境校验直接判失败。
除了任务本身,生成器还会注入随机扰动。比如在用户输入里加一个不相关的背景句“我今天心情不太好”,或者把订单日期格式从“2025-01-10”改成“1月10日”。这些扰动看起来不起眼,但做多了之后你会清晰地看到Agent能力曲线的真实形状。
3.3 可观测性埋点
评估Agent最怕的就是“黑盒”,你只知道结果失败,不知道过程发生了什么。所以我在沙盒里做了全量埋点,Agent每调用一次工具、每次状态变更、每一步思考过程,都记录成结构化日志。一条典型的执行轨迹日志长这样:
{ "task_id": "REACH-TASK-0041", "milestones": [ {"step": 1, "name": "解析用户意图", "status": "passed"}, {"step": 2, "name": "查询用户会员信息", "status": "passed", "tool": "get_member", "args": {"member_id": "M10086"}, "latency_ms": 320}, {"step": 3, "name": "查询订单列表", "status": "failed", "tool": "list_orders", "args": {"status": "all"}, "error": "missing required param: member_id"} ], "deviation_events": [ {"type": "repeated_tool_call", "tool": "list_orders", "count": 4}, {"type": "hallucinated_state", "content": "订单已自动退款"} ], "final_state": { "refund_status": "not_initiated" } }有了这种数据,后面做归因分析才不是瞎猜。我可以清楚地看到Agent在哪个里程碑开始掉链子,掉链子的原因是参数问题、状态判断问题、还是上下文遗忘问题。
4. 观测与归因:分数出来后,怎么知道它死在哪一环
4.1 失败分类:评估中最有价值的产出
跑完一堆用例之后,光有一个完成率数字没有意义,因为“失败”和“失败”之间差别很大。我按照轨迹数据把失败原因分成五类,每类对应不同的修复方向:
| 失败模式 | 典型特征 | 修复建议 |
|---|---|---|
| 规划失败 | 一开始就走错方向或遗漏关键步骤 | 优化任务拆解提示词,加入步骤检查 |
| 工具选择错误 | 该用A接口却调用了B接口 | 补充工具描述,增加接口用途示例 |
| 参数组装失败 | 接口调对了但参数缺失或格式错误 | 注入参数格式模板,增加参数校验反馈 |
| 状态漂移 | 中间某步返回异常后未恢复到正确状态 | 增加状态校验节点,出错后强制回退 |
| 幻觉脱轨 | 编造工具返回值或虚拟操作结果 | 对Agent返回值做校验,拒绝不合法的状态 |
这五类里,幻觉脱轨是我最警惕的,因为它在代码评测里几乎不可能发现。曾经有个Agent在调用售后接口失败后,直接跟用户说“已经为您提交退款申请了”,但根本没有任何接口调用记录。这种问题不通过环境状态校验,你根本抓不到。
4.2 轨迹回放与里程碑对齐
每一条测试轨迹我都会保存下来,方便回放。回放的时候不是只看对话,而是把Agent的每一步动作和环境状态的每次变化排在一起,时间轴对齐。这样就能看到:Agent说是查了订单,但订单查询接口实际没有被调用过;或者Agent调用了一个接口,但当时的环境状态已经变了,它用的是旧数据。
里程碑对齐是一种更精细的复现方式。每个任务复盘时会维护一个“预期状态-实际状态”对照表,比如任务目标是给用户发货,预期状态是“包裹状态更新为已发出”,实际状态是“包裹状态还是待揽收”,那这个里程碑在Agent认为“成功了”与实际状态之间就产生了明显的断裂,这往往是Agent混淆“我说完成了”和“我真的完成了”的关键证据。
4.3 反事实诊断法:一种高效的定位手段
这是我从系统测试里借来的思路。当一个用例失败时,先固定一个变量,改动其他条件,看失败是否有变化。比如怀疑是工具描述不清晰导致选择错误,就把那个工具的description改得更详细,再跑一遍同一批用例;如果failure变少了,说明确实是描述问题。如果没用,就继续怀疑别的变量。
这种方法效率非常高,因为它把归因从“猜测”变成了“对照实验”。我会给每个可疑因子建一个“反事实分支”:分支A是原始环境,分支B是在某个环节注入修正后的环境,两个分支跑同样的任务集,然后对比指标。多轮迭代之后,你对Agent本身的弱点、工具层的缺陷、Prompt的短板,基本能摸得一清二楚。
4.4 从一次真实故障看完整归因流程
我之前那个“查订单发货”的失败用例,完整走一遍归因大概是这样的。第一步,先看轨迹回放,发现Agent在“查询会员信息”之后没有把member_id存入当前执行上下文,后面调用订单接口时又把member_id丢了。第二步,看里程碑对齐表,问题定位在“意图解析->信息查询->订单列表”这一段。第三步,做反事实实验,修复上下文传递逻辑之后,同一批用例完成率从58%跳到了82%。最终结论不是模型能力不够,而是我们的执行框架在状态传递上有个漏洞。这个结论如果只靠肉眼看对话记录,是不可能快速定位的。
5. 三个我从Agent-Reach实测里发现的高频坑
5.1 模型把“查询成功”误判为“任务完成”
第一个高频坑,是Agent把“拿到了信息”和“完成了任务”混为一谈。有一类任务是“查询用户订单并判断是否可退款”,很多Agent在查到订单后就输出“可以退款”,但并没有调用退款操作。从对话角度看,它的回答挺合理;从环境状态看,退款流水根本不存在。这个坑在纯聊天评测里完全测不出来,但Reach的环境校验一卡就现原形。
对策是在提示词里明确区分“查询类任务”和“操作类任务”,并且对操作类任务增加“最后一步必须是确认操作结果”的要求。更硬的办法是环境层限制:没有调用对应写接口,最终状态就不可能达标,Agent说什么都没用。
5.2 长链路里的记忆衰减:中间步骤越多,偏差越大
第二个高频坑,是Agent在长链路任务里会“忘事”。比如一个七步任务,前三步依赖用户最初的输入,执行到第五步时,它可能已经忘了用户的原始约束,开始用“猜”的补充缺失信息。我们测过一组任务,步骤从3步增加到7步,部分模型的完成率几乎线性下降,而且“编造参数”的数量明显上升。
这个问题靠模型本身的能力提升之外,工程侧的缓解也很重要。我采用的方案是在框架里做“显式状态摘要”,每完成一个重要里程碑,就把当前关键信息写入一个结构化的state对象,后续环节直接读取state而不是依赖上下文重新推断。这算是一个Agent开发上的老经验,但Reach的测试数据把这个问题的严重性摆到了台面上。
5.3 工具失败后不会优雅降级
第三个高频坑是“一根筋”。Agent调用的某个接口失败了,它的第一反应不是换方案,而是原地重试,最高记录是一个Agent连续重试同一个失败的接口9次,然后放弃。我们后来在任务环境里故意注入了一些“间歇性故障”的工具,专门测试Agent在失败后的行为。
表现好的Agent会做降级:主路径失败后,会尝试替代接口、简化任务目标、或者明确告知用户当前无法完成并给出建议。表现差的Agent就是死磕或崩溃。这个坑暴露出的其实是工具层设计问题,我们在Agent的规划提示词里加入了“失败分支处理”的要求,要求在每一步规划时同时生成主方案和备选方案,脱轨率降了不少。
6. 在真实项目里落地Agent-Reach的几点经验
6.1 从评估结果反推Prompt和工具设计
评估不能只停留在“出结论”,而是要形成闭环。我的流程是:跑完一轮Reach测试,自动生成问题聚合报告,比如“80%的工具选择错误集中在物流查询接口”,然后带着这份报告去调整工具描述、Prompt策略、参数Schema。下一轮再跑,指标有变化就保留,没变化就再想别的方案。
这套闭环跑起来之后,你很快会发现一个规律:很多问题根本不是模型笨,而是工具定义得烂。工具的功能描述含糊、参数示例缺失、错误返回信息不完整,都会让Agent表现得像“智力低下”。先把工具文档用人类新员工的标准写清楚,比换更大的模型见效更快。
6.2 建立回归基线库:把失败用例变成资产
我强烈建议每个Agent项目都建一个“回归基线库”,把每轮Reach测试中发现的有价值的失败用例保留下来,分类标记好。这个库的价值在于,任何人修改了Agent的Prompts、工具配置、模型版本后,都可以先跑一遍基线库,确保没有引入新的退化。
我们在基线库里目前积累了上千条用例,每次改动后跑一次全量回归大概需要一晚上,但是非常值得。有些改动从直觉上“更好”,跑完基线库才发现某个敏感场景被破坏了,这种问题如果不建库,基本只能等线上事故来暴露。基线库还有一个额外好处:新同学入职后不用只靠读文档理解业务,直接看这些用例,就能快速理解哪些场景容易出问题、为什么容易出问题。
6.3 频率和成本怎么平衡:分层评估
Reach测试做得越全越好,但成本是现实的。现在我跑的是三层结构:
- 第一层是冒烟层:每个代码变更后跑几十个高价值用例,十分钟内出结果,主要防致命错误;
- 第二层是回归层:每天晚上跑基线库全量用例,输出指标趋势和失败分类;
- 第三层是探索层:每周用任务生成器批量生成新用例,测试尚未覆盖的能力边界。
这三层里,探索层产出最不稳定,但价值最大,因为下一次线上事故往往就藏在还没测过的那个角落里。
6.4 一个最小可用的评估循环
如果你看完这些想在自己的项目里跑起来,但还没有完整的沙盒环境,可以先做一个最小版本。核心只需要四样东西:一个任务清单、一个目标状态判断函数、一个轨迹记录器、一份失败分类表。评估循环的逻辑可以简化成下面这个结构:
def evaluate_agent(agent, tasks, state_checker): for task in tasks: env = reset_environment(task.initial_state) trajectory = [] for step in range(task.max_steps): action = agent.next_action(env.current_state) trajectory.append(action) result = env.apply(action) if state_checker.is_goal_achieved(env.state): record_success(task, trajectory) break else: record_failure(task, trajectory, classify_failure(trajectory))最初用这个几十行的骨架去跑,你就可以看到每个Agent的完成率、脱轨率、失败分类,剩下的再逐步把环境、虚拟API、埋点做厚。实际落地过程中,我在这一套体系上最大的体会是:Agent评估的本质是“建立一个你可以信任的镜子”,让你在改动任何东西的时候,都能快速看清自己到底有没有让它变得更好。这个问题想清楚了,后面的一切都是执行细节。