上一节介绍了智能体的八种常见失效方式,以及 Harness Engineering 中对应的治理思路。本章进一步说明这些机制的目标、边界和最小实现。
mindmap
root["Harness Engineering 驾驭机制"]
["1.Agent Loop 四相循环"]
["解决故障:循环失控"]
["核心逻辑:四段闭环、最大迭代硬限制、双重终止条件"]
["类比:驾校开车标准化流程"]
["代码要点:循环内置完成状态判断"]
["2.Tool Use 工具编排"]
["解决故障:工具异常被静默吞噬"]
["核心逻辑:统一 Schema、结构化错误、稳定的工具契约"]
["类比:USB-C标准接口统一故障码"]
["代码要点:统一封装safe_tool_call"]
["3.Progress Tracking 进度追踪"]
["解决故障:进程中断状态丢失"]
["核心逻辑:JSON持久化、断点续跑"]
["最佳实践:进度文件+Git双Checkpoint"]
["类比:游戏自动存档"]
["代码要点:save_progress / load_progress"]
["4.Context Management 上下文管理"]
["解决故障:上下文溢出、Prompt缓存失效"]
["核心逻辑:Token监控、固定System Prompt"]
["策略:历史摘要压缩 / 重置上下文"]
["踩坑警告:系统提示词禁止写入动态信息"]
["类比:工位文件归档清理"]
["5.Feature List 任务拆解"]
["解决故障:上下文溢出(源头缓解)"]
["核心逻辑:任务清单拆分,单次仅执行一项任务"]
["清单持久化到磁盘,按需读取以节省上下文"]
["类比:Trello待办看板"]
["代码要点:get_next_task获取未完成任务"]
["6.Verification Loop 验证闭环"]
["解决故障:工具报错后AI虚假自洽成功"]
["核心逻辑:真机自动化测试,以返回码为准"]
["约束:禁止修改测试用例掩盖bug"]
["类比:QA验收测试"]
["代码要点:subprocess执行自动化用例"]
["7.Subagents 子代理分治"]
["解决故障:上下文溢出(分流方案)"]
["核心逻辑:子代理独立上下文,隔离中间日志"]
["类比:CEO外包专业顾问,仅接收最终报告"]
["代码要点:sub_messages全新消息列表"]
["8.Generator-Evaluator三角色架构"]
["解决故障:无独立自动化评审、自评偏袒"]
["核心逻辑:规划/生成/评审角色分离、对抗校验"]
["类比:论文审稿机制"]
["代码要点:独立Evaluator调用LLM校验产出"]
机制 X 故障模式 对照矩阵
| 机制 \ 故障 | 循环失控 | Context 溢出 | Cache Miss | Tool 错吞 | 状态丢失 | 缺权限闸 | 缺自动化评审 | 成本失控 |
|---|---|---|---|---|---|---|---|---|
| Agent Loop四相循环 | 主解 | - | - | - | - | 辅 | - | 辅 |
| Tool Use工具编排 | - | - | - | 主解 | - | - | - | - |
| Progress Tracking进度追踪 | - | - | - | - | 主解 | - | - | - |
| Context Management上下文管理 | - | 主解 | 主解 | - | - | - | - | 辅 |
| Feature List任务拆解 | - | 源头 | - | - | - | - | - | 辅 |
| Verification Loop验证闭环 | - | - | - | 下游 | - | - | - | - |
| Subagents子代理分治 | - | 另一解 | - | - | - | - | - | 辅 |
| Generator-Evaluator生成 - 评估对抗 | - | - | - | - | - | - | 主解 | - |
以下是对应的故障机制的详解。
Agent Loop 四相循环(循环失控)
怎么让一个循环不失控。
一句话定义:把一次 agent 迭代拆成 Gather Context(收集上下文)→ Take Action(执行动作)→ Verify(验证结果)→ Iterate(迭代改进)四个阶段,并硬性加上 max_iterations 和显式终止条件。
flowchart TD
%% 中心圆形节点:Agent Loop
Center((Agent
Loop))
%% 四个流程节点
G["Gather Context
(收集上下文)"]
T["Take Action
(执行动作)"]
V["Verify
(验证结果)"]
I["Iterate
(迭代改进)"]
%% 顺时针闭环
G --> T
T --> V
V --> I
I --> G
%% 配色贴近原图
style G stroke:#64748b
style T fill:#fffbeb,stroke:#d97706
style V fill:#f0fdf4,stroke:#059669
style I fill:#f5f3ff,stroke:#6d28d9
style Center stroke:#aaa,fill:#f8f8f8
%% 顶部文本标注(多数渲染器支持note)
note["🔴 HARNESS BOUNDARY: MAX_ITERATIONS (拦截保护层)"]
参考资料:Building agents with the Claude Agent SDK(Anthropic,2025-09-29,作者 Thariq Shihipar)。
解哪条故障:故障 ① 循环失控——naive agent 的 while True 在这里被替换成 for i in range(MAX_ITER),每轮还有显式的 verify 阶段做”能不能停”的判断。
生活化类比:驾校教练教你开车的四步口诀——“看路况 → 踩油门 → 看前方 → 调整方向”。每一步都有明确动作,开完一圈看有没有到终点;到了就停、没到就再来一圈——但你不会无限圈兜下去,因为教练给你定了”跑 10 圈就必须停”的硬规则。
1 | # 机制:Agent Loop 四相循环 |
这段代码里最关键的一行是 for i in range(MAX_ITER) 取代了 naive agent 的 while True。我们可能会觉得”这不就是加了个计数器吗”——对,就是加了个计数器。但这个计数器背后的工程思维是:agent 不该完全信任模型的自我终止判断,系统必须有一个强制出口。Anthropic 官方博文里用的词是 “structured loop with guaranteed exit”——有保证的出口,这就是我们的第一道刹车。
Tool Use工具编排(Tool错误处理为空字符串)
工具失败了 agent 要看得见
一句话定义:所有工具调用包裹在结构化 schema 里(名字、参数、返回格式统一),失败时返回 {"status": "error", "error": "..."} 这样的结构化回传,而不是空字符串或异常被吞。
参考资料:Building agents with the Claude Agent SDK(Anthropic,2025-09-29)。MCP(Model Context Protocol)则是连接 AI 应用与外部数据源、工具和工作流的开放协议。它与单次 Function Calling 的工具 schema 有交集,但两者不是简单的“标准版 / 非标准版”关系。
解哪条故障:故障 ④ tool 错误处理为空字符串——naive agent 里 except: result = "" 是典型症状,修复方式是把 error 变成结构化 return,而不是字符串或异常。
生活化类比:USB-C 接口。任何设备插上 USB-C 线,host 都能通过标准协议读到对方的身份、能力、故障码;不兼容的设备也能返回”设备不识别”,而不是一条死线静默。工具 schema 就是 agent 世界的 USB-C。
1 | # 机制:Tool Use 工具编排 |
对比 naive agent 里的 except: result = "" 和这段代码的 return {"status": "error", "error": str(e)}——两者差别只有一行,但工程含义完全不同。前者是”失败了装作没事”,后者是”失败了把失败本身当作第一公民返回”。agent 下一轮看到 status: error 才会触发修复路径,看到空字符串就以为成功。结构化错误是 agent 能跑长任务的第一块基石。
Progress Tracking 进度追踪(状态丢失)
kill 进程或者关电脑之后,agent 能不能从断点接着跑
一句话定义:把每一步的进度写到一个持久文件(Anthropic 官方用的是 claude-progress.txt)+ 在完成里程碑时打 git commit,这样即使 session 断了,重启时能读取进度文件从断点续传。
参考资料:Effective Harnesses for Long-Running Agents(Anthropic,2025-11-26,作者 Justin Young)。原文原话:”a claude-progress.txt file that keeps a log of what agents have done”。
生活化类比:游戏存档。开放世界游戏不可能指望玩家一次通关,必须每过一个小任务自动存档;agent 跑长任务也是同样——每完成一个子步骤就”存档”,断了能”读档”接着跑。
1 | # 机制:Progress Tracking |
这段代码看上去简单到不值得写成独立机制——不就是 JSON 读写吗?但我们需要想清楚一件事:没有它,agent 跑 30 分钟失败一次就得从零开始。Anthropic 官方博文给出的最佳实践更强:progress 文件 + git commit 双重 checkpoint——前者记录 agent 的思考过程,后者记录代码的物理改动,两个维度的断点都可以续上。
Context Management 上下文管理(context 溢出 / cache miss)
context window 要溢出怎么办
一句话定义:持续监控当前 messages 的 token 数,超过阈值时触发压缩(compaction)或重置(reset),同时保持 system prompt 前缀稳定不变以命中 prompt cache。
参考资料:Automatic Context Compaction Cookbook(Claude Agent SDK)。压缩、裁剪和新会话恢复是可组合的策略,不应描述成厂商已经从 compaction 单向迁移到 reset。
解哪条故障:一次救两条——故障 ② context 溢出(压缩能降维度)和故障 ③ cache miss(保持前缀稳定才能命中 cache)。
生活化类比:工位清理。你工位上的文件堆到爬不动的时候,有两种策略:压缩(把过期文件归档成一页摘要) 或 重置(今天新文件从空桌开始)。agent 的 context 也一样,到某个阈值就得做出这个选择。
1 | # 机制:Context Management |
这段代码里最容易被忽略的是 messages[0] 这一行——system prompt 永远不动。为什么?因为 prompt cache 是按前缀匹配的,前缀一变整个 cache 作废,延迟翻倍、费用翻倍(解 ③ 号故障)。很多 Agent 开发者第一反应是”在 system prompt 里加时间戳或当前任务”,这是 cache miss 最常见的起因。正确做法是:system prompt 是恒定的”角色定义”,当前任务进 user 消息,永远不要动前缀。
【踩坑预警】 在 system prompt 里加时间戳、用户名、当前任务
后果:很多开发者把当前任务状态、时间戳或 task_id 写进 system prompt,以为”让模型知道当前在做什么”更好——实际上这会让每轮的 system prompt 前缀都不同,prompt cache 全部失效,延迟和费用双双翻倍。在一个长任务里,这项失误的成本可能是数十美元。
正确做法:system prompt 只放固定的”角色定义”(永不变),当前任务、时间、task_id 全部放进 user 消息。
排查方法:看 API 返回里的cached_tokens字段——如果你在多轮循环中这个字段始终是 0 或远小于 system prompt 的 token 数,说明前缀被改动了、cache 没命中。【常见误区】 把 context window 当成”memory”
后果:很多初学者以为 “context 越大 agent 记得越多”,于是把所有历史对话都塞进 context 等同于”长期记忆”——实际上 context window 只是一次请求的短期工作区,超阈值必须清理;跨 Session 信息需要由进度文件、Checkpoint、数据库或其他持久化机制承担。
正确做法:把 context window 当”工位”(本次会话要用的文件)、把 progress 文件当”档案柜”(跨 session 持久的记忆);需要 Agent 跨天保留的信息应写入可靠的外部存储,而不能只依赖当前 Context。
Feature List 任务拆解(context 溢出的源头)
解决的是 context 溢出的源头——一次让 agent 做太多事
一句话定义:把大任务拆成 JSON 格式的清单([{id, task, status}]),强制 agent 单次迭代只做一件事,每件事做完标记状态再取下一件。
参考资料:Effective Harnesses for Long-Running Agents(Anthropic,2025-11-26)。原文给出了完整的 JSON feature 结构示例,passes 字段标注 true/false。
解哪条故障:故障 ② context 溢出的源头——naive agent 一次把 “读文件 + 修 bug + 跑 pytest” 全塞进去,context 自然爆。Feature List 把它切成 3 个独立子任务,每个子任务 context 小得多。
生活化类比:待办清单。你早上不会一口气处理 10 件事,而是列一个清单、做完一件划掉一件。agent 也一样——清单是它的外置工作记忆,不依赖 context window。
1 | # 机制:Feature List 任务拆解 |
这个机制可以把它理解成 agent 世界的 Trello 看板。每个 task 是一张卡片、有明确的 pending / in_progress / passes 状态。Anthropic 的示例将清单保存在 JSON 文件中,以磁盘内容作为事实来源;Agent 执行时仍需按需把相关任务读取到 Context。
Verification Loop 验证闭环(Tool 错误的下游)
跟 Tool Use是一组搭档——Tool Use管”工具层面的结构化错误”,Verification Loop任务层面的真机验证”
一句话定义:每个 feature 完成后,用 Playwright / Puppeteer / pytest 等真机工具去实际验证功能,而不是只看 LLM 自己说”我觉得做好了”。验证失败就回退重做,验证通过才标记 passes=true。
参考资料:Effective Harnesses for Long-Running Agents(Anthropic,2025-11-26)。原文明确提到 Puppeteer MCP server 用于前端功能验证,以及一句非常狠的规则:”It is unacceptable to remove or edit tests because this could lead to missing or buggy functionality”(不可接受删掉或修改测试,因为这会导致功能缺失或带 bug)。
解哪条故障:故障 ④ tool 错误吞的下游——光在工具调用层面返回 error 不够,任务层面必须有”真机验证”才能确认功能真的跑通。
生活化类比:QA 验收测试。开发说”我修好了”不算数,得 QA 真的点按钮、真的跑场景、真的截图对照才算数。
1 | # 机制:Verification Loop |
对比 naive agent 的 run_pytest 函数(它丢掉了 returncode),这里显式保留 result.returncode == 0 作为 passed 判定——“通过”必须来自外部真实反馈,而不是 agent 自己说通过了。Anthropic 官方还额外约束:”不能改测试让它通过”——如果 agent 发现测试在报错,正确路径是”去修实现”,错误路径是”把测试改成能过”。这是保证验证闭环不被 agent 作弊的最后一道防线。
Subagents 子代理分治(context 溢出的另一种解法)
context 溢出的第二种解法——和 Context Management压缩、Feature List拆分互补。
一句话定义:把特定子任务派发给一个独立 context 的子 agent,子 agent 在自己的 messages 列表里工作、完成后只把结果返回主 agent,主 Agent 通常只接收子任务结果,从而减少中间工具日志和试错过程对主 Context 的占用。
参考资料:Building agents with the Claude Agent SDK(Anthropic,2025-09-29)。Claude Code 等编码 Agent 可以通过子 Agent 将任务放入独立上下文(参见 Effective Harnesses 中关于 Subagents 的讨论)。
解哪条故障:故障 ② context 溢出的另一解法——子 Agent 可以在独立 Context 中处理多轮试错和大量工具调用,主 Agent 主要接收结构化结果,但仍需承担任务编排和结果整合成本。
生活化类比:外包给顾问。CEO 把一块专业问题外包给咨询顾问,顾问自己开了一个独立项目(独立资料库、独立讨论),几周后只交一份结论给 CEO。CEO 的主 context 没有被顾问讨论的中间文档污染。
1 | # 机制:Subagents 子代理分治 |
注意 sub_messages = [...] 这一行——它不是从主 agent 的 messages 复制过来的,而是全新的独立列表。这是 Subagents 机制的核心:context 是隔离的。主 agent 拿到的只有子 agent 的最终 return 值,中间子 agent 跑了 20 轮工具调用主 agent 一无所知——正因为”一无所知”,主 context 才能保持干净。
Generator-Evaluator(三角色分工与独立评审)
它不再是在单 agent 上打补丁,而是直接引入多 agent 架构
Anthropic 的文章说该架构“受到 GAN 启发”,但这里并不存在 GAN 的训练过程、损失函数或参数更新。更准确的理解是:把规划、生成和评审职责分开,用独立评审减少生成者自评带来的偏差。
一句话定义:把任务处理流拆成三个角色:Planner(规划者)拆任务,Generator(生成者)完成实现,Evaluator(评估者)依据独立标准检查产物。评估输入应尽量聚焦可观察结果和验收标准,而不是接受生成者的自我声明。
参考资料:Harness Design for Long-Running Application Development(Anthropic,2026-03-24,作者 Prithvi Rajasekaran)。原文原话:”Taking inspiration from Generative Adversarial Networks (GANs)”,并明确设计了 three-agent architecture—planner, generator, and evaluator。
解哪条故障:故障 ⑦ 缺自动化评审——naive agent 里没有任何独立评审角色,agent 自己说做完就做完;Generator-Evaluator 架构把”评审”作为一个独立 agent 机械执行的步骤,打破”生成者自评偏见”。
生活化类比:论文评审制度。作者(Generator)写论文,审稿人(Evaluator)不看作者的写作过程只看论文本身,按评分标准打分;编辑(Planner)在作者和审稿人之间做协调,决定修改方向和是否录用。三个角色分离才能保证论文质量,让作者自己评自己的论文质量无法保证。
1 | # 教学示例:真实项目应把 call_llm 替换为模型客户端,并启用结构化输出。 |
示例要求 Planner 和 Evaluator 返回结构化 JSON,并对结果做最小校验。真正的可靠性仍应来自外部证据,例如 pytest、类型检查、静态分析或浏览器测试;“换一个 Agent 再评一次”只能降低部分自评偏差,不能替代确定性验证。
走到这里,我们已经把八大机制的核心逻辑过了一遍。手里现在有:Agent Loop 的四相刹车(救①)/ Tool Use 的结构化错误(救④)/ Progress Tracking 的游戏存档(救⑤)/ Context Management 的工位清理(救②③)/ Feature List 的待办清单(救②源头)/ Verification Loop 的 QA 闸(救④下游)/ Subagents 的外包顾问(救②另一解法)/ Generator-Evaluator 的论文评审(救⑦)——这八个工具,就是你接下来看任何 Agent 产品时的解析框架。