教程2026年11月4日9,514 浏览约 10 分钟阅读

10次测试2次出错:AI Agent如何被伪造结果污染?

同一道题运行10次,2次未执行工具却返回虚假结果。拆解AI Agent上下文污染、Observation伪造及Harness三权分离修复方案。

10次测试2次出错:AI Agent如何被伪造结果污染?

摘要

在一次 Agent 评测里,同一个用例被重复执行了 10 次。题目本身很简单:Anthropic 的《How we contain Claude》里提到过 MCP 吗?正常路径应当一致:模型提出 Action,Harness 执行工具,模型读到结果后作答。实测结果是 8 个样本答“有”,2 个样本答“没有”。只看最终答案,这会被归为“模型不稳定”。顺着 trace 往下看,问题并不在检索结果波动,而是有两个执行样本根本没有拿到检索结果。本文完整复盘这起事故:从 trace 缺口、对象混淆,到上下文污染的成因,再到“执行权、上下文权、审计权三权分离”的修复,以及回归测试与工程启示。所有事实均来自原项目的实测记录。

1. 现象:同题异答,根因不在模型

正常路径是什么样的

一个检索类 Agent 用例的正常执行路径有五个环节:

  1. 模型提出 Action;
  2. Harness 真正执行 scan_term(term="MCP");
  3. 工具返回真实结果;
  4. Harness 生成 Observation;
  5. 模型根据 Observation 回答。

这次发生了什么

正式运行 run_861667d15e764d84b9484dff51cd162e 中,10 个 attempt 里有 8 个走了正常路径,它们都真实调用了 scan_term,每次得到的命中数都是 3。另外 2 个 attempt 的最终答案却说:MCP 在文中出现了 0 次。

只看最终答案,很容易把它解释成模型不稳定:同一道题,有时答对,有时答错。但顺着 trace 往下看,问题不是“检索结果偶尔不同”,而是有两个执行样本根本没有拿到检索结果。这 2 个失败样本最终声称命中数为 0,但它们的 trace 里没有任何真实的 scan_term 执行记录。

2. 关键区别:Agent 评测不是普通文本评测

这也是 Agent 评测与普通文本评测的重要区别。普通文本评测只比较最终答案,只能看到“答错了”。保存完整 trace,才能继续追问三个问题:

  1. 它为什么这么答?
  2. 它掌握了什么证据?
  3. 它以为它掌握了什么证据?

这三个问题的价值在于,它们把“结果对不对”推进到“推理依据从哪里来”。而这恰恰是定位本次事故的钥匙。

3. trace 里的缺口:模型说执行过,工具没有执行

失败样本的真实情况

两个失败 attempt 中,有一个尝试过一次 list_documents,但没有任何真实的 scan_term span;另一个连真实工具调用都没有。项目实测确认:失败 attempt attempt_f736af93cc9345698574f56212337554 唯一一次真实工具调用是 list_documents,没有真实 scan_term 执行。

正常 trace 里应当分别出现:模型提出的 scan_term Action、Harness 派发的工具调用、工具自身产生的 span 和耗时、工具返回的真实 payload、以及 Harness 写入的 Observation。在失败 trace 里,这些执行事实都不存在。

模型输出了什么

模型输出中却出现了类似下面的内容:

Action: scan_term[{"term":"MCP"}]
Observation: {"count":0,"hits":[],"tool":"scan_term"}

它先写了一个准备执行的 Action,又替系统写了下一次工具结果,然后继续生成后续 Thought,最后甚至输出了 Finish。从文本表面看,这段内容非常像一个完整的 ReAct 回合:Thought → Action → Observation → Thought → Finish。但从系统执行事实看,它只经历了一个阶段:模型生成了文本。没有工具派发,没有工具 span,也没有 Harness 生成的 Observation。

4. 一段模型文本,怎么变成了后续推理的“事实”

三个对象必须分开

这里需要区分三个对象:

对象谁产生代表什么
Action模型请求系统执行什么
ObservationHarness系统实际得到什么结果
Answer模型面向用户的最终表达

Action 是提议,不是执行记录;Observation 是执行事实,不是模型可以补写的旁白。把这两者分清,是整个修复的前提。

旧实现埋下的坑

旧实现存在一个危险细节:模型违反协议时,Harness 已经正确识别出 protocol_error,却仍然把模型的原始输出追加到了下一轮 History。于是下一轮模型看到的上下文近似于:

Assistant: Action: scan_term...
Assistant: Observation: {"count":0,...}

