模型没换、数据没动、提示词改了又改,Agent 的评估分数还是往下掉。上周帮朋友排查一个客服 Agent 项目,评估报告摊开在桌上:任务完成率从 82% 掉到 63%,多轮会话满意度跌了一整个等级,更离谱的是同一个错误在三天里反复犯了九次。朋友的第一反应是换模型,大模型账单翻了两倍,效果纹丝不动。我跟他一起把全链路日志拉出来翻了两个多小时,结论特别残酷:模型根本没变笨,是它的反馈回路坏了。
“反馈回路”这四个字听起来像控制论术语,但在 Agent 系统里它就是一套最朴素的机制:Agent 有没有办法知道自己做错了,有没有能力从错误里恢复,有没有渠道把经验沉淀下来。一棵树不会因为看不到自己的影子就停止生长,但 Agent 会。当它看不见自己行为的后果、接不到失败的信号、评判标准又前后漂移的时候,它就会表现出一种奇特的状态——单看某个回答好像还行,整体任务却一塌糊涂。这不是模型能力退化,而是环路被切断了。
这篇东西想把我在实际项目里排查“Agent 变笨”的经验完整写出来,适合正在做 Agent 开发、评估、上线维护的工程师,也适合那些被“模型效果下降”折腾得睡不着的产品经理。我会从反馈回路的拆解讲起,再到四种典型症状、一整套修复流程、以及一份可以直接抄的排查速查表。
1. 先把“反馈回路”拆开:它不是日志,是闭环
很多人以为反馈回路就是“记录日志”,这是最大的误解。日志只负责存,反馈回路要负责让 Agent 用起来。一个健康的反馈回路至少包含四个环节,缺一个都会让模型表现失真。
1.1 回路里的四个环节:感知、判断、修正、沉淀
感知段负责“看见结果”。比如工具调用返回了什么状态码、用户是否点了踩、任务执行到哪一步超时了、上下文里哪些信息被丢弃了。判断段负责“评估好坏”,比如这一步输出和目标是否一致、重试是否有价值、当前计划是否已经偏离主线。修正段负责“调整动作”,比如换一种工具策略、向用户澄清需求、压缩上下文或者主动终止任务。沉淀段负责“把经验留下来”,比如把失败样例写进 few-shot 示例、更新记忆库、补充评估用例。
模型推理只发生在判断段这一小块区域。你换一个更强的模型,它能改善判断质量,但如果感知段采集不到数据、修正段的动作发不出去、沉淀段什么都没记住,那再强的模型也只是在一间黑屋子里做推理。我见过一个项目把 GPT-4 换成更强的模型,指标反而掉了,原因就是上下文里积累了太多重复失败的历史,模型被噪声带偏,新模型更聪明,也更容易被错误上下文误导。
1.2 反馈回路的物理载体:日志、追踪、缓存、记忆、评估服务
在工程视角下,反馈回路不是一个抽象概念,而是由一组具体组件构成的链路。任务追踪系统记录每一次调用的入参和出参;消息队列负责把工具调用结果送回主流程;记忆服务负责读写长期上下文;评估服务定时跑回归测试集;缓存层决定反馈信号是即时到达还是延迟一天。
这些组件任何一个出问题,反馈链路就断了。最常见的是工具调用环节:代码里调了一个天气 API,返回了 HTTP 500,但你的代码只取了响应体里的 message 字段,把 “Internal Server Error” 当普通文本拼进 prompt。模型收到的是“服务说 Internal Server Error”,它不知道这是权限问题、限流问题还是参数问题,只能猜。猜错的概率当然高。这就是典型的感知段损坏——反馈信号在物理层面就没传回来。
1.3 为什么“模型没变笨”但“Agent 变笨了”
这个问题一句话就能解释:模型在跑分测试里表现稳定,但在真实 Agent 环境里依赖的输入质量变了。两者不是一回事。模型能力是静态的,Agent 表现是动态的。反馈回路是连接“模型能力”和“业务表现”之间的传动轴,传动轴松了,发动机功率再大,车轮也不转。
所以评估完发现 Agent 变笨,第一步不是怀疑模型权重,而是怀疑这个传动轴。传动轴坏了的表现很具迷惑性——它不是彻底不工作,而是时好时坏。有时候错误反馈能传回来,Agent 就能纠正;有时候传不回来,Agent 就在原地打转。这种间歇性故障会让你误以为是模型随机性太大。
2. 诊断之前,先看懂“变笨”的四种典型症状
反馈回路坏掉不是只有一个表现。我在不同项目里见过四种截然不同的“变笨”模式,每种对应的故障点完全不一样。先看懂症状,再动刀。
2.1 症状一:单轮回答还行,多轮任务永远走不到终点
现象是 Agent 在单轮对话里表现出色,一问一答很精确,但一旦给出一个需要三步以上的任务,它总是完成两步就停了,或者做完第二步直接开始总结。审核对话记录会发现,Agent 在每一步之后没有把“距离最终目标还有多远”重新纳入上下文。
这属于修正段失效。典型原因是目标信息在上下文中被挤掉了。上下文窗口有限,中间步骤越多,最初的用户目标越容易被后续的工具输出淹没。如果反馈回路里没有“目标保持”机制——比如定期把最核心的约束重新注入上下文——模型就会把局部任务当成全部任务。修复方向不是换模型,而是在每一步规划后强制重写一次短期目标卡片。
2.2 症状二:同一个错误反复犯,Agent 像失忆了一样
昨天它调用订单接口时把排序参数写错了,今天同样场景下它又错了一遍。你去看错误日志,发现昨天的错误和今天的错误一模一样,连报错行号都一样。这说明 Agent 虽然“经历”了错误,但没有“消化”错误。
这就是沉淀段坏了。错误发生之后,反馈回路里没有任何环节把这次失败的经验写入记忆或者 few-shot 示例。模型是无状态的,它每次对话都是从零开始,如果你不把经验存下来,它下次只会重新犯错。很多团队加了记忆模块,但记忆模块的写入经常静默失败——写入接口超时了、埋点没上报、向量库索引构建失败,代码里 catch 了一下就吞掉了异常。模型永远看不到失败的记忆,当然只会重蹈覆辙。
2.3 症状三:离线评估分数掉得厉害,人工抽查却觉得还行
这是最让人抓狂的一种。自动化评估平台打了 60 分,你自己点开十个案例,觉得发挥还挺正常。这种背离出现时,事故往往不在 Agent 本身,而在评估服务。
评估集被污染了。比如你把最近一周线上失败的对话加入了回归测试集,但没注意这些对话里已经有新的回复覆盖了旧答案;或者评分 prompt 换了个新版本,措辞变了,分数基准就漂了;又或者模型升级后输出格式从 Markdown 变成了纯文本,LLM-judge 对格式特别敏感,给你扣了一堆格式分。反馈回路里的“判断段”失真了,你就接收到了一堆错误的负反馈,然后拿着错误的信号去调 Agent,越调越乱。
2.4 症状四:并发一高就变傻,低峰期啥事没有
你压测到 20 并发,错误率飙升,降到 2 并发,一切正常。很多人把这种问题归咎于模型限流,但很多时候是反馈链路在高并发下断了。
并发场景最常见的故障点有三个:一是日志采集端丢数据,采样率在压力下自动降级,导致追踪链路断成碎片;二是上下文读写出现竞态,两个并发任务同时更新同一个用户记忆,后写覆盖先写,Agent 记忆里存了另一个任务的中间状态;三是缓存淘汰策略太激进,高频用到的工具结果被提前挤出,Agent 不得不反复调用慢接口,超时概率上升。这些都不是模型问题,是反馈系统的吞吐问题。
3. 反馈回路故障的实战修复流程
诊断完成之后,真正动手修链路时我一般走四步。这套流程我用了很多次,不敢说覆盖所有场景,但至少能在半天之内把问题范围缩小到一个明确的组件。
3.1 第一小时只做一件事:把 Trace 完整串起来
没有完整的追踪数据,后续所有判断都是瞎猜。我要求所有 Agent 项目的入口层都必须生成一个 request_id,然后这个 id 贯穿模型调用、工具调用、中间缓存、记忆读写和最终回答。日志里每一行核心记录都要带这个 id。以下是入参里最基础的接入方式:
import uuid import logging request_id = uuid.uuid4().hex logger = logging.getLogger("agent") logger.info({"event": "agent_start", "request_id": request_id, "task": user_query}) result = agent.run(user_query, metadata={"request_id": request_id}) logger.info({"event": "agent_end", "request_id": request_id, "result": result, "tokens": token_usage})关键细节是 metadata 里传 request_id 还不够,工具调用层也必须把它透传出去。第三方 HTTP 请求的 header 里加一个 X-Request-Id,下游服务报错时你能通过同一个 id 把上下游日志拼接起来。做过分布式追踪的人都知道,分布式系统排障最大的痛点是日志对不上号,Agent 系统里也一样——模型调一个工具,工具再调一个外部 API,三层日志如果各用各的编号,你根本无法判断是模型决策错了还是工具执行错了。
串完追踪之后,重点看三类记录:错误记录、截断记录、重试记录。错误记录是工具调用失败的状态码和 message;截断记录是上下文超限时模型到底丢掉了哪些内容;重试记录是每一次重试发生的时间间隔和重试原因。这三类是反馈回路最脆弱的部位。
3.2 用回归基线区分“模型问题”和“链路问题”
很多人排查时控制变量做得不严谨。今天换个模型,明天调个 prompt,后天又改了上下文压缩策略,最后指标下降了都不知道是哪一步引入的。我这套办法很简单:固定一个 30 到 50 条的回归基线集,每天跑一次,只允许一个变量发生变化。
回归基线集的设计有几个硬性要求。选例必须来自真实线上请求,覆盖成功和失败两类;只改输出格式或者回答长度,不许改任务类型;每条样例都必须有可以自动比对的标准答案。跑分时固定模型版本、固定 temperature 参数、固定 prompt 模板,唯一允许变的是 Agent 框架代码和链路配置。
举个例子,我最近排查一个项目的评估下跌,第一天跑分 78 分,第二天还是 78 分,第三天突然降到 61 分。前三天我没动过关模型,那问题必然出在链路或者外部依赖上。后来查出来是某个搜索工具改了返回结果格式,时间字段从字符串变成了时间戳,Agent 解析不了,所有涉及时间比较的步骤全军覆没。如果不是固定了模型变量,我可能会浪费时间怀疑模型 API 是不是悄悄换了版本。
模型层面也得做对照。A/B 两个模型在相同链路上分别跑回归集,如果 A 模型分数稳定、B 模型分数跳水,那才敢说是模型的问题。不要一上来就全量切模型,要在影子环境里跑两天。影子模式下新模型和旧模型同时消费同样的线上请求,只记录不对外返回,这是成本最低的模型对比方式。
3.3 修链路里最常见的五个薄弱点
回归基线和 Trace 都准备好了,接下来就是修复。根据我的经验,反馈回路最常见的故障点集中在这五处,按出现的频率排序:
上下文截断静默发生。Agent 的上下文快到上限时,系统会把历史对话摘要压缩,但压缩后的文本丢了关键约束。修复方案是记录每一次截断动作,同时把“不可压缩字段”做成配置,比如用户 ID、订单号、时限要求等关键字段跳过摘要,原样保留。
工具调用错误被降级成普通文本。工具返回了结构化错误,代码层直接 str(error) 塞进 prompt。修复方案是定义统一的 AgentFunctionError,包含 error_code、retryable、suggestion 三个字段,让模型能判断该重试还是该换方案。
重试机制没有退避策略。Agent 调用限流接口失败后立即重试,连续重试三次全失败,还白白消耗 token。修复方案是重试间隔按指数退避,比如第一次 1 秒、第二次 4 秒、第三次 16 秒,超过三次就放弃并向上抛出结构化错误。
记忆写入失败被吞掉。记忆服务超时后 catch 到异常但没有任何降级处理,Agent 在同一个会话里读不到自己刚存的短期记忆。修复方案是记忆写入后必须返回写成功的确认,否则该步标记为“记忆不可用”并降级为无记忆模式。
评估集被旧数据污染。回归测试集里的样例被新的线上回复覆盖,或者同一批失败案例反复进入测试集导致模型开始“背答案”。修复方案是评估集按版本管理,每次更新都要记录变更时间,定期检查重复度。
这五处占了反馈回路故障的八成以上。修的时候不要贪多,一次只改一个点,改完立刻在回归基线上验证,确认指标没有反向波动再改下一处。
3.4 上线后指标:用在线信号替代“感觉”
离线回归只能抓大问题,真正让反馈回路长期健康运转,必须依赖在线指标。我维护 Agent 服务的时候,看板上有六个核心指标,任何一个触发告警我都立刻排查:
| 指标名 | 计算方式 | 告警阈值 |
|---|---|---|
| 工具调用失败率 | 调用失败次数 / 总调用次数 | 超过 15% |
| 重试率 | 发生重试的会话数 / 总会话数 | 超过 30% |
| 上下文使用率 | 每次请求 token 数 / 窗口上限 | 超过 85% |
| 会话完成率 | 成功结束会话数 / 总会话数 | 低于 60% |
| 用户负面反馈率 | 点踩或投诉次数 / 总交互数 | 超过 5% |
| 记忆读写失败率 | 记忆服务失败次数 / 总读写次数 | 超过 1% |
这六个指标里我特别看重“记忆读写失败率”。它平时数值很低,容易被忽视,但一旦超过 1%,说明 Agent 的沉淀段已经在出问题了。1% 听起来很低,按一天十万次读写算,就是一千次失败,这一千次失败里有多少次发生在关键时刻,谁也说不准。
在线指标的采集尽量用异步队列,不要阻塞主流程。但也要注意异步采集也有自己的风险——队列积压会导致实时性变差,反馈信号延迟。我见过一个项目因为日志队列积压了半小时,Agent 都跑完任务了,评估系统才收到结果,导致响应不及时。解决方案是给队列加监控,积压超过阈值就放弃低优先级日志,保住决策日志的完整。
4. 反馈回路设计里容易忽略的细节
修复常见故障只能把系统拉回及格线,想让 Agent 长期保持聪明,还得注意几个设计层面的细节。这些坑不太起眼,但每一处都能让反馈回路“带病运行”。
4.1 反馈信号的粒度:不要只给最终对错
很多团队设计的反馈回路只有一个信号:用户最终点了赞还是踩。这个信号粒度太粗,模型无法判断自己到底是哪一步做错了。用户点踩可能因为回答太慢,也可能因为推荐了错误的产品,如果只给一个整体负分,模型只能盲目调整。
我建议在关键节点埋“事件级反馈”。工具调用成功算一个事件,上下文压缩算一个事件,用户中途纠正了某个信息也算一个事件。把这些事件喂给评估模型,让判断段知道具体是哪一步偏离了预期。比如用户先说“我要红色的”,Agent 生成了蓝色商品,用户纠正说“不是蓝色是红色”,这就是一次非常明确的反馈信号,可以拆出来单独作为一条训练样本。
4.2 评估集污染:别让 Agent 学会“背答案”
Agent 评估和传统模型评估有个本质区别:Agent 是动态交互的,测试集里的历史对话一旦被新回复覆盖,旧答案就失效了。更麻烦的是,如果你反复使用同一批失败案例做回归,模型通过上下文学习慢慢会“适应”这些特定的输入,分数虚涨,但真实性能没变。
维护评估集的有效做法是分池管理。一个雷打不动的盲测池,每周从线上随机抽取新案例加入;一个回归池,只在框架升级时调用;一个专项池,针对工具调用、多轮记忆、长上下文这些具体能力单独测试。三个池子互相独立,避免重叠。盲测池里的案例必须保证模型没在训练和调优过程中见过,否则就失去盲测的意义了。
4.3 并发环境下的反馈失真
高并发下反馈回路的失真比想象中更隐蔽。最典型的例子是记忆更新竞态:用户同时打开两个会话,会话 A 和会话 B 都要更新同一个长期记忆条目。如果两个更新操作没有加锁,后提交的会覆盖先提交的,最终记忆里只剩会话 B 的状态。用户下次对话时,Agent 只记得 B 的内容,对 A 的内容一无所知,表现就像失忆了。
解决办法是给记忆读写加乐观锁,写入时带上版本号,版本冲突则丢弃较旧的那次更新。另外,异步日志采集在并发下也可能乱序,给每条日志加一个单调递增的 sequence_id,排障时按序号重排。没有 sequence_id 的话,同样一条错误日志可能被错误地归因到另一个请求上,你就又开始怀疑模型了。
4.4 人工反馈的闭环价值:比 LLM-judge 更可靠
我见过太多团队过度依赖 LLM-judge 做自动评估,完全忽视人工审核。LLM-judge 本身也是一个模型,它也有自己的偏好和盲区,用它给 Agent 打分等于让一个模型评判另一个模型,反馈信号里天然带噪声。
人工反馈应该设计成一套激励机制来保证闭环。比如客服 Agent 每处理完一个会话,弹出一个一次性的评分按钮,客服人员一键标记“解决/未解决”。这些人工标签比模型自评可靠得多,因为它们来自真实用户场景。把这些人工标签回填进偏好数据集,定期微调评估模型或者优化提示词,反馈回路的判断段才能越来越准。我每两周做一次人工结果与自动评分的一致性比对,如果偏差率超过 10%,优先检查评估服务而不是 Agent 本身。
5. 常见问题排查速查表
最后把这些年积累的排查经验做成速查表,遇到“Agent 变笨”可以先对着表过一遍。表格不能覆盖所有情况,但足以应付九成以上的线上事故。
| 现象 | 可能坏掉的回路环节 | 快速检查动作 | 修复方向 |
|---|---|---|---|
| 多轮任务中途失焦,单轮表现正常 | 修正段 | 查看日志里上下文截断记录,确认用户原始目标是否还在上下文里 | 增加短期目标保持机制,关键约束不可压缩 |
| 同一个错误反复出现 | 沉淀段 | 查看记忆写入日志是否有异常,确认失败样本是否入库 | 修复记忆写入失败被吞掉的问题,增加写入确认 |
| 离线评估分数掉,人工体验正常 | 判断段 | 对比评估集版本和评分 prompt 版本是否变更 | 回到上一个评估集版本,检查评分标准漂移 |
| 并发一高效果就崩盘 | 感知段 | 查看日志采样率是否降级,检查记忆更新是否有版本冲突 | 加日志缓冲和 sequence_id,记忆加乐观锁 |
| 工具调用失败后模型瞎猜 | 感知段 | 查看工具错误透传是文本还是结构化对象 | 定义 AgentFunctionError,透传 error_code 和 retryable |
| 模型突然输出变短或变长 | 评估集/判断段 | 查看是否升级了模型版本或修改了 temperature | 回滚版本,或者把在线效果与离线分数分开评估 |
| Agent 经常重复调用同一个工具 | 修正段 | 查看工具返回值有没有被缓存,决策循环是否走出死循环 | 加工具调用去重和循环检测,超过 N 次强制切换策略 |
这张表的使用方法不是逐行对照,而是先定位症状最接近的一行,然后用 Trace 数据验证。比如你发现工具调用失败后模型在瞎猜,先在日志里搜最近的工具错误记录,看错误对象有没有 status_code 和 error_code。如果只有一段 string,直接改工具调用层,把结构化错误透传进去。
我再分享一个印象比较深的真实案例。有个电商客服 Agent 上线两个月后,售后处理成功率从 70% 掉到 45%。一开始团队都在怀疑模型回滚,查了 API 调用记录发现版本没变。后来把 Trace 串起来才发现问题在上下文压缩模块:当对话超过窗口上限触发摘要压缩时,系统把“用户要求不开发票”这个关键约束当成普通文本丢掉了。Agent 在后续流程里反复向用户确认开票信息,用户连发三次“我说了不要发票”,Agent 还在道歉并解释发票规则。修复方案是在压缩策略里加了一个“关键约束保留清单”,把用户明确表达过否定、时间、数量这类字段标记为不可压缩。改完之后回归基线跑了一周,成功率回到 68%。模型自始至终都是同一个,变的只是反馈回路里那个不起眼的压缩模块。
排查 Agent 变笨的过程中,我个人体会最深的一点是:先修复“看见能力”,再谈“思考能力”。一个 Agent 如果能完整看见自己的错误、及时修正、记住经验,哪怕模型弱一点,它的稳定表现也远胜于一个模型很强但链路千疮百孔的系统。所以我的建议是,在项目初期就把 Trace 埋点和回归基线建起来,哪怕只有二十条用例也够了。等到评估分数真掉了的那一天,你才知道该去看哪里,而不是拿着模型 API 账单干瞪眼。