Agent 老是散架?该修的是 harness,不是提示词
大多数 agent 失败不是推理失败,是环境失败。提示词改变一次尝试,harness 改变每一次尝试。19 节清单:从任务契约到变更回执,从工具网关到"构建是为了删除"。
大多数人在改错的那一层。
agent 失败的时候,他们重写提示词。再失败,就加更多指令。然后换模型、加工具、把上下文窗口调大,希望下一次跑出来不一样。
但很多 agent 失败不是推理失败,是环境失败:
- 它不知道该看哪些文件;
- 它在对的地方用了错的工具;
- 它把上一轮会话里做的决定丢了;
- 它没跑检查就宣称成功;
- 它在一次部分失败之后重复了同一个动作;
- 它拿到了本该需要审批的权限。
模型未必是问题所在;模型周围的系统不完整。这个系统叫 harness,而设计它正在变成一门自己的工程学科。
提示词改变一次尝试,harness 改变每一次尝试。
下面这份指南来自英文长文《Harness Engineering: The Complete Guide to Building AI Agents That Don't Fall Apart》(Lunar,2026-09-06)。原文 19 节,本文完整编译全部要点,并在最后加上我们的对照整理与边界说明。
一、模型不是 agent
模型能推理、生成、比较、选择。但 agent 还必须与真实环境交互:理解任务、找到相关上下文、选择并使用工具、保持状态、遵守权限、检查结果、从失败中恢复、证明工作确实完成了。
模型是这套系统里的推理引擎,harness 是让推理变得可运行的一切。
user request
|
v
+-----------------------------+
| HARNESS |
| contract | context | policy |
| tools | state | checks |
| traces | recovery |
+-----------------------------+
|
v
model
|
v
real environment
一个强模型装在弱 harness 里,仍然是弱 agent。 它可能偶尔给出惊艳的单项回答,但在长任务、变化的环境与部分失败面前,表现会不稳定。
harness 工程的目标不是消除模型的不确定性,而是把不确定性关进一个能观察、能验证、能恢复的系统里。
二、从一份任务契约开始
大多数 agent 任务从一句模糊的意图开始:"优化一下注册流程。" 这句话够对话用,不够自主执行用。
在 agent 动手之前,harness 应该把它变成一份任务契约,回答五个问题:
- 必须存在的结果是什么?
- 范围之内包含什么?
- 什么不能变?
- 什么证据能证明完成?
- 哪些动作需要人批准?
objective: reduce onboarding drop-off
scope:
- signup flow
- onboarding analytics
constraints:
- do not change authentication
- preserve existing mobile behavior
acceptance:
- tests pass
- analytics event is emitted
- screenshots cover desktop and mobile
approval_required:
- production deployment
- database migration
这件事改变的是 agent 的问题:从"我下一步该做什么?"变成"哪个动作让环境朝契约里的结果前进?"
没有契约,agent 会为"看起来在做事"优化;有契约,它才能为"被验证的完成"优化。
三、给地图,不给手册
把整个仓库、整套文档、整段历史都塞进上下文,不叫上下文工程,叫上下文洪水。
harness 应该先给一张小地图,等细节相关时再让它去取。
PROJECT MAP
product rules -> docs/product/
architecture -> docs/architecture.md
frontend -> apps/web/
backend -> services/api/
tests -> tests/
commands -> docs/commands.md
release rules -> docs/release.md
这就是渐进披露:任务 → 项目地图 → 相关子系统 → 具体文件 → 局部说明。上下文该因为任务需要而展开,而不是因为信息存在而展开。
一个合格的"上下文编译器"要决定四件事:
- 什么永远需要;
- 什么可以稍后检索;
- 什么已经过期;
- 什么可以摘要,什么必须逐字保留。
目标不是最大上下文,而是每个 token 的最大信号。
四、造工具网关,不要堆工具
给 agent 二十个工具,不会让它变强,只会给它二十种犯错的方式。
每个工具都该有清晰的契约:
TOOL: edit_file
inputs:
path
patch
preconditions:
path exists
path is inside allowed workspace
success evidence:
patch applied
resulting diff returned
failure behavior:
no partial overwrite
structured error returned
risk class:
reversible
harness 要控制工具怎么被暴露、怎么被使用:隐藏无关工具、校验参数、限制路径与域名、挂超时、让重试幂等、归一化输出、对高风险动作要求确认、返回证据而不是一句 success。
由此产生一条重要的分离:
model decides intent
gateway validates action
tool changes environment
sensor observes result
模型可以提议一个动作;由网关决定这个动作是否足够合法、可以执行。
五、把脑、手、历史分开
很多脆弱的 agent 把所有东西都塞进一段越来越长的转录里:推理、工具调用、文件、决定、错误、旧观察,全都在抢同一个上下文窗口。
更强的系统把三种职责分开:
| 部分 | 职责 | 它不需要什么 |
|---|---|---|
| 脑(Brain) | 规划、推理、选择 | 不需要每一段原始事件 |
| 手(Hands) | 在受控环境里执行工具 | 不需要理解整个目标 |
| 历史(History) | 保存持久事实、决定与运行状态 | 不需要会推理 |
模型需要的是正确的当前状态,不是全部事件;沙箱需要的是安全执行一个有界的动作,不是全盘理解;会话日志需要的是在当前上下文消失之后仍然留住发生了什么。
这种分离让长跑 agent 更容易恢复、检查和修复,也让你能替换其中一块而不用重建全部。
六、记忆必须变成持久状态
对话历史不是可靠记忆,它只是一个事件流。有用的记忆应该被转换成显式状态,至少保留四类:
| 类别 | 内容 |
|---|---|
| FACTS | 关于环境的稳定信息 |
| DECISIONS | 做过的选择以及理由 |
| PROGRESS | 已完成 / 进行中 / 阻塞 / 剩余 |
| LESSONS | 应当改变未来行为的失败 |
facts:
- checkout validation lives in services/orders
decisions:
- reuse the existing validation pipeline
- reason: avoids a second source of truth
progress:
completed:
- added server-side rule
remaining:
- update integration test
lessons:
- local test command requires TEST_DB_URL
这比"回放五十页转录、指望模型注意到那一行"有用得多。原始历史存起来做审计,持久状态编译出来做执行。
七、完成需要证据
agent 说"done"不是任务完成的证据,那只是又一个模型输出。完成必须由环境里可观察的变化来判定。
| 主张 | 证据 |
|---|---|
| "bug 修好了" | 之前失败的测试现在通过 |
| "页面能用了" | 浏览器流程走通 |
| "迁移是安全的" | dry run 与回滚都通过 |
| "报告是对的" | 数值与源数据一致 |
| "任务完成了" | 每一条验收检查都通过 |
harness 应该先跑最便宜的确定性检查:
syntax
-> types
-> focused tests
-> integration tests
-> visual or semantic review
-> human approval
不要用另一个模型去回答编译器、schema、校验和、查询或测试能回答的问题。用模型处理歧义,用代码处理管道。
模型可以提议"任务完成了",只有环境能证明它。
八、验证应该攻击结果
Worker 和 evaluator 不该共享同一个目标:worker 努力做出最强的解法,evaluator 努力找出它该被拒绝的理由。
worker
-> produces candidate
verifier
-> checks contract
-> searches for missing cases
-> tests unsupported claims
-> attempts to break result
survives -> accept
fails -> return targeted evidence
这种不对称很重要。如果你让同一个 agent、在同一个上下文里"复查一下自己的活",它通常会保住那个制造了错误的假设。
有用的验证环节应该具备:明确的拒绝标准;能拿到产物;能拿到验收契约;需要时能换工具或换新上下文;以及"只拒绝、不负责修"的权限。
验证不是第二意见,是一次未遂的反证(attempted disproof)。
九、模型提议,策略授权
有些规则永远不该依赖"模型是否记得住":没批准不发布、不暴露密钥、不在工作区之外写、不超预算、没跑过的测试不许标通过。
这些不是提示词里的建议,它们是策略。最安全的设计把策略放在推理回路之外:
| 风险档 | 动作 | 门 |
|---|---|---|
| 低风险 | 读文件、搜索、查看 | 自动 |
| 可逆变更 | 改工作区、跑测试 | 自动 + trace |
| 外部副作用 | 发消息、部署、购买 | 显式批准 |
| 不可逆 / 敏感 | 删数据、轮换凭证、全局发布 | 硬闸或禁止 |
后果越强,门越硬。 而自主并不是没有控制,自主是在一个被清晰强制的边界里自由行动的能力。
(这一条和我们拆 DeepSeek super-agent 时看到的设计是同一个思路:权限由代码强制,不由提示词决定。见文末对照。)
十、恢复要针对失败类别
最常见的"恢复"策略是:出错了,再试一次。那不是恢复,那是重复。
harness 应该先分类,再选动作:
| 失败类别 | 正确动作 |
|---|---|
| 工具超时 | 退避后重试 |
| 参数非法 | 修工具调用 |
| 缺上下文 | 检索具体来源 |
| 测试失败 | 检查失败行为 |
| 权限被拒 | 申请批准或走安全路径 |
| 需求自相矛盾 | 升级给人 |
| 同一失败反复出现 | 停止循环 |
一次重试至少应该改变一个相关条件,否则你就是在花钱复现同一个失败。
observe
-> decide
-> act
-> measure
-> accept / repair / escalate / stop
每个循环都要有预算:最大尝试次数、最大时间、最大花费、最大破坏范围、升级条件。可靠的 agent 知道怎么继续,也知道什么时候继续不再理性。
十一、把指令变成基础设施
当 agent 指令解释"本地现实"时,它很有用;但指令本身是弱强制。一条规则如果反复重要,就把它往下挪一层。
| 从 | 到 |
|---|---|
| "记得用格式化工具" | 自动跑格式化 |
| "不要跨层 import" | 加一条架构测试 |
| "迁移要带回滚" | CI 里要求回滚文件存在 |
| "不要改生成文件" | 阻断对生成路径的写入 |
| "每个外部主张都要引用" | 校验引用覆盖率 |
于是有了一条指令阶梯:解释 → 清单 → 模板 → 自动检查 → 强制策略。
能往下挪就往下挪。提示词负责解释判断,harness 负责强制不变量。
十二、观察过程,不只看结果
一个干净的最终产物可以掩盖一个糟糕的过程:它可能访问了错的数据、忽略了失败的命令、把外部动作重试了两次、花掉十倍预算、或者用错误的理由得到了正确的答案。
你需要能把这次运行重建出来的 trace:
09:14 contract created
09:15 context source loaded: architecture.md
09:17 file edited: checkout.ts
09:18 focused test failed: duplicate coupon
09:21 implementation repaired
09:22 focused test passed
09:24 integration test passed
09:25 external deployment blocked: approval required
一份有用的 trace 要记:状态迁移、上下文来源、工具输入输出、环境变化、验证结果、重试原因、批准决定、成本与延迟。
目的不是监视,是局部修复:当一次运行在第 18 步崩掉时,你该能从一个可信的检查点重启,而不是从零重放整个任务。
十三、每次运行都要一份变更回执
长转录很难评审。一次运行结束时,harness 应该编译出一份小小的变更回执:
OBJECTIVE
Fix duplicate coupon application during checkout.
CHANGED
- checkout validation logic
- focused regression test
VERIFIED
- lint passed
- unit tests passed
- checkout integration test passed
NOT VERIFIED
- production payment provider
DECISIONS
- preserved existing coupon priority order
RISKS
- legacy mobile client was not available locally
APPROVAL NEEDED
- deploy to staging
回执不是"模型说了什么"的摘要,而是系统能证明什么的摘要。它给人一个紧凑的评审面,也给下一个 agent 会话一个可信的起点。
最好的交接不是"这是对话记录",而是"这是状态、证据,和未解决的风险"。
十四、每次失败都该升级 harness
最弱的团队修掉失败的产出;最强的团队同时修掉让这个失败成为可能的系统。
失败之后问八个问题:契约含糊吗?上下文不可见吗?暴露了错的工具吗?缺了前置条件吗?结果不可验证吗?策略被留在提示词里吗?恢复范围过宽吗?trace 不够吗?
然后把教训变成可复用的改进:失败 → 诊断 → 新的传感器 / 规则 / 地图 / 测试 / 工具契约 → 未来的运行自动变好。
这就是 harness 飞轮:修好一个答案只帮一次,修好 harness 帮每一次。
十五、harness 自己也会腐坏
不是越多越好。模型在变强、工具在变强、任务在变,昨天的护栏可能变成今天的摩擦:为旧模型写的绕行方案,会挡住新模型的好策略。
old model limitation
-> harness workaround
-> model improves
-> workaround remains
-> system becomes slower or less capable
把 harness 组件当生产代码对待:对每个 router、evaluator、记忆层、重试规则都问——它防的是哪种失败?这种失败现在还有多常见?它增加了多少延迟和复杂度?同样的结果现在能不能更简单地达到?如果删掉会发生什么?
最好的 harness 不是最大的那个,而是能可靠闭合"意图到证据"之间差距的最小系统。
构建是为了删除(build to delete)。
十六、最小可行 harness:六层
你不需要一个编排平台才开始。分层搭:
| 层 | 内容 |
|---|---|
| 1 有界的任务 | 目标、范围、约束、验收检查 |
| 2 可读的环境 | 项目地图、命令、局部说明、已知依赖 |
| 3 受控的动作 | 带类型的工具、参数校验、路径与权限边界、结构化结果 |
| 4 持久的执行 | 显式运行状态、检查点、决定、教训 |
| 5 证据 | 确定性检查、对抗式验证、变更回执 |
| 6 恢复与学习 | 失败分类、有界重试、升级、把反复出现的失败变成 harness 更新 |
先建能消除你当下真实失败的最小一层。 不要因为"单个提示词偶尔需要澄清"就上多 agent 架构——复杂度要靠观察到的失败来赚。
十七、可复用的 harness spec
在给 agent 实质自主权之前,把这八节填完:
AGENT HARNESS SPEC
1. CONTRACT
objective / scope / constraints / acceptance evidence
2. CONTEXT
always-loaded map / retrieval sources / local instructions / freshness rules
3. TOOLS
allowed tools / preconditions / side effects /
success evidence / timeout and retry policy
4. STATE
facts / decisions / progress / lessons / checkpoint format
5. POLICY
automatic actions / approval-required / prohibited / budget limits
6. VERIFICATION
deterministic checks / adversarial checks / acceptance rule
7. RECOVERY
failure classes / retry limits / escalation conditions / safe rollback
8. OBSERVABILITY
trace events / metrics / final change receipt
这些字段没定义,agent 就不是自主的,它是在即兴发挥。
十八、在正确的层级度量
token 数不是终点,尝试过的任务数也不是。有用的单位是被接受的工作:
accepted outputs
--------------------------------
human review minutes + run cost
另外跟踪:一次通过率、工具失败后的恢复率、重复失败率、每任务人工干预次数、没有依据的"完成"声明、从请求到被验证结果的时间、各组件的 harness 开销。
这能防住一个常见错觉:一个 agent 看起来极其高产,实际上在制造昂贵的评审工作。 目标不是更多 agent 活动,而是每单位人类注意力换到更多可信结果。
十九、什么时候不需要重 harness
不是每次模型调用都需要一个操作系统。以下情况用简单提示词就够:任务短、输出容易检查、失败代价低、没有外部副作用、人一直在回路里。
该上 harness 的信号:工作跨多个工具或会话、环境会变、动作有真实后果、完成与否难以人工判断、同一种失败反复出现、人工评审成了瓶颈。
harness 的目的不是让 demo 看起来高级,而是让真实工作可靠。
真正的转变
第一代 AI 产品围绕提示词构建;下一代正在围绕环境构建。
问题不再只是"怎么让模型答得更好",而是:怎么建一个系统,让好动作容易发生、危险动作被控制、失败可见、完成可证明?
这就是从提示词工程到 harness 工程的转变。模型提供智能,harness 提供结构,两者合起来才有可靠执行。
如果 agent 老是散架,别再往提示词里加形容词了——去搭它成功所需要的环境。
我们的整理:这份清单和我们写过的三篇怎么对
| 这份指南的机制 | 我们站内的对应拆解 | 差异 |
|---|---|---|
| "模型提议,策略授权"+风险四档 | DeepSeek super-agent:把「别打扰人」写成一张准入表:显式 / 推断 / 准入表三档 + 不受理的情况 | 本篇给的是通用原则;那篇是已落地的实现(不可变 Mandate、默认 observe、恢复前重校验) |
| "每次失败升级 harness"、"有界循环" | 循环在节点之内,图在节点之间:循环负责做对、图决定哪几步存在 | 那篇讲控制流结构,本篇讲运行环境;两者互补,不重叠 |
| "验证要攻击结果"、"完成需要证据" | Agent 安全事故九起与六项控制:真实事故里的控制项 | 那篇是事后取证,本篇是事前设计 |
三件最容易今天就做、收益最大、成本最低的事:
- 写一份任务契约(第五问"哪些动作要批准"经常被漏)。一份 YAML 就能把"看起来在做事"变成"可验收"。
- 把一条提示词下沉成检查。挑一条你重复提醒过的规则(不许跨层 import、必须带迁移回滚),把它变成 CI 里的一条断言。这一步比再加十句提示词有效。
- 加一份变更回执。哪怕只是一个 Markdown 模板,要求 agent 每次收尾填
VERIFIED / NOT VERIFIED / DECISIONS / RISKS。它会同时改善评审体验和下一轮的上下文。
边界
- 本文完整编译英文长文《Harness Engineering: The Complete Guide to Building AI Agents That Don't Fall Apart》(Lunar / @LunarResearcher,X Article,2026-09-06,约 2,900 万浏览)。原文为经验性方法论,没有基准测试、没有实测数据;刊出时间距本文约 57 天,属"近 60 天"窗口内。
- 原文的推荐语与引流(Substack、收藏、转发)未保留;结构图与代码块按原文顺序保留,转为等价的表格与代码块。
- 这不是"用了这套就不会失败"的保证。文中若干主张(如"同一上下文里自我复查会保住错误假设")是作者的工程经验判断,没有给出实验;我们在正文里按"主张"而非"结论"处理。
- 本文由 Software 2.0 编译整理,转载请注明出处;我们未对其中的方法做本地复测。
Sources
- 原文:Lunar《Harness Engineering: The Complete Guide to Building AI Agents That Don't Fall Apart》https://x.com/LunarResearcher/status/2096570562625655088(X Article 正文:https://x.com/i/article/2096536117130481673),2026-09-06
- 作者主页:https://x.com/LunarResearcher
同主题阅读
这是「Agent / MCP」专题的一部分。完整地图见:Agent / MCP / 自动化完全指南