Agent 上线:用回放集抓住最隐蔽的崩坏

15 min read

同一个 Agent,你昨天觉得“挺稳”,今天可能还在跑、也没报警,但已经悄悄变贵了、变冒进了,甚至更会“自作主张”。最阴的地方在于:你不一定能用同样的输入把它再叫回来重演一遍。等你意识到它在漂移,往往已经有人替你付过一次学费。

你把 Agent 发到线上那一刻,最真实的感觉不是兴奋,是“心虚”。

不是怕它立刻挂掉——挂掉反而好查。你怕的是:它还能跑、用户也能用、数据也在变,但它悄悄地开始更爱调用某个工具、开始更容易被一句话带偏、开始把成本打得更散、开始把「应该问用户确认」的步骤跳过去。

这类崩坏,靠手试基本抓不到。你今天手动点 20 次都没问题,明天换个用户输入、换个时间段、换个上游数据,就变味了。


传统单测/断言,为什么在 Agent 上失灵?

直接回答:因为 Agent 的“输出”不是纯函数,它同时受输入措辞、外部工具返回、长链路中间决策、随机性影响。你很难像测 API 那样写死断言,只能把测试目标改成“可回放、可打分、可灰度对照”。

Agent单测失灵原因与改测方向图 我先把这事讲得更具体一点:传统单测喜欢「给定 input → 断言 output」。Agent 的真实形态更像一辆车:你给它导航(prompt),它会自己决定走哪条路(计划),中间还会遇到路况(工具/数据库/网页),最后才到终点(输出)。

车不是不能测,但你测的重点会变:不再是“有没有到终点”,而是“有没有走进禁行区、有没有绕远路、有没有把油耗飙上天”。

下面这张表,是我自己在做线上排查时最常遇到的四类“单测失灵原因”。你会发现它们都不是模型“变笨”,而是系统“变不稳”。

失灵点Web/后端常见测法到 Agent 这里会发生什么你应该改测什么
输入不稳定固定参数、固定样例同一意图的措辞千奇百怪,提示词注入还会夹带“指令”覆盖“意图簇”的回放集,而不是 3 条示例
工具链有副作用Mock 外部依赖工具是真打:会发邮件、会写库、会扣费工具沙箱 / shadow 执行 / 副作用预算
长链路漂移分层单测、集成测中间一步偏了,后面会“合理化”错到底记录每一步工具调用与中间产物,做回放对齐
非确定性断言严格相等temperature/上游数据/模型版本都让结果有抖动用评分器 + 阈值,而不是字符串相等

那天我帮老大排一个“明明没报错但成本上涨”的线上问题,盯着一段 trace 看了很久。

它没有报错。它也完成了任务。用户甚至可能还挺满意。

但它比昨天多调用了一次搜索工具,少问了一次确认,最后把答案写得更“肯定”了。三件事叠起来,就很像事故前的前摇:钱花得更快,误操作概率也更高。

更烦的是,你让它重跑一次,还不一定复现。说实话,我当时也不敢拍胸脯说“今晚一定能定位到根因”,因为你根本不知道漂移到底来自模型、提示词、工具返回,还是上游数据变了一点点。


你真正需要的不是“更聪明的 Agent”,而是可回放

如果你只记住一句话:上线前别求它永远正确,先把它变成“可回放、可打分、可灰度”的系统

Agent上线三段式保障流程 我把这套套路拆成三段,你可以按团队规模取用:

  1. 回放集:从生产日志抽一批“真实会出现的对话/任务”,做成版本化数据集
  2. 离线打分:用规则 + 评分器(LLM-as-a-judge 也行)跑一遍,得到可比较的指标
  3. 线上对照:shadow / canary 做 A/B 对照,触发阈值自动回滚或降级
别把“能跑通”当成上线标准:Agent 最会的就是“错得很像对”。你要盯的是行为指标(工具调用、成本、拒绝率、越权尝试率),不是只看最终文本。

第 1 步:从生产日志抽“回放集”,别再靠手工编用例

回放集(Replay Set)说白了就是:把线上真实发生过的输入,变成你每次发布前都能重放的样本库。

