
从偶尔成功到稳定可用AI Agent 的评测闭环接手过几个内部Agent项目之后我最大的感受是AI Agent这个赛道demo人人会做但一进真实环境就原形毕露。我见过不少团队在演示的时候效果惊艳等到业务方真的丢进来几十个真实任务成功率直接从看起来还行掉到完全没法用。这里面的问题往往不是模型选得不好也不是prompt写得不到位而是整个开发流程里缺了一个关键环节——评测闭环。说白了一个Agent从偶尔成功走向稳定可用靠的不是灵光一现的调参而是把评测变成研发流程的基础设施。这篇内容想分享的就是我在这类项目里逐步搭建评测闭环的完整思路指标怎么定、测试集怎么构造、评测结果怎么归因、迭代怎么反哺以及落地过程中踩过的那些坑。如果你手头的Agent也处于时好时坏阶段这篇文章应该能帮你把问题从玄学变成工程。1. 偶尔成功的Agent离稳定可用到底差在哪很多人在复盘Agent不稳定的时候第一反应是大模型能力不够。但我在实际排查中发现真正的问题往往更具体Agent不是一个单一模型而是模型 规划 工具调用 上下文管理的组合系统。任何一个环节抖动都会表现为最终任务的失败。所以第一步不是调prompt而是把不稳定这件事拆成可以观察、可以量化的形态。1.1 把不稳定拆成可观察的几种形态我习惯把Agent的不稳定表现分成四类每一类的排查方向完全不同。第一类是链路局部失败。比如任务需要调用三个工具前两个都正常第三个工具传入参数的时候格式错了或者API返回的字段没被正确解析。这种失败在单次演示中很容易被忽略但在批量跑任务时占比往往不低。特点是你重试一次可能又成功了所以看起来像偶发。第二类是长会话漂移。Agent在处理多轮任务时前面几轮还能记住用户的约束条件到第五轮之后就开始失忆比如用户一开始强调预算不能超过5000到后面Agent推荐了8000的方案。这种问题在短对话评测里几乎测不出来只有专门构造长上下文case才能暴露。第三类是措辞敏感。同一个意图换一种表达方式Agent就走不通了。比如帮我查一下昨天的销售额能成功昨天卖了多少钱就卡住。这看起来像模型理解能力问题但很多时候是意图识别规则写得过死或者工具描述里缺少同义表达。第四类是环境依赖。外部API超时、返回格式微调、并发量波动都会让Agent的行为发生变化。这类问题最隐蔽因为本地测试一切正常一上生产就随机翻车。如果你能明确说出自己项目里的不稳定主要是哪一类就已经比大多数人强了。因为下一步的指标设计和测试集构造完全是围绕这些失败形态来做的。1.2 为什么传统测试思维在Agent上行不通做传统软件开发的时候我们习惯写单元测试、集成测试每个用例有明确的输入和预期输出。这套思维在Agent这里会碰壁核心原因是两个。第一个原因是Agent的输出空间是开放性的。同一个任务模型可以走不同的工具路径、用不同的措辞回复只要最终结果对路径是多样的。你没法像断言一个函数返回值那样断言必须调用某个工具且参数完全相等。传统用例的硬断言模式在Agent评测里会产生大量误报。第二个原因是概率性输出。即使输入完全相同大模型生成的结果也可能不同尤其是temperature没调低、或者模型版本有upstream更新的时候。你跑一次过了跑三次可能挂两次这时候单次执行的通过与否根本没有统计意义。我见过有人试图用多跑几次看有没有一次成功来评估Agent还美其名曰best-of-n评估。这在研究场景有一定价值但在工程化场景是自欺欺人——用户不能每次都在你背后帮你重试。真正要测的应该是稳定通过率也就是多次运行中成功的比例。所以评测闭环的第一个认知转变是不要用做软件的思路测Agent也不要靠几次手动演示判断好坏而是把评测当成一套持续运行的、带统计口径的工程系统。2. 评测指标怎么定才不会自欺欺人指标是评测闭环的锚。指标定错了后面所有工作都是白费。我在项目里通常把指标分成四层每一层回答不同的问题。只盯一个端到端成功率是不够的因为你还需要知道为什么失败而原因藏在更细的指标里。2.1 从任务成功率到四层指标体系第一层是任务成功率Task Success Rate。这是最核心的北极星指标代表Agent在给定任务下完全达成用户目标的比例。注意达成目标和回复了内容是两回事后面讲判定器的时候细说。第二层是过程指标。包括工具调用正确率调用对了工具、参数合法、步骤完成率多步任务中完成的步骤占比、平均回合数完成一个任务需要多少轮交互过长说明效率低。这些指标的作用是定位瓶颈任务成功率低到底是第一步就错还是最后一步没收尾第三层是资源指标。包括单任务平均token消耗、平均延迟、工具调用失败率。Agent跑得再准如果成本是人工的十倍、延迟让用户等得不耐烦生产上依然不可用。我见过一个项目成功率从60%调到85%但token消耗翻了三倍最后业务方还是不敢上线。第四层是回归指标。这不是单次评测的指标而是跨版本对比的指标新版本在老测试集上的通过率和旧版本相比是升了还是降了。没有这一层你所谓优化就可能是拆东墙补西墙。在设计指标的阶段还有一个很容易犯的错误只统计成功/失败二值不统计失败模式分布。同样是成功率60%40个失败case里如果有30个都是同一个原因比如日期格式解析错误那这是一个非常好修的问题如果40个case是40种不同原因那问题就复杂得多。所以指标报表里一定要带失败原因分类这个下面单独讲。2.2 判定器选型规则、模型评判还是混合有了指标还得有判定器judge来判断一个任务到底算成功还是失败。这是评测闭环里最容易出争议的地方我见到的方案大致有三种。规则判定写死一些断言逻辑比如最终回复里必须包含订单号、返回的JSON里status字段必须等于success。优点是稳定、可解释、零成本缺点是只能覆盖结果可结构化的任务对开放性的对话任务无能为力。LLM-as-judge用一个大模型来评判Agent的回答是否达成用户目标。比如给它任务描述、Agent的执行轨迹和最终输出让它输出pass/fail。优点是能处理开放性任务缺点是裁判模型本身也有误差而且裁判费也不便宜。我之前测试过裁判模型把对话式FAQ任务的误判率控制在5%以内是可以做到的但前提是要给裁判模型足够清晰的评分规则不能只说请判断是否成功。人工抽检让业务方或标注员抽查评测结果修正误判。优点是准确率高缺点是成本高、速度慢只能做抽样校准不能全量覆盖。我的习惯是混合式能写规则的任务用规则判定写不了规则的用LLM-as-judge然后每周抽10%的case人工复核再把复核结果反馈回裁判prompt里。这套组合下来判定的可信度比单纯用一种方式高很多。2.3 指标落地的一种简单数据格式评测不只是跑一遍看结果它需要数据格式支撑。我在项目里用的是一种非常简单的JSONL格式每条case包含任务描述、环境构造信息、期望结果描述、判定器类型和判定规则跑完评测后追加执行轨迹和判定结果。结构大致长这样{ case_id: order_query_001, task: 查询订单20250101的物流状态, env_setup: {user_id: test_user_1, order_id: 20250101}, expected: 返回该订单的最新物流节点, judge: {type: llm, criteria: 最终回复中包含物流城市和状态}, trace: [], verdict: , cost: {prompt_tokens: 0, completion_tokens: 0} }数据格式设计的原则是一条case必须能复现。任务描述、环境快照、判定规则三者缺一不可。没有环境快照你跑完一次失败想复现debug都无从下手。3. 测试集是闭环的燃料构造逻辑、难度分层与防污染指标回答怎么算好测试集回答拿什么测。我见过不少人在这步偷懒随便扔二三十条业务方给的例子进去就宣称有了评测集。这样的评测集既覆盖不了真实场景的多样性也撑不起后续的迭代回归。3.1 测试案例的三个来源测试案例的核心来源有三个我建议三管齐下。第一个来源是真实用户会话。把线上Agent的实际对话日志拉出来挑出那些典型的、任务目标明确的会话脱敏后整理成case。这是优先级最高的来源因为真实用户的需求分布和话术跟你自己想象出来的完全不一样。我接手的一个客服Agent项目初期测试集全是工程师编的规范场景一上真实日志成功率的评估结果直接掉了20个点不是Agent变差了而是之前测的根本不是真实难度。第二个来源是业务方提供的高频任务清单。问业务运营你们每天被用户问得最多的50个问题是什么把这些变成case。这类case通常覆盖了产品最核心的价值路径优先级最高。第三个来源是工程师构造的边界和对抗case。比如超长上下文、多步工具链、用户中途改需求、模糊表达、包含特殊字符的输入。这类case不是真实高频场景但它们是用来压测Agent稳定性的能暴露出真实场景中潜伏的问题。3.2 难度分层与数量起步建议我建议把测试集分成三层P0冒烟层、P1常规层、P2对抗层。P0冒烟层大概10-20条是这都不能过就别谈上线的核心路径比如登录后查询主菜单、提交一个正常格式的表单。P1常规层是日常真实任务的主体覆盖各业务模块。P2对抗层是那些故意刁难的case用来防止Agent在常规任务上表现好了一遇到稍微偏离模板的输入就崩。数量上我的建议是起步50条稳定后100-300条。我见过有人一上来就追求上千条结果维护成本过高两周就放弃了。50条虽然不算多但如果你能保证每条case的质量、不断根据线上数据更新效果会好过500条凑数的。还有一个容易被忽略的点测试集要持续更新。线上用户的提问分布会变工具接口会变模型版本也会变。固定一套测试集跑半年评测结果会逐渐失真。我的习惯是每个月从线上日志里新增10%的新case同时淘汰那些所有版本都稳定通过的僵尸case。3.3 防污染测试集不是训练素材这条特别容易踩尤其是早期团队。防污染的核心原则是测试集里的case绝不能以任何形式出现在Agent的prompt、few-shot示例、或者微调数据里。我遇到过一件事某同学为了提升效果把几个高频问题的手写标准答案塞进了Agent的prompt里当作few-shot示例结果这几个case的测试通过率直接拉满。可一旦换个相近但不同的问法Agent反而表现更差因为它学会了模仿示例格式而不是真正理解了任务。这种虚假的评测分数会把你引向完全错误的方向。更隐蔽的污染是模型的预训练数据泄漏。如果你的测试集大量使用公开的、常见的任务描述模型可能在预训练阶段就见过类似内容了。这个没法完全消除但可以通过使用真实的业务数据、私有环境快照来降低风险。4. 让评测结果说实话运行机制与失败归因评测闭环的核心不只是跑case、出分数而是让每次评测结果都能还原出Agent在哪个环节、以什么方式失败的。这需要一套规范的运行机制和归因方法。4.1 随机性管理为什么temperature0还不够很多人跑评测时会设置temperature0以为这样输出就确定了。实际上temperature0只是让采样行为接近贪心解码但在float精度、batch大小、甚至GPU内核实现的影响下结果仍然可能不完全一致。另外Agent里往往还有多个模型调用主模型、工具选择模型、记忆总结模型任何一个环节的微小波动都会传导给下游。所以我跑评测的时候会做两件事。第一把temperature设为0或低值减少无效波动。第二每个case重复运行多次统计通过率而非二值结果。默认我跑3次如果某个case在3次中通过率低于2/3就会标记为不稳定case重点排查。用代码描述的话评测调度大概是这个逻辑def run_eval(agent, dataset, n_runs3): results [] for case in dataset: pass_cnt 0 traces [] for _ in range(n_runs): env case.build_environment() # 构造干净的环境快照 trace agent.run_with_trace(case.task) verdict case.judge(trace) traces.append({trace: trace, verdict: verdict}) pass_cnt int(verdict.passed) results.append({ case_id: case.case_id, pass_rate: pass_cnt / n_runs, traces: traces }) return results注意里面有个细节每次运行都重新构造环境快照。这样能避免上一次运行留下的数据库状态或临时文件污染下一次结果。4.2 失败归因模板与优先级矩阵评测跑完不能只看分数。我要求每次评测结束后必须把所有失败case的trace拿出来做归因。归因不是猜测而是对照trace看失败发生的位置。我把Agent失败归成五大类在实际项目中能覆盖绝大多数情况失败类型典型trace特征大概率根因优先级建议规划错误步骤顺序混乱、绕远路、漏步骤任务拆解prompt不清晰或模型能力不足高工具调用错误参数缺失、参数格式错、调用不存在的工具工具schema描述模糊、参数解析逻辑有bug高上下文丢失前几轮信息后几轮未使用记忆窗口太短、总结策略丢失关键信息中指令遵循偏差忽略了任务里的约束条件如只要摘要用户指令与系统指令优先级未定义清楚中环境/判定异常API超时、环境变量缺失、判定器本身误判外部依赖不稳、判定规则过宽/过严低先修归因之后用一个简单的优先级矩阵决定先修什么按失败频率 × 修复成本排序。一个失败频率30%但修复成本极低的bug肯定排在失败频率5%但需要重构的架构问题前面。我实践中一个很有意思的发现是很多看起来像模型能力不够的失败归因到最后是工具schema写得不够清楚。比如工具描述里只说传入日期参数没说格式是YYYY-MM-DD还是YYYY/MM/DD模型就会随机猜测错误率自然上去了。这一类问题修复成本极低收益却立竿见影。4.3 评测调度跑一次全量回归要多久评测闭环要跑起来还需要解决调度问题。全量300条case、每条重复3次就是900次任务执行。如果Agent每次任务要调3-5次模型就是几千次大模型调用跑一次可能要半小时甚至更久。所以我建议分两档快速冒烟档和全量回归档。快速冒烟档只跑P0冒烟层每次代码改动后本地跑目标控制在5分钟以内全量回归档跑全部case每天定时跑一次比如夜间第二天早上看结果。这样既保证了调试时的即时反馈又不会让全量回归的成本拖垮迭代速度。如果全量回归一次超过1小时就要考虑成本问题了。常见的优化办法是把整个测试集按模块分组只跑改动影响到的模块的case或者先用规则判定做初筛只有初筛不通过的case才动用LLM-as-judge二次判定能省不少token。5. 用评测结果驱动迭代而不是继续手调评测闭环的最后一环是让结果反哺开发。很多团队评测跑起来了但迭代还是靠拍脑袋今天觉得prompt不够长就加两句明天觉得不行又改回来。评测结果没能真正驱动决策闭环就断了。5.1 先修高频失败再看架构隐患拿到一份评测报告我通常按这个顺序处理第一步把所有失败case按归因分类汇总照上面的优先级矩阵挑出高频且低成本的问题当天解决第二天回到评测看通过率是否改善。这一步能快速建立正反馈让团队相信评测体系有用。第二步看失败模式的分布趋势。如果工具调用错误连续一周都在30%以上说明工具层设计有系统性问题这时候不是修几个case就行的需要重新设计工具schema或工具选择逻辑。如果规划错误占比上升则要看是不是最近调整了任务拆解prompt导致的。第三步做A/B对照实验。评测闭环的价值之一是让A/B对比变得可执行。改prompt、改工具逻辑时分别在两套配置下跑同一个测试集对比通过率、成本和延迟。我之前调工具描述时先出了两个版本用100条case各跑3次发现版本A通过率78%、版本B通过率85%但B的平均延迟多了1.2秒。有了数据决定就变得很干脆。5.2 防止过拟合测试集的三条纪律评测驱动迭代有一个隐藏风险团队会不自觉地去刷测试集分数导致Agent在测试集上表现很好一到真实环境又露馅。我给自己定了三条纪律来对抗这个问题。第一条永远保留一个盲测集。从线上日志中随机摘出一批新case不提前告诉任何人每隔几周跑一次。如果测试集分数在涨而盲测集分数不涨说明过拟合了需要停下来检查是在学习任务还是在背诵case。第二条测试集不参与prompt调试过程。调试的时候用tiny验证集大概5条只在确定改动方向后才跑全量测试集。这样可以避免反复跑测试集导致的选择效应——你改一版、跑一次、看分数、再改本质上是在对测试集的噪声做拟合只不过你以为是拟合信号。第三条定期从线上扒新case。把真实用户会话里当时Agent没答好的case加入测试集。这比构造边界case更有效因为线上永远能给你惊喜。6. 落地评测闭环时我踩过的几个坑这部分算是经验总结都是真金白银换来的。写出来帮大家绕开。6.1 测试集被模型背下来了项目早期我们用的测试case里包含了不少高频常见问题直接拿网上公开的模板改写。后来发现测试集分数高得离谱但线上真实用户的相似问题处理得一塌糊涂。排查才发现测试集里那些过于通用的问题模型在预训练阶段已经见过很多遍了等于开卷考试。从那以后所有测试case都要求带上业务上下文、私有数据环境确保模型不是靠记忆答题。6.2 判定器过宽测出90%的假通过率有一段时间我们的测试通过率稳定在90%以上看起来很美。后来人工抽检了一下发现里面大概有20%的case其实是误判——模型回复了相关但不完整的内容裁判模型却给了pass。原因是我们LLM-as-judge的判定标准里只写了是否包含关键信息没写是否完整满足所有约束条件也没有要求裁判模型输出理由。修完判定标准、强制输出理由之后通过率回调到了70%出头。那次之后我定了一条规矩LLM判定器的输出必须附带判定理由否则视为无效。6.3 环境抖动带来大规模误报刚开始把评测跑在共享测试环境上结果某天跑出来一批case集体失败大家慌了半天最后发现是公共API的限流策略被触发了。从那以后评测统一跑在独立环境里每个case重新初始化数据库状态外部依赖用mock或专门的测试账号。评测环境的稳定性直接决定了评测结果的可信度。6.4 评测与迭代脱节闭环名存实亡还遇到过一种情况评测脚本写好了定时任务也在跑但团队没人看结果。复盘的时候发现原因很现实——评测报告是一份几十页的日志没人有时间逐条看。后来我把输出改成一页纸摘要先给通过率曲线、失败模式Top3、新增失败case列表再配上一条可以点击进去看trace的链接。这才让评测结果真正进入日常迭代流程。最后说一个我个人的体会评测闭环的价值不在于第一次搭起来就跑出多高的分数而在于它让你终于能分清模型问题和系统问题。以前Agent挂了大家只能大眼瞪小眼猜原因现在翻开trace归因、修bug、看通过率变化每一步都有数据兜底。只要你手头有任何一个偶尔成功的Agent尽早把闭环跑起来比再调十版prompt都管用。