为什么模型一样强,效果却天差地别——Harness 才是那个变量
Harness 是模型权重之外的全部工程基础设施,五子系统是组件、四层防御是落地。
立靶:换了同一个模型,为什么有人翻车有人封神
同一个 Opus、同一段任务,A 团队的 agent 跑通宵自动收尾,B 团队的 agent 第三步就开始幻觉、丢上下文、改坏代码再也回不去。两边模型权重一模一样。差的那个变量,walkinglabs 给了它一个名字——Harness。
我们太容易把 agent 的能力归因到模型本身:「Opus 比 Sonnet 强多少」「换个模型行不行」。但一个能干活的 agent 产品里,模型只是引擎,能跑多远取决于引擎之外的整辆车:你给它的指令怎么组织、工具是否够用又克制、环境能否复现、长任务的进度记在哪、它怎么知道自己干对了。这些加起来才是 Harness,而它常常被当成「工程细节」随手糊弄。
walkinglabs 的核心断言很硬:Agency(感知—推理—行动的能力)来自模型训练,但能力被发挥多少,由 Harness 决定。模型是司机,Harness 是车。司机再好,车没方向盘、没仪表盘、没刹车,照样开沟里。这一讲先把「车」拆成零件,再看一套真实项目是怎么把零件装起来的。
框架一:Harness 的五子系统
walkinglabs 把「模型权重之外的一切」拆成五个子系统。注意它们不是可选项,是缺一个就有一类失败模式:
| 子系统 | 它管什么 | 缺了会怎样 | 最小落地形态 |
|---|---|---|---|
| ① Instructions 指令 | 把任务规则、风格、边界写成结构化文档 | agent 每次从零猜规则,行为漂移 | AGENTS.md / CLAUDE.md,约 100 行 |
| ② Tools 工具 | 给 agent 执行动作的接口 | 该干的干不了,或权限过大闯祸 | 够用且最小权限的工具集 |
| ③ Environment 环境 | 依赖锁定、可复现的运行环境 | 「在我机器上能跑」,agent 那边全红 | 依赖锁文件 + 一键 init 脚本 |
| ④ State 状态 | 把进度、决策持久化到文件 | 长任务一断就失忆,无法续跑 | 进度文件 / 任务图 |
| ⑤ Feedback 反馈 | 显式的验证命令,让 agent 自验 | agent 自我感觉良好,错也不知道 | 可执行的 verify 命令 |
配套三条设计原则,决定这五个子系统要做成什么样:
- 地图优于手册:指令要简洁(约 100 行主文档),把细节拆成分册按需加载,而不是塞一本几千行的「操作大全」给模型自己翻。主文档是地图,告诉它「去哪查」。
- 约束而非微管:写可执行的规则(「verify 字段为空的任务不准开工」),而不是命令式的流水账(「第一步做 X,第二步做 Y」)。规则给边界,让模型在边界内自己决策;微管则是用 prompt 模拟 if-else,把 agent 退化成提线木偶。
- 消融量化:想知道哪个子系统真有用,就固定模型,逐个移除子系统,测各自的边际贡献。凭感觉「指令很重要吧」不算工程,跑一遍带 State 和不带 State 的成功率差多少,才是数据。
框架二:五子系统 ↔ 四层防御 对照映射
五子系统回答「Harness 有哪些组件」,但没回答「在一个真实项目里怎么把它们装起来、谁管谁」。本课程自己的项目(一个把学习材料重写成公开课讲义的静态站点)就跑了一套四层防御体系,正好是五子系统的落地骨架。两套框架对照看,会发现它们是「零件清单」和「装配图」的关系:
| 四层防御 | 干的事 | 对应吃下哪些子系统 | 本项目的具体载体 |
|---|---|---|---|
| L1 持久化层 | 业务语义/规则/进度从 LLM 记忆迁到确定性文件 | ① Instructions + ④ State | CLAUDE.md(规则)+ STATUS.md(进度,每次收尾必更新)+ 跨会话 Memory |
| L2 方法论层 | 开发纪律:单一事实源 + 线性切片 + fixture 先行 | ④ State + ⑤ Feedback | features.json(status 机)+ verify 硬闸门 + 三件套 PRD/SPEC/architecture |
| L3 自动化钩子层 | 确定性自动化,不靠模型自觉 | ③ Environment + ⑤ Feedback | .claude/settings.local.json 的 Stop hook(每轮自动把请求追加进进度文件)+ init 脚本 |
| L4 上下文隔离层 | 把吃 context 的脏活派给子 agent,独立 context 跑完只回结论 | ② Tools(子 agent 即一种工具) | .claude/agents/ 项目专属子 agent,主线只拿结论不读原文 |
读这张表的关键不在「谁对谁」一一对应,而在两点:
第一,五子系统是分类视角(按功能切),四层防御是分层视角(按可靠性切)。同一个 CLAUDE.md,在五子系统里是 Instructions,在四层防御里属于 L1——因为它的核心价值是「把规则从会幻觉的记忆里搬到不会变的文件里」。四层防御多出来的那条暗线是可靠性递增:L1 把易失的搬成持久的,L2 把口头约定变成带状态机的硬闸门,L3 把「记得做」变成「自动做」,L4 把「context 爆炸」变成「隔离不污染」。
第二,State 和 Feedback 是被两套框架同时反复强调的。五子系统里它们是④⑤,四层防御里 L1 管 State、L2 管 Feedback、L3 还在加固 Feedback。这不是巧合:长任务 agent 翻车,十有八九栽在「忘了进度」和「不知道自己错了」这两件事上。
case:本项目怎么靠 State + Feedback 不翻车
这门课的内容来源是一个上百页的个人知识库。如果让主 agent 直接整篇读笔记再重写讲义,会发生两件坏事:主 context 被原文撑爆,以及没有任何机制判断「这讲到底写对没」。四层防御是这么接住的:
- L4 隔离脏活:批量读知识库笔记这种吃 context 的活,派给项目专属子 agent,prompt 完全自包含,子 agent 在独立 context 里读完,只回提炼后的讲义草稿,不把原文贴回主线。
- L2 的 verify 硬闸门:每讲在
features.json里都有verify字段,规则是「字段为空 = 不准开工」。草稿写完只能标in_progress;只有 build 产出正确 HTML、关键标题出现、grep无 emoji、导航可点达,才允许改passing。Feedback 不是「我觉得行了」,是一条能跑的检查命令。 - L1 的 STATUS 续跑:每次开工先读
STATUS.md(一句话状态 + 下次入口 + 踩坑),收尾必更新。哪怕 session 中断,下一次也能从断点接上。 - L3 的 Stop hook:每轮对话结束,hook 自动把用户请求追加进进度文件的「增量流水」区,不依赖 agent 自觉记录。
注意一个值得抄的反直觉点(来自自我改进型 harness 的实践):给持久化文件加硬字符上限是 feature 不是 bug。比如把用户画像文件压在约 500 token、记忆主文件约 800 token,强制只留相关信息。否则文件越堆越多,未来的 session 反而被更重的 context 拖累,越跑越笨。State 要的是「精准的少」,不是「全量的多」。
可操作做法:给你自己的项目装一套最小 Harness
不必一上来就上四层,按可靠性顺序补:
- 先写 Instructions(L1 一半):建一个约 100 行的主指令文档。原则是地图优于手册。超过 150 行就该往分册拆。
- 补 State(④ / L1 另一半):建一个
STATUS.md,强制「每次收尾更新」。长任务再加一个带状态机的任务清单文件。 - 补 Feedback(⑤ / L2):给每个任务写一条能跑的 verify 命令。立铁律——verify 为空不准开工。这一条比任何 prompt 调优都更能止损。
- 该自动的自动化(L3):把「每次都要记得做」的事做成 hook 或脚本,别指望模型每轮都自觉。
- context 要爆了再上隔离(L4):当某类活会大量吃 context,派自包含的子 agent 去独立 context 跑,只回结论。
- 想知道值不值,就消融:固定模型,把某一层关掉跑一遍,看成功率掉多少。
收口
模型决定能力的上限,Harness 决定你能拿到这个上限的多少。五子系统告诉你车有哪些零件,四层防御告诉你怎么把零件按可靠性装成一辆能跑长途的车——而最先决定生死的,永远是那两个最朴素的零件:它记不记得自己干到哪(State),它知不知道自己干对没(Feedback)。