Learn Harness Engineering 课程全总结:12 讲核心要点
Learn Harness Engineering 课程全总结:12 讲核心要点
本文是 Learn Harness Engineering 课程全部 12 讲的系统总结。从理解 Harness 的本质到搭建完整的 Agent 运行环境,每一讲的核心论点、关键概念、实践方法和真实案例均已完整收录,无省略。
目录
- 第一讲:模型能力强,不等于执行可靠
- 第二讲:Harness 到底是什么
- 第三讲:让代码仓库成为唯一的事实来源
- 第四讲:把指令拆分到不同文件里
- 第五讲:让跨会话的任务保持上下文连续
- 第六讲:让 agent 每次工作前先初始化
- 第七讲:给 agent 划清每次任务的边界
- 第八讲:用功能清单约束 agent 该做什么
- 第九讲:防止 agent 提前宣告完成
- 第十讲:跑通完整流程才算真正验证
- 第十一讲:让 agent 的运行过程可观测
- 第十二讲:每次会话结束前都做好交接
第一讲:模型能力强,不等于执行可靠
核心论点:模型能力和执行可靠性是两回事。遇到失败,先检查 harness,后考虑换模型。同一个模型在空白环境里和在有完整 harness 的环境里,产出有本质差异。模型没变,变的是马具。
关键概念
- 能力鸿沟(Capability Gap):模型在基准测试(如 SWE-bench Verified)上的表现和真实任务上的表现之间的巨大落差。SWE-bench Verified 通过率仅 50-60%,意味着近一半的真实 issue 解不了。
- Harness 诱导失败:模型本身能力足够,但因为执行环境有结构性缺陷而失败。Anthropic 的对照实验证明:Opus 4.5 配完整 harness 6 小时 $200 做出可玩的游戏,裸跑 20 分钟 $9 游戏核心功能跑不起来。
- 诊断循环:执行 → 观察失败 → 定位到 harness 的哪一层 → 修补那一层 → 重新执行。这是 harness 工程的核心方法论。
- 完成定义(DoD,Definition of Done):一组可以用命令验证的条件——测试通过、lint 没报错、类型检查通过。没有显式的 DoD,agent 就会自己编一个。
五种 Agent 典型失败模式
- 需求描述模糊:agent 只能自己猜,猜错概率大
- 隐性约定没写下来:agent 无从遵守——它不知道"所有 API 必须走 OAuth 2.0"这条规矩
- 环境配置有缺口:agent 把上下文花在修环境上,真正该干的活反而没精力做
- 验证手段缺失:没有测试、没有 lint,agent 自己觉得做完了就算完成
- 跨会话状态丢失:每个新会话都要重新探索项目,超过 30 分钟的任务失败率急剧上升
核心要点
- 模型能力和执行可靠性是两回事,千里马也得配上好马具。
- 失败的时候先看 harness,再看模型。换模型是成本最高的选择。
- 每次失败都是一个信号:你的 harness 有结构性缺陷。
- 五层防御排查:任务规范、上下文供给、执行环境、验证反馈、状态管理。
- 一个
AGENTS.md文件可能比你换一个更贵的模型更有效。
第二讲:Harness 到底是什么?
核心论点:"harness"在 AI coding agent 圈子被用得越来越多,但大部分人说 harness 时指的只是一个 prompt 文件。一个 prompt 文件不是 harness。Harness 由五个子系统组成:指令、工具、环境、状态、反馈。不是模型权重的部分,全是 harness。
关键概念
- 什么是 Harness:模型权重之外的一切工程基础设施。OpenAI 把工程师的核心工作概括为设计环境、表达意图、构建反馈循环。Anthropic 直接把 Claude Agent SDK 称为"通用 agent harness"。
- 仓库是唯一事实来源:agent 看不到的东西对它来说就不存在。所有必要的上下文都必须在仓库里,通过结构化的文件和清晰的目录组织来呈现。
- 给地图,不给说明书:
AGENTS.md应该是目录页,不是百科全书。100 行左右就够了,放不下就拆分到docs/目录里,让 agent 按需去读。 - 约束而非微操:好的 harness 用可执行的规则约束 agent,而不是在指令里逐条叮嘱。OpenAI 说"执行不变量,不要微管实现";Anthropic 发现解决方案是把"干活的人"和"检查的人"分开。
- "控制变量排除法"量化价值:保持模型不变,逐个移除五个子系统,看哪个移除后性能下降最多。下降最多的组件边际贡献最大。但要定位真正瓶颈,还需结合失败记录和归因分析,不能只靠拆除实验。
Harness 五子系统模型
| 子系统 | 职责 | 关键实践 |
|---|---|---|
| 指令 | 让 agent 看懂项目规则 | 创建 AGENTS.md / CLAUDE.md,含项目概览、技术栈、硬约束、文档链接 |
| 工具 | 确保 agent 所需的工具可随时调取 | 不要因为"安全考虑"禁掉 shell,按最小权限原则开放工具权限,确保环境状态自描述 |
| 环境 | 运行环境可重现 | pyproject.toml/package.json锁定依赖,.nvmrc/.python-version指定运行时版本 |
| 状态 | 跨会话连续性 | 用 PROGRESS.md 记录进度,用 DECISIONS.md 记录决策原因 |
| 反馈 | 验证结果反馈 | 显式列出验证命令 |
核心要点
- Harness = 指令 + 工具 + 环境 + 状态 + 反馈,五个子系统缺一不可。
- 五个子系统中,反馈子系统投入最少、回报最高——先把验证命令写清楚。
- 用"控制变量排除法"量化各子系统边际贡献;定位瓶颈需结合失败记录和原因归因。
- Harness 和代码一样会腐化,需定期审计,像还技术债一样还 harness 债。
真实案例
一个 TypeScript + React 项目(约 20,000 行代码),四个阶段:
- 阶段 1:只有 README,5 次运行成功 1 次(20%)
- 阶段 2:添加 AGENTS.md(技术栈版本、命名约定、架构决策),成功率升到 60%
- 阶段 3:添加验证命令
yarn test && yarn lint && yarn build,成功率升到 80% - 阶段 4:引入进度文件模板,成功率稳定在 80-100%
模型一个字没改,成功率从 20% 到接近 100%。
第三讲:让代码仓库成为唯一的事实来源
核心论点:不在仓库里的知识对 agent 来说等于不存在。Agent 的输入只有三样——任务描述、仓库文件内容、工具执行的输出。不在仓库里的信息 agent 永远看不到。你必须给它一张足够好的地图。
关键概念
- 知识可见性缺口:项目总知识中不在仓库里的比例。缺口越大,agent 失败概率越高。
- 系统记录(System of Record):代码仓库是项目决策、架构约束、执行状态和验证标准的权威信息源。仓库说了算。
- 全新会话测试:开全新 agent 会话,只让它看仓库,测试能否回答五个基本问题——这是什么系统?怎么组织的?怎么跑?怎么验证?现在进度如何?答不上来的问题越多,地图空白越大。
- 发现成本:agent 为了在仓库里找到关键信息需要消耗的上下文。信息越隐蔽,发现成本越高。
- 知识衰减率:仓库中单位时间内变得过时的知识条目比例。过时的文档比没有文档更危险——它会误导 agent 还以为自己是对的。
画好地图的四条原则
- 知识靠近代码:API 认证规则放在 API 代码旁,而不是藏在全局文档里
- 标准化入口文件:AGENTS.md 作为着陆页,50-100 行,快速回答三个问题
- 最小但完备:删掉不影响 agent 决策质量的规则,但全新会话测试的每个问题都必须有答案
- 和代码一起更新:把知识文档放在对应模块目录里,改代码时自然看到
ACID 类比管理 Agent 状态
- 原子性(Atomicity):每个逻辑工作单元一个 git 提交,要么全做要么不做
- 一致性(Consistency):定义"一致状态"的验证谓语——所有测试通过,lint 无报错。不一致的中间状态不要提交
- 隔离性(Isolation):多 agent 并发时用独立进度文件或 git 分支隔离
- 持久性(Durability):关键知识必须写进 git 跟踪的文件里。脑子里的不算
真实案例
一个约 30 个微服务的电商平台:架构决策散落在 Confluence(部分过时)、Slack(难以搜索)、资深工程师的脑子里(不可扩展)。引入 AI agent 后 70% 的任务需要人工干预——几乎每次失败都涉及 agent 违反"所有人都知道但从未写入仓库"的隐性约束。
改造:根目录创建 AGENTS.md → 每个微服务目录添加 ARCHITECTURE.md → 创建集中的 CONSTRAINTS.md → 每个服务目录添加 PROGRESS.md → 改造后同一 agent 能在冷启动时回答所有关键项目问题。
第四讲:把指令拆分到不同文件里
核心论点:"加条规则"是短期的止痛药、长期的毒药。入口文件是路由器,不是百科全书。50-200 行就够了。
巨型指令文件的问题
- 上下文预算被吃掉:600 行 AGENTS.md 占用 10K-20K tokens,对 128K 窗口就是 8-15%
- 中间迷失(Lost in the Middle):Liu 等人 2023 年研究表明,LLM 对长文本中间部分的信息利用效率显著低于两端
- 分不清轻重:硬约束("不得使用 eval()")和软建议("优先使用函数式风格")以相同格式呈现,agent 无法区分
- 维护衰减:指令只增不减,信噪比持续下降
- 矛盾累积:不同时期加的指令出现矛盾,agent 每次随机选一条
指令信噪比(Instruction SNR)
- 定义:入口文件中与当前任务相关的指令条数 ÷ 总指令条数
- 现状:文件膨胀到 600 行时,SNR 常低于 0.3
- 目标:理想 SNR 应为 0.7-1.0(大部分指令与当前任务相关)
- 作用:SNR 是衡量入口文件质量的量化指标, SNR 越低,agent 上下文利用率越差
注意:SNR 不是一个绝对值标准,而是相对参考。它会受任务类型影响——做 bug 修复和做新功能的"相关指令"不同。
推荐的文件结构
- 入口文件 AGENTS.md(50-200 行):项目概览 + 快速开始命令 + 全局硬约束(不超过 15 条)+ 指向专题文档的链接
- 专题文档(50-150 行/每个):按主题放在 docs/ 目录下,agent 按需读取
- 信息直接放在代码里:类型定义、接口注释等——agent 读代码时自然看到
维持指令健康的核心原则
- 每条指令标明来源(为什么加这条?)、适用条件(什么时候需要?)、过期条件(什么时候删?)
- 重要信息放顶部或底部,利用"中间迷失"效应
- 定期审计,删掉必要的 vs 不必要的——像管理代码依赖一样管理(升级/降级/依赖管理)指令
关键数据:某 SaaS 团队的 AGENTS.md 从 50 行膨胀到 600 行,重构后(裁剪到 80 行 + 创建 3 个专题文档)成功率从 45% 升到 72%,安全约束遵循率从 60% 升到 95%。
第五讲:让跨会话的任务保持上下文连续
核心论点:上下文窗口是有限的资源。长任务一定会跨会话,跨会话一定会丢信息。解决方案不是更大的窗口,而是更好的状态持久化。
关键概念
- 上下文焦虑:Anthropic 观察到,当 agent 感觉上下文快满了,它们会表现"赶工收尾"行为——匆忙结束、跳过验证、选简单方案而非最优方案
- 重建成本:新会话恢复到可执行状态所需的时间。好的 harness 能把重建成本从 15 分钟压到 3 分钟
- 漂移(Drift):agent 的理解跟代码仓库实际状态之间的偏差。每个会话边界都会引入漂移
两种上下文管理策略
- 压缩(Compaction):同一会话内把早期对话摘要化。优点是保留连续性,缺点是"为什么"经常丢失,且上下文焦虑并未消除——agent 知道上下文曾经很大
- 重置(Context Reset):全新会话从持久化工件重建。优点是干净的心理状态("我没赶时间"),缺点是依赖交接工件的完备性
四个工具
- 进度文件(PROGRESS.md):记录当前状态(commit hash + 测试状态 + lint 状态)+ 已完成 + 进行中 + 已知问题 + 下一步
- 决策日志(DECISIONS.md):记录"什么决策 + 为什么 + 什么时候"。让新会话理解设计意图
- git 检查点:每完成一个原子工作单元就 commit
- 标准化流程:每次"上班"读 PROGRESS.md + DECISIONS.md +
make check;每次"下班"更新 PROGRESS.md +make check+ commit
混合策略
- 短任务(30 分钟内):同一会话内完成
- 长任务:用结构化工件维持连续性
- 判断标准:任务需要的上下文超过窗口的 60% → 开始准备交接
关键数据
定量对比:一个含 12 功能点的博客系统,需要 5 个 agent 会话。没有状态持久化:12 点只完成 7 个(58%),隐含缺陷 43%,重建 15 分钟。有状态持久化:12 点全部完成(100%),隐含缺陷降到 8%,重建 3 分钟。重建时间减少约 78%
第六讲:让 agent 每次工作前先初始化
核心论点:初始化和实现的优化目标不同,混在一起只会互相拖后腿。Agent 在做功能前,必须先经历一个独立的初始化阶段。
初始化的五个产出
- 可运行的环境:项目能启动、依赖装好
- 可验证的测试框架:至少有一个示例测试通过
- 启动就绪清单:告诉后续会话"怎么跑 + 怎么测 + 做到哪了"
- 任务分解:把项目拆成有序的任务列表 + 每个任务有验收标准
- git 检查点:提交干净的 checkpoint
启动就绪清单四个条件
| 条件 | 说明 | 验收方式 |
|---|---|---|
| 能启动 | 从零开始make setup成功 | 全新环境验证 |
| 能测试 | make test至少一个测试通过 | 验证测试框架 |
| 能看进度 | 新 agent 只看仓库就能回答"怎么跑 + 怎么测" | 全新会话测试 |
| 能接手下一步 | 任务分解文件存在 + 至少 3 个任务 | 检查清单 |
热启动 vs 冷启动
- 冷启动:从空目录开始,agent 需自行推断项目结构,效果差
- 热启动:用项目模板(create-react-app / fastapi-template 等)预置标准目录结构和依赖配置
- 做法:把通用初始化步骤预置到模板里,只留下项目特有的初始化工作
关键数据
Anthropic 实验:使用独立初始化阶段的项目,多会话场景中功能完成率比混合方式高 31%
React 项目对比:
-混合:100 行功能代码 + 基础设施,重建 20 分钟
-独立初始化:20 分钟只做初始化,后续 3-4 个会话中把时间全部收回
-总重建时间:混合比独立初始化多约 60%
第七讲:给 agent 划清每次任务的边界
核心论点:Agent 天生就有"多做一点"的冲动——看到相关的事情就顺手一起做了。但注意力是有限的资源,同时做太多事情往往每件都做不好。WIP=1 是 agent harness 的默认安全设置。
两个共生问题
- 过度延伸(Overreach):一次会话中激活的任务数超过最优值。量化:同时做 5 个功能但 0 个跑通
- 不足完成(Under-finish):已启动的任务中通过端到端验证的比例低于阈值。仅"写了代码"不等于"做了"
两者互相加剧:overreach 导致注意力分散 → under-finish → 半成品代码增加系统复杂度 → 下一个任务的 overreach → 恶性循环
Little 法则的启示
L = lambda × W(在制品 = 交付率 × 前置时间)。如果 L 过大(同时做太多事),每个任务的 W 必然增加。对 agent 而言,这意味着每个功能从开始到验证通过的时间被拉长,失败概率被放大。
四个实施方法
强制执行 WIP=1:明确写进 CLAUDE.md——"每次只做一个功能点 + 当前功能点端到端验证通过后才能开始下一个 + 不要在实现功能 A 时'顺便'重构功能 B"
显式完成证据:每个功能任务都有可执行的验证命令
范围表面外部化:用可读写的文件(JSON/ Markdown)记录所有任务的状态——哪个在做 + 什么行为算完成 + 通过了什么验证
监控 VCR(Verified Completion Rate)= 已通过验证的任务数 / 已启动的任务数。VCR < 1.0 → 阻止新任务启动
关键数据
Anthropic 实验:使用"小下一步"策略(WIP=1)的 agent,任务完成率比使用宽泛提示的 agent 高 37%
REST API 项目(8 个功能点)对比:
-无约束:第一个会话同时启动 5 个 → 3 个会话结束时完成 3 个(37.5%)
-WIP=1:每个会话只做 1 个 → 4 个会话结束时完成 7 个(87.5%)
-总代码:WIP=1 更少(800 vs 1200 行),但有效代码更多
第八讲:用功能清单约束 agent 该做什么
核心论点:功能清单不是备忘录——它是整个 harness 的基础数据结构。调度器靠它选任务,验证器靠它判完成,交接器靠它生成报告。没有它,所有组件就没有可以依赖的共识。
三元组结构
每个功能项包含三个要素 —— 缺一项就不完整:
- behavior(行为描述):告诉 agent 做什么
- verification(验证命令):告诉 agent 怎么算做完
- state(当前状态):告诉 agent 现在到哪了
用 JSON 格式举例:
json { "id": "F03", "behavior": "POST /cart/items with {product_id, quantity} returns 201", "verification": "curl -X POST http://localhost:3000/api/cart/items ... | jq .status == 201", "state": "passing", "evidence": "commit abc123, test output log" }
状态机模型
| 状态 | 含义 | 可转移至 |
|---|---|---|
not_started | 未开始 | →active |
active | 进行中 | →passing/blocked |
blocked | 被阻塞 | →active |
passing | 已验证通过 | 不可逆 |
"通过状态门控":active→passing的唯一方式是验证命令执行成功。agent 不能自己改状态——它只能提交验证请求,harness 执行验证并根据结果决定是否允许转移。这是最基本的约束——也是作为"原语"(primitive)的关键所在。
为什么功能清单必须是"原语"(primitive)
- 文档可以被忽略——原语不能被绕过
- 功能清单服务四个 harness 组件:调度器(读状态 → 选下一个
not_started) +验证器(执行验证 → 判断状态转移) +交接报告(自动生成交接摘要) +进度追踪(统计状态分布 → 提供健康度指标) - 反向压力:
not_started项的数量 = harness 对 agent 的压力(压力归零 = 项目完成)
粒度校准
每个功能项应该是"一次性会话能完成"的范围:
- ✅合适粒度:"用户可以添加商品到购物车"
- ❌太粗:"实现购物车"(一次性会话完成不了)
- ❌太细:"创建 Cart 模型的 name 字段"(管理开销大)
关键数据
电商平台 10 个功能项对比:
-备忘录模式:3 个会话后笔记成了"购物车基本完成但还有 bug"——新会话花 20 分钟推断状态
-结构化模式:一次性读取功能清单 → 3 分钟内知道:F01-F05 是passing+ F06 是active+ F07-F10 是not_started→ 直接从 F06 继续
-完成率:结构化模式比自由形式高 45%,零重复实现
第九讲:防止 agent 提前宣告完成
核心论点:Agent 系统性地过度自信。Guo 等人 2017 年(ICML)经典论文证明——现代神经网络系统性地过度自信。Agent 也一样——它觉得做完了,但实际上差得远。解决方案:把"干活的人"(generator)和"检查的人"(evaluator)分开,且 evaluator 需要专门调校。
过早完成声明的常见套路
- 代码语法正确、逻辑看起来合理
- 静态检查,没有明显错误
- 跳过了实际运行(或只跑了部分测试)
- 单元测试全部通过(但集成测试/E2E 测试都没跑)
- 得出结论:"嗯,做完了"
三层终止检查
| 层级 | 成本 | 检查内容 |
|---|---|---|
| 第 1 层:静态分析 | 最低 | 语法正确 + 类型检查通过 |
| 第 2 层:运行时行为验证 | 中等 | 单元测试/集成测试通过 + 应用能正常启动 |
| 第 3 层:系统级确认 | 最高 | E2E 测试通过 + 完整用户流程走通 |
完成优先级约束:第 1 层没通过 → 不许进第 2 层;第 2 层没通过 → 不许进第 3 层;核心功能没验证通过之前 → 不许做重构
Generator + Evaluator 分离的价值
Agent 在评估自己的工作时系统性地过度正面评价——同一个模型既生成又评估,内在地倾向于对自己慷慨。
Anthropic 实验数据(同一任务 + 同一模型 Opus 4.5):
| 架构 | 成本 | 时长 | 核心功能 |
|---|---|---|---|
| 单一 agent 裸跑 | $9 | 20 分钟 | ❌ 不可用 |
| 三 agent 架构(planner + generator + evaluator) | $200 | 6 小时 | ✅ 可用 |
Evaluator 也需要调校
Evaluator不是一开始就那么强——早期版本的 evaluator 也会识别出合理的问题,然后说服自己这些问题"不过" ——最终也会"……,但……"
调校的关键信号:当 evaluator 使用"尽管/但是/虽然/…… 这种权衡" 的方式为代码变更辩护 → 这一面红旗就是"需要被校准"的信号
错误消息设计
OpenAI Codex 实践——面向 agent 的错误消息必须包含三种要素:
- 什么出了问题(what)
- 为什么是问题(why)
- 该怎么改(fix)
例:
❌"测试失败"
✅"测试失败:POST /api/reset-password 返回了 500。请在环境变量中检查邮件服务配置是否已正确设置。邮件模板文件应位于 templates/reset-email.html。"
第十讲:跑通完整流程才算真正验证
核心论点:单元测试对组件边界缺陷系统性盲视——隔离设计恰好使其无法检测交互问题。只有 E2E 测试能证明系统级缺陷不存在。
单元测试的五种盲区
| 缺陷类型 | 描述 | 单元测试 | E2E 测试 |
|---|---|---|---|
| 接口不匹配 | 渲染进程传给 preload 的文件路径格式不一致 | ✗ | ✓ |
| 状态传播错误 | ORM 缓存层持有旧结构 | ✗ | ✓ |
| 资源生命周期问题 | 文件句柄获取/释放跨越多个组件 | ✗ | ✓ |
| 环境依赖性 | 在 mock 环境正确但在真实环境失败 | ✗ | ✓ |
| 错误传播 | 服务层异常未能传到 UI 层 | ✗ | ✓ |
E2E 测试的两个效应
检测效应:E2E 测试捕获了 5 个跨组件边界缺陷(在一个 Electron 文件导出功能中,单元测试一个都没发现)
行为效应:当 agent 知道它的工作要过 E2E 测试时,它的编码行为也会改变:
- 写代码时会考虑"这个接口和上游怎么对接"
- 尊重架构边界(在有约束的系统里,E2E 测试迫使 agent 遵守边界规则)
- 处理异常路径(E2E 测试包含故障场景,迫使 agent 考虑异常处理)
架构规则的"可执行"原则
- 每条架构约束必须有对应的测试或 lint 规则来机械执行
- "执行不变量,不微管实现"——OpenAI Codex 工程实践的核心原则
- 每次提交自动检查——不能只写在文档里等人来看
"审查反馈提升"(Review Feedback Elevation)
流程:发现 agent 的新类型错误 → 转化为自动化检查规则
每个被捕获的缺陷类别都变成永久防线,harness 自动变强
第十一讲:让 agent 的运行过程可观察
核心论点:可观察性是 harness 的架构属性——设计时必须考虑的核心能力,不应仅作为"事后追加"的功能。
缺少可观察性导致的四类问题
| 问题 | 描述 |
|---|---|
| 无法区分"正确"与"看似正确" | 代码审查看起来正确,但运行时因边界条件在特定输入下产生了错误结果;只有运行时追踪能揭示实际执行路径偏离了预期 |
| 评估变成"玄学" | 没有评分标准和验收条件,评估者只能依赖隐式假设;同一输出不同评估者可能给出截然不同的结论 |
| 重试变成"盲猜" | agent 不知道失败原因,重试方向随机——可能在错误方向反复尝试,修复无关代码路径,忽略真正故障根源 |
| 会话交接信息断崖 | 缺乏可观察性意味着新会话必须从零诊断系统状态;Anthropic 观察发现这占会话总时间的 30-50% |
双层可观察性
运行时可观察性和过程可观察性——两者缺一不可,相互增强。
| 层级 | 职责 | 内容 |
|---|---|---|
| 运行时可观察性 | 回答"系统做了什么" | 日志、追踪、进程事件、健康检查 |
| 过程可观察性 | 回答"为什么这个变更应被接受" | 冲刺合同、评分标准、验收条件 |
冲刺合同(Sprint Contract)
在每个任务开始前,生成者和评估者协商一份合同,明确这次做什么、怎么做算通过:
| 要素 | 内容 |
|---|---|
| 范围(Scope) | 修改哪些组件 + 每个组件的验证标准 |
| 验证标准(Verification Criteria) | 例如每个组件的 Lighthouse 评分 ≥ 80 |
| 排除项(Exclusions) | 例如"不处理打印样式" + "不处理第三方组件暗色模式" |
没有冲刺合同:生成者和评估者的隐式预期不一致,循环 3-4 次,总耗时约 45 分钟。
有冲刺合同:一次迭代出高质量结果,总耗时约 15 分钟。
效率差 3 倍,区别只在可观察性。
评估评分标准
把"好不好"从主观判断变成基于证据的多维结构化评分:
| 维度 | A | B | C | D |
|---|---|---|---|---|
| 代码正确性 | 所有测试通过 | 主流程通过 | 部分通过 | 编译失败 |
| 架构合规 | 完全合规 | 轻微偏离 | 明显偏离 | 严重违反 |
| 测试覆盖 | 主流程+边缘场景 | 仅主流程 | 仅有骨架 | 无测试 |
第十二讲:每次会话结束前都做好交接
核心论点:OpenAI 和 Anthropic 一致指出——单次运行成功并不够,每个会话退出时的状态质量直接决定下一个会话的效率。清洁状态是"完成"的必要条件。
软件演化定律(Lehman's Law)
一个持续变更的系统,如果没有人主动管理,它的复杂性一定会增加。
OpenAI 在长达 5 个月的 Codex 实验中观察到一个清晰的现象:
- Agent 会复制仓库中已有的模式,即使那些模式是不一致或次优的
- 每个会话都会引入新的偏差
- 熵增是默认方向
- 只有主动的清洁操作才能对抗它
来源:Lehman,《Programs, Life Cycles, and Laws of Software Evolution》
清洁状态 ≠ "代码能编译"
清洁状态的要求远比"代码能编译"要多。构建通过是最基本的前提——下一个会话不应该一上来就先修别人的构建错误。所有测试也必须通过,包括会话开始前就存在的旧测试——你这次改动不能破坏已有的功能。
清洁状态五个维度——缺一不可:
| 维度 | 要求 | 验证方式 |
|---|---|---|
| 构建通过 | 项目能够正常构建 | npm run build成功 |
| 测试全部通过 | 所有测试通过(含先前的旧测试) | npm test成功 |
| 进度已记录 | 功能清单/进度文件已更新为机器可读的工件 | 文件存在且内容最新 |
| 临时工件已清理 | 无残留的调试日志、临时文件、注释掉的代码、过时的 TODO 标记 | 显式扫描确认 |
| 启动路径可用 | 新会话可不依赖人工干预直接开始工作 | 环境初始化 + 代码加载 + 上下文获取 + 任务选择可达 |
双模式清理策略
| 模式 | 时机 | 任务 | 原则 |
|---|---|---|---|
| 即时清理 | 每个会话结束时 | 清理本次会话的临时文件、更新功能清单、确保构建和测试全部通过 | 谁产生的垃圾谁负责清掉,像引用计数一样 |
| 定期清理 | 每周一次 | 系统扫描,处理累积的结构性问题、更新质量文档、跑基准测试检测质量漂移 | 定期全身体检,不让小问题拖成大病 |
质量文档
质量文档是一份持续更新的文件,对代码库中每个模块记录质量评分和评价。新会话一打开就能直接看到当前每个模块的状态。
| 评估项 | 每模块评分 |
|---|---|
| 验证状态 | 通过 ✓ / 部分通过 ✗ |
| 测试稳定性 | 稳定 / 不稳定 |
| 架构边界 | 合规 / 有违规 / 严重违反 |
| 代码规范 | 遵循 / 部分遵循 / 不遵循 |
| agent 可理解性 | 容易 / 中等 / 困难(逻辑分散在多个文件) |
Harness 简化
Harness 中每个组件的存在,都源于模型在某个方面尚无法独立完成。随着模型能力不断演进,这些前提会逐渐过时。
推荐做法:
- 每月挑选一个 harness 组件,暂时禁用它
- 跑一遍基准任务集
- 如果结果没有退化,就永久移除
- 如果退化,恢复该组件,或换一个更轻量的替代方案
幂等清理
定义:一个操作无论执行一次还是一百次,结果都一样。
为什么需要:清理失败时你需要重跑一遍,重跑必须产生相同的结果。
幂等清理操作举例:
rm -f /tmp/debug-*.log # -f 确保文件不存在时不报错 git checkout -- .env.local # 恢复到已知状态,多跑几次结果相同 npm run test # 验证清理没有破坏任何功能清洁状态——实测数据对比
一个使用 agent 持续开发的 Electron 应用,12 周演化过程的实际对比:
| 指标 | 无清洁策略(第12周) | 有清洁策略(第12周) |
|---|---|---|
| 构建通过率 | 68% | 97% |
| 测试通过率 | 61% | 95% |
| 新会话启动时间 | 60 分钟以上 | 9 分钟 |
| 过时工件数量 | 103 个 | 11 个 |
到第 12 周的差距:
- 构建通过率高 29 个百分点
- 测试通过率高 34 个百分点
- 新会话启动时间减少约 85%
每个会话只多花 5 分钟做清理,12 周下来却省了几十个小时的混乱时间。
课程总览——全表
| 讲次 | 主题 | 核心概念 |
|---|---|---|
| 01 | 模型能力强≠执行可靠 | 能力鸿沟、诊断循环、完成定义 |
| 02 | Harness 到底是什么 | 五子系统:指令+工具+环境+状态+反馈 |
| 03 | 仓库作为唯一事实来源 | 知识可见性缺口、全新会话测试、ACID 类比 |
| 04 | 把指令拆分到不同文件 | 巨型文件陷阱、中间迷失效应、信噪比 SNR |
| 05 | 跨会话上下文连续 | 上下文焦虑、重建成本、漂移 |
| 06 | 独立初始化阶段 | 启动就绪清单、热启动策略 |
| 07 | 给 agent 划清边界 | 过度延伸 Overreach、不足完成 Under-finish、WIP=1、VCR |
| 08 | 功能清单是 harness 原语 | 三元组结构、状态机模型、通过状态门控 |
| 09 | 防止提前宣告完成 | 三层终止检查、校准偏差、Generator+Evaluator 分离 |
| 10 | E2E 测试 | 组件边界缺陷、架构约束可执行、审查反馈提升 |
| 11 | 可观察性 | 双层可观察性、冲刺合同、评估评分标准 |
| 12 | 会话结束做好交接 | 清洁状态五维度、质量文档、幂等清理、Harness 简化 |
一句话总结课程:
Harness = 指令 + 工具 + 环境 + 状态 + 反馈。失败的时候先检查 harness,再检查模型。让 agent 真正可靠的唯一途径,是显式、可验证、持久化的工程基础设施,而不是"一个更强的模型"。
来源:本文内容总结自 Learn Harness Engineering 全部 12 讲(中文版),完整内容及引用来源请参见原文。