上个月我做了一次例行评估,结果让我有点坐不住:同一套评测集,Agent的任务完成率从82%掉到61%,其中几个细分场景直接腰斩。第一反应当然是查模型——版本没变、温度没变、API 调用也没有异常报错。那问题出在哪?我翻了一周日志,最后确认根因跟模型权重一点关系都没有,而是它外面那层"反馈回路"坏了:工具返回被静默解析错、记忆里混进了脏数据、反思环节的输出压根没有回到下一轮上下文里。
这不是个例。我见过太多团队在评估变差后第一时间换模型、调 prompt、加 few-shot,忙活几天指标纹丝不动,最后才发现是 Agent 的运行时链路出了结构性故障。如果你正在做 Agent 评估和调优,或者用 LangChain、LlamaIndex 这类框架搭过 Agent,这篇内容应该能帮你省下几个通宵。我会从"变笨"的现象分类讲起,拆开 Agent 的反馈回路,再给你一条完整的排查链路和修复操作,最后聊聊怎么在日常迭代中防止回路悄悄坏掉。
1. 先别急着怪模型:为什么"评估变差"是我排查过的头号假线索
1.1 "变笨"的三种现象,对应完全不同的排查方向
很多人看到评估指标下跌就下意识进入"模型调优模式",但"变笨"这个描述太笼统了。我一般会把异常分成三类,每一类的排查方向完全不同。
第一类是稳定退化:同一批 case 反复跑,失败集合高度重合,成功率呈现持续下滑趋势。这种形态几乎可以排除随机因素,大概率是链路上某个结构性环节变了——工具返回格式改了、记忆插件升级了、上下文截断逻辑被调过,这些都是反馈回路的组成部分。
第二类是随机波动:同一批评估样本跑五次,成功率忽高忽低,失败了重新跑又能过。这种情况先别怀疑模型能力,优先查采样温度、网络超时和重试逻辑。我遇到过一种情况:某工具接口偶尔延迟超过 10 秒,Agent 在等待中触发超时,走了错误补偿路径,导致任务失败,而直接重跑时接口恢复正常,case 又过了。
第三类是局部 case 崩坏:不是整体指标降,而是某一类任务集体失败。比如所有"需要调用日历 API"的 case 都挂了,其他场景完全正常。这时候要优先怀疑特定工具或特定输入形态的变化,而不是模型整体退化。
这三类现象的排查方向差异很大,如果你在第一步就扎进模型调参,很可能把时间浪费在错误的方向上。我见过团队成员为了一个稳定退化的问题连续换了好几个模型版本,最后发现是工具返回的 schema 里多了一个 error 字段,解析器没有处理它。
| 现象形态 | 典型表现 | 优先排查方向 |
|---|---|---|
| 稳定退化 | 失败集合重合度高,指标持续下滑 | 链路结构变化,工具、记忆、上下文管理 |
| 随机波动 | 多次运行方差大,重跑可过 | 采样参数、超时重试、外部依赖稳定性 |
| 局部崩坏 | 特定类别 case 集体失败 | 特定工具变更、评估集污染、输入形态变化 |
1.2 为什么换模型往往没用,以及评估集本身也可能是一把"坏尺子"
先声明一个观点:我不是说模型不会退化。模型供应商更新版本、量化精度变化、API 侧行为微调都可能影响输出质量。但在 Agent 架构里,模型只是整个闭环里的一个决策单元,用户感知到的"Agent 能力"其实是感知、记忆、规划、工具调用、返回解析、反思修正这一整条回路的产出。回路里任何一个环节断了,指标都会跌,跟模型权重没有直接关系。
这就像一辆车,引擎功率没有变,但传动轴裂了,你踩油门只会听到异响,车速该掉还是掉。这时候你换一台更大马力的引擎,问题依然存在,只是异响更大了一点。
另外,评估指标变差还有一种非常尴尬的可能——尺子本身坏了。评估集的 golden 答案过期了、评分 prompt 被人改过但脚本没同步、某个数据清洗任务把原始输入文件格式改掉了,这些都会让 Agent 一直"被变笨"。所以我做评估异常排查的第一步,从来都是先验证数据链路,确认评估脚本、golden 答案和评分逻辑都没有变化,再进入系统本身。
有个很典型的例子:我维护的一个评估集里,某条 golden 答案是从外部文档自动生成的快照,后来外部文档字段更新了,但快照没刷新。Agent 按照新字段给出了更准确的结果,反而被扣了分,评估指标"变差"了。这不是 Agent 的问题,是尺子的问题。
2. 反馈回路到底由哪些环节构成:把 Agent 运行循环拆到信号级
2.1 一个典型 Agent 运行循环里的七个信号流转点
要排查反馈回路,首先得知道回路里有哪些节点。我通常会把一个标准 Agent 的运行循环拆成七个环节,每个环节都是信号的一次流转。
第一,读取目标与约束。Agent 从用户输入和系统提示词中提取任务目标,以及必须遵守的约束条件。第二,记忆与上下文检索。Agent 从短期上下文和长期记忆里拉取相关信息,包括历史对话、之前获得的工具返回、用户的偏好设置等。第三,规划与决策。大模型基于当前信息决定下一步动作,是直接回答、调用某个工具,还是向用户澄清。第四,工具执行。运行时(也就是 harness 或者 Agent 框架)负责实际调用外部 API、执行代码或查询数据库。第五,返回解析。把工具的原始输出转换成模型可以理解的结构化结果,同时判断这次调用是否成功。第六,状态更新与反思。Agent 判断当前任务是否完成,更新记忆,必要时做自我反思和纠错。第七,生成最终输出。如果任务完成,把内部状态转化最终答案;如果没完成,回到第三步继续循环。
这个分解方式和"harness 和 Agent 的区别"是直接相关的。模型加 prompt 是 Agent 的决策内核,但真正让决策落地的是外面这层 harness——它负责维护上下文、调用工具、解析返回、写记忆。很多"变笨"的问题就出在 harness 上,但大家习惯把账算在模型头上。
2.2 反馈回路坏掉的表现:不是只有一个断点
回路断掉的表现形式很多,我把常见的失效模式归成五类,方便你对照自查。
信号传不到,是最隐蔽的一种。反思环节明明输出了"我应该修正之前的策略",但 harness 没有把这段反思注入下一轮的上下文,模型在下一轮根本看不到自己的反思,等于白反思。信号传错,典型场景是解析器把包含 error 字段的返回当成正常结果提取出来,模型基于错误数据继续决策。信号被污染,是记忆系统写入了脏数据,比如一次失败尝试的过程被记录下来,后续每次检索都会把它翻出来干扰决策。信号被截断,是滑动窗口把早期关键信息挤出了上下文,模型忘了用户约束或者重复做已经完成的步骤。信号被误读,是工具返回格式微变后,模型对字段含义理解发生偏移——最常见的是 boolean 语义反了、单位换算不一致,这类问题很难在总指标上看出来,但会让局部 case 成批失败。
这五类失效模式在日志里留下的痕迹各不相同,我后面会详细展开怎么定位。这里先把概念建立起来:反馈回路不是某一个组件,而是模型、运行时、记忆、工具、评估五者之间的信号链路,任何一处断掉,外部表现都是"Agent 变笨了"。
3. 从总指标异常到单环节定位:一次完整故障排查实录
3.1 排查前置工作:确认"变笨"不是评估噪声或数据漂移
我在定位反馈回路之前,一定会先做三件前置工作。它们不解决实际问题,但能避免你花三天时间查一个根本不存在的问题。
第一,固定环境做 A/B 验证。把模型版本、温度参数、max_tokens、工具超时统统固定,用同一批评估样本连续跑至少三轮,统计成功率的方差。如果方差大于 5 个百分点,先别下任何结论,继续跑,把噪声排除掉。第二,检查评估集变更记录。翻 git log 或者配置管理记录,确认评估脚本、golden 答案、评分 prompt 在评估滑坡的时间窗口内有没有改动。哪怕只是"顺手改了个标点",也要确认它不影响评分逻辑。第三,检查外部依赖状态。你调用的第三方 API 是不是更新了返回格式?数据库表结构有没有调整?这些问题 Agent 自身完全无辜,但它会通过工具调用失败来体现。
完成这三步,如果指标还是异常,那才有资格说"系统可能有问题"。我的经验是,这三步能过滤掉大约 30% 的假报警——很多评估下降根本就不是系统的问题。
3.2 给 Agent 补上结构化 Trace,从"黑盒试错"转成"环节定位"
进入系统排查后,最重要的一件事就是看运行轨迹。很多 Agent 项目的日志只有最终输出和总耗时,中间过程完全黑盒。这种情况下你只能靠猜:是不是 prompt 不够好?是不是该换模型?我后来学到的教训是——排查开始之前,先把结构化 trace 补上,否则一切推断都是空中楼阁。
结构化 trace 的意思是,在运行循环的每个关键节点都记录一条日志,包含 step 编号、模型的决策输出、工具返回的原始文本、解析后的结构化结果、记忆读取的内容摘要、上下文截断的数量、token 消耗等字段。我一般用 JSON Lines 格式落盘,每条记录一行,方便后续用 jq 或 Python 脚本做分析。
{ "step": 4, "action": "search_memory", "action_input": {"query": "用户之前是否指定过输出格式"}, "memory_reads": [ {"id": "mem_8821", "score": 0.87, "content": "用户要求最终以表格输出"} ], "model_output": "call_search_memory", "tool_result_raw": "{\"status\":\"ok\",\"data\":[...]}", "tool_result_parsed": {"items": 3, "valid": true}, "context_snapshot": {"truncated_chars": 3410, "lost_tags": ["goal", "done_steps"]}, "tokens": {"prompt": 4215, "completion": 132} }有了 trace,排查就变成了"看"而不是"猜"。你会很直观地发现:哦,第 4 步模型决定调用记忆检索,但检索回来的内容和当前任务毫无关系;或者第 6 步反思模块输出了一大段修正意见,但第 7 步的上下文快照里根本没有这段文字。这种精度是普通日志给不了的。
我把 trace 补上以后,定位故障的时间从以天计缩短到以小时计。没有 trace 的时候,遇到指标下滑只能把各个可能环节全部改一遍看效果;有 trace 以后,直接找到信号断掉的那个节点,改一行代码就能验证。
3.3 三个真实案例复盘:解析器静默失败、记忆检索漂移、反思循环空转
三个案例都是我在实际项目中碰到的,而且都属于"反馈回路坏了"但表象是"模型变笨了"的情况,逐个讲一下排查过程。
第一个案例是工具返回解析器静默失败。有一次评估指标下滑,核心是"所有需要调用外部天气服务的任务成功率从 78% 掉到 45%"。第一反应是服务挂了,但手动调 API 发现一切正常。后来翻 trace,发现一个问题:解析器只提取了返回 JSON 里的 data 字段,忽略了 status 字段。那天外部服务端更新了返回结构,把错误信息从 HTTP 状态码迁移到了 body 里的 error 字段,HTTP 状态码还是 200。解析器不知道这个变化,把含 error 的返回当成正常数据提取,Agent 拿到错误数据后还自信地往下执行。修复方法很简单:给解析器加 schema 校验,status 不是 ok 就抛错误,同时保留原始返回文本作为事后审计凭证。
第二个案例是记忆检索漂移。某次 Agent 开始引用一些明显过时的结论,比如用户已经改口了,它还在按旧偏好执行。查 trace 发现,记忆检索返回的 top-3 内容和用户当前问题的相关性明显偏低。进一步查,发现是记忆模块升级了 embedding 模型之后,向量化方式变了,但没有对已有记忆做全量重新向量化。新旧向量处在不同的语义空间里,检索算子混着算,召回自然混乱。修复方案是重新向量化全量记忆,同时把写入记忆前的过滤逻辑加上:低质量过程记录不进记忆库。
第三个案例是反思循环空转。Agent 在评估集里反复出现同一个错误,trace 显示模型的反思模块确实输出了自我修正意见,比如"我不应该直接调用,应该先查询用户是否授权"。但仔细一看,下一轮对话的输入里压根没包含这段反思。也就是说,harness 把反思输出打印到了日志里,但没有把它写回到下一轮的 system message 或对话历史中。模型辛苦反思了半天,下一轮它又忘了自己反思过什么。修复方式是把反思结果显式地注入下一轮系统提示词里,并做一条断言:如果反思注入失败,harness 要报错而不是静默继续。
这三个案例折射出一个共性问题:反馈回路里的每一个信号节点,都需要有"如果信号没传过去怎么办"的防御逻辑,否则任何一环静默失败,最终都会表现为模型能力下降。
4. 按环节逐个补漏:修复反馈回路的落地操作
4.1 先把返回解析器做厚:校验、重试、留痕
修复反馈回路的顺序很有讲究,我建议从工具返回解析器开始,因为这是最靠近信号链路入口的位置,而且修复成本最低、收益最明显。一个被我称为"做厚"的解析器,至少要有四层逻辑。
第一层是 schema 校验。用 Pydantic 或 JSON Schema 对工具返回做结构校验,字段缺失、类型不对、枚举值超出范围都要明确报错,而不是默默容忍。第二层是业务状态校验。HTTP 200 不代表业务成功,必须校验业务字段的语义。比如前面提到的 status 字段,解析之前先确认它是不是 ok,否则直接走失败分支。第三层是失败重试与降级。校验失败时,先重试一次,间隔 300 毫秒;重试还失败就进入降级路径——告诉模型"工具调用失败,请询问用户或给出替代方案",而不是伪造一个空结果让模型继续。第四层是留痕。工具返回的原始文本、解析后的结构、校验结果,一律落盘。这个习惯在排障时价值极大,因为问题下线之后,你还能复盘当时到底发生了什么。
这里有一个很容易被忽略的点:工具返回的"空值"在解析时必须和"正常空结果"区分开。很多解析器把空数组、空字符串当成合法返回,导致 Agent 以为"查到了,但结果为空",实际上可能是查询条件本身拼错了。我建议解析器对空结果额外打一个标记,让模型知道"调用成功了但结果为空",这样它才有可能去修正查询条件。
4.2 治理记忆通道:写入前过滤,检索时校准
记忆是反馈回路里最容易被污染、又最晚被发现问题的一个环节。我见过太多的"突然变笨"案例,最后都指向记忆库里混入了脏数据——某次失败尝试的完整过程被写进去,某段带情绪的用户抱怨被当成事实入库,某条过期偏好没有被标记失效,等等。
治理记忆必须做两件事:写入前过滤和检索时校准。写入前过滤我通常用双轨制:一条硬规则,敏感信息、临时噪声、失败尝试中的中间步骤,不允许写入长期记忆;一条软过滤,用一个成本较低的 LLM judge 判断这段内容是否有长期保留价值,没有就丢弃。双轨都通过才允许入库。这一步能拦掉大约 80% 的脏数据。
检索时校准的核心是给记忆库加"引用溯源"。检索结果不止返回内容,还要携带来源信息、写入时间和置信度。Agent 引用某条记忆时,如果这条记忆的写入时间距离现在太久,或者置信度低于阈值,应该给模型一个"这条记忆可能过期"的提醒。在记忆系统里引入衰减机制,对多轮对话中的临时偏好和长期规则做不同保留周期,效果也不错。
另外提一句安全方面的问题:如今 Agent 记忆系统面临的内容污染威胁不少,用户输入里可能夹带指令试图改写后续记忆,这类攻击不完全是模型能力问题,更多是记忆写入链路缺少内容校验。我的做法是,记忆写入前对原文做无害化过滤,识别指令性内容并阻断入库,避免脏数据通过记忆通道污染后续所有决策。
4.3 修复反思与目标保持:让模型看见"自己走到哪了"
反馈回路里最容易被忽略的环节是"目标保持"。长任务中,Agent 的早期目标感知会随着上下文滚动而衰减。如果没有显式维护目标状态,模型做着做着就"跑偏"了——它在回应局部问题,而不是在执行最初的任务。
我的做法是引入一个"固定持久区",放在上下文窗口的头部,专门存放三类信息:初始目标、已完成步骤、未变更的约束条件。滑动窗口做截断时,优先保留持久区内容,把更早的对话历史用摘要替换,而不是简单按 token 数直接砍掉最老的文本。这个操作看起来没有技术难度,但对长任务的成功率影响极大。很多"Agent 后面几轮突然变傻"的问题,其实不是模型不行,而是它的工作记忆里已经丢失了最初的指令。
反思环节的修复也一样。反思模块输出的修正意见,必须显式地注入下一轮上下文,而且要有注入标记,比如固定的 "REFLECTION:" 前缀,让模型清楚这段内容来自上一轮的自我纠错,优先级高于普通历史对话。同时,反思不能无限循环。我踩过的一个坑是反思 prompt 本身设计得过于保守,模型每轮都觉得自己"需要继续验证",结果一个任务跑了二十多轮还在原地打转。给反思加轮数上限,同时限制反思触发条件,只在检测到工具调用失败或者关键约束被违反时才触发。
4.4 评估尺子也要修:golden 答案版本化、评分脚本同步
前面说过,评估集本身也可能是一把坏尺子。修复反馈回路的同时,顺手把评估体系也修一修,不然下次你还会被假报警坑一次。
我给评估体系提三个要求。第一,golden 答案必须带版本号和过期标记。golden 答案往往是从某个时间点的数据快照生成的,外部世界变了它不会主动变。超过一定期限或者被人工标记"已过期"的答案,在汇总指标时要单独标注,不能让过期答案拖累整体评分。第二,评分 prompt 和评分脚本必须同步版本。我遇到过评分 prompt 改了一句话,但执行脚本还按旧规则解析结果,导致模型输出格式正确但被扣分,而脚本报错信息被吞掉的诡异现象。版本管理不是只针对模型和代码,评分规则同样要进版本库。第三,评估输出要做失败归类。不仅记录"这个 case 失败",还要记录失败发生在哪个环节——是模型决策错误、工具调用失败、还是结果格式不合规。这样才有真正的环节级健康视图,而不是只盯着一个总分。
5. 防止回路再次坏掉:把健康度监控装进 Agent 日常
5.1 给每个环节设一个"能被追踪"的信号指标
修好回路只是开始,更关键的是日常迭代中能提前发现回路又要断。我建议在 Agent 的可观测体系里,为每个信号流转环节设置一个基础健康度指标,并给出明确的报警边界。
| 环节 | 关键指标 | 报警阈值参考 |
|---|---|---|
| 工具调用 | tool_call_error_rate | 单日超过 5% |
| 返回解析 | parse_failure_rate | 单日超过 2% |
| 记忆检索 | memory_hit_rate、记忆冲突率 | 命中率低于 60% 或冲突率持续升高 |
| 反思注入 | reflection_injected_ratio | 低于 100% 即报警 |
| 上下文管理 | truncation_ratio、关键字段丢失次数 | 截断比例超过 20% |
| 运行循环 | avg_loop_count、死循环率 | 超过 10 轮的比例高于 5% |
这里面的指标不是一个抽象概念,而是可以直接从 trace 里查出来的。只要你的结构化日志是齐全的,这些指标做一次统计就能落地。我经历过最典型的例子就是 reflection_injected_ratio——它从 100% 掉到 60% 的那天,基本上就是 Agent 开始"变笨"的前一天。如果这个指标在那个时间就触发了报警,我就能提前一天发现问题,而不是等评估集跑完才回头翻日志。
5.2 回归集、灰度评估和版本记录的配套做法
健康度监控解决的是"回路正在坏"的发现问题,而回归集、灰度评估和版本记录解决的是"回路即将因为变更而坏"的预防问题。
我现在的做法是维护一个 200 到 300 条的回归评估集,覆盖各主要任务类型,每天跑批一次。但看指标不只盯总分,还要看每个环节的子指标和每个任务类别的分桶趋势。分数下降可能只有一两个点,但如果工具调用失败率在同一时间段内上涨了三个点,那问题多半在工具侧,先查外部依赖再查模型。
灰度评估则适用于上线新的环节——换一个新的记忆插件、改工具返回解析逻辑、调整上下文管理策略,都不要直接全量切。先圈定一个特定场景的评估子集,跑一遍,看反馈链路的健康度指标和任务成功率两个维度,都达标再逐步放量。我吃过一次亏:换了新版 embedding 模型后全量切上去,等到评估指标回来报告问题时,已经过了两天,而期间所有任务都在用那个混乱的检索结果。
版本记录这件事,很多人只对模型版本敏感,对 prompt 版本、harness 版本、记忆插件版本、工具 schema 版本完全不敏感。我的建议是,把所有这些都作为 trace 元数据的一部分写入日志。这样任何一次指标波动,你都可以迅速回答"当前跑的是哪一套配置",而不是对着几个版本的组合猜来猜去。
最后再分享一个实际操作中的体会:这套排查做完之后,我反而对"模型变笨"这件事没那么恐慌了。Agent 工程的复杂度已经从模型转移到了回路的稳定性。模型如果真退化了,你换回来就能看到指标回升;但回路坏了,你换什么模型都救不回来。评估变差时,先别急着怪模型,把 trace 打开,看看信号是在哪个环节断的。多数情况下,把回路修好,模型本身就能回到它本来该有的水平。