AI Agent 评测闭环:从偶尔成功到稳定可用的工程实践
1. 为什么你的 Agent 总是“薛定谔的可用”做 AI Agent 开发的人大概率都经历过这种崩溃时刻同一个任务早上跑通了下午再跑就报错换个输入措辞原本流畅的多步推理直接卡在第二步演示的时候一切正常上线之后用户随便一用就翻车。这种“偶尔成功”的状态几乎是每个 Agent 项目从 Demo 走向生产环境的必经之路也是最劝退的一关。我见过太多团队卡在这个阶段。他们不是模型选得不好也不是 Prompt 写得不够花哨问题出在一个更底层的地方——没有评测闭环。传统软件测试有明确的输入输出断言一个函数输入 3 返回 6跑一万次都是这个结果。但 Agent 不一样它是由大模型驱动的多步决策系统每一步的输出都是概率性的工具调用的参数可能因为一个标点符号的差异而完全不同中间任何一步的微小偏差都会沿着调用链被放大。你今天手动试了五次都成功不代表明天它还能稳定工作。这篇文章想聊的就是怎么把 Agent 从“偶尔成功”拉到“稳定可用”。核心抓手只有一个建立一套可重复、可量化、可回归的评测闭环。我会从评测集怎么建、指标怎么定、自动化怎么跑、失败怎么归因这几个维度把我在实际项目中踩过的坑和总结出来的方法完整拆一遍。不管你现在是在做客服 Agent、代码生成 Agent 还是数据分析 Agent这套思路都能直接套用。先给一个我自己的判断标准一个 Agent 什么时候算“稳定可用”不是它在你手里跑通了而是它在你的评测集上连续三个版本迭代核心指标波动不超过 5%且失败案例都有明确的归因分类。达不到这个标准就别急着上线。2. 评测集不是测试用例的堆砌2.1 从真实流量里“捞”出有代表性的任务很多人建评测集的第一反应是我手动写几十条测试用例不就行了我一开始也这么干结果发现两个致命问题。第一你手写的用例天然带着“上帝视角”你知道正确答案是什么所以你的 Prompt 会不自觉地往那个方向引导测出来的成功率虚高。第二手写用例覆盖不了真实用户的“野路子”输入——用户不会按你想象的格式提问他们会打错字、会中途改需求、会一次塞三个任务进来。我的做法是评测集的种子必须来自真实流量。在 Agent 上线初期哪怕只有几十个用户也要把所有的交互日志完整记录下来。然后从这些日志里筛选出三类任务高频任务出现次数最多的前 20%、高难度任务人工判断需要多步推理或多次工具调用的、边界任务输入格式异常、意图模糊、包含多个子任务的。这三类按 5:3:2 的比例构成评测集的基础。这里有个实操细节日志筛选不要只看“成功”的案例。失败的案例往往更有价值因为它们暴露了 Agent 的能力边界。我一般会把失败案例单独打标在评测时作为“负样本”使用——一个健康的 Agent 应该能明确拒绝或优雅降级那些它搞不定的任务而不是硬着头皮胡编。2.2 任务描述要“去歧义”但不能“去难度”筛选出任务之后下一步是把它整理成标准化的评测条目。每条评测条目包含四个字段任务描述、预期结果、判定标准、难度标签。这里最容易踩的坑是把任务描述写得太清楚。举个例子真实用户的输入可能是“帮我看看上个月那个项目的进度顺便把延期的那几个任务负责人找出来”。你在整理评测集的时候如果把它改写成“查询项目 A 在 2024 年 3 月的进度并列出所有延期任务的负责人姓名”那你就把难度降低了。Agent 需要处理的恰恰是“上个月”这种相对时间、“那个项目”这种指代消解、“顺便”这种隐含的多任务意图。所以我的原则是保留原始输入的模糊性和口语化特征只在必要的时候补充上下文信息。预期结果怎么写也有讲究。对于有明确答案的任务比如“计算 123 乘以 456”预期结果就是精确值。但对于开放式任务比如“写一段产品介绍”预期结果没法写死这时候需要写的是判定标准——比如“必须包含产品名称、核心功能、适用场景三个要素且不能出现事实性错误”。判定标准要具体到可以被另一个人或另一个模型独立执行的程度。2.3 评测集的版本管理和“防污染”机制评测集建好之后必须像代码一样做版本管理。我见过团队把评测集放在一个 Excel 里改来改去最后不知道哪个版本对应哪个模型版本回归测试的结果完全没法对比。正确的做法是评测集用 JSON 或 YAML 格式存储每次修改都提交到 Git打上版本标签。模型每次迭代都要在同一个评测集版本上跑一遍结果存档。还有一个容易被忽视的问题评测集污染。如果你用评测集里的数据去微调模型或者把评测集的题目泄露到了 Prompt 的 few-shot 示例里那评测结果就完全失真了。我的做法是维护两个集合一个“开发集”用于日常调试和 Prompt 优化一个“测试集”严格保密只在版本发布前跑一次。测试集的数量不用多但必须覆盖所有核心场景且绝不参与任何训练或调试过程。3. 指标设计别只看“成功率”3.1 成功率是个好指标但远远不够“成功率”是大多数人第一个想到的指标但它太粗了。一个 Agent 成功率 80%听起来还行但你不知道那 20% 的失败是“完全跑偏”还是“差一步就对了”。而且成功率不区分任务难度——如果评测集里 80% 都是简单任务那 80% 的成功率其实很低。我通常会把指标拆成三层结果指标、过程指标、效率指标。结果指标看最终产出对不对过程指标看中间步骤合不合理效率指标看花了多少代价。三层结合起来才能完整刻画一个 Agent 的真实水平。指标层级具体指标说明结果指标任务完成率最终输出满足判定标准的比例结果指标事实准确率输出中事实性错误的比例过程指标工具调用准确率调用了正确的工具且参数无误的比例过程指标步骤冗余率实际步数超出最优步数的比例过程指标意图理解准确率正确识别用户核心意图的比例效率指标平均 Token 消耗完成单任务的平均 Token 用量效率指标平均响应延迟从输入到最终输出的端到端时间效率指标平均工具调用次数完成单任务的平均工具调用次数这张表里的每一个指标都需要在评测时单独记录。不要指望一个“总分”能告诉你问题出在哪。我一般会要求评测系统输出每个任务的详细 Trace包括每一步的输入输出、工具调用记录、Token 消耗然后从 Trace 里自动计算这些指标。3.2 过程指标比结果指标更能暴露问题结果指标告诉你“坏了”过程指标告诉你“哪里坏了”。举个例子一个任务最终失败了如果只看结果指标你只知道它没完成。但看过程指标你会发现Agent 其实理解了意图也调用了正确的工具但工具返回的结果它没解析对导致后续步骤全错。这种情况下修复方向就很明确——加强工具返回结果的解析逻辑而不是去改 Prompt 或者换模型。工具调用准确率是我最看重的过程指标之一。Agent 调用工具时常见的错误包括调用了不存在的工具、参数类型错误、参数值超出范围、该调用工具的时候没调用直接编答案、不该调用的时候乱调用。这些错误在最终结果上可能表现为“任务失败”但根因完全不同。我的做法是在评测系统里对每一次工具调用做校验记录错误类型最后按类型统计。步骤冗余率也很关键。有些 Agent 为了“保险”会把同一个工具反复调用好几次或者绕了一大圈才找到正确路径。这种冗余不仅浪费 Token 和延迟还增加了出错概率。计算冗余率需要一个“最优步数”的基准这个基准可以人工标注也可以用另一个更强的模型来生成参考路径。3.3 指标权重的动态调整策略不同阶段指标的权重应该不一样。在 Agent 开发早期我最关注的是意图理解准确率和工具调用准确率因为这两个是基础能力基础不牢后面全白搭。到了中期重点转向任务完成率和事实准确率因为这时候基础能力已经稳定要开始抠最终产出的质量。到了上线前的冲刺阶段效率指标的权重会提上来因为用户对延迟和成本是有感知的。权重的调整不需要太复杂我一般用一个简单的加权公式总分 w1×完成率 w2×准确率 w3×工具准确率 w4×(1-冗余率) w5×(1-归一化延迟)。权重值根据当前阶段的目标来定比如早期 w10.2, w20.2, w30.4, w40.1, w50.1上线前调整为 w10.3, w20.3, w30.2, w40.1, w50.1。这个公式不追求绝对精确它的作用是让团队在迭代时有一个统一的“指挥棒”。4. 自动化评测流水线怎么搭4.1 评测执行器的核心逻辑手动跑评测集是不可持续的。你今天跑 50 条明天跑 100 条后天模型一改又要重跑人力根本扛不住。自动化评测流水线是评测闭环的基础设施它的核心逻辑其实不复杂读取评测集 → 逐条调用 Agent → 记录 Trace → 自动判定 → 汇总报告。但魔鬼在细节里。首先Agent 的调用必须是可复现的。这意味着你需要固定随机种子如果模型 API 支持、固定温度参数、固定工具版本。我一般会把温度设成 0 或者一个很小的值比如 0.1确保同一个输入每次跑出来的结果尽量一致。如果模型 API 不支持固定种子那就同一个任务跑 3 次取多数结果用投票来降低随机性。其次Trace 的记录要足够细。每一步的 Prompt、模型的原始输出、解析后的工具调用、工具返回的原始结果、最终输出全部要落盘。我通常用 JSON Lines 格式存储每行一个任务的完整 Trace方便后续用脚本分析。Trace 里还要带上时间戳和 Token 计数方便算效率指标。4.2 自动判定规则引擎 模型裁判的组合拳自动判定是评测流水线里最难的部分。对于有精确答案的任务规则引擎就够了——字符串匹配、数值比较、JSON 结构校验这些都能用代码搞定。但 Agent 的很多任务输出是自然语言没法用规则判定。这时候就需要“模型裁判”。用一个能力足够强的模型通常是比被测 Agent 用的模型更强的那一档来充当裁判给它任务描述、预期结果、判定标准、Agent 的实际输出让它判断是否通过。模型裁判的 Prompt 要写得非常具体把判定标准拆成一条条可检查的项让裁判逐项打勾。但模型裁判也有坑。第一裁判模型可能有偏见比如倾向于给“看起来流畅”的输出打高分哪怕内容是错的。第二裁判模型和被测模型如果是同一个家族可能存在“互相包庇”的情况。我的应对策略是规则引擎能判的绝不交给模型裁判模型裁判只用于开放式任务同时定期用人工抽检来校准模型裁判的准确率如果发现裁判和人工判断的一致率低于 90%就要重新调整裁判 Prompt。还有一个实用技巧对于关键任务我会让两个不同的裁判模型分别判定如果两者结论不一致就自动标记为“需人工复核”。这样既保证了自动化效率又不会漏掉争议案例。4.3 回归测试的触发时机和对比分析评测流水线搭好之后什么时候跑我的经验是每次 Prompt 修改、每次模型版本切换、每次工具接口变更都必须触发全量回归测试。不要觉得“只改了一个标点”就不跑Agent 系统里一个标点的改动可能导致完全不同的行为。回归测试的结果要和上一个版本做对比。我一般会生成一个对比报告包含总体指标变化、按任务难度分层的指标变化、新增失败案例、修复的失败案例。重点关注两类变化一是核心指标下降超过 5% 的必须定位原因二是某个难度层级的指标突然恶化往往意味着改动引入了针对性的问题。对比分析的时候不要只看数字。我会把新增的失败案例逐条看一遍判断是“真回归”以前能过现在过不了还是“评测集本身有问题”比如预期结果写错了。如果是真回归立刻回滚或者修复如果是评测集问题修正评测集后重新跑。5. 失败归因从“又挂了”到“知道为什么挂”5.1 建立失败分类体系没有归因的评测就是耍流氓。你跑了一百条挂了二十条然后呢不知道这二十条为什么挂下次改 Prompt 还是瞎改。我的做法是建立一套失败分类体系把每次失败都归到具体的类别里。我常用的分类维度有六个意图理解错误没搞懂用户要什么、规划错误任务拆解错了或者步骤顺序不对、工具选择错误用了不该用的工具或者该用没用、参数错误工具调用的参数不对、结果解析错误工具返回的结果没理解对、生成错误最终输出有事实错误或格式问题。这六类基本能覆盖 90% 以上的失败场景。归因的过程可以半自动化。对于工具调用相关的失败评测系统可以直接从 Trace 里检测出来——比如工具返回了错误码但 Agent 没有处理或者调用的工具名称不在注册列表里。对于意图理解和生成相关的失败就需要模型裁判或者人工来判断了。我一般会让评测系统先自动打一个初步标签然后人工复核时修正。5.2 从失败分布看系统瓶颈归因做完之后把失败案例按类别统计你会得到一张“失败分布图”。这张图比任何单一指标都更能告诉你系统的瓶颈在哪。如果意图理解错误占比最高说明你的 Prompt 在意图识别环节有问题可能需要增加意图分类的 few-shot 示例或者引入一个专门的意图识别前置步骤。如果工具选择错误占比高说明工具的描述不够清晰或者 Agent 对工具能力的理解有偏差需要优化工具注册信息的写法。如果结果解析错误占比高说明工具返回的数据结构太复杂Agent 解析不了可能需要加一层结果预处理。我遇到过一个典型案例某个 Agent 的失败率一直降不下来归因之后发现 60% 的失败都是“结果解析错误”。进一步看 Trace 发现工具返回的是一个嵌套三层的 JSONAgent 在提取深层字段时经常搞错路径。解决方案很简单——在工具和 Agent 之间加一个适配层把嵌套 JSON 拍平成扁平结构再返回给 Agent。改完之后失败率直接降了一半。5.3 失败案例的“复活”机制失败案例不是用来存档的是用来“复活”的。每修复一个失败类别就要把对应的失败案例重新跑一遍确认修复有效。我一般会维护一个“失败案例池”每次回归测试时除了跑全量评测集还会额外跑一遍这个池子里的案例看修复进度。更进一步我会把已经修复的失败案例升级为评测集的正式条目。这样做的目的是防止回归——你修好了这个问题不代表下次改动能保持住。把它纳入常规评测集每次回归都会检查才能真正锁住修复成果。这个“失败→归因→修复→纳入评测集”的循环就是评测闭环的核心飞轮。每转一圈Agent 的稳定性和评测集的覆盖度都提升一点。转到一定圈数之后你会发现 Agent 的“偶尔成功”变成了“稳定可用”而你对它的能力边界也有了清晰的认知——你知道它在什么情况下会失败也知道失败之后大概是什么原因。6. 让评测闭环真正转起来的几个实操心得6.1 评测频率和成本的平衡全量评测集跑一次的成本不低尤其是当评测集有几百条、每条都要调用多次模型的时候。如果每次改一个 Prompt 都跑全量Token 账单会很难看。我的策略是分层评测日常开发用“冒烟集”20-30 条核心任务快速验证基本功能没坏版本发布前跑“全量集”做完整回归每周跑一次“扩展集”包含边界案例和长尾任务做深度体检。冒烟集的选取很关键它必须覆盖所有核心场景且对改动敏感。我一般会从全量集里挑那些“历史上失败次数最多”和“最近一次回归中表现波动最大”的任务组成冒烟集。这样每次跑冒烟集都能最快发现回归问题。6.2 人工抽检不可省略自动化评测再完善也不能完全替代人工。我坚持每周做一次人工抽检从评测结果里随机抽 10-20 条自己看 Trace判断自动判定是否准确。这个习惯帮我发现过好几次模型裁判的误判——有些输出看起来格式完美但内容其实是胡编的模型裁判没看出来人工一看就发现了。人工抽检还有一个作用发现“评测集没覆盖到的新问题”。自动化评测只能检查你已经想到的问题但用户的实际使用中会出现你没想到的输入模式。人工抽检的时候我经常会发现一些“这也能行”的案例然后把它补充到评测集里。6.3 评测闭环的“最后一公里”和 CI/CD 打通评测流水线如果只是手动触发那它迟早会被遗忘。真正让评测闭环转起来的关键一步是把它和 CI/CD 打通。我的做法是在代码仓库里配置一个钩子每次提交涉及 Prompt、工具定义、模型配置的改动时自动触发冒烟集评测。如果冒烟集通过率低于阈值比如 90%直接阻止合并。这个机制一开始会有点烦因为经常拦住一些“我觉得没问题”的改动。但正是这种“烦”逼着你在提交前先本地跑一遍评测而不是把问题留到线上。跑了一段时间之后团队会形成习惯改 Prompt 之前先想好怎么验证改完之后先跑评测再提交。这个习惯一旦养成Agent 的迭代速度反而会加快因为你知道每次改动都有安全网兜着。6.4 一个容易被忽视的细节评测环境的隔离最后说一个很小但很致命的坑评测环境和生产环境必须隔离。我见过团队在评测时直接调用生产环境的工具接口结果评测产生的数据污染了生产数据库或者评测的高频调用触发了生产接口的限流导致线上服务抖动。正确的做法是评测环境使用独立的工具 Mock 或者沙箱环境所有工具调用都指向测试数据。如果某些工具实在没法 Mock比如依赖外部实时数据的那至少要做速率限制并且在评测期间监控生产环境的健康度。这个细节看起来不起眼但一旦出事就是生产事故。评测闭环这件事说到底就是把“凭感觉”变成“看数据”把“碰运气”变成“可复现”。它不会让你的 Agent 一夜之间变聪明但它能让你清楚地知道 Agent 现在有多聪明、哪里还不够聪明、以及每次改动是让它更聪明了还是更笨了。有了这个认知从“偶尔成功”到“稳定可用”就只是时间问题了。