这段内容本身带有 Observation: 标记。即使 Harness 的事件系统知道它是协议错误,下一轮模型看到的仍然是一段“很有工具结果味道”的历史文本。它可能无法可靠区分:这是系统拒绝的非法响应,还是上一轮工具真的返回了 0?

上下文污染的形成

一旦“0 个命中”进入后续上下文,错误前提就开始接管推理。模型不是因为检索到了 0 而回答 0,而是因为它先虚构了 0,又把虚构结果当成了后续推理的输入。这就是上下文污染。它比一次普通的协议解析失败更隐蔽,因为:协议错误已经被记录;原始输出也存在;下一轮模型仍然会回答问题;最终答案看起来像“检索没有结果”。只看错误码或最终答案,很容易把根因归错。

它也是可回归的:修复测试会构造一条伪造 "count":0 的模型响应,并断言这段内容没有进入下一次模型提示词。

5. 为什么“在提示词里强调不要伪造”还不够

系统提示词已经写明:Observation 由系统执行 Action 后追加,不得自行编造或输出 Observation。这句话仍然必要,因为它向模型解释了协议。但它不能作为系统的最后一道防线。原因是 Agent 面临的输入和普通后端不同:

  • 模型输出不是强类型函数返回值,而是需要解析的文本;
  • 同一个模型可能在不同上下文中产生不同格式;
  • 一次协议违规不会自动变成编译错误;
  • 模型可能同时输出多个阶段,形成一个看起来自治的假回合;
  • 后续模型调用本身也会把历史当成高可信上下文。

所以存在两个不同层次的保证。提示词负责“要求模型尽量遵守协议”,Harness 负责“保证系统只接受真实执行产生的事实”。前者影响概率,后者定义边界。如果只有提示词,Harness 的职责就会退化成“相信模型的自述”。而安全边界不能建立在被约束对象的自述上。

关于意图,当前判断是:模型生成伪造 Observation 时,不应推断它“有意欺骗”。更准确的描述是:模型在当前上下文中越过了协议边界,把一个预测出来的工具结果当成了已经发生的事实。

6. 修复:把执行权、上下文权和审计权切开

修复没有选择“遇到协议错误就重试一次”这种局部办法。重试可能掩盖问题,却不会改变上下文权威。最终交给三个不同组件。

执行权只属于 Harness

模型只能提出 Action。只有 Harness 完成协议解析、参数校验、工具派发和结果封装后,才能写入新的 Observation。模型文本中出现自己的 <observation> 或行级 Observation:,会直接被拒绝为协议错误。

上下文权只接受可验证事实

协议错误响应不再原样进入下一轮 History。History 中只保留一条明确的占位提示:上一条响应格式错误,原文已由 Harness 省略;请重新按协议输出一个 Thought 和一个 Action,不得编造 Observation。

关键是“原文已省略”。这样下一轮模型知道上一轮失败了,但看不到伪造的 count=0,也就不能把它继续当成证据。

审计权保留完整原始输出

隔离上下文不等于销毁证据。模型的原始响应仍然保存在 trace artifact 中,可以回答:当时模型到底输出了什么;它是在哪个字段伪造了 Observation;协议错误的具体解析原因是什么;修复前后的行为是否真的变化。

三者的关系可以概括为:原始输出 → 只进入审计 artifact;可验证执行结果 → 才能进入下一轮 History;用户答案 → 只能来自模型最终 Finish,不能混入 Action 或 Observation。

这比“把错误响应删掉”更稳妥。简单删除会让故障无法复盘;原样保留又会污染上下文。正确做法是分仓保存:审计需要原文,推理只接受受控事实。同时,Agent Loop 版本从 react-langgraph-v1 升级为 react-langgraph-v2。这不是装饰字段,而是让 trace 能区分修复前后的运行语义。

项目实测:真实 v2 探针 run_14ee6d1b673f4f00ae32f36d8a1ddcb8 的 trace 标记为 react-langgraph-v2 和 chat-v3,真实执行了 list_documents、scan_term、list_blocks,没有出现协议错误。

7. 回归测试要证明“污染没有发生”

只测试“协议错误被识别”还不够。旧实现已经能识别错误,却仍然存在上下文污染。更有价值的断言应该直接检查下一次模型输入:

  • 第二次调用可以看到 protocol_error;
  • 第二次调用不能看到 count: 0;
  • 第二次调用不能看到 forged;
  • 第二次调用必须看到“原文已省略”。

