Harness Engineering:决定 Agent 成败的,是模型之外的那圈工程
模型越来越聪明,为什么 Agent 还是动不动就翻车?答案不在模型里,而在模型外面那一圈工程系统——harness。本文从概念讲到实战,附带可直接照抄的检查清单。
一、先看一个反直觉的事实
同一个 Claude / Codex 模型,同一道任务,换两套"外壳",成功率能从 20% 跳到接近 100%——模型一个字没改。
这是 learn-harness-engineering 课程(见底部参考文档)里反复出现的实测结论:一个 TypeScript/React 项目,只给模型一个 README 时成功率约 20%;把"规则、状态、验证"这套外壳补齐后,成功率逼近 100%。没有换模型,只换了 harness。
OpenAI 的案例更极端(2026 年 2 月,Ryan Lopopolo):一支小队从 2025 年 8 月底的空仓库起步,约五个月里 0 行人工代码,每一行都由 Codex 生成,最终产出约 100 万行代码(含应用逻辑、基础设施、工具链与文档)、1500 个合并的 PR,团队从 3 人涨到 7 人、人均吞吐反而上升到 每人每天 3.5 个 PR。这群人几乎没碰业务代码,他们在干一件事——设计让模型可靠产出代码的环境。据他们估算,整体只花了手写代码约 1/10 的时间。
这件事现在有了名字:Harness Engineering(脚手架工程 / 框架工程)。
一句话定义:
Agent = Model + Harness。 模型负责"聪明",harness 负责"可靠"。
二、什么是 Harness?
最常见的误解:以为 harness 就是一个写得好的 system prompt,或者一个 CLAUDE.md 文件。错。
各家给的定义高度一致,核心都是同一句话:
Harness = 模型权重之外,所有决定"智能能落地多少"的工程基础设施。 (LangChain 的说法:"every piece of code, configuration, and execution logic that isn't the model itself.")
它包括但远不止 prompt:工具的定义与执行、文件系统与沙箱、状态持久化、验证机制、上下文管理、子智能体编排、确定性的钩子/中间件(compaction、续跑、lint 拦截等不靠模型自觉、由代码强制执行的环节)、可观测性……
打个最贴切的比方:
把 Agent 当成一个今天入职的资深工程师。 他很聪明,但对你的代码库一无所知。他需要:项目文档、跑得起来的环境、能用的工具、知道"完成"的标准、以及交班记录。 这些东西的总和,就是 harness。你给的脚手架越完整,这个"新人"发挥出的能力就越接近他的真实水平。
关键洞察:今天 Agent 失败,多数不是"不够聪明",而是"看不到 / 跑不动 / 不知道何时算完"——这些全是 harness 问题,不是模型问题。 这就是所谓的 capability–execution gap(能力与执行的鸿沟)。
三、Harness 的五个子系统
把一个完整的 harness 拆开,可以收敛成五个子系统。下面这套五分法综合自三处:WalkingLabs 的 learn-harness-engineering 课程、LangChain 的组件全景、以及 Anthropic 的长任务实践。建议按下面这张表逐项自查:
一点说明(各家划分并不完全一致):
learn-harness-engineering课程原本的五件套是 Instructions / State / Verification / Scope / Session Lifecycle——把"会话生命周期"单列为一项。本文改用更贴近 LangChain/Anthropic 的切法:把"工具与环境"(两家都重点强调,且最直接决定 Agent 能否跑得起来)提为核心一项,而把"会话生命周期"并入第四节的长任务方法(初始化/执行分离、干净重启)。子系统怎么命名、归到几项都不重要,别漏项才重要。
┌─ Instructions 指令:项目是什么、约束是什么
├─ Tools/Env 工具与环境:能做什么、跑得起来
Harness ─────┼─ State 状态:上次进行到哪了
├─ Scope 边界:这次只做一件事,做到什么算完
└─ Verification 验证:怎么自证做对了| 子系统 | 解决的问题 | 典型载体 | 投入产出比 |
|---|---|---|---|
| Instructions(指令) | Agent 不知道项目背景与红线 | AGENTS.md / CLAUDE.md,约 100 行 |
高 |
| Tools / Environment(工具与环境) | 能力够不够、环境跑不跑得起来 | 工具集、pyproject.toml/package.json、.nvmrc、Docker/devcontainer |
高 |
| State(状态) | 跨会话失忆,重复劳动 | PROGRESS.md / claude-progress.txt、git 提交历史 |
中高 |
| Scope(边界) | Agent 一次想干太多,半途而废 | 机器可读的 feature list(JSON) | 中高 |
| Verification(验证) | 自我感觉良好,假装做完了 | 测试、lint、type-check、E2E | 最高 |
下面逐个拆。
1. Instructions:把"新人须知"写进仓库
原则不是"写得越多越好",而是 progressive disclosure(渐进式披露):一个 100 行左右的核心指令文件,说清楚项目是什么、技术栈、首次运行命令、不可违反的约束、文档入口。细节按需链接出去,而不是一次性糊一篇百科全书给模型——后者只会稀释注意力。
核心铁律:仓库即唯一事实来源(repo as the system of record)。 Agent 不能像人一样转头问同事,凡是它需要的东西,必须在仓库里看得到。
2. Tools / Environment:少给约束,多给能力
- 工具权限遵循 最小权限,而不是一律禁掉——限制过严,Agent 就只能靠猜测。
- 环境要自描述、可复现:依赖文件、运行时版本、容器配置都齐了,Agent 才能一键起服务。Anthropic 的实践里专门有个
init.sh负责拉起开发环境。 - 一个被低估的能力:给 Agent 一个能跑 bash / 执行代码的沙箱。比起预置一堆窄工具,"能自己写代码并运行"才是通用自治的关键——它能自己验证、自己排错。
3. State:像倒班工人一样交接
长任务跨越多个上下文窗口,每个新会话都是一张白纸。Anthropic 把这比作"轮班的工程师之间没有交接文档"。
解法是把状态落盘:
- 进度文件(
claude-progress.txt/PROGRESS.md)记录:做完了什么、正在做什么、卡在哪。 - 用 git 提交和提交信息留下可追溯的痕迹。
- 每个会话开始先读进度和 git log,结束前更新进度。
效果:有 harness 时,Agent 能精确地"从上次断点接着干";没有时,它要么重做,要么每次从头开始。
4. Scope:一次只做一件事
Agent 的通病是贪多——一次想实现过多功能,结果哪个都没做完。
Anthropic 的做法很直接:把任务拆成 200+ 条颗粒度极细的 feature list,每条标注 pass/fail,用 JSON 而不是 Markdown(JSON 结构化,模型更不容易擅自改写)。每个会话只做一个 feature,做完、测过、提交、更新进度,再进下一个。
5. Verification:把"好不好"变成"过没过"
这是投入最低、回报最高的子系统,也是最容易被忽略的。
模型有两个致命倾向:
- 过早宣布胜利——没测就说"做完了"。
- 自我表扬——让它评价自己的产出,它几乎总说"很棒",哪怕在人看来一塌糊涂。
对策:
- 在文档里显式列出验证命令(test / lint / typecheck),让 Agent 每次都能跑。
- 对 Web 应用,要求用浏览器自动化(如 Puppeteer MCP)做端到端验证,而不是"看起来对就行"。Anthropic 实测这一条让"功能真的可用"的准确率大幅提升。
- 把主观质量变成可打分的客观标准。Anthropic 做前端时,把"好不好看"这种主观判断拆成四个可评维度:design quality、originality、craft、functionality,再用 few-shot 例子校准评审者。
四、长任务的进阶方法
短任务把上面五件事做好就够了。但当任务要跑几个小时、跨几十个上下文窗口时,还需要三个进阶模式。
1. 初始化 Agent 与执行 Agent 分离
Anthropic 的长任务 harness 是双角色架构:
- Initializer Agent(初始化):第一个会话搭地基——写
init.sh、建进度文件、做首次 git 提交、生成那份 200+ 条的 feature list。 - Coding Agent(执行):后续每个会话遵循固定流程——读进度 → 先跑 E2E 冒烟测试 → 完成一个 feature → 提交 → 更新进度,离场时必须留下可合并的干净状态。
2. 生成与评估分离(Generator / Evaluator)
不要让同一个 Agent 既当运动员又当裁判。独立的生成者和评估者能破解"自我表扬"。评估者只在任务超出"单个模型独自能可靠完成"的范围时才值得加——任务很简单时,这套开销反而是浪费。
Anthropic 的经典对照实验:一句话 prompt 让做个复古游戏。
- 单个 Agent 独自跑(solo run):$9、20 分钟,产出一个跑不起来的应用。
- 三 Agent harness(规划+生成+评估):$200、6 小时,产出一个能玩、UI 精致的游戏。
质量差距撑得起这笔投入——前提是任务确实难。
3. 上下文重置 优于 上下文压缩
当模型感到"token 快用完了"会出现 context anxiety(上下文焦虑)——提前匆忙收尾。直觉做法是把历史**压缩(compaction)**塞进新窗口;但 Anthropic 发现,直接清空上下文、配合结构化的交接产物(progress 文件 + feature list)重新开始,效果反而更好。这也呼应 LangChain 说的 "Ralph Loop":在干净的新窗口里重新注入 prompt,对抗 context rot(上下文腐烂)。
五、案例深读:OpenAI 的 100 万行代码是怎么撑住的
OpenAI 这篇《Harness Engineering: Leveraging Codex in an Agent-First World》是目前最翔实的实战记录。把它拆开看,几乎每条经验都能对应回前面的五个子系统——但有几个点足够反直觉,值得单独讲。
1. 工程师的角色变了:不写代码,造环境
早期进展比预期慢,原因不是 Codex 不行,而是环境欠规约——Agent 缺工具、缺抽象、缺内部结构。于是工程师的核心工作变成一句话:"缺了什么能力,怎么把它做得对 Agent 既可见又可强制?"
出问题时,修法几乎从不是"让模型再努力一点",而是回头补 harness。人只在更高的抽象层工作:排优先级、把用户反馈翻译成验收标准、验证结果。连修复也是让 Codex 自己写。
2. 给地图,别给一千页手册
他们一开始也试过"一个大 AGENTS.md",失败得很典型:
- 上下文是稀缺资源——大文件把任务、代码、相关文档全挤出去了;
- 全是重点 = 没有重点——Agent 退化成局部模式匹配;
- 瞬间腐烂——很快堆满过期规则、无人维护;
- 难以校验——一整块大文件没法做机械检查(覆盖率、新鲜度、交叉链接)。
改法:把 AGENTS.md 当目录(table of contents),不当百科全书。 一个约 100 行的 AGENTS.md 只做"地图",真正的事实来源放在结构化的 docs/ 目录里——设计文档、执行计划(active/completed/技术债追踪)、生成的 DB schema、产品规格、*-llms.txt 参考资料等。计划是一等公民:小改动用临时轻量计划,复杂工作落成签入仓库的执行计划,带进度和决策日志。
这就是 渐进式披露:Agent 从一个小而稳的入口进入,再被"教"去哪里找下一步。而且机械强制——专门的 linter 和 CI 校验知识库是否最新、是否交叉链接;还有个**"doc-gardening"(文档园丁)Agent** 定期扫描过期文档、自动提修复 PR。
3. 一句最该记住的话:Agent 看不见的,就等于不存在
From the agent's point of view, anything it can't access in-context while running effectively doesn't exist.
那条对齐了团队架构的 Slack 讨论?如果 Agent 检索不到,它就跟"三个月后入职的新人不知道"一样不存在。结论:不断把上下文塞进仓库——代码、markdown、schema、可执行计划,这些才是 Agent 唯一看得见的东西。
由此推出一组反直觉取舍:优先选 Agent 能完全内化的依赖和抽象。所谓"无聊"的技术因为 API 稳定、可组合、在训练集里出现多,反而更好被模型建模。有时候自己重写一个子集,比绕开一个不透明的上游库更划算——他们没用通用的 p-limit,而是手写了个 map-with-concurrency,深度集成自家 OpenTelemetry、100% 覆盖、行为完全可控。
4. 让应用本身对 Agent 可读(这是吞吐的真瓶颈)
代码吞吐上来后,瓶颈变成人的 QA 能力。对策是把 UI、日志、指标直接做得对 Codex 可见:
- 每个 git worktree 能独立启动一份应用实例,一个改动配一个实例;
- 把 Chrome DevTools Protocol 接进 Agent 运行时,做了 DOM 快照、截图、导航的 skill——Codex 能自己复现 bug、验证修复、对 UI 行为推理;
- 给每个 worktree 配临时的可观测性栈,Codex 用 LogQL 查日志、PromQL 查指标。于是 "保证服务启动 800ms 内完成"、"这四条关键链路里没有 span 超过 2 秒" 这种 prompt 变得可执行。
效果:单个 Codex run 经常连续干 6 小时以上(通常在人睡觉的时候)。
5. 强制不变量,而非微观管理
文档撑不住一个全自动生成的代码库的连贯性,架构约束才行。他们的口号是 "enforce invariants, not micromanage implementations":
- 要求"在边界处解析数据形状",但不规定怎么做(模型自己爱用 Zod,没人指定);
- 整个应用建在刚性分层架构上:每个业务域固定分层(Types → Config → Repo → Service → Runtime → UI),依赖方向严格校验,横切关注点(鉴权、连接器、遥测、特性开关)只能从单一的 Providers 接口进入;
- 全部用 自定义 linter(当然也是 Codex 写的)和结构化测试机械强制;自定义 lint 的报错信息直接把修复指引注入 Agent 上下文。
一句点睛:这种架构本来是公司几百个工程师才需要的;有了 Agent,它成了一开始就得有的前提——约束正是"快而不失序"的前提。 原则是 "中心强制边界,局部允许自治":严格守住边界、正确性、可复现;边界之内,给 Agent 充分自由。代码不总符合人类审美,只要正确、可维护、对未来的 Agent 可读,就达标。
6. 吞吐量改变了合并哲学
当 Agent 吞吐远超人的注意力时,很多传统工程规范反而有害:仓库几乎没有阻塞性的合并门禁,PR 短命,测试 flake 用"重跑一次"解决而非无限期阻塞。因为在这种系统里,纠错很便宜,等待很贵。——这在低吞吐环境下是不负责任的,在这里却往往是对的。
往深一层,这背后是整套工程实践的经济学内核:token、算力、Agent 工时都不再稀缺,唯一真正稀缺的,是"需要人同步介入"的那点注意力。 于是几乎所有工程决策都收敛到同一个目标——把人的同步注意力省到极致。合并门禁的取舍、QA 瓶颈的破解、后台自动清理,本质上都是在省这一项。看懂了这条,前面那些反直觉的取舍就都顺理成章了。
7. 熵与垃圾回收:把人的品味"编码一次,永久强制"
全自动也带来新问题:Codex 会复刻仓库里已有的模式,包括其中不好的,于是必然漂移。
他们一开始靠人手清——每周五(20% 的工时)专门清理 "AI slop",根本不 scale。 真正的解法是把 golden principles(黄金法则) 编码进仓库,配一套周期性清理流程:
- 优先用共享工具包,把不变量收敛在一处,而不是到处手写零散的 helper;
- 不"YOLO-style"地探数据——而是在边界处校验、或用带类型的 SDK,避免 Agent 建在猜出来的数据形状上;
- 一组后台 Codex 任务定期扫描偏差、更新质量评分、提定向重构 PR——大多能一分钟内 review 完、自动合并。
这套机制像垃圾回收。技术债是高息贷款:持续小额偿还,永远好过让它复利滚大再痛苦集中还。 人的品味只需被捕捉一次,之后在每一行代码上被持续强制。
8. 自治的临界点
随着测试、验证、review、反馈处理、恢复都被编码进系统,这个仓库最近跨过了一道门槛:给一句话 prompt,Codex 能端到端地——校验当前状态 → 复现 bug → 录一段失败视频 → 实现修复 → 驱动应用验证 → 再录一段修复后的视频 → 开 PR → 回应人和 Agent 的反馈 → 检测并修复构建失败 → 只在需要判断时才升级给人 → 合并。
但他们明确提醒:这高度依赖该仓库特定的结构与工具投入,不要假设能直接泛化——至少现在还不能。
一句话收束这一节:人的纪律没有消失,只是从代码本身转移到了脚手架上。
六、方案对比:到底要做到哪一档?
按投入由轻到重,分三档:
- A. 裸 prompt —— 只给一个 README,什么都不补。
- B. 单 Agent + 五子系统 —— 补齐第三节那五项;一个 Agent,可跨会话。
- C. 多 Agent 全套 —— B 之上再叠第四节的进阶方法(初始化/执行分离、生成/评估分离、上下文重置)。
怎么选,看一个问题:这个任务,一个 Agent 在一个会话里能不能干完?
- 能,且做完即弃 → A
- 要持续迭代、跨多会话 → B(绝大多数情况)
- 一个 Agent 独自搞不定(几小时、跨几十个窗口、质量要求高)→ C
| A | B | C | |
|---|---|---|---|
| ▲ 得到 | |||
| 任务成功率 | 低 | 高 | 很高 |
| 跨会话连续性 | 无 | 强 | 强 |
| 质量可控性 | 低 | 高 | 很高 |
| ▼ 付出 | |||
| 搭建成本 | 几乎为零 | 中 | 高 |
| 单位任务花费 | 低 | 中 | 高 |
| 维护负担 | 低 | 中 | 中高 |
| → 选它的场景 | 一次性探索 | 默认:要持续迭代的活 | 超出单模型能力的硬任务 |
结论:
- 绝大多数团队的最优解是 B——投入产出比最高,是性价比最佳点。不要一上来就叠加多 Agent。
- C 只在任务"超出单个模型独自能力"时才划算:几小时以上、跨多窗口、质量要求高(如完整应用、复杂重构)。它总评不比 B 高,是因为简单任务里那套开销纯属浪费——复杂度要匹配任务难度。
- A 只适合一次性、用完即弃的探索。 任何要持续迭代的东西,停在 A 都会反复付出返工代价。
一个反复被验证的纪律:模型升级后,回头把 harness 逐件做消融测试(ablation)。 Anthropic 在 Opus 4.6 上就发现,模型变强后原来的"sprint 契约"机制可以整个删掉,质量不降、token 大降。Harness 不是只增不减——模型补上的能力,harness 就该减负。
七、照抄即用:Harness 自查清单
落地时,对着这张表勾一遍:
- Instructions
- 有一个 ≤100 行的
AGENTS.md/CLAUDE.md,写清项目、技术栈、首次运行命令、红线约束 - 细节走链接,不堆成百科全书
- Agent 需要的一切都在仓库里(repo = 唯一事实来源)
- 有一个 ≤100 行的
- Tools / Environment
- 依赖、运行时版本、容器配置齐全,可一键起环境(
init.sh) - 给了能跑 bash / 执行代码的沙箱
- 权限按最小权限给,而不是一刀切禁
- 依赖、运行时版本、容器配置齐全,可一键起环境(
- State
- 有进度文件,记录"做完/在做/卡住"
- 会话开始读进度+git log,结束前更新
- 用 git 提交留痕
- Scope
- 任务拆成机器可读的 feature list(建议 JSON)
- 每会话只做一个 feature
- Verification
- 文档里显式列出 test / lint / typecheck 命令
- Web 应用有端到端验证(浏览器自动化)
- 主观质量已拆成可打分的客观维度
- 长任务额外项
- 初始化与执行 Agent 分离
- 生成与评估分离(任务够难才上)
- 用上下文重置 + 交接产物,而非一味压缩
八、为什么这件事会长期重要
有人会问:模型不是越来越强吗,等它足够聪明,harness 不就没用了?
恰恰相反。LangChain 和 OpenAI 的判断一致:模型越强,harness 的重心只是从"打补丁填模型的坑"转向"为智能优化系统"——但它不会消失。 就像今天模型早已不傻,prompt engineering 依然重要一样。
还有一层更硬的耦合常被忽略:今天的强模型,是和 harness 一起被训练出来的。 LangChain 指出,Claude Code 这类产品在后训练阶段就把"模型 + harness"放进同一个回路联调,模型被专门优化以适配它训练时的那套外壳。这也解释了一个反直觉现象——把一个强模型塞进一个陌生的 harness,往往跑不出它的真实水平。 模型与 harness 是共同进化的关系,不是谁迟早取代谁。
OpenAI 那句话点破了行业的转向:
在 agent-first 的世界里,工程师的主要工作不再是写代码,而是设计让 Agent 可靠产出的环境。
模型是发动机,harness 是底盘、传动和仪表盘。光有发动机,跑不远,也不安全。
附:参考文档
- LangChain — The Anatomy of an Agent Harness:
Agent = Model + Harness的组件全景。 - Anthropic — Effective harnesses for long-running agents:初始化 Agent、feature list、自验证的实战。
- Anthropic — Harness design for long-running apps:生成/评估分离、上下文重置、可打分质量。
- OpenAI — Harness Engineering: Leveraging Codex in an Agent-First World:100 万行代码与黄金法则。
- WalkingLabs — learn-harness-engineering(在线讲义):12 讲 + 6 个动手项目的系统课程(五子系统框架的来源)。
- WalkingLabs — awesome-harness-engineering:资源合集(上下文、评估、护栏、编排、Benchmark)。