Harness Engineering 完全指南:让 AI 编程智能体真正可靠地工作
从"模型不行"到"环境不行"的认知转变——一套系统的方法论,让 Codex、Claude Code 等 AI Agent 在真实工程项目中稳定交付。
1. 为什么你的 AI Agent 总翻车
你说你也算见过世面的人了——Claude Pro 订着,GPT-4o 的 API key 也有,SWE-bench 排行榜上的数字你比谁都清楚。有一天你终于想让 AI agent 帮你改一个真实项目,信心满满地交代下去。结果呢?加了功能但测试挂了,改了 bug 但引入了新 bug,跑了 20 分钟然后自信满满地说"完成了"——你一看代码,根本不是你要的东西。
这时候你的第一反应是什么?"这模型不行,得换一个更贵的。"
且慢。先别急着掏钱包。问题可能根本不在模型身上。
1.1 千里马也得配马鞍:能力鸿沟的真相
先看一组数据。截至 2025 年底,最强的 coding agent 在 SWE-bench Verified 上的通过率大约在 50-60%。这还是精心挑选过的、有明确 issue 描述和测试用例的任务。换到你日常开发的场景——需求模糊、没有现成测试、隐含的业务规则散落在各处——这个数字只会更低。
但这组数据背后藏着一个反直觉的事实。
Anthropic 做过一个对照实验:同一个 prompt("做一个 2D 复古游戏编辑器"),同一个模型(Opus 4.5)。
| 条件 | 时间 | 花费 | 结果 |
|---|---|---|---|
| 裸跑(无 harness) | 20 分钟 | $9 | 核心功能跑不起来 |
| 完整 harness(三 agent 架构) | 6 小时 | $200 | 游戏可正常游玩 |
模型没换。Opus 4.5 还是那个 Opus 4.5。换的是马鞍。
OpenAI 在 2025 年发布的 harness engineering 文章里说得更直白:Codex 在一个 harness 搭得好的仓库里,表现能从"不可靠"变成"可靠"。注意他们的用词——不是"好了一点",是质变。
💡 什么是 Harness? 就像一匹千里马,没马鞍你也能骑,但骑不了多远、跑不了多快、摔下来也不稀奇。Harness 就是那个马鞍——模型权重之外的一切工程基础设施。指令文件、工具配置、运行环境、状态管理、验证反馈,全部都算。
这就是"能力鸿沟"(Capability Gap)的真相:模型在基准测试上的表现和真实任务上的表现之间的巨大落差,大部分不是模型的问题,而是环境的问题。
1.2 Agent 到底卡在哪:五层失败分析
那具体是什么在出问题?我们可以把 agent 失败的原因归纳为五层防御,从最表层到最深层逐层排查:
第一层:任务规范不清晰。 你说"加个搜索功能",agent 理解的跟你完全不一样——搜什么?全文本还是结构化?要不要分页?要不要高亮?你没说清楚,agent 就自己猜。猜对了是运气,猜错了你再改,改的成本比一开始说清楚还高。
第二层:上下文供给不足。 就算你说清了,项目里隐含的架构约定 agent 也不知道。你们团队统一用 SQLAlchemy 2.0 的新语法,但 agent 默认写了 1.x 的代码。所有 API 端点必须走 OAuth 2.0 认证,但这个规则只存在于你脑子里和一个三个月前的 Slack 消息里。Agent 看不到这些——它不是不想遵守,是压根不知道有这回事。
第三层:执行环境有缺陷。 开发环境配置不完整、依赖缺了、工具版本不对。Agent 把宝贵的上下文窗口花在了 pip install 失败、Node 版本不对这些事上,而不是解决你的核心任务。
第四层:验证反馈缺失。 没有测试、没有 lint、或者验证命令根本没告诉 agent。Agent 写完代码,自己看了看觉得没问题,就说完成了。Anthropic 还观察到一个现象:当 agent 感觉上下文快满了,它们会匆忙结束当前工作,跳过验证步骤——这被称为"上下文焦虑"。
第五层:状态管理断裂。 长任务跨会话时,上次会话的发现全丢了,每个新会话都得重新探索项目结构、理解代码组织。缺乏持久化状态的 agent 在超过 30 分钟的任务中失败率急剧上升。
失败归因检查流程:
任务失败
│
├─→ 任务说清楚了吗? ──→ 第一层:任务规范
├─→ 项目约束告诉它了吗? ──→ 第二层:上下文供给
├─→ 环境能跑起来吗? ──→ 第三层:执行环境
├─→ 有验证手段吗? ──→ 第四层:验证反馈
└─→ 跨会话状态保留了吗? ──→ 第五层:状态管理💡 关键洞察: 养成"每次失败都归因到具体层"的习惯,你会发现"模型不行"这个结论在你的日志里出现得越来越少。
1.3 遇到失败先修 Harness:诊断循环方法论
核心原则就一句话:遇到失败,先别换模型,先检查 harness。
如果同一个模型在类似的、结构良好的任务中能成功,那优先假设是 harness 的问题。这就像汽车抛锚——你不会第一时间怀疑是发动机坏了,你会先看看是不是没油了。
具体怎么做?五步走:
① 每次失败都归因到具体层。 不要笼统地说"模型不行",而是问:是任务没说清楚?是上下文不够?是没有验证手段?把每次失败归到五层防御中的某一层。养成这个习惯,你会发现"模型不行"这个结论在你的日志里出现得越来越少。
② 给每个任务写显式的完成定义。 不要说"加个搜索功能",要说:
完成标准:
- 新增 GET /api/search?q=xxx 端点
- 支持分页,默认 20 条
- 返回结果包含高亮片段
- 所有新代码通过 pytest
- 类型检查通过(mypy --strict)没有显式的完成定义,agent 就会自己编一个——它编的那个,大概率不是你想要的。
③ 创建 AGENTS.md 文件。 在仓库根目录放一个文件,告诉 agent 这个项目的技术栈、架构约定、验证命令。这是 harness 工程的第一步,也是投入产出比最高的一步。一个 AGENTS.md 文件可能比你换一个更贵的模型更有效——我不是在开玩笑。
④ 建立诊断循环。 不要把失败当作"模型又犯傻了",而是当作"harness 又暴露了一个缺陷"的信号:
诊断循环:
执行任务 ──→ 观察失败 ──→ 定位到五层中的某一层
│
下次不再犯 ←── 修补那一层 ←──────────┘几轮下来,你的 harness 会越来越强,agent 的表现会稳定提升。就像修路——每填一个坑,下一段路就更平坦。
⑤ 量化改进。 记个简单的日志:每个任务成功了没有,失败了是哪一层的问题。跑几轮之后你就能看出来哪个层是瓶颈,集中火力修那个层。
💡 实操建议: 找一个 agent 在你的项目中反复失败的任务。跑一次,记录失败,归因到五层中的某一层,修那一层,再跑。重复三到五轮,记录每一轮的改善——你会亲眼见证"同一个模型、越来越好的结果"这件事。
1.4 OpenAI 百万行代码实验:环境决定产出
如果你觉得上面的道理太抽象,这里有一个真实的、大规模的、可以查证的实验。
OpenAI 在 2025 年做了一件激进的事:用 Codex 从一个空的 git 仓库起步,构建一个完整的内部产品。关键约束是——人类永远不直接写代码。这不是噱头,而是为了逼团队搞清楚一个问题:当工程师的主要工作不再是写代码,而是设计环境、表达意图、构建反馈回路时,到底什么变了?
五个月后的成绩单:
| 指标 | 数据 |
|---|---|
| 代码总量 | ~100 万行(应用 + 基础设施 + 工具 + 文档) |
| 团队规模 | 3 名工程师 |
| 合并 PR 数 | ~1,500 个 |
| 人均日产 PR | 3.5 个 |
早期进展比预期慢——不是 Codex 不行,而是环境不够完整。Agent 缺少必要的工具、抽象和内部结构来推进高层次目标。工程师的工作变成了:把大目标拆成小积木(设计、编码、审查、测试),让 agent 去搭建,然后用这些积木解锁更复杂的任务。
当某件事失败了,修复几乎从来不是"更努力",而是"agent 缺什么能力,怎么让它既可理解又可执行"。
💡 核心发现: 同一个模型,在空白环境里和在有完整 harness 的环境里,产出有本质差异。模型没变,变的是环境。 来源:OpenAI: Harness engineering — Leveraging Codex in an agent-first world
一个更接地气的例子
如果百万行代码的实验离你太远,来看一个中等规模的真实案例。
一个团队用 Claude Sonnet 给一个 Python Web 应用(FastAPI + PostgreSQL + Redis,约 15,000 行代码)添加新的 API 端点。
第一次尝试(无 harness):
- 只给了一句话:"在
/api/v2/users下添加用户偏好设置端点" - Agent 花了 40% 的上下文窗口探索仓库结构
- 产出了看似合理的代码,但没遵循项目的错误处理模式
- 用了旧版 SQLAlchemy 语法
- 宣称完成,但端点实际有运行时错误
- 下一个会话还得重新做发现工作
第二次尝试(加了 harness):
- 加了
AGENTS.md(描述项目架构和技术栈版本) - 加了显式的验证命令(
pytest tests/api/v2/ && python -m mypy src/) - 加了架构决策记录
- 同一模型在三次独立运行中全部成功
- 上下文使用效率提高了约 60%
模型没变。变的还是 harness。
这就是第一章的核心结论——在你掏钱包换更贵的模型之前,先花时间搭一个像样的 harness。 投入产出比高得惊人。
2. Harness 到底是什么:五子系统模型
"harness"这个词在 AI coding agent 的圈子里被用得越来越多了,但说实话,大部分人说的 harness 其实就只是"一个 prompt 文件"。这不是 harness。 就像你开了一家餐厅,只有食材——没有灶台、没有刀具、没有菜谱、没有出菜流程——那不叫餐厅,那叫冰箱。
这一章要给你一个精确的、可操作的 harness 定义。不是学术论文里的抽象概念,而是你今天就能拿去用的框架。
2.1 从"一个 prompt 文件"到完整厨房
想象你是一个刚入职的工程师,被丢进一个没有任何文档的项目里。没有 README,代码里没有注释,没有人告诉你怎么跑测试,CI 配置文件藏在某个角落里。你能写出好代码吗?也许能——如果你足够聪明又足够有耐心。但你会花大量时间在"搞清楚这个项目是怎么回事"上,而不是在"解决问题"上。
AI agent 面对的困境一模一样,而且更糟——你至少可以问同事,agent 只能看到你放在它面前的文件和它能执行的命令。它不能拍拍同事的肩膀问"哎,这个项目的 ORM 用的是哪个版本?"
那什么才算是一个完整的"厨房"?
OpenAI 在他们的 harness engineering 文章里把工程师的核心工作概括为三件事:设计环境、表达意图、构建反馈循环。Anthropic 则更侧重状态持久化、显式恢复路径和结构化的进度跟踪。两家公司的侧重点不同,但都在说同一件事:
💡 Harness 的精确定义: 模型权重之外的一切工程基础设施。指令文件、工具配置、运行环境、状态管理、验证反馈——不是模型权重的部分,全是 harness。
OpenAI 把仓库当作"记录系统"——所有必要的上下文都必须在仓库里。他们的经验是,AGENTS.md 应该是目录页,不是百科全书。100 行左右就够了,放不下就拆分到 docs/ 目录里,让 agent 按需去读。
Anthropic 的发现更深一层:好的 harness 用可执行的规则来约束 agent,而不是在指令里逐条叮嘱。"执行不变量,不要微管实现"——给 agent 框架约束,让它在框架内自由发挥。
从 prompt 文件到完整厨房的进化路径:
Level 0:什么都没有 → "请帮我加个搜索功能"
Level 1:一个 prompt 文件 → AGENTS.md 描述了项目概况
Level 2:加上验证命令 → 写了测试和 lint 的执行方式
Level 3:加上环境配置 → 锁定了依赖版本和运行时
Level 4:加上状态管理 → 有进度文件和交接文档
Level 5:完整厨房 → 五个子系统全部就位每往上走一级,agent 的可靠性就跳一个台阶。而大部分人还停在 Level 0 和 Level 1 之间——然后抱怨"模型不行"。
2.2 五子系统详解:指令、工具、环境、状态、反馈
回到厨房的类比。一个完整的厨房有五个功能区,harness 也有五个子系统:
① 指令子系统(菜谱架)
这是 agent 的"菜谱"——告诉它这个项目是什么、怎么做、什么不能做。创建 AGENTS.md(或 CLAUDE.md),内容包括:
- 项目概览和目的(一句话说清楚这是什么)
- 技术栈和版本(Python 3.11、FastAPI 0.100+、PostgreSQL 15)
- 首次运行命令(
make setup、make test) - 不可违反的硬约束("所有 API 必须走 OAuth 2.0")
- 指向更详细文档的链接
关键原则:给地图,不给说明书。 AGENTS.md 应该是目录页,不是百科全书。100 行左右就够了。
② 工具子系统(刀具架)
确保 agent 有足够的工具访问权限。不要因为"安全考虑"把 shell 给禁了——agent 连 pip install 都跑不了,还怎么干活?但也别什么都开放,按最小权限原则来。
核心是保证 agent 能做到:读写文件、执行命令、安装依赖、运行测试。这四件事如果有一件被卡住,相当于木匠缺了锤子——手艺再好也使不上劲。
③ 环境子系统(灶台)
让环境状态自描述。agent 不应该猜你用的是 Node 16 还是 18,不应该在 pip install 失败后花 10 分钟排查依赖冲突。
环境自描述的最小配置:
Python 项目:
pyproject.toml → 锁定依赖和版本
.python-version → 指定 Python 版本
Makefile → 统一入口(setup / test / lint)
Node 项目:
package.json → 锁定依赖
.nvmrc → 指定 Node 版本
tsconfig.json → TypeScript 配置
通用加分项:
docker-compose.yml → 数据库等外部服务
devcontainer.json → 完全可重现的开发环境④ 状态子系统(备菜台)
长任务必须有进度跟踪。用一个简单的 PROGRESS.md 文件记录:哪些做完了、哪些在做、哪些被阻塞。每个会话结束前更新,下一个会话开始时读取。
没有状态子系统的 agent 就像一个每天早上失忆的厨师——昨天切了一半的菜在哪儿?汤炖到了第几步?全忘了,每天从头来。
⑤ 反馈子系统(出菜检查口)
这是投入产出比最高的子系统。在 AGENTS.md 里显式列出验证命令:
验证命令:
- 测试:pytest tests/ -x
- 类型检查:mypy src/ --strict
- Lint:ruff check src/
- 完整验证:make check(包含以上全部)没有反馈子系统,agent 就像一个只会做菜但不会试吃的厨师——菜做没做熟,咸了还是淡了,全靠运气。
五个子系统缺一个,就像厨房里少了一个功能区——菜还是能做,但总是别扭。
| 子系统 | 厨房类比 | 核心问题 | 缺失后果 |
|---|---|---|---|
| 指令 | 菜谱架 | 做什么菜、怎么做 | Agent 自己猜,猜错概率高 |
| 工具 | 刀具架 | 有什么工具可用 | 手艺施展不开 |
| 环境 | 灶台 | 在什么条件下做 | 花大量时间排查环境问题 |
| 状态 | 备菜台 | 做到哪一步了 | 每次都从头来 |
| 反馈 | 出菜检查口 | 做得对不对 | 自信地交出半生不熟的菜 |
2.3 常见工具的 Harness 解剖:Claude Code / Cursor / Codex
理论讲完了,来看看你每天用的工具,它们的 harness 长什么样。
Claude Code 的设计天然体现了 harness 思想:
- 📋 指令:读仓库里的
CLAUDE.md - 🔧 工具:能用 shell 跑命令,访问文件系统
- ⚙️ 环境:在你的本地环境里执行
- 📝 状态:有会话历史,支持
/compact压缩上下文 - ✅ 反馈:能跑测试看结果
但如果你不告诉它怎么跑测试,反馈子系统就是断的——菜做没做熟谁也不知道。
Cursor 也是类似的逻辑:
- 📋 指令:
.cursorrules文件 - 🔧 工具:内置终端
- ⚙️ 环境:能读你的项目结构和 lint 配置
- 📝 状态:相对弱——你关掉 IDE 再打开,上次的上下文就没了
- ✅ 反馈:能看 lint 结果,但需要你配置
Cursor 的短板在状态子系统——跨会话的连续性几乎为零。
Codex(OpenAI)走了一条不同的路:
- 📋 指令:
AGENTS.md+ 仓库结构 - 🔧 工具:完整的沙盒环境
- ⚙️ 环境:用 git worktree 隔离每个任务的运行环境
- 📝 状态:通过 PR 和 commit 保持状态
- ✅ 反馈:配合本地可观测性栈(日志、指标、追踪)
Codex 在有 AGENTS.md 和清晰验证命令的仓库里,表现远超在"裸"仓库里。
AutoGPT 则是反面教材——缺乏结构化的状态管理导致长任务中上下文不断累积,缺乏精确的反馈机制导致 agent 陷入循环。很多人说 AutoGPT "不行",但其实是它的 harness 不行——你给它一个破灶台,再好的食材也做不出菜来。
| 工具 | 指令 | 工具 | 环境 | 状态 | 反馈 |
|---|---|---|---|---|---|
| Claude Code | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| Cursor | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐ |
| Codex | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| AutoGPT | ⭐ | ⭐⭐ | ⭐ | ⭐ | ⭐ |
💡 实操建议: 用这张表审计你当前使用的工具。找出最弱的那个子系统——那就是你应该优先加强的方向。很多时候不是工具不行,是你没给它配齐"厨具"。
2.4 如何量化各子系统的边际贡献
"直觉告诉你反馈子系统最重要"——但直觉不靠谱。你需要一个量化方法。
Anthropic 提出了一个优雅的诊断方法叫**"逐个组件拆除法"**(也可以叫"等模型对照实验"):保持模型不变,逐个移除五个子系统中的某一个,看每次移除后性能下降多少。下降最多的那个,就是你当前的瓶颈。
等模型对照实验步骤:
1. 选一个模型和一个有挑战性的任务
2. 在完整 harness 下跑一次,记录基线
3. 移除指令(删掉 AGENTS.md),跑一次
4. 恢复指令,移除反馈(不给验证命令),跑一次
5. 恢复反馈,移除状态(不提供进度文件),跑一次
6. 每次只移除一个,对比性能下降幅度
7. 排出各子系统对你项目的重要性排名来看一个真实案例。一个团队用 GPT-4o 开发 TypeScript + React 前端应用(约 20,000 行代码),经历了四个阶段——其实就是在一件一件地添置厨具:
| 阶段 | 操作 | 成功率 | 主要失败原因 |
|---|---|---|---|
| 1. 空厨房 | 只有 README | 20% | 选错包管理器、命名不规范、跑不了测试 |
| 2. 装上菜谱架 | 加 AGENTS.md | 60% | 环境问题 + 验证缺失 |
| 3. 开起检查口 | 加验证命令 | 80% | 偶发的环境不一致 |
| 4. 备菜台就位 | 加进度文件 | 80-100% | 基本消除 |
四次迭代,模型一个字没改,成功率从 20% 到接近 100%。你没有换更贵的食材,你只是把厨房收拾利索了。
Anthropic 还发现了一个有趣的现象:随着模型变强,某些 harness 组件会变得不再关键(比如更强的模型可能不需要那么详细的指令)。但总会有新的关键组件出现——模型越强,你越需要更好的验证和状态管理来匹配它的能力。
💡 核心原则: Harness 和代码一样会腐化。定期审计,像还技术债一样还 harness 债。当模型升级后,记得重新跑一次拆除实验,看看瓶颈是否转移了。
3. 仓库即规范:让代码仓库成为唯一的事实来源
你团队的架构决策散落在 Confluence、Slack、Jira、和几个资深工程师的脑子里。对人类来说这勉强够用——你可以问同事、搜聊天记录、翻文档。实在不行还能去茶水间堵人。
但对 AI agent 来说,不在仓库里的信息等于不存在。
这不是夸张。想想 agent 的输入都有什么:系统提示和任务描述、仓库里的文件内容、以及工具执行的输出。就这三样。 你的 Slack 历史、Jira 工单、Confluence 页面、和周五下午跟同事在茶水间聊的架构决定——agent 全都看不到。它不能"去问一下",也不能"搜一下聊天记录"。它就是一个被关在仓库里的工程师——仓库外面的事,它一概不知。
所以问题变成了:你给不给这个工程师一张好地图?
3.1 知识可见性缺口:你有多少隐性知识
先来做一个思想实验。列出你的项目中所有对开发工作重要的决策和约束:
- 用哪个版本的 Python / Node?
- ORM 语法统一用新版还是旧版?
- API 端点的认证方式是什么?
- 错误处理模式是怎样的?
- 数据库迁移的流程是什么?
- 哪些目录结构的命名约定是强制的?
- 前端组件的命名和分层规则?
- 哪些操作是绝对禁止的?
现在标注一下:这些知识中,有多少写进了仓库?有多少只在人的脑子里、Slack 消息里、或者一个三个月没更新的 Confluence 页面里?
两者的差距就是你的"知识可见性缺口"。
知识可见性缺口 = 项目总知识 - 仓库内的知识
─────────────────────────
项目总知识
缺口越大 → agent 猜的次数越多 → 猜错概率越高 → 失败率越高一个团队维护约 30 个微服务的电商平台。架构决策散落在:Confluence(部分过时)、Slack(难以搜索)、几个资深工程师的脑子里(不可扩展)、以及零星的代码注释(不系统)。引入 AI agent 后,70% 的任务需要人工干预。几乎每次失败都涉及 agent 违反了某个"所有人都知道但从未写入仓库"的隐性约束。
这就像一个新来的员工,没人告诉他"中午点外卖要在群里接龙"——他自己猜,猜错了被骂,但骂完了还是没人告诉他规则。
💡 关键洞察: 比没有文档更危险的是过时的文档。知识衰减率——仓库中单位时间内变得过时的知识条目比例——是你的隐形敌人。过时的文档会让 agent 走错方向还以为自己是对的。
3.2 冷启动测试:五个问题检验仓库质量
怎么检验你的地图画得够不够好?做一个**"冷启动测试"**。
操作很简单:开一个全新的 agent 会话,不提供任何口头上下文,只让它看仓库内容,然后问它五个问题:
冷启动测试五问:
Q1:这是什么系统?(项目目标和技术栈)
Q2:怎么组织的?(目录结构和模块划分)
Q3:怎么运行?(安装依赖、启动服务、环境配置)
Q4:怎么验证?(测试命令、lint 命令、类型检查)
Q5:现在进度如何?(当前在做什么、做完了什么、卡在哪)能回答几个,你的地图就画了几分。 5 分满分,3 分及格。
每个答不上来的问题,意味着 agent 在每次新会话中都得花上下文窗口去猜或者探索。这就引出了另一个关键概念——发现成本:agent 为了找到一条关键信息需要消耗多少上下文。
信息放得越隐蔽,发现成本越高,留给实际任务的预算越少。把关键信息藏在十层目录深处的 README 里,就像把灭火器锁在地下室的保险柜里——不是没有,是用的时候找不到。
| 信息位置 | 发现成本 | 效果 |
|---|---|---|
AGENTS.md 第一屏 | 几乎为零 | Agent 立即获得 |
| 仓库根目录的文档 | 低 | Agent 简单探索即可 |
| 模块目录下的文档 | 中等 | Agent 需要导航到对应目录 |
| 代码注释里 | 较高 | Agent 需要读具体文件 |
| Confluence / Slack | ∞ | Agent 永远找不到 |
💡 实操建议: 今天就对你的主力项目做一次冷启动测试。记录 agent 答不上来的问题,然后花 30 分钟把这些答案写进仓库——这 30 分钟会在之后的每一次 agent 会话中替你节省时间。
3.3 地图怎么画:知识靠近代码的四个原则
知道了缺口在哪,怎么补?四个原则:
原则 1:知识靠近代码。 一条关于 API 端点认证的规则,应该放在 API 代码旁边,而不是藏在一个巨大的全局文档里。每个模块目录下放一个简短的文档,说清楚这个模块的职责、接口和特殊约束。
就像图书馆的书架标签——你想找历史类书籍,直接去标着"历史"的书架,不用把整个图书馆翻一遍。
原则 2:用标准化的入口文件。 AGENTS.md(或 CLAUDE.md)是 agent 的"着陆页"。它不需要包含所有信息,但必须能让 agent 快速回答"这是什么项目"、"怎么跑"、"怎么验证"这三个问题。50-100 行就够了。
原则 3:最小但完备。 每条知识都应该有明确的使用场景。如果你删掉某条规则不影响 agent 的决策质量,那这条规则就不应该存在。但冷启动测试中的每个问题都必须有答案。不多不少,刚好够用。
原则 4:和代码一起更新。 把知识更新跟代码变更绑定在一起。最简单的方法:把架构文档放在对应的模块目录里。改代码的时候自然会看到文档,改代码之后 CI 提醒你检查文档是否需要更新。
具体的仓库结构长这样:
project/
├── AGENTS.md # 入口:项目概览、运行命令、硬约束
├── src/
│ ├── api/
│ │ ├── ARCHITECTURE.md # API 层的架构决策
│ │ └── ...
│ ├── db/
│ │ ├── CONSTRAINTS.md # 数据库操作的硬约束
│ │ └── ...
│ └── ...
├── PROGRESS.md # 当前进度:做了什么、在做什么、被什么阻塞
└── Makefile # 标准化的操作命令:setup / test / lint / check注意这个结构的精妙之处:AGENTS.md 在根目录(入口)、架构文档在对应模块目录(靠近代码)、PROGRESS.md 在根目录(全局可见)。每条信息都放在 agent 最有可能找到它的地方。
3.4 用 ACID 原则管理 Agent 状态
这个类比来自数据库的事务管理——你可能觉得这是在把简单的事情搞复杂,但实际上它给了你一个非常实用的检查框架。
原子性(Atomicity)—— 要么全做,要么不做。
每次"逻辑操作"(比如"添加新端点并更新测试")用一个 git commit 原子化。中途挂了就 git stash 回滚。没有"做了一半"的中间状态。
就像银行转账——你不能只扣款不入账。
一致性(Consistency)—— 定义什么叫"合格"。
定义"一致状态"的验证谓词——所有测试通过、lint 无报错、类型检查通过。Agent 每次操作后跑验证,不一致的中间状态不要 commit。
一致状态检查:
pytest tests/ -x → 全部通过 ✓
mypy src/ --strict → 无错误 ✓
ruff check src/ → 无警告 ✓
三个都通过 → 可以 commit
任一失败 → 修复后再 commit隔离性(Isolation)—— 各干各的,互不干扰。
多个 agent 并发工作时,状态文件要避免竞争条件。简单方案:每个 agent 用独立的进度文件,或者用 git 分支隔离。两个厨师不能同时往同一口锅里放盐——放重了谁负责?
持久性(Durability)—— 脑子里的不算,写在纸上的才算。
关键的项目知识用 git 跟踪的文件持久化。临时状态可以只在会话内存里,但跨会话必须的知识必须写到文件里。
| ACID 维度 | Agent 状态管理 | 检查方式 |
|---|---|---|
| 原子性 | 一个 commit = 一个完整操作 | git log 看提交是否自洽 |
| 一致性 | 每次 commit 前跑验证 | make check 全部通过 |
| 隔离性 | 分支或独立进度文件 | 无合并冲突 |
| 持久性 | 关键知识写入 git 跟踪的文件 | PROGRESS.md 是否最新 |
💡 核心原则: 用 ACID 框架审计你的项目状态管理——不需要做到完美,但至少要知道你在哪个维度是弱的。大部分项目最弱的维度是持久性:太多知识只存在于上一次会话的上下文里,下一次会话就丢了。
4. 指令文件架构:把指令拆分到不同文件里
你开始认真对待 harness 了——好事。你建了个 AGENTS.md,把你能想到的所有规则、约束、历史教训都塞了进去。一个月后这个文件膨胀到了 300 行,两个月 450 行,三个月 600 行。
然后你发现 agent 的表现反而变差了。
这就是"巨型指令文件"陷阱。就像你往行李箱里塞东西——觉得什么都有用,什么都往里装,结果拉链快崩了,想找换洗内衣得把整个箱子翻一遍。
4.1 巨型指令文件陷阱:膨胀的恶性循环
最常见的恶性循环是这样的:
膨胀的恶性循环:
Agent 犯了个错
│
├─→ "加条规则防止这个" ──→ 加到 AGENTS.md
│ │
├─→ 暂时管用 │
│ │
├─→ Agent 又犯了另一个错 │
│ │
└─→ 再加一条 ──────────────────┘
重复 N 次后 → 文件膨胀到不可控这不是你的错。每次出问题就"加条规则"感觉很合理,就像每次出门忘带东西就往包里多塞一样。但累积效应是灾难性的:
上下文预算被吃掉了。 Agent 的上下文窗口是有限的。一个 600 行的指令文件可能占掉 10,000-20,000 tokens。到真正需要理解代码的时候,预算已经不够了——就像行李箱塞满了"万一用得着"的东西,结果放不下真正需要带的电脑。
优先级冲突。 文件里混合了不可违反的硬约束("不得使用 eval()")、重要的设计指导("优先使用函数式风格")、和某个特定场景的历史教训("上周修了一个 WebSocket 内存泄漏")。这三条规则的重要性完全不同,但在文件里看起来一模一样。Agent 没有可靠的信号来区分。
维护衰减。 大文件天生难维护。指令过时了没人删——因为删除的后果不确定("也许别的地方依赖这条规则?"),但加新指令是无成本的。结果文件只增不减,信噪比持续下降。
矛盾累积。 不同时期加的指令之间开始出现矛盾——一条说"用 TypeScript 严格模式",另一条说"某些遗留文件允许用 any"。Agent 每次随机选一条遵循。就像你妈说"穿厚点",你爸说"别穿太多",你站在门口不知道听谁的。
4.2 中间迷失效应与指令信噪比
上面说的这些问题,背后有一个学术研究做了很好的量化。
Liu 等人在 2023 年的论文 "Lost in the Middle" 中证明了一个关键发现:LLM 对长文本中间部分的信息利用效率显著低于两端。 也就是说,放在文件开头和结尾的指令更容易被遵循,埋在中间的指令更容易被忽略。
你的 AGENTS.md 有 600 行,第 300 行写的是"所有数据库查询必须用参数化查询"——这是安全硬约束。但它被埋在中间,agent 几乎一定会忽略它。就像你行李箱里那瓶防晒霜——明明在最底层,但你翻三遍也找不到。
这就引出了另一个实用概念——指令信噪比(SNR):
指令信噪比 = 与当前任务相关的指令数
────────────────────
文件中总指令数
场景分析:
做 bug 修复 → 文件里有 50 行部署指令 → SNR 很低
加 API 端点 → 文件里有 30 行前端规范 → SNR 很低
写测试 → 文件里有 40 行 CI 配置说明 → SNR 很低SNR 越低,agent 的上下文预算浪费越多,任务相关的注意力越分散。
一个 SaaS 团队的真实数据:600 行的 AGENTS.md 重构前后对比——
| 指标 | 重构前(600 行单文件) | 重构后(80 行路由 + 专题文档) |
|---|---|---|
| 任务成功率 | 45% | 72% |
| 安全约束遵循率 | 60% | 95% |
| 上下文浪费 | ~15% 被无关指令占用 | ~3%(按需加载) |
安全约束遵循率从 60% 飙升到 95%——因为从 600 行文件的中间移到了路由文件的顶部,不再被"中间迷失"了。
4.3 路由文件架构设计:入口 50 行 + 专题文档
知道了问题和原理,解决方案其实很直观:常用信息放手边,偶尔用的收起来,用不上的别带。
核心思路是把 AGENTS.md 当作路由器,不是百科全书。它的任务是快速引导 agent 找到需要的信息,而不是一次性灌输所有信息。
入口文件 AGENTS.md 控制在 50-200 行,只放四样东西:
# AGENTS.md
## 项目概览
Python 3.11 FastAPI 后端,PostgreSQL 15 数据库。
## 快速开始
- 安装:`make setup`
- 测试:`make test`
- 完整验证:`make check`
## 硬约束(不可违反)
- 所有 API 必须走 OAuth 2.0 认证
- 所有数据库查询必须用 SQLAlchemy 2.0 语法
- 所有 PR 必须通过 pytest + mypy --strict + ruff check
- 不得使用 eval() 或 exec()
- 环境变量通过 pydantic-settings 管理,禁止硬编码
## 专题文档(按需阅读)
- [API 设计规范](docs/api-patterns.md) — 添加新端点时必读
- [数据库操作约束](docs/database-rules.md) — 涉及数据库修改时必读
- [测试标准](docs/testing-standards.md) — 编写测试时参考
- [部署流程](docs/deployment.md) — 部署相关任务时参考每个专题文档 50-150 行,按主题放在 docs/ 目录下或对应模块目录旁。Agent 只在需要时才去读。就像行李箱里的收纳袋——内衣一个袋,洗漱一个袋,充电器一个袋。找东西不用翻整个箱子。
渐进式披露的信息层级:
第 1 层:AGENTS.md(每次都读)
→ 项目概览 + 运行命令 + 硬约束 + 专题链接
→ 50-200 行
第 2 层:专题文档(按需读取)
→ docs/api-patterns.md、docs/database-rules.md ...
→ 每个 50-150 行
第 3 层:代码内文档(读代码时顺带看)
→ 类型定义、接口注释、配置文件里的说明
→ 和代码绑定,不需要单独管理💡 核心原则: 好的 harness 设计和好的 UI 设计一样——不把所有选项一次性砸到用户脸上。先给概要信息,需要的时候再给详细信息。这就是"渐进式披露"(Progressive Disclosure)。
4.4 指令的生命周期管理:来源、适用条件、过期条件
拆分解决了架构问题,但还有一个隐患:指令会过时。 这个问题在单文件时代就存在,拆分后依然需要面对。
解决方案:每条指令都应该标明三个元数据——
指令生命周期三标签:
来源: "为什么加这条规则?"
→ 链接到触发它的 issue / PR / 事故
适用条件: "这条规则在什么时候需要?"
→ "涉及数据库操作时" / "添加新 API 端点时"
过期条件: "什么情况下可以删掉这条规则?"
→ "升级到 SQLAlchemy 3.0 后" / "该遗留服务下线后"举个例子,一条好的指令长这样:
<!-- 来源:PR #234 修复了 SQL 注入漏洞 -->
<!-- 适用:所有涉及数据库查询的代码 -->
<!-- 过期:永不过期——安全硬约束 -->
- 所有数据库查询必须用参数化查询,禁止字符串拼接而一条需要清理的指令长这样:
<!-- 来源:上周 WebSocket 连接泄漏事故 -->
<!-- 适用:修改 WebSocket 相关代码时 -->
<!-- 过期:根因修复后(已在 PR #456 修复) -->
- 注意 WebSocket 连接的 cleanup 逻辑 ← 应该删除或转化为测试用例定期审计策略: 像管理代码依赖一样管理你的指令——用不上的依赖就该删掉,不然它们只会拖慢系统。
建议每月做一次"指令大扫除":
- 检查每条指令的过期条件是否已满足
- 把历史教训类的指令转化为测试用例(这才是它们应该存在的形式)
- 删除矛盾的、冗余的、模糊的条目
- 确认入口文件仍然在 200 行以内
💡 "加条规则"是短期的止痛药,长期的毒药。 每次想加规则前先问自己:这条规则放专题文档是不是更合适?能不能转化为一个自动化测试?如果答案是"能",那就别加到指令文件里。
5. 跨会话连续性:让失忆工匠写好日记本
你让 Claude Code 帮你实现一个完整的功能,它跑了 30 分钟,做了大部分工作,但上下文快满了。你开个新会话继续,然后发现:它不记得上次做了什么决策、为什么选了方案 A 而不是方案 B、哪些文件已经改过、测试跑到什么状态了。它得花 15 分钟重新探索一遍项目,而且可能跟上次的做法不一致。
想象一下,如果你是一个工匠,每天早上醒来都不记得昨天干了什么。你得重新认识整个工地——哪面墙砌了一半、为什么用的是红砖而不是青砖、水电管道走到哪了。更糟的是,你可能会把昨天已经装好的窗户拆了重来,因为你不记得它已经装过了。
这就是 AI coding agent 在跨会话任务中面对的困境。解决方案不是更大的窗口,而是一个靠谱的日记本。
5.1 上下文窗口不是无限的:为什么 Agent 会断片
上下文窗口是有限的。这不是一个可以通过模型升级解决的问题——即使窗口大小增长到 1M tokens,复杂任务依然会用完。为什么?因为 agent 不只是在生成代码,它还要:
- 理解代码库结构
- 跟踪自己的决策历史
- 处理工具执行的输出
- 维护对话上下文
这些信息加起来增长得比窗口扩容快得多。
更深层的问题:agent 产生的信息不是均匀重要的。 中间推理步骤包含决策的"为什么"——为什么选了方案 A 而不是方案 B,为什么用了这个库而不是那个库。最终输出只包含"是什么"——代码本身。压缩策略通常保留后者但丢了前者。
下一个会话看到代码但不知道为什么这么写,可能会"优化"掉一个有意为之的设计决策。
连续性断裂后的四重灾难:
① 决策漂移 → 上次选了方案 B,这次不知道为什么,选了方案 A
② 重复劳动 → 不确定某项工作是否已完成,重新做了一遍
③ 方向偏移 → 几个会话累积下来,实现偏离了原始需求
④ 验证缺口 → 上次的测试结果全丢了,得重新跑一遍一个实际案例:一个 agent 被要求实现带用户认证的博客系统,12 个功能点,需要 5 个会话。没有日记本的情况下——到会话 5,仓库有大量冗余代码,但核心认证功能仍未通过端到端测试。12 个功能点只完成了 7 个,其中 3 个有隐含的正确性问题。
5.2 上下文焦虑:Agent 的非理性加速收尾
Anthropic 在他们的长运行 agent 研究中发现了一个非常有意思的现象:当 agent 感觉上下文快满了,它们会表现出"过早收敛"的行为。
具体表现:
- 匆忙结束当前工作,跳过验证步骤
- 选一个简单的方案而不是最优方案
- 草草写完代码后立即声称"完成了"
- 放弃正在探索的、更好的设计方向
这就像你考试时发现时间快到了,赶紧随便选几个选择题一样。Anthropic 把这叫**"上下文焦虑"(Context Anxiety)**——一种非理性的资源焦虑。
这个现象的危害在于:它是系统性的。不是偶尔犯一次,而是每次接近上下文限制时都会触发。如果你的任务恰好在上下文窗口的 80% 处需要做一个关键决策,agent 几乎一定会选快的而不是对的。
上下文焦虑的典型时间线:
0-50% 窗口使用 → 正常工作,充分探索,仔细验证
50-70% 窗口使用 → 开始简化方案,减少探索
70-85% 窗口使用 → 跳过验证步骤,加速收尾
85%+ 窗口使用 → 草率完成,宣布"做完了"💡 关键洞察: 如果你发现 agent 在长会话末尾的输出质量明显低于开头,这很可能不是模型能力的问题,而是上下文焦虑在作祟。解决方案不是"让它别着急"(你叮嘱了也没用),而是在焦虑出现之前就做好交接准备。
5.3 连续性工件三件套:进度文件 + 决策日志 + Git 检查点
核心思路:把 agent 当成一个会失忆的超级工程师来管理。 每次它要"下班"之前,必须把关键信息写下来,让下一个"接班"的 agent 能快速上手。
工具 1:进度文件(PROGRESS.md)—— 日记本的核心。
# 项目进度
## 当前状态
- 最新 commit: abc1234 (feat: add user preferences endpoint)
- 测试状态: 42/43 通过 (test_pagination_edge_case 失败)
- Lint: 通过
## 已完成
- [x] 用户模型和数据库迁移
- [x] 基础 CRUD 端点
- [x] 认证中间件集成
## 进行中
- [ ] 分页功能 (90% - 边界条件测试失败)
## 已知问题
- test_pagination_edge_case 在空结果集时返回 500
- 需要确认是否要在列表中包含已删除用户
## 下一步
1. 修复分页边界条件 bug
2. 添加"是否包含已删除用户"的查询参数
3. 更新 API 文档工具 2:决策日志(DECISIONS.md)—— 日记本里的备忘。
记录重要的设计决策和原因。不需要详细的设计文档,只需要"什么决策、为什么、什么时候做的":
## 2024-01-15: 使用 Redis 缓存用户偏好
- 原因: 读取频率高(每次 API 调用都需要),数据量小
- 否决方案: PostgreSQL 物化视图(变更频率高,维护成本不划算)
- 约束: 缓存 TTL 设为 5 分钟,写入时主动失效工具 3:git 提交作为检查点。
每完成一个原子工作单元就提交。commit message 要说清楚做了什么和为什么。这是免费的、自动版本化的状态快照。
把这三件套串起来:在 AGENTS.md 里写明上班和下班的流程。
每次会话开始时(上班打卡):
1. 读 PROGRESS.md 了解当前状态
2. 读 DECISIONS.md 了解重要决策
3. 跑 make check 确认仓库处于一致状态
4. 从 PROGRESS.md 的"下一步"部分继续工作
每次会话结束前(下班打卡):
1. 更新 PROGRESS.md
2. 如果做了重要决策,更新 DECISIONS.md
3. 跑 make check 确认一致状态
4. 提交所有已完成的工作使用日记本后的同一个博客系统案例:重建时间从 15 分钟降到 3 分钟,功能完成率从 58% 升到 100%,隐含缺陷率从 43% 降到 8%。工匠还是那个会失忆的工匠,但有了日记本,他的每一天都从昨天停下的地方开始。
5.4 压缩 vs 重置:选对策略取决于你的模型
当上下文快满了,你有两个选择。选哪个,取决于你用的模型。
策略 A:压缩(Compaction)
在同一个会话里把早期对话摘要化。比如 Claude Code 的 /compact 命令。
- ✅ 优点:保留连续性,agent 能看到"是什么"
- ❌ 缺点:"为什么"经常在摘要中丢失
- ❌ 更关键:压缩并不能消除上下文焦虑——agent 知道上下文曾经很大,心理上仍然倾向于加速收尾
策略 B:重置(Context Reset)
完全清空上下文,开一个新会话,从持久化工件重建。
- ✅ 优点:干净的心理状态——新会话没有"我快没时间了"的焦虑
- ❌ 缺点:依赖交接工件的完备性——如果日记本里漏了关键信息,新会话可能走错方向
Anthropic 的实测数据给出了一个清晰的指导:
| 模型 | 上下文焦虑程度 | 推荐策略 |
|---|---|---|
| Sonnet 4.5 | 严重 | 必须用重置 + 完备的交接工件 |
| Opus 4.5 | 大幅减弱 | 可以靠压缩管理,不依赖重置 |
这意味着:harness 设计需要对目标模型有具体的理解,而不是套用通用模板。
实操建议——混合策略:
判断标准:
任务预计 < 30 分钟 → 单会话内完成,不需要交接
任务预计 30-60 分钟 → 关注上下文使用率,60% 时准备交接
任务预计 > 60 分钟 → 提前规划会话边界,每个会话有明确的交付目标
上下文使用 > 60% → 停下来更新 PROGRESS.md,准备交接
上下文使用 > 80% → 立即交接,不要等到 90% 才动💡 核心原则: 上下文窗口是有限的资源,长任务一定会跨会话——这是客观现实,不是可以通过"更大的窗口"解决的问题。给失忆的工匠一个靠谱的日记本,让每一天都从昨天停下的地方开始,而不是从零开始。
6. 初始化阶段:先打地基再砌墙
你开了一个新的 agent 会话,让它"帮我加个搜索功能"。它上来就开始改代码——精神可嘉。改了 20 分钟发现测试框架没配好,又花 10 分钟搞测试框架,然后发现数据库迁移脚本格式不对,又折腾了一会儿。最后搜索功能倒是加了,但整个会话的效率很低——大部分时间花在了"搞清楚这个项目怎么运作"上。
更好的做法:先打地基,再砌墙。 在让 agent 开始干活之前,先用一个独立的阶段把基础环境搭好、验证命令跑通、项目结构搞清楚。你不能边打地基边砌墙,不然墙砌到一半地基还没干,整栋楼都得推倒重来。
6.1 地基和墙的区别:两种不同的优化目标
初始化和实现的优化目标完全不同:
| 维度 | 初始化阶段(打地基) | 实现阶段(砌墙) |
|---|---|---|
| 目标 | 最大化后续所有实现的可靠性和效率 | 最大化已验证功能的数量和质量 |
| 产出 | 基础设施:环境、测试框架、文档 | 业务代码:功能、接口、逻辑 |
| 价值体现 | 后续 3-4 个会话中成倍收回 | 当前会话立即可见 |
| 风险特征 | 做不好会导致系统性问题 | 做不好只影响单个功能 |
当你把两者混在一起的时候,agent 面临一个多目标优化问题——它要同时搭基础设施和写功能代码。在没有显式优先级设定的情况下,agent 自然倾向于写代码(因为那是直接可见的产出),而牺牲基础设施(因为它的价值只能在后续会话中体现)。
就像让施工队同时打地基和砌墙——他们大概率会急着砌墙,因为墙看得见、能交差。但地基没打好的房子,后面出的问题是系统性的。
6.2 混在一起做的三重代价
代价一:地基打不牢。 Agent 花了 80% 的精力写功能代码,剩下 20% 随便搭了点基础设施。测试框架配了但没验证过,lint 规则设了但太宽松,进度文件没创建。这些缺陷在第一个会话里不明显(因为 agent 还记得它做了什么),但到第二个会话就暴露了——新 agent 不知道项目怎么跑、怎么测、做到哪了。
代价二:未验证的累积。 在测试框架配好之前写的功能代码,等回头补测试的时候可能发现设计上就有问题,早知道的话应该用不同的方式实现。就像在地基没干的时候就开始贴瓷砖,等发现地面不平时,瓷砖全得撬掉重来。
代价三:上下文预算浪费。 初始化工作消耗了大量上下文预算,留给实际功能实现的反而不够了。结果第一个会话只完成了一半的功能,第二个会话还得从头理解项目。两头都没占着。
Anthropic 的实验数据:使用独立初始化阶段的项目,多会话场景中的功能完成率比混合方式高 31%。关键是——初始化阶段投入的时间在后续 3-4 个会话中就能完全收回。
6.3 自举契约:能启动、能测试、能看进度、能接手
初始化的完成条件不是"写了多少代码",而是自举契约的四个条件都满足了:
条件 1:能启动。 项目能启动、依赖都装好、没有环境问题。make setup 从零开始能成功。
条件 2:能测试。 至少有一个示例测试能通过。这证明测试框架本身是配好的——就像地基上立了一根柱子,证明地基能承重。
条件 3:能看进度。 有进度文件和任务分解,新会话能快速知道做了什么、在做什么、下一步做什么。
条件 4:能接手。 一个全新的 agent 会话,只看仓库内容(不给任何口头上下文),能无歧义地接着干。
初始化阶段的产出应该是一个完整的自举契约文档:
# 初始化契约
## 启动命令
- 安装依赖:`make setup`
- 启动开发服务器:`make dev`
- 运行测试:`make test`
- 完整验证:`make check`
## 当前状态
- 所有依赖已安装并锁定
- 测试框架已配置(pytest + mypy)
- 示例测试通过(1/1)
- Lint 规则已配置(ruff)
## 项目结构
- src/ — 源代码
- src/api/ — API 端点
- src/db/ — 数据库模型
- tests/ — 测试文件加上任务分解——把整个项目拆成有序的任务列表,每个任务有明确的验收标准:
## Task 1: 用户认证基础
- 实现 JWT 认证中间件
- 添加登录/注册端点
- 验收:pytest tests/test_auth.py 全部通过
## Task 2: 用户资料页面
- 实现用户资料 CRUD
- 验收:pytest tests/test_profile.py 全部通过用这个检查清单验收初始化:
初始化验收清单:
[ ] make setup 从零开始能成功
[ ] make test 至少有一个测试通过
[ ] 新 agent 会话能只看仓库回答"怎么跑"和"怎么测"
[ ] 任务分解文件存在且有至少 3 个任务
[ ] 所有内容已提交到 git6.4 热启动 vs 冷启动:用模板预置基础设施
冷启动是从空目录开始,agent 要猜项目结构、选技术栈、配环境——就像在荒郊野岭从头搞起。
热启动是从项目模板开始,基础设施已经就位——就像在有通水通电的工地上开工。
差距有多大?看一个 React 前端项目的对比:
| 维度 | 混合方式(边打地基边砌墙) | 独立初始化(先打地基) |
|---|---|---|
| 第一个会话 | 同时做脚手架 + 首个功能 | 只做初始化:模板 + 测试 + 契约 |
| 第二个会话重建时间 | ~20 分钟(推断结构和流程) | ❤️ 分钟(看契约直接干) |
| 总重建时间(全周期) | 基线 | 减少约 60% |
| 功能完成率 | 基线 | 提升 31% |
不要从空目录开始。用一个项目模板(create-react-app、fastapi-template 等)预置好标准的目录结构、依赖配置和测试框架。把通用的初始化步骤预置到模板里,只留下项目特有的初始化工作。
热启动策略:
通用模板预置的内容:
✓ 目录结构(src / tests / docs)
✓ 依赖管理(pyproject.toml / package.json)
✓ 测试框架配置
✓ Lint / 格式化配置
✓ Makefile(setup / test / lint / check)
✓ AGENTS.md 模板
每个项目特有的初始化:
→ 项目概览和技术栈版本
→ 业务相关的硬约束
→ 任务分解
→ 初始 git 检查点💡 核心原则: 初始化投入的时间不是额外的成本,是前期投资。地基打得越扎实,上面的楼盖得越快。慢即是快。
7. 任务边界控制:WIP=1 工作流
你让 Claude Code "给这个项目加上用户认证功能",结果它同时开始改数据库 schema、写路由、改前端组件、还顺手重构了错误处理中间件。两个小时后你一看——12 个文件被修改,800 行新代码,但没有一个功能是端到端跑通的。
贪多嚼不烂。 Agent 天生就有"多做一点"的冲动——看到相关的事情就顺手一起做了,和那种在超市本来只打算买瓶酱油、结果推着满满一车出来的人一个德行。问题是,agent 同时做太多事情的结果是每一件都做不好。
7.1 过度延伸与不足完成:一对难兄难弟
这两个问题不是独立的,而是互相加剧。
恶性循环:
Overreach(同时启动太多任务)
│
├─→ 注意力分散,每个任务获得的推理资源不足
│
├─→ Under-finish(大量半成品,无法端到端验证)
│
├─→ 半成品代码增加系统复杂度
│
└─→ 复杂度导致下一个任务更容易 Overreach用数学说:假设 agent 的上下文容量为 C,同时激活 k 个任务,每个任务平均获得 C/k 的推理资源。当 C/k 低于完成单个任务所需的最小阈值时,所有任务都做不完。就像你的胃就那么大——同时塞十个包子进去,十个都消化不良。
来看一个真实数据对比:
| 策略 | 代码行数 | 涉及文件 | 端到端验证通过率 | 最终完成率 |
|---|---|---|---|---|
| 自助餐模式(无约束) | ~1200 行 | 12 文件 | 20% | 37.5% (3/8) |
| 单盘模式(WIP=1) | ~800 行 | 4 文件/任务 | 100% | 87.5% (7/8) |
总代码量更少,但有效代码更多。 Anthropic 的数据也证实:agent 生成的代码行数和实际完成的功能数量呈弱负相关——写得越多,完成得越少。
7.2 强制 WIP=1:做完一个再做下一个
WIP=1 来自 Kanban 方法论。核心思想极其简单:限制同时在进行的任务数量为 1。
在你的 AGENTS.md 里加上这段:
## 工作规则
- 每次只做一个功能点
- 当前功能点端到端验证通过后,才能开始下一个
- 不要在实现功能 A 时"顺便"重构功能 B
- 如果发现了需要修复的问题,记到 PROGRESS.md 里,但不要现在动手就像吃自助餐——一次只拿一盘,吃完再去拿。
Anthropic 在工程博客中明确指出:使用"小下一步"策略(等价于 WIP=1)的 agent,任务完成率比使用宽泛提示的 agent 高 37%。OpenAI 在 Codex 工程实践中也发现,没有显式范围控制的任务,完成率会暴跌。
💡 核心原则: "少做但做完"永远优于"多做但做半"。一口吃不成胖子,一次做一个,反而完成得最多。
7.3 完成证据:行为验证通过才算完成
WIP=1 解决了"同时做太多"的问题,但还有一个问题:怎么判断"做完了"?
如果不给明确的定义,agent 会用"代码看起来没问题"来代替"行为通过测试"。这就像学生说"我写完卷子了"——写满了不代表做对了。
完成证据(Completion Evidence) 必须是可执行的、可验证的:
## 功能清单
F01: 用户注册
验证: curl -X POST /api/register \
-d '{"email":"test@example.com","password":"123456"}' \
| jq '.status' == 201
状态: passing ✅
F02: 用户登录
验证: pytest tests/test_auth.py::test_login_success
状态: pending ⏳
F03: 密码重置
验证: pytest tests/test_auth.py::test_password_reset_flow
状态: not_started ⬜注意区分:
| 判断方式 | 可靠性 | 举例 |
|---|---|---|
| "我写完了" | ❌ 不可靠 | Agent 自己说的 |
| "代码能编译" | ⚠️ 有限 | 编译通过不代表逻辑正确 |
| "单元测试通过" | ✅ 较可靠 | 验证了单个函数的行为 |
| "端到端测试通过" | ✅✅ 可靠 | 验证了完整的用户流程 |
一个任务从"进行中"变成"已完成",必须满足可执行的验证条件。 没有这个约束,agent 会不断地"做了但没做完",积累越来越多的技术债务。
7.4 范围表面外部化:用文件记录任务状态
最后一个关键:任务状态不能只在对话里说,必须在仓库里有机器可读的记录。
这就是"范围表面外部化"。用一个文件记录所有任务的 DAG 结构——每个节点是一个工作单元,边是依赖关系,状态只有四种:
范围表面(SCOPE.md 或 features.json):
任务状态枚举:
⬜ not_started → 还没开始
⏳ in_progress → 正在做(WIP=1 下最多只有 1 个)
🚧 blocked → 被外部依赖阻塞
✅ passing → 验证通过,已完成任何新会话都能直接读这个文件,知道:哪个任务在做?什么行为算完成?已经通过了什么验证?
同时引入一个关键指标——VCR(Verified Completion Rate):
VCR = 已通过验证的任务数 / 已启动的任务数
VCR = 1.0 → 完美:做了多少,完成了多少
VCR < 1.0 → 有未完成的任务,阻止新任务启动
VCR = 0.2 → 危险:overreach 严重,停下来先收拾残局Harness 应该持续跟踪 VCR。当 VCR < 1.0 时,阻止新任务启动——先把未完成的做完,再开始新的。这就是 WIP 限制和完成证据共同产生的"完成压力"。
💡 核心原则: Agent 不缺做事的能力,缺的是做完事的纪律。WIP=1 + 完成证据 + 范围表面外部化——三件套一起用,把"贪多嚼不烂"变成"一口一口吃,反而吃得最多"。
8. 功能清单:Harness 的脊梁骨
你让 agent 做一个电商网站,跑完之后它告诉你"做完了"。你打开代码一看——用户认证有了,但购物车的结算按钮点了没反应,支付流程根本没接上。
问题是:你从来没告诉它"做完"的标准是什么。 所以它用自己的标准——"代码写了不少,看起来挺完整"。就像你让朋友帮你买菜,说"买点水果",他拎了一袋柠檬回来——他要的水果和你要的水果,不是同一个水果。
功能清单在很多人眼里就是个备忘录——写下来怕忘了,写完扔在一边。但在 harness 的世界里,功能清单不是给人看的备忘录,而是整个 harness 的脊梁骨。脊梁骨断了,全身都瘫。
8.1 从备忘录到原语:功能清单的四个消费者
文档是给人看的,原语是给系统用的。文档可以被忽略,原语不能被绕过。
功能清单作为 harness 原语,服务四个关键组件:
功能清单的四个消费者:
① 调度器 → 读状态,选下一个 not_started 的功能
就像工厂的排产系统——看完订单才知道下一步做什么
② 验证器 → 执行验证命令,判断是否允许状态转移
就像质检——不是你说合格就合格,得通过检验
③ 交接报告 → 从功能清单自动生成会话交接摘要
就像换班时自动生成的交接表——不用手写
④ 进度追踪 → 统计各状态分布,提供项目健康度指标
就像仪表盘——一眼看出项目走到哪了没有功能清单,这四个组件全都失效。调度器不知道该让 agent 做什么,验证器不知道该检查什么,交接报告无法生成,进度追踪无从谈起。
对比一下:
| 维度 | 备忘录模式 | 原语模式 |
|---|---|---|
| 进度记录 | "购物车基本完成了" | F03: passing ✅ |
| 新会话恢复时间 | ~20 分钟(推断状态) | ~3 分钟(读清单) |
| 重复实现风险 | 高 | 零 |
| 功能完成率 | 基线 | +45% |
8.2 三元组结构:行为描述 + 验证命令 + 当前状态
每个功能项是一个三元组。缺了任何一项,这个功能项就不完整——就像三条腿的凳子少一条腿。
{
"id": "F03",
"behavior": "POST /cart/items with {product_id, quantity} returns 201",
"verification": "pytest tests/test_cart.py::test_add_item -x",
"state": "passing",
"evidence": "commit abc123, 2024-01-16"
}- 行为描述告诉 agent 做什么——"用户可以添加商品到购物车"
- 验证命令告诉它怎么算做完——
pytest tests/test_cart.py - 当前状态告诉它现在到哪了——
not_started/active/blocked/passing
用 Markdown 格式同样可以:
## 功能清单
| ID | 行为描述 | 验证命令 | 状态 |
|----|---------|---------|------|
| F01 | 用户可以注册账户 | `pytest tests/test_auth.py::test_register` | ✅ passing |
| F02 | 用户可以登录 | `pytest tests/test_auth.py::test_login` | ✅ passing |
| F03 | 用户可以添加商品到购物车 | `pytest tests/test_cart.py::test_add_item` | ⏳ active |
| F04 | 用户可以结算购物车 | `pytest tests/test_cart.py::test_checkout` | ⬜ not_started |
| F05 | 支付完成后生成订单 | `pytest tests/test_order.py::test_create` | ⬜ not_started |在 AGENTS.md 里写清楚规则:
## 功能清单规则
- 功能清单文件: /docs/features.md
- 每次只激活一个功能项(WIP=1)
- 功能项验证命令必须通过才能标为 passing
- 不要修改功能清单的状态——由验证脚本自动更新8.3 状态机模型与通过状态门控
每个功能项有四种状态,状态转移由 harness 控制,不是 agent 想改就能改:
功能项状态机:
⬜ not_started ──→ ⏳ active ──→ ✅ passing
│
└──→ 🚧 blocked ──→ ⏳ active
状态转移规则:
not_started → active : 调度器选中该任务
active → passing : 验证命令执行成功(唯一路径)
active → blocked : 发现外部依赖未满足
blocked → active : 阻塞条件解除
passing → ??? : 不可逆!passing 了就是 passing 了关键在于通过状态门控:功能从 active 变成 passing 的唯一方式是验证命令执行成功。Agent 不能直接把状态改成 passing——它只能提交代码、跑验证,然后由 harness 根据结果决定是否允许状态转移。
这就是"干活的人"和"判分的人"分离——不是你说考过了就考过了,得看成绩单。
还没通过的功能项数量就是 harness 对 agent 施加的反向压力。压力归零 = 项目完成。这给了整个系统一个清晰的收敛目标。
8.4 粒度校准:一次会话能完成的范围
功能清单的最后一个关键:每个功能项应该是"一次会话能完成"的范围。
太粗了做不完,太细了管理开销大。就像切牛排——不能整块啃,也不能切成肉沫。
| 粒度 | 示例 | 评价 |
|---|---|---|
| 太粗 | "实现购物车" | ❌ 一个会话做不完,必然 under-finish |
| 合适 | "用户可以添加商品到购物车" | ✅ 单一行为,可验证,一个会话够 |
| 太细 | "创建 Cart 模型的 name 字段" | ❌ 管理开销大于实际价值 |
校准方法:问自己两个问题——
- 这个功能项能在一次 agent 会话(30-60 分钟)内从
not_started做到passing吗? 如果不能,拆细。 - 这个功能项有独立的、可验证的用户行为吗? 如果没有,它可能太细了——合并到上一级。
一个好的功能清单,通常是 8-15 个功能项。少于 5 个说明粒度太粗,多于 20 个说明粒度太细或者项目本身太大,应该分阶段。
💡 核心原则: 功能清单是 harness 的脊梁骨——调度器、验证器、交接器、进度追踪器全都依赖它。三元组不可缺(行为+验证+状态),状态转移由 harness 门控,粒度控制在一次会话能完成的范围。脊梁骨挺直了,整个 harness 才能站得住。
9. 防止提前宣告完成:外部化终止判定
你让 agent 实现"密码重置"功能。它改了数据库 schema、写了 API 端点、加了邮件模板,跑了单元测试(全部通过),然后自信地告诉你"做完了"。你实际一跑——密码重置链接发不出去、数据库迁移半途失败、端到端流程根本没走过一遍。
卷子写满了,不代表做对了。
9.1 置信度校准偏差:为什么 Agent 总说"做完了"
这不是偶然事件。Guo 等人在 2017 年 ICML 的经典论文证明:现代神经网络系统性地过度自信——模型自报的置信度显著高于实际准确率。AI 编码 agent 也一样:它"觉得"做完了,但实际上差得远。
滑坡效应几乎总是同一个套路:
过早完成声明的滑坡路径:
代码看着还行(语法正确、逻辑合理)
│
├─→ 静态检查没有明显错误
│
├─→ 跑了单元测试,全绿 ✅
│
├─→ 跳过了集成测试和端到端验证
│
└─→ "代码看起来没问题" = "功能已完成" ← 错!从任务规范到代码实现到运行时行为,每次转换都可能引入偏差,而每次跳过的验证都加剧了信息不对称。
更深层的问题:Anthropic 2026 年的研究发现,当 agent 被要求评估自己的工作时,它系统性地过度正面评价——即使人类观察者认为质量明显不达标。就像让学生自己给自己判卷——对自己的答案总是特别宽容。
9.2 单元测试通过 ≠ 任务完成:隐藏的验证缺口
这是最常见的陷阱,也是最危险的一个。单元测试的设计哲学——隔离被测单元、模拟依赖——恰好使其无法检测跨组件问题:
接口不匹配。 渲染进程传给预加载脚本的文件路径是相对路径,但预加载脚本期望绝对路径。各自的单元测试都用了 mock,都通过了。只有端到端跑通时才发现问题。就像乐队里每个乐手各自练习都完美,但合在一起才发现定调不一样。
状态传播错误。 数据库迁移改了表结构,但 ORM 的缓存层还持有旧结构的缓存条目。单元测试每次都是全新的 mock 环境,不会暴露这种跨层状态不一致。
环境依赖性。 代码在测试环境(一切 mock)行为正确,在真实环境因配置差异、网络延迟、服务不可用而失败。就像在排练室唱得很好,上台演出时音响设备出了问题。
另一个毒药是**"顺便重构"**。Agent 在核心功能还没验证通过时就开始重构代码、优化性能。重构会改变已验证和未验证代码之间的边界,可能破坏之前隐式正确的代码路径。——数学大题还没做完,就跑去把前面选择题的答案重新抄一遍格式。
9.3 三层终止校验:语法 → 行为 → 系统
完成判定不应该由 agent 自己做。Harness 独立执行终止校验,输入是运行时信号,不是 agent 的置信度。
三层终止校验架构:
第一层:语法与静态分析(最低限度)
├── lint 通过(ruff / eslint)
├── 类型检查通过(mypy / tsc)
└── 编译通过(无语法错误)
→ 成本最低,信息量最小,但必须通过
第二层:运行时行为验证(核心证据)
├── 单元测试通过
├── 应用能成功启动并达到就绪状态
└── 关键路径在运行时执行成功
→ 不仅要写了,还要能跑
第三层:系统级确认(最后防线)
├── 端到端测试通过
├── 集成验证(跨组件交互)
└── 用户场景模拟
→ 不仅要能跑,还要跑对
规则:第 1 层没通过,不许进入第 2 层
第 2 层没通过,不许进入第 3 层
核心功能没验证通过之前,不许做重构在 AGENTS.md 里写清楚:
## 完成定义
- 功能完成 = 端到端验证通过,不是"代码写完了"
- 必须运行的验证层级:
1. `make lint` — 语法与静态分析通过
2. `make test` — 单元测试和行为验证通过
3. `make e2e` — 端到端流程验证通过
- 层层递进,前一层没通过不许进入下一层
- 核心功能验证通过之前,不许重构9.4 分离生成者与评估者:独立阅卷制度
解决方案不是让 agent "更客观"——同一个模型既生成又评估,内在地倾向对自己慷慨。解决方案是把"干活的人"和"检查的人"分开。
就像考试不能让学生自己批改自己的卷子——得有个独立的阅卷老师。
Anthropic 的三 agent 架构实验证明了这一点:
| 配置 | 架构 | 结果 |
|---|---|---|
| 裸跑 | 单 agent,无 harness | 核心功能跑不起来 |
| 单 agent + harness | 有指令和验证 | 功能完整但质量不稳定 |
| 三 agent 架构 | Planner + Generator + Evaluator | 全功能通过,质量稳定 |
三 agent 架构的核心:
- Planner:扩展需求,拆解任务
- Generator:逐功能实现代码
- Evaluator:用 Playwright 实际点击测试,独立评估质量
这是同一个模型(Opus 4.5),同一段提示词。区别只在 harness。
即使你不用三 agent 架构,也至少要做到一件事:错误消息要像好老师的红笔批注。
OpenAI 在 Codex 实践中提出了一个特别有效的模式:
❌ 坏的错误消息(只画红叉):
"Test failed"
✅ 好的错误消息(红笔批注三要素):
"Test failed: POST /api/reset-password returned 500.
① 什么错了: 邮件服务配置缺失
② 在哪里: 检查 environment variables 中的 SMTP_HOST
③ 怎么修: 模板文件应该在 templates/reset-email.html"这种具体的、可操作的反馈让 agent 能自我修正,而不需要人类介入。
💡 核心原则: Agent 系统性地过度自信——这是客观现实,不是可以通过"叮嘱它仔细点"来解决的。完成判定必须外部化,生成者和评估者必须分离。不能让学生自己批自己的卷子。
10. 端到端验证:跑通完整流程才算真正验证
你让 agent 给 Electron 应用加一个文件导出功能。它写了渲染进程组件、预加载脚本、服务层逻辑,每个组件的单元测试都通过了。Agent 说"做完了"。你实际一点击导出按钮——文件路径格式不对、进度条没反应、大文件导出时内存泄漏。5 个组件边界缺陷,单元测试一个都没发现。
这就像一个合唱团排练——每个声部单独唱的时候都完美,但合在一起的时候,女高音比男低音快了半拍,伴奏的调子和主旋律差了半个音。每个部分都"对"了,但整体跑调了。
10.1 单元测试的四大盲区
单元测试的设计哲学是隔离——模拟依赖,专注被测单元。这个哲学使单元测试快速且精确,但也制造了系统性的盲区:
盲区一:接口不匹配。 渲染进程传给预加载脚本的文件路径是相对路径,但预加载脚本期望绝对路径。各自的单元测试都用了 mock,都通过了。只有端到端跑通时才发现问题。就像两个声部各自练的时候都觉得节奏没问题,一合才发现一个用 4/4 拍一个用 3/4 拍。
盲区二:状态传播错误。 数据库迁移改了表结构,但 ORM 的缓存层还持有旧结构的缓存条目。单元测试每次都是全新的 mock 环境,不会暴露这种跨层状态不一致。
盲区三:资源生命周期问题。 文件句柄、数据库连接、网络套接字的获取和释放跨越多个组件。单元测试为每个测试创建和销毁独立资源,不会暴露资源竞争或泄漏。就像排练时每个声部轮流用麦克风,但演出时所有声部同时上台——话筒不够用了。
盲区四:环境依赖性。 代码在测试环境(一切 mock)行为正确,在真实环境因配置差异、网络延迟、服务不可用而失败。
Google 测试金字塔的启示:
单元测试能检测的缺陷 ⊂ 集成测试能检测的缺陷 ⊂ 端到端测试能检测的缺陷
每往上一层,检测能力增强,但执行成本也增加。
对 agent 来说,15 秒的端到端测试 vs 2 秒的单元测试?
→ 完全可以接受。省下来的人工排查时间远大于此。10.2 端到端测试改变 Agent 的编码行为
这是很多人没意识到的一点:当 agent 知道它的工作要过端到端测试时,它的编码行为会改变。
| 无 E2E 测试时的行为 | 有 E2E 测试时的行为 |
|---|---|
| 只关注单个函数正确性 | 考虑"这个接口和上游怎么对接" |
| 忽略架构边界 | 被迫遵守边界规则 |
| 跳过异常处理 | 被端到端故障场景逼着考虑错误路径 |
| 用 mock 糊弄通过 | 必须在真实环境跑通 |
就像知道最终要合在一起唱,练习的时候就会注意听其他声部。端到端测试不仅检测缺陷,更重要的是引导 agent 写出更好的代码。
10.3 架构规则自动化:从文档到可执行检查
架构约束写在文档里等人来看?没用。 Agent 不会主动去翻文档,而且即使翻了也可能忽略。架构规则必须从"写在纸上"变成"跑在 CI 里"。
OpenAI 在 Codex 工程实践中采用了"分层领域架构"——每个业务领域被分成固定的层:Types → Config → Repo → Service → Runtime → UI,依赖方向严格向前,任何违规通过自定义 lint 机械执行。
关键原则:执行不变量,不微管实现。 比如要求"数据在边界解析",但不规定用哪个库。
# 把架构规则变成可执行检查的例子:
# 规则:渲染进程不能直接访问文件系统
grep -r "require('fs')" src/renderer/ && exit 1 \
|| echo "OK: no direct fs access in renderer"
# 规则:数据库查询只能在 repo 层
grep -r "prisma\." src/service/ && exit 1 \
|| echo "OK: no direct DB access in service layer"每次在代码审查中发现新类型的 agent 错误,就把它变成自动化检查。这就是审查反馈提升——每个被捕获的缺陷类别都变成永久防线。一个月后你的 harness 会比月初强得多。
10.4 面向 Agent 的错误消息设计:三要素模式
传统的错误消息是给人看的——"Test failed"。人类工程师看到这句话会去看日志、看堆栈、猜原因。Agent 不会。Agent 需要的错误消息是:告诉它具体怎么修。
OpenAI 在 Codex 实践中提出了三要素模式:
❌ 坏的错误消息(只画红叉):
"Direct filesystem access in renderer"
✅ 好的错误消息(三要素):
"ERROR: Found direct import of 'fs' in src/renderer/App.tsx:12
① WHAT: 渲染进程不能直接访问 Node.js API
② WHY: 安全沙箱限制,渲染进程无 Node.js 权限
③ FIX: 将文件操作移至 src/preload/file-ops.ts,
通过 window.api.readFile() 调用"三要素缺一不可:
| 要素 | 作用 | 缺失后果 |
|---|---|---|
| WHAT(什么错了) | 定位问题 | Agent 不知道从哪里开始修 |
| WHY(为什么错了) | 理解约束 | Agent 可能绕过约束而不是遵守它 |
| FIX(怎么修) | 直接可操作 | Agent 原地转圈,反复犯同样的错 |
这把测试失败变成了自我修正的反馈闭环。就像合唱排练时指挥不只说"你唱错了",而是说"这里你快了半拍,听一下女低音的节奏,在第 32 小节进入"。
💡 核心原则: 每个声部单独唱得再好,也比不上一次完整的合唱排练。端到端测试是验证系统级正确性的唯一手段,架构规则必须可执行,错误消息要面向 agent 设计。只有整体跑通了,才算真正完成。
11. 可观测性:给 Agent 装上仪表盘
你让 agent 做一个功能,它跑了 20 分钟,改了一堆文件,然后告诉你"做完了但有两个测试失败"。你问它为什么失败,它说"不太确定,可能是时序问题"。你问它改了哪些关键路径,它说"让我看看代码……"。
这不是 agent 能力不够,是你的 harness 没有给它装仪表盘。想象你在开一辆没有仪表盘的车——没有速度表、没有油量表、没有发动机故障灯。你能开,但你不知道开多快、还剩多少油、发动机是不是快爆了。技术再好的司机,蒙着眼也得出事。
11.1 可观测性缺失的四类代价
当 harness 缺乏可观测性时,四类问题系统性出现:
代价一:无法区分"正确"和"看似正确"。 一个函数在代码审查时看起来完全正确,但运行时因为边界条件处理错误,在特定输入下产生了不正确结果。只有运行时追踪能揭示实际执行路径偏离了预期。
代价二:评估变成玄学。 没有评分标准和验收条件时,评估者依赖隐式假设。同一个输出,不同评估者可能给出截然不同的评价。就像体操比赛没有评分标准——这个裁判觉得动作优雅,那个裁判觉得落地不稳,谁说了算?
代价三:重试变成盲猜。 Agent 不知道为什么失败时,重试方向是随机的。每次盲重试都消耗 token 和时间——修复了不相关的代码路径而忽略真正的故障根源。
代价四:会话交接信息断崖。 缺乏可观测性意味着新会话必须从零诊断系统状态。Anthropic 的观察表明,这种重复诊断可能占会话总时间的 30-50%。
11.2 双层可观测性:运行时信号 + 过程工件
可观测性不是"多打点日志"那么简单。它分两层,缺一不可:
双层可观测性架构:
第一层:运行时可观测性(仪表盘)
├── 应用生命周期:启动 → 就绪 → 运行 → 关闭
├── 功能路径执行:入口 → 检查点 → 出口
├── 数据流:组件间的数据流转记录
├── 资源利用:异常的资源使用模式
└── 错误和异常:完整的错误上下文
→ 回答"系统做了什么"
第二层:过程可观测性(导航系统)
├── 冲刺合同:范围、验证标准、排除项
├── 评估评分标准:按维度的结构化评分
├── 任务轨迹:完整的决策路径记录
└── 交接工件:机器可读的状态快照
→ 回答"为什么这样做"运行时信号解释"发生了什么",过程工件解释"为什么这样做"。 速度表和导航系统各有各的用处——光有速度表不知道去哪,光有导航不知道开多快。
Agent 自己解决不了这个问题,因为:它不知道自己不知道什么(不会主动记录自己没意识到需要的信号),日志格式不统一(不同会话各写各的),过程可观测性不是日志能解决的(冲刺合同和评分标准是结构化工件)。
11.3 冲刺合同与评估评分标准
冲刺合同(Sprint Contract) 是过程可观测性的核心工具——在编码开始前,生成者和评估者协商的短期协议。就像施工队开工前签的施工协议。
# 冲刺合同: 暗色模式支持
## 范围
- 修改主题切换组件
- 更新全局 CSS 变量
- 添加暗色模式测试
## 验证标准
- 每个组件的视觉回归测试通过
- 主流程端到端测试通过
- 无样式闪烁 (FOUC)
## 排除项
- 不处理打印样式
- 不处理第三方组件暗色模式评估评分标准 把"好不好"变成可量化的评分——就像给体操比赛定评分标准:
| 维度 | A(优秀) | B(良好) | C(及格) | D(不及格) |
|---|---|---|---|---|
| 代码正确性 | 所有测试通过 | 主流程通过 | 部分通过 | 编译失败 |
| 架构合规 | 完全合规 | 轻微偏离 | 明显偏离 | 严重违反 |
| 测试覆盖 | 主流程+边缘 | 仅主流程 | 仅有骨架 | 无测试 |
有了冲刺合同和评分标准,效率差 3 倍:
| 场景 | 迭代次数 | 总耗时 |
|---|---|---|
| 没有仪表盘(模糊需求 + 主观评估) | 3-4 次 | ~45 分钟 |
| 有完整仪表盘(冲刺合同 + 评分标准) | 1 次 | ~15 分钟 |
11.4 Anthropic 三 Agent 架构实验:Planner-Generator-Evaluator
Anthropic 在 2026 年发布了一项系统性的 harness 实验,用三种架构跑同一个任务("用 Web Audio API 做一个浏览器端 DAW")。三个 agent 各司其职:
三 Agent 架构的可观测性设计:
Planner(规划者)
├── 接收 1-4 句话的用户需求
├── 扩展成完整产品规格
└── 重点:高层技术设计,不指定实现细节
→ 约束交付物,让 agent 在执行中找路径
Generator(生成者)
├── 按 sprint 逐功能实现
├── 每个 sprint 前和 evaluator 协商冲刺合同
└── 按合同施工,不按感觉施工
Evaluator(评估者)
├── 用 Playwright MCP 像用户一样点击测试
├── 按四个维度评分(产品深度/功能性/视觉/代码质量)
└── 不达标则打回,附具体反馈和证据关键发现:Evaluator 不是天生就会评估的。 早期版本会识别出合理的问题,然后说服自己这些问题不严重,最终批准工作。调校方式是:读 evaluator 的日志,找到它的判断和人类判断分叉的地方,更新 QA 的 prompt 解决那些问题。经过几轮开发循环,评分才变得合理——就像训练新验收工程师,一开始太宽容,出了几次事故后学会了严格。
另一个重要发现:随着模型进化,harness 组件需要动态调整。 当 Opus 4.6 发布后,sprint 拆分机制变成了不必要的开销——模型已经能自主处理工作分解。但 evaluator 在任务接近模型能力边界时仍然提供实际价值。
💡 核心原则: 可观测性是 harness 的架构属性——不是事后添加的功能,而是设计时必须考虑的核心能力。仪表盘不是可选配件,是出厂标配。没有仪表盘的车能开,但迟早出事。
12. 会话交接与清洁状态:每次下班前倒好垃圾
你的 agent 跑了一下午,改了 20 个文件,提交了代码,会话结束。下一个 agent 会话开始,一上来就发现:构建失败了、测试红了、临时调试文件到处都是、功能清单没更新、进度完全不清楚。新会话的前 30 分钟全花在"搞清楚上一个会话到底干了什么"上。
这就像大学宿舍——你不倒垃圾,下一个室友进来就得替你收拾。
12.1 熵增定律:不清理就是指数级累积技术债
Lehman 的软件演化定律告诉我们:持续变更的系统,除非主动管理,否则复杂性必然增加。 这对 AI 编码 agent 尤其成立——agent 每次会话都会引入变更,如果不在退出时清理,技术债务会指数级累积。
看看实际数据——12 周无清洁策略的退化曲线:
| 时间 | 构建通过率 | 测试通过率 | 新会话启动时间 |
|---|---|---|---|
| 第 1 周 | 100% | 100% | 5 分钟 |
| 第 4 周 | 95% | 92% | 15 分钟 |
| 第 8 周 | 82% | 78% | 35 分钟 |
| 第 12 周 | 68% | 61% | 60+ 分钟 |
同样的项目,有清洁策略:第 12 周仍然是 97%、95%、9 分钟。每天多花 5 分钟打扫,12 周后省下几十小时的混乱时间。
OpenAI 在 5 个月的 Codex 实验中也观察到:agent 会复制仓库中已有的模式——即使那些模式是次优的。第一个人在桌上放了一个杯子,第二个人觉得"反正已经乱了"又放了一个,一周后桌上堆满了。
12.2 清洁状态五维检查清单
清洁状态不是单一的"代码能编译"。五个维度缺一不可:
## 会话退出检查清单
- [ ] ① 构建通过:`npm run build` 或 `make build` 无错误
- [ ] ② 测试通过:`npm test` 全部绿色,包括会话前已存在的测试
- [ ] ③ 进度已记录:功能清单状态已更新,PROGRESS.md 已写入
- [ ] ④ 无过时工件:无 console.log / debugger / TODO / 临时文件
- [ ] ⑤ 启动路径可用:`npm run dev` 能正常启动,下一个会话无需人工干预这就像搬出宿舍时的检查表——水电不能断的、地上不能有垃圾的、钥匙要交回的、个人物品要带走的。
12.3 双模式清理策略:即时清理 + 定期大扫除
结合两种清理模式——就像吃完饭马上洗碗(即时清理)+ 每周拖一次地(定期清理):
即时清理(每个会话结束时):
- 清理本次会话创建的临时工件
- 更新功能清单状态
- 确保构建和测试通过
- 提交 Git 检查点
定期清理(每周一次):
- 全系统扫描——处理累积的结构性问题
- 更新质量文档(对每个模块持续评分的活跃工件)
- 运行基准测试检测漂移
质量文档的示例——就像宿舍的卫生检查评分表:
# 质量文档
## 用户认证模块 (质量: A)
- 验证通过: 是 | agent 可理解: 是
- 测试稳定性: 稳定 | 架构边界: 合规
## 支付模块 (质量: C) ← 优先处理
- 验证通过: 部分(支付回调未测试)
- agent 可理解: 困难(逻辑分散在 3 个文件)
- 测试稳定性: 不稳定(2 个 flaky 测试)新会话读这个文档就知道优先处理哪里。质量评分最低的模块先修。
12.4 定期简化 Harness:移除不再必要的约束
Harness 里的每个组件之所以存在,是因为模型无法独立做好某件事。但随着模型改进,这些假设会过时。 就像你大一的时候需要学长带着选课,到了大三你自己就知道怎么选了。
推荐做法:每月挑一个 harness 组件,暂时禁用它,跑基准任务。
- 如果结果没退化 → 永久移除(减少管理开销)
- 如果退化了 → 恢复或用更轻量的替代
Anthropic 的实验数据证明了这一点:当 Opus 4.6 发布后,sprint 拆分机制变成了不必要的开销——模型的原生能力已经可以自主处理工作分解。移除后,agent 能连续工作超过两小时而不跑偏,反而更流畅。
一个更深层的原则:随着模型改进,harness 的有趣组合不是变少了,而是移动了。 以前必须解决的问题被模型能力覆盖了,但新的能力边界打开了以前不可能的 harness 设计。AI 工程师的工作是持续找到下一个有价值的组合——就像定期检查宿舍公约,大四了还在执行"每天 10 点熄灯"就不太合理了。
💡 核心原则: 熵增是默认状态——不主动清理只会越来越乱。清洁状态是完成的必要条件,不是可选的善后工作。五维检查 + 双模式清理 + 定期简化——你不倒垃圾,下一个室友就得替你倒。
13. 实战项目指南
仅仅阅读理论是不够的——你需要亲自搭建环境,观察 Agent 在不同规则下的真实表现。本课程配套 6 个渐进式实战项目,从零开始构建一个完整的 Agent 工作环境。
13.1 项目总览与学习路径
| 项目 | 名称 | 核心目标 | 对应章节 |
|---|---|---|---|
| P01 | 提示词驱动 vs 规则驱动 | 对比裸 prompt 和带 harness 的 agent 表现差异 | 第 1-2 章 |
| P02 | 让项目可读并接住上次的工作 | 重构仓库结构,使其对 AI 友好 + 建立交接机制 | 第 3-4 章 |
| P03 | 跨会话工作连续性 | 设计状态文件和初始化脚本,实现多次中断后无缝恢复 | 第 5-6 章 |
| P04 | 运行反馈与行为修正 | 引入运行时反馈,让 agent 自检代码是否能跑通 | 第 7-8 章 |
| P05 | 工作评审与自我验证 | 建立独立评审环节,防止幻觉和过早宣告胜利 | 第 9-10 章 |
| P06 | 搭建完整的 Agent 工作环境 | 综合演练——带可观测性的最终 harness 环境 | 第 11-12 章 |
13.2 如何进行
每个项目文件夹包含:
starter/:你的起始工作区solution/:参考的最终实现(卡住时可参考)- 指导文档:说明任务背景和具体目标
推荐工作流:
- 先读对应章节的理论
- 用你习惯的 AI Coding Agent(Claude Code、Cursor、Codex 等)在
starter/目录下完成任务 - 对比你的实现和
solution/的差异 - 记录观察——agent 在有/无特定 harness 组件时的行为差异
13.3 各项目要点
Project 01:提示词驱动 vs 规则驱动。 这是入门实验。用同一个任务,分别用纯 prompt 和带 AGENTS.md 的 harness 跑一遍,量化比较完成率、代码质量和返工次数。你会亲眼看到 harness 的价值。
Project 02:让项目可读。 重构一个"对人类友好但对 AI 不友好"的仓库,让 agent 能快速理解项目结构。建立 PROGRESS.md 和交接机制。
Project 03:跨会话连续性。 在一个需要 3 次以上会话才能完成的任务中,设计状态文件和初始化脚本,实测"断片"恢复时间。
Project 04:运行反馈。 引入功能清单三元组和运行时验证,观察 agent 在有反馈循环时如何自我修正行为。
Project 05:工作评审。 建立独立的评估环节(可以是脚本、第二个 agent、或人工检查),防止"卷子写满但做错"的问题。
Project 06:综合环境。 把前 5 个项目的所有组件整合——指令分层 + 功能清单 + 三层验证 + 可观测性 + 清洁状态检查。这是毕业设计。
💡 建议按顺序进行。 每个项目在前一个的基础上递进,跳过前面的项目会导致后面缺少必要的上下文。
14. 资料库与工具包
理论要落地,就需要开箱即用的模板。以下资源可以直接复制到你的仓库里——先把这几样放进项目里,再开始让 agent 持续工作,通常就已经能明显降低返工和瞎猜。
14.1 最小可用模板包:四个文件起步
你的仓库/
├── AGENTS.md (或 CLAUDE.md) ← 根指令文件
├── docs/
│ ├── features.md ← 功能清单(三元组)
│ └── progress.md ← 进度日志
└── scripts/
└── init.sh ← 初始化脚本| 文件 | 解决什么问题 | 对应章节 |
|---|---|---|
AGENTS.md | Agent 不知道该遵守什么规则 | 第 2-4 章 |
features.md | Agent 不知道"做完"是什么意思 | 第 7-8 章 |
progress.md | 新会话不知道上次做到哪了 | 第 5-6 章 |
init.sh | 每次启动都要重新摸索 | 第 6 章 |
14.2 进阶模板:会话交接、清洁检查、评审标准
当项目进入多会话持续开发阶段,按需补上:
session-handoff.md:会话交接模板——记录本次完成了什么、下次该从哪继续clean-state-checklist.md:清洁状态五维检查清单——会话退出前逐项核对evaluator-rubric.md:评审评分标准——按维度的结构化评分模板
14.3 OpenAI 高级资源包:完整仓库骨架
如果你的仓库已经进入多模块、多阶段、多角色协作,可以直接升级到 OpenAI 风格的高级结构:
your-repo/
├── AGENTS.md ← 分层指令入口
├── docs/
│ ├── architecture.md ← 架构约束(可执行)
│ ├── features.json ← 功能清单(JSON 格式)
│ ├── quality-scorecard.md ← 质量文档
│ └── execution-plan.md ← 执行计划
├── scripts/
│ ├── init.sh ← 初始化
│ ├── verify.sh ← 三层验证
│ └── cleanup.sh ← 幂等清理
└── .harness/
├── sprint-contract.md ← 当前冲刺合同
└── session-log/ ← 会话轨迹记录14.4 Harness Creator 技能集:让 Agent 帮你建 Harness
最酷的部分:你可以让 agent 自己帮你建 harness。 Learn Harness Engineering 提供了一套 Harness Creator 技能,包含:
- create-harness:从零开始为一个仓库生成最小可用的 harness 文件
- audit-harness:检查已有 harness 的完整性,找出缺失的组件
- upgrade-harness:根据项目复杂度升级 harness(从最小版到高级版)
使用方法:将技能文件复制到你的 agent 工作环境,然后告诉 agent "用 create-harness 技能给这个仓库建 harness"。
💡 先四个文件起步,跑几次会话后根据痛点逐步添加。 不要一开始就搞全套——harness 也要渐进式构建,和你教 agent 做的事情一样。
15. 延伸阅读与参考资料
本指南中引用的所有核心文献,按主题分类便于深入研究。
15.1 官方文献:OpenAI / Anthropic 原始文档
| 文献 | 核心贡献 |
|---|---|
| Harness Engineering - OpenAI | 提出 harness engineering 概念,分层领域架构,架构约束自动化 |
| Building Effective Agents - Anthropic | Agent 有效性的核心原则,工件外部化 |
| Harness Design for Long-Running Apps - Anthropic | 三 agent 架构实验,冲刺合同,evaluator 调校 |
| Effective Harnesses for Long-Running Agents - Anthropic | 清洁会话退出,长期可靠性 |
| Claude Code Best Practices - Anthropic | CLAUDE.md 最佳实践,指令分层 |
15.2 学术论文:置信度校准、中间迷失、软件演化
| 论文 | 核心贡献 |
|---|---|
| On Calibration of Modern Neural Networks - Guo et al. (ICML 2017) | 证明现代神经网络系统性地过度自信 |
| Lost in the Middle - Liu et al. (2024) | 长上下文中间信息的注意力衰减 |
| Programs, Life Cycles, and Laws of Software Evolution - Lehman (1980) | 软件演化定律,复杂性必然增长 |
15.3 工程实践:测试金字塔、可观测性、Kanban
| 书籍/资源 | 核心贡献 |
|---|---|
| How Google Tests Software - Whittaker et al. | 测试金字塔模型 |
| Clean Code - Robert C. Martin | 代码清洁性系统化原则 |
| Design by Contract - Bertrand Meyer | 契约式设计,功能清单的理论基础 |
| Observability Engineering - Charity Majors | 现代可观测性工程 |
| Site Reliability Engineering - Google | 可观测性在生产系统中的应用 |
| Dapper - Google (Sigelman et al.) | 大规模分布式追踪 |
15.4 工具链:SWE-bench、SWE-agent、OpenTelemetry
| 工具 | 用途 |
|---|---|
| SWE-bench | AI 编码 agent 的标准化评测基准 |
| SWE-agent | 基于 LLM 的软件工程 agent 框架 |
| OpenTelemetry | 可观测性标准化(traces, metrics, logs) |
| Playwright | 端到端测试框架,Anthropic evaluator 使用 |
| Chaos Engineering - Netflix | 主动注入故障验证系统弹性 |
全书完。
Harness Engineering 的核心就一句话:问题不在模型,在环境。 给千里马配上合适的马鞍——指令分层、功能清单、三层验证、可观测性、清洁状态——它就能跑得又快又稳。
从今天开始,给你的 AI Agent 建一个 harness 吧。四个文件起步,从第一个项目开始。
祝你的 agent 不再翻车。 🏇