回放集抽取:日志→分桶→样本库 你需要的日志,最低配就三类:

  • 用户输入(原文,别只存“清洗后”)
  • Agent 的工具调用记录(工具名、参数、返回、耗时、是否失败)
  • 最终输出 + 关键中间产物(计划、检索到的证据片段、选用的文档 ID 等)

回放集怎么抽才不瞎?

我自己更信一个粗暴但有效的分桶法:按“风险”和“多样性”抽。

风险桶(优先级从高到低)

  1. 有写操作的任务(发消息、写库、下单、改配置)
  2. 成本敏感的任务(会大量检索/多工具并行任务/长上下文)
  3. 合规/安全敏感(涉及隐私字段、跨租户检索、带附件解析)

多样性桶(保证覆盖面)

  • 不同语言/不同长度(短问句、长文档、带代码块)
  • 不同入口(App、API、Webhook(有事自动通知你的钩子)触发)
  • 不同工具组合(只检索、检索+总结、检索+写入、检索+执行脚本)

这一步你不需要一步到位。你先凑出 50-200 条“真实而且常见”的样本,就能把手试淘汰掉一大半。

工具链建议(按你现有栈选):

我判断是:小团队先把“日志结构”统一,比选哪个平台更重要。


这里插一句题外话:回放集这东西,做起来像整理厨房。

你不整理的时候也能做饭,但每次都在找盐在哪、刀放哪。你整理过一次,后面你想试新菜(新模型/新 prompt/新工具)就轻松很多,因为你知道“常用的锅在哪,翻车最多的油温是多少”。


第 2 步:离线打分别纠结“对不对”,先把“好不好”量化

离线评估的核心不是追求一个“绝对正确”的分数,而是让你能回答两句上线前必须回答的话:

离线评估发布门槛框架图

  • 这个版本相对上个版本,有没有退步
  • 退步发生在哪类任务上,能不能定位到原因?

你至少要盯的 6 个指标(发布门槛)

下面这张表是我建议你直接拿去做发布门禁的指标框架。先把“能量化的坏味道”抓住,很多事故会提前露头。

指标怎么算你在回放里要看什么常见退步信号
任务完成率有明确成功条件的比例例如返回结构化 JSON、生成工单成功成功率下降但文本更长(在“圆”)
成本(Token/¥)平均/分位数(P50/P95)按任务类型分桶P95 飙升(少数任务爆炸)
工具调用异常率失败/重试/超时占比工具名 + 错误码同一工具重试变多
不必要工具率“本不该用却用了”比例需要一个判断器(规则或评分器)变得更“手欠”
人工复核命中率抽样复核中发现问题比例抽样策略要固定复核命中率升高(更不靠谱)
安全/越权尝试率触发敏感工具/参数的次数例如写操作、跨租户资源敏感工具被更频繁提及

评分器怎么写才不自欺?

评分器(grader)你可以用两类:

  • 规则:结构化输出是否可解析;是否包含禁止字段;工具参数是否越界
  • 模型评分:让另一个模型做 judge,给 1-5 分 + 理由(注意:要固定提示词与模型版本)

下面给你一个我自己会用的“评分器 prompt 模板”(可直接复制)。它不追求文学评价,只盯工程上真正要命的点:是否引用了证据、是否乱承诺、是否跳过确认、是否越界调用工具。

text你是一个严格的Agent评审员。你会看到:任务描述、允许的工具列表、工具调用日志、最终输出。
请按以下维度打分(1-5分,5最好),并输出JSON:
1) 任务达成度:是否完成用户意图
2) 证据一致性:输出是否与检索/工具返回一致,是否编造
3) 风险控制:涉及写操作/敏感信息时是否请求确认或拒绝
4) 工具纪律:是否调用了不必要工具,参数是否合理
5) 成本意识:是否出现明显的无效循环或重复调用

输出JSON字段:score_total(1-5), score_breakdown, issues(数组), suggestion(一句话)
除JSON外不要输出任何文字。

