技术 · 深入

Agent 谎报成功了,问题多半不在提示词

模型说完成只是声明,证据要写在代码里
Agent 跑完回你一句「已完成」,你信不信?这份做法把「完成」从模型的声明改成代码里的证据 —— 围绕模型搭六件东西:工具注册表 · 模型 · 上下文管理 · 护栏 · agent 循环 · 验证步骤。同一个模型、同一句提示词一个字不改,只加代码:从 v0「点了按钮、撞上登录页、却报告成功」,经过护栏、确定性验证、线束侧登录,到第 6 轮迭代真的把事做成。核心判据只有一句:模型说完成是声明,不是证据 —— 验证要写在代码里。这套适合已经在用编码 agent、却被「假成功」反复坑到的人。
1
先留住一次假成功 — 挑一次 agent 报完成但结果不对的现场,把它的工具调用轨迹整份导出来当基线
2
把工具列成表 — 注册表四列:名字 · 参数 · 返回值 · 失败时返回什么错误码
3
给循环装护栏 — 最大迭代数 + 最大消息数 + 朴素上下文裁剪(保 system 与任务,砍中间)
4
加确定性验证 — 用普通代码读工具调用轨迹判做成没做成;判否就重试,上限 3 次
5
把易碎步骤挪进线束 — 登录 / 密钥 / 固定重试这类确定性动作放 harness,⛔ 不放进 prompt
6
六件各归其位 — harness 不是 agent 循环本身,是「围绕循环的一切」—— 循环外面还得有一层循环
阶段 1 · 先承认「完成」是声明
拿到一份可复核的失败现场
动作:挑一次 agent 报完成、但结果其实不对的现场(越具体越好:哪个任务、哪个仓库、哪一步)
预期结果:轨迹里能指出一句模型自述完成的话,且它之后没有任何对应的成功证据(没调用写操作 / 那个动作其实失败了)
动作:把当时的工具调用轨迹整份导出来 —— 调了哪些工具、参数是什么、各自返回了什么
预期结果:这条失败能按同样的输入重复出来 —— 不是偶发
动作:在轨迹里标出它从哪一句开始「自己宣布完成」
预期结果:能用一句话复述这次失败从哪一步开始跑偏(复述不出来,说明轨迹还没读透 —— 回去重读,别急着改代码)
✓ 符合 → 进入阶段 2 · 把六件套列成表
✗ 不符 → 回到 「挑现场」—— 最常见断点是轨迹根本没留(只留了聊天记录)⇒ 先把工具调用做成可观测的,再回来挑
harness-01-baseline.md
【输入说明】我把这次 agent 的工具调用轨迹贴给你(含每一步的工具名、参数、返回值)。任务目标是:〔填写这次任务要做成什么〕。【方法】① 逐条读轨迹,标出模型在哪一句声明「完成」;② 从该句往后找「支持完成的证据」——写操作是否真的发生、返回值是否表示成功;③ 若证据缺失,指出第一个导致后续全部无效的动作。【输出格式】三段:声明句 / 证据核验表(动作·返回值·是否成立)/ 断点定位(第几步)。【约束】⛔ 不以模型的自述作为成功依据;⛔ 不推测未出现在轨迹里的动作。
阶段 2 · 把六件套列成表
把「围绕模型的一切」写成一张可勾选的清单
动作:逐项确认六件:① 工具注册表 ② 模型 ③ 上下文管理(装什么 / 裁什么)④ 护栏(迭代上限 / 消息上限)⑤ agent 循环 ⑥ 验证步骤
预期结果:六行都填上了,且「验证步骤」那一行不是空的
动作:每一件后面写两列:「现在有没有」「没有的话谁补」——并注明这件是代码管的还是prompt 管的
预期结果:六件里至少三件标成「代码管的」(若六件全落在 prompt 里,说明还没搭线束)
✓ 符合 → 进入阶段 3 · 加护栏与确定性验证
✗ 不符 → 回到 回到阶段 1 —— 最常见断点是验证步骤填不出来,说明你还没定义「怎么算做成」⇒ 先把这个定义写出来(哪怕是三条可机器判的条件)
harness-02-sixparts.md
【输入说明】我给出这个 agent 现在的实现方式(工具清单、系统提示词、循环代码或框架配置)。【方法】按六件套逐项判定:工具注册表 / 模型 / 上下文管理 / 护栏 / agent 循环 / 验证步骤 —— 每件给:存在与否(有 / 无 / 部分)· 由谁承担(代码 / prompt / 框架默认)· 缺它会发生什么。【输出格式】六行表:件名 | 现状 | 承担者 | 缺失后果;表后一行写「最该先补的一件」。【约束】⛔ 不把「模型自己会注意」算作承担者;⛔ 不做代码实现建议以外的架构扩写。
阶段 3 · 护栏 + 确定性验证
让 agent 失败时失败得诚实
动作:加三道护栏:最大迭代数(经验值 15 步封顶)· 最大消息数 · 朴素上下文裁剪(保 system 与任务,砍中间)
预期结果:同一条失败再跑一次,它不再报告成功,而是明确失败("诚恳地失败")
动作:加一段普通代码当验证步骤:读工具调用轨迹判断这次到底做成了没有 —— ⛔ 不看模型怎么说
预期结果:重试次数能在运行日志里直接数出来,且 ≤ 3
动作:验证判否 ⇒ 重试,上限 3 次;三次都不过就明确报失败
预期结果:护栏触发时有一条可读的终止原因(迭代超限 / 消息超限 / 验证不通过)
✓ 符合 → 进入阶段 4 · 把易碎步骤挪进线束
✗ 不符 → 回到 回到阶段 2 —— 最常见断点是验证函数写成「看模型输出」(等于自己验自己)⇒ 改成读工具调用轨迹;另见断点:护栏只设了迭代数、没设消息数
harness-03-verify.md
【输入说明】我给你这次任务的「成功」定义(〔填写:什么状态算做成〕)与该 agent 的工具清单。【方法】写一段确定性校验逻辑(⛔ 不调用任何模型):① 从工具调用轨迹里取出与成功判定相关的动作;② 逐条比对返回值与成功条件;③ 任一条不成立则返回 fail(原因, 第几步) 并允许重试,重试上限 3。【输出格式】伪代码或目标语言代码 + 三行说明:判什么 · 怎么判 · 重试策略。【约束】⛔ 校验逻辑里不得出现「模型认为成功」这类判据;⛔ 不得把校验失败静默吞掉。
阶段 4 · 把确定性动作挪进线束
让密钥与登录不经过模型
动作:把登录 / 密钥 / 固定重试这类确定性步骤移到 harness:循环每步前检查当前状态,命中登录页就由 harness 用环境变量填入凭据
预期结果:模型可见的上下文里找不到任何凭据明文(历史里也没有)
动作:把上下文管理与会话日志显式归位:会话日志放在循环之外(agent 重启不影响它)
预期结果:同一任务能连续跑通,且成功是被验证步骤判出来的,不是被模型宣布的
动作:跑一次完整任务,确认这一版能在有限迭代内跑通
预期结果:把会话日志单独停在循环之外:手动重启一次 agent,任务能接着上次的状态继续 —— 接不上,说明日志还挂在循环里
✓ 符合 → 完成 —— 六件套齐备。此后新 bug 先按「是不是 harness 里的代码该改」过一遍
✗ 不符 → 回到 回到阶段 3 —— 最常见断点是凭据进了 prompt(改了但历史里还在)⇒ 先清历史再挪;另一断点是会话日志仍挂在循环内部
harness-04-login.md
【输入说明】我给你这个 agent 的循环代码(或框架配置)与需要登录的目标站点信息(⛔ 不要给我真实凭据,用占位符)。【方法】① 找出循环里「需要已登录状态」的每个步骤;② 在 harness 侧加一处前置检查:命中登录页则从环境变量取值填入,并向模型声明「已登录」,⛔ 不让模型看到凭据;③ 把会话日志/状态存储移出循环体,给出改动清单。【输出格式】改动清单(文件 · 位置 · 改什么 · 为什么)+ 一段自检步骤(怎么确认凭据没进上下文)。【约束】⛔ 不输出任何真实密钥;⛔ 不改动与登录无关的业务逻辑。
[C级]IBM 工程师 Tejas Kumar《What Is an Agent Harness?》六组件拆解(AI Engineer Europe 演讲成文) [C级]同一实验的逐版本分支实录(v0 谎报成功 → v3 线束侧登录 · prompt 全程未改) [C级]Agent Harness 分层对照(OpenAI / Anthropic / DeepSeek 三家口径差异) 同类:Agent 四模块搭建:把「一个会干活的 AI」拆成可复制的四件 · Agent 边界:什么时候该让它自己决定 · Agent 交接:换手时别让它从零开始