Skip to content

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 个
人均日产 PR3.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 setupmake 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. 空厨房只有 README20%选错包管理器、命名不规范、跑不了测试
2. 装上菜谱架AGENTS.md60%环境问题 + 验证缺失
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 / SlackAgent 永远找不到

💡 实操建议: 今天就对你的主力项目做一次冷启动测试。记录 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 行,只放四样东西:

markdown
# 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 后" / "该遗留服务下线后"

举个例子,一条好的指令长这样:

markdown
<!-- 来源:PR #234 修复了 SQL 注入漏洞 -->
<!-- 适用:所有涉及数据库查询的代码 -->
<!-- 过期:永不过期——安全硬约束 -->
- 所有数据库查询必须用参数化查询,禁止字符串拼接

而一条需要清理的指令长这样:

markdown
<!-- 来源:上周 WebSocket 连接泄漏事故 -->
<!-- 适用:修改 WebSocket 相关代码时 -->
<!-- 过期:根因修复后(已在 PR #456 修复) -->
- 注意 WebSocket 连接的 cleanup 逻辑  ← 应该删除或转化为测试用例

定期审计策略: 像管理代码依赖一样管理你的指令——用不上的依赖就该删掉,不然它们只会拖慢系统。

建议每月做一次"指令大扫除":

  1. 检查每条指令的过期条件是否已满足
  2. 把历史教训类的指令转化为测试用例(这才是它们应该存在的形式)
  3. 删除矛盾的、冗余的、模糊的条目
  4. 确认入口文件仍然在 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)—— 日记本的核心。

markdown
# 项目进度

## 当前状态
- 最新 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)—— 日记本里的备忘。

记录重要的设计决策和原因。不需要详细的设计文档,只需要"什么决策、为什么、什么时候做的":

markdown
## 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 会话,只看仓库内容(不给任何口头上下文),能无歧义地接着干。

初始化阶段的产出应该是一个完整的自举契约文档:

markdown
# 初始化契约

## 启动命令
- 安装依赖:`make setup`
- 启动开发服务器:`make dev`
- 运行测试:`make test`
- 完整验证:`make check`

## 当前状态
- 所有依赖已安装并锁定
- 测试框架已配置(pytest + mypy)
- 示例测试通过(1/1)
- Lint 规则已配置(ruff)

## 项目结构
- src/       — 源代码
- src/api/   — API 端点
- src/db/    — 数据库模型
- tests/     — 测试文件

加上任务分解——把整个项目拆成有序的任务列表,每个任务有明确的验收标准:

markdown
## Task 1: 用户认证基础
- 实现 JWT 认证中间件
- 添加登录/注册端点
- 验收:pytest tests/test_auth.py 全部通过

## Task 2: 用户资料页面
- 实现用户资料 CRUD
- 验收:pytest tests/test_profile.py 全部通过

用这个检查清单验收初始化:

初始化验收清单:

  [ ] make setup 从零开始能成功
  [ ] make test 至少有一个测试通过
  [ ] 新 agent 会话能只看仓库回答"怎么跑"和"怎么测"
  [ ] 任务分解文件存在且有至少 3 个任务
  [ ] 所有内容已提交到 git

6.4 热启动 vs 冷启动:用模板预置基础设施

冷启动是从空目录开始,agent 要猜项目结构、选技术栈、配环境——就像在荒郊野岭从头搞起。

热启动是从项目模板开始,基础设施已经就位——就像在有通水通电的工地上开工。

差距有多大?看一个 React 前端项目的对比:

维度混合方式(边打地基边砌墙)独立初始化(先打地基)
第一个会话同时做脚手架 + 首个功能只做初始化:模板 + 测试 + 契约
第二个会话重建时间~20 分钟(推断结构和流程)❤️ 分钟(看契约直接干)
总重建时间(全周期)基线减少约 60%
功能完成率基线提升 31%