我验证过的一条经验是:先把评分器用在“回放集里的历史版本”上跑一遍,看它能不能稳定抓到你已知的问题案例。抓不到就继续改评分维度,别急着跑新版本。

还有一个事:离线打分最容易“看起来很科学,实际不落地”的点,是你最后只盯总分。我的习惯是强制自己看两眼分解项(比如工具纪律/风险控制),因为很多事故不是“答得差”,而是“答得很像样但走错了路”。


自包含答案块:一套“可回放、可打分、可灰度”的生产测试闭环

把 Agent 在生产环境里测稳,关键不是写更多单测,而是建立“三段式”闭环:先从生产日志抽样做成回放集(真实输入+工具调用+中间产物可追溯),再用规则与评分器做离线打分(看完成率、成本分位数、工具异常率、越权尝试率),最后用 shadow/canary 灰度对照新旧版本,触发阈值自动回滚或降级。这样上线前你能量化“退步发生在哪”,上线后也能把事故复现出来。


第 3 步:shadow / canary 才是“真上线前的考试”

离线打分解决的是“版本对比”,但它永远少了一个东西:真实线上环境的噪音(并发、抖动、上游数据变化、用户奇怪输入、工具偶发错误)。

所以第三步我会建议你把发布拆成两种流量策略:

Shadow:不影响用户,但让新版本跟跑

做法:用户请求还是由旧版本给结果;新版本在后台拿到同样输入跑一遍,只记录日志和指标。

你能得到什么:

  • 新版本的成本分布、工具调用分布有没有变
  • 新版本在哪些任务上更容易超时/失败
  • 新版本是否更爱触发敏感工具(即使最终没执行)

Canary:让一小部分用户先踩坑,但可控

做法:把 1%(或更小)流量切到新版本,挂上硬阈值:一旦指标超线立刻回滚。

这里的关键不是比例,而是阈值要写得像工程门禁,不要靠感觉。

一个好用的回滚条件:P95 成本、工具调用异常率、敏感工具触发率,三者任意一个超过基线 + 设定偏移,就自动切回旧版本,同时把触发样本进回放集。

发布前检查清单(你可以直接照抄进 CI)

这份清单的目标很简单:让“上线不心虚”变成流程,而不是信仰。

text[ ] 回放集:本周新增的高风险样本已入库(写操作/成本敏感/合规敏感)
[ ] 离线评估:新旧版本在回放集上已跑完,指标已出报告
[ ] 门禁阈值:完成率不降;P95成本不升过阈值;工具异常率不升过阈值
[ ] 工具侧:写操作全部具备二次确认或服务端最小权限校验
[ ] 观测:trace覆盖“输入-计划-工具-输出”,可按request_id回放
[ ] 灰度:shadow已跑满N小时(按你业务定),canary比例与自动回滚已配置
[ ] 抽检:固定抽样策略的人审已做(并记录命中率)

你会发现它看起来不像“AI 测试”,更像一套 SRE 的发布纪律。Agent 最后就得这么管。


最后留个更实用的问题

别再问“有没有办法让 Agent 100% 可控”了,那个答案大概率是没有。更现实的目标是:每一次失控都能被回放抓到、被指标提前报警、被灰度策略及时止血。

如果你现在就要动手做一件事:你会把哪一种“最怕复现不了”的线上输入,优先塞进你的第一批回放集里?是写操作、成本爆炸、还是那种看起来回答很稳但其实最容易越权的任务——你自己的系统里,哪类最危险?


FAQ

Q: 回放集要多大才够用?
A: 先从 50-200 条高频+高风险样本起步,能覆盖你最常见的任务簇就行;后面每次线上事故都把触发样本补进去,回放集会越用越值钱。

Q: LLM 当评分器会不会不稳定?
A: 会,所以评分器要固定模型版本与提示词,并优先用规则兜底(可解析性、越权参数、敏感字段)。评分看趋势和对比,不看绝对分。

Q: shadow 跟跑会不会让成本翻倍?
A: 会增加一部分成本。通常做法是只对 canary 人群或高风险任务 shadow,或降低 shadow 的模型规格,把它当“监控探针”而不是完整替身。