这类测试把修复目标从“事件存在”推进到了“错误信息没有越界进入下一轮推理”。还需要一个真实运行探针,因为 fake provider 只能证明控制流,不能证明真实模型、真实协议和真实 trace 能协同工作。v2 探针的价值就在这里:它确认真实工具 span、loop version、prompt version 和协议行为都在同一条可审计链路上。

完整回归至少需要三层:

层级证明什么
单元测试伪造 Observation 不会进入下一轮 History
真实单样本探针新版本能在真实 Agent 链路上完整运行
N/K 正式评测多 attempt 聚合后,产品语义和成本边界没有被破坏

当前确定性测试已经覆盖第一层;v2 单样本探针覆盖第二层;正式 N/K 运行继续承担第三层观察。

8. 对 Agent 工程的启示

这个案例最有意思的地方,不是“模型居然会犯这种错”,而是它同时暴露了 Agent 系统的两个常见误区。

不要把模型输出当成可信程序输出

模型输出首先是一段外部输入,必须经过解析、校验和权限边界。无论它看起来多么像 JSON、函数调用或工具结果,都不能绕过 Harness 直接改变系统状态。判断一个 Agent 是否真正有 Harness,不应看它有没有 tools 参数,而应问:工具是谁执行的?Observation 是谁写的?模型文字能否伪造执行事实?非法输出去了哪里?下一轮上下文接收的是原文还是受控状态?

不要把可观测性当成上下文隔离

trace 能记录问题,不代表问题不会影响下一轮推理。一个系统完全可能同时做到:正确记录 protocol_error;正确保存原始输出;然后错误地让原始输出继续进入 History。可观测性的目标是“事后能看见”,上下文隔离的目标是“错误事实不能继续参与推理”。两者互补,但不能互相替代。

概率系统更需要确定性防线

大模型让 Agent 能够处理开放任务,也让同一条协议在不同运行中出现偏差。Harness 工程不是给模型再加一层“更聪明的提示词”,而是把不可靠的生成过程包进可验证的执行结构:模型负责提出候选行为;Harness 负责验证、执行与记账;Trace 负责保存执行事实和原始现场;评测负责观察随机行为是否越过产品边界。在这个分工里,模型可以犯错;系统不能把错误悄悄升级成事实。

这种“分层守界”的思维同样适用于更外层的系统结构。当 Agent 通过 API 接入多个模型与工具时,调用链上的每一步都需要鉴权、路由与计量。一个统一的服务网关能把模型调用、工具执行和用量记账收拢在同一层,让“谁调用了什么、花了多少、走到哪一步”都可追溯。这也正是 koalaapi 作为 API 网关所承担的角色:把不可靠的生成过程包进可验证的执行结构,让 Agent 调用链路具备统一的入口、配额与审计能力,从外部与 Harness 内部的边界一起构成完整防线。

9. 一页速查

  • 现象:同一个检索问题运行 10 次,8 次真实得到 3 个命中,2 次没有执行 scan_term,却回答“0 个命中”。
  • 直接原因:模型在一条响应中自行写出了 Action 和 Observation,Harness 将整条响应识别为 protocol_error。
  • 真正造成污染的旧行为:协议错误响应虽然被记录,却仍然被原样追加到下一轮 History,使模型生成的 count=0 看起来像真实工具结果。
  • 修复原则:模型只能提出 Action;只有 Harness 能派发工具并生成 Observation;协议错误原文只进入 trace artifact,不进入下一轮推理上下文;History 只收到“原文已省略、不得伪造”的受控提示;用确定性测试断言伪造结果没有进入下一次 prompt;用 loop version 区分修复前后语义。

最重要的一句话:Agent 可以说“我调用了工具”,但只有 Harness 能证明工具真的执行过;模型输出是待验证的输入,系统执行记录才是事实。

10. 结语

本次事故的真正价值,在于它把“评测不一致”还原成了一个可定位、可修复、可回归的系统问题。只比较最终答案,会得出“模型不稳定”的误导结论;保存完整 trace,才能发现“执行样本根本没拿到结果”这一层根因。修复的核心不是加重提示词,而是把执行权、上下文权、审计权切开,让模型可以犯错,让系统不把错误升级成事实。

标签AI Agent上下文污染Agent HarnessReAct工具调用Observation协议错误Trace追踪上下文隔离智能体可靠性大模型工程
Koala API · 一站式大模型 API 中转

把博客读到的,落地到你的下一个项目

国内直连 · 兼容 OpenAI SDK · GPT / Claude / Gemini 等主流模型聚合

延伸阅读

免费注册