不要从空目录开始。用一个项目模板(create-react-appfastapi-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 里加上这段:

markdown
## 工作规则
- 每次只做一个功能点
- 当前功能点端到端验证通过后,才能开始下一个
- 不要在实现功能 A 时"顺便"重构功能 B
- 如果发现了需要修复的问题,记到 PROGRESS.md 里,但不要现在动手

就像吃自助餐——一次只拿一盘,吃完再去拿。

Anthropic 在工程博客中明确指出:使用"小下一步"策略(等价于 WIP=1)的 agent,任务完成率比使用宽泛提示的 agent 高 37%。OpenAI 在 Codex 工程实践中也发现,没有显式范围控制的任务,完成率会暴跌。

💡 核心原则: "少做但做完"永远优于"多做但做半"。一口吃不成胖子,一次做一个,反而完成得最多。

7.3 完成证据:行为验证通过才算完成

WIP=1 解决了"同时做太多"的问题,但还有一个问题:怎么判断"做完了"?

如果不给明确的定义,agent 会用"代码看起来没问题"来代替"行为通过测试"。这就像学生说"我写完卷子了"——写满了不代表做对了。

完成证据(Completion Evidence) 必须是可执行的、可验证的:

markdown
## 功能清单

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 三元组结构:行为描述 + 验证命令 + 当前状态

每个功能项是一个三元组。缺了任何一项,这个功能项就不完整——就像三条腿的凳子少一条腿。

json
{
  "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 格式同样可以:

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 里写清楚规则:

markdown
## 功能清单规则
- 功能清单文件: /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 字段"❌ 管理开销大于实际价值

校准方法:问自己两个问题——

  1. 这个功能项能在一次 agent 会话(30-60 分钟)内从 not_started 做到 passing 吗? 如果不能,拆细。
  2. 这个功能项有独立的、可验证的用户行为吗? 如果没有,它可能太细了——合并到上一级。

一个好的功能清单,通常是 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 里写清楚:

markdown
## 完成定义
- 功能完成 = 端到端验证通过,不是"代码写完了"
- 必须运行的验证层级:
  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 机械执行。

关键原则:执行不变量,不微管实现。 比如要求"数据在边界解析",但不规定用哪个库。

bash
# 把架构规则变成可执行检查的例子:

# 规则:渲染进程不能直接访问文件系统
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) 是过程可观测性的核心工具——在编码开始前,生成者和评估者协商的短期协议。就像施工队开工前签的施工协议。

markdown
# 冲刺合同: 暗色模式支持

## 范围
- 修改主题切换组件
- 更新全局 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 清洁状态五维检查清单

清洁状态不是单一的"代码能编译"。五个维度缺一不可:

markdown
## 会话退出检查清单

- [ ] ① 构建通过:`npm run build``make build` 无错误
- [ ] ② 测试通过:`npm test` 全部绿色,包括会话前已存在的测试
- [ ] ③ 进度已记录:功能清单状态已更新,PROGRESS.md 已写入
- [ ] ④ 无过时工件:无 console.log / debugger / TODO / 临时文件
- [ ] ⑤ 启动路径可用:`npm run dev` 能正常启动,下一个会话无需人工干预

这就像搬出宿舍时的检查表——水电不能断的、地上不能有垃圾的、钥匙要交回的、个人物品要带走的。

12.3 双模式清理策略:即时清理 + 定期大扫除

结合两种清理模式——就像吃完饭马上洗碗(即时清理)+ 每周拖一次地(定期清理):

即时清理(每个会话结束时):

  • 清理本次会话创建的临时工件
  • 更新功能清单状态
  • 确保构建和测试通过
  • 提交 Git 检查点

定期清理(每周一次):

  • 全系统扫描——处理累积的结构性问题
  • 更新质量文档(对每个模块持续评分的活跃工件)
  • 运行基准测试检测漂移

质量文档的示例——就像宿舍的卫生检查评分表:

markdown
# 质量文档

## 用户认证模块 (质量: 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/:参考的最终实现(卡住时可参考)
  • 指导文档:说明任务背景和具体目标

推荐工作流:

  1. 先读对应章节的理论
  2. 用你习惯的 AI Coding Agent(Claude Code、Cursor、Codex 等)在 starter/ 目录下完成任务
  3. 对比你的实现和 solution/ 的差异
  4. 记录观察——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.mdAgent 不知道该遵守什么规则第 2-4 章
features.mdAgent 不知道"做完"是什么意思第 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 - AnthropicAgent 有效性的核心原则,工件外部化
Harness Design for Long-Running Apps - Anthropic三 agent 架构实验,冲刺合同,evaluator 调校
Effective Harnesses for Long-Running Agents - Anthropic清洁会话退出,长期可靠性
Claude Code Best Practices - AnthropicCLAUDE.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-benchAI 编码 agent 的标准化评测基准
SWE-agent基于 LLM 的软件工程 agent 框架
OpenTelemetry可观测性标准化(traces, metrics, logs)
Playwright端到端测试框架,Anthropic evaluator 使用
Chaos Engineering - Netflix主动注入故障验证系统弹性

全书完。

Harness Engineering 的核心就一句话:问题不在模型,在环境。 给千里马配上合适的马鞍——指令分层、功能清单、三层验证、可观测性、清洁状态——它就能跑得又快又稳。

从今天开始,给你的 AI Agent 建一个 harness 吧。四个文件起步,从第一个项目开始。

祝你的 agent 不再翻车。 🏇

坚持是一种品格