为什么强大的模型仍然会失败?learn-harness-engineering 第一课:模型能力不等于可靠执行
为什么强大的模型仍然会失败?learn-harness-engineering 第一课:模型能力不等于可靠执行
导读:本篇文章以 learn-harness-engineering 仓库中 docs/ar/lectures/lecture-01-why-capable-agents-still-fail/index.md(第一讲:Strong Models Don't Mean Reliable Execution)为核心,讲解一个反直觉的事实——当 AI 编程智能体搞砸任务时,问题往往不在模型本身,而在于模型之外的一切。读完本文,你将掌握五层防御的诊断框架、可落地的
AGENTS.md与 Definition of Done 写法,并能在仓库配套的 Project 01 对照实验中亲手验证"同一个模型在弱 Harness 与完整 Harness 下产出天差地别"这一结论。
能力不等于可靠:Capability Gap 从何而来
截至 2025 年底,最强编程智能体在 SWE-bench Verified 上的通过率大约只有 50%–60%。这个数字初看不错,但请注意其构成:那是经过精挑细选的任务、清晰的 Issue 描述、现成的测试用例。一旦把日常需求交给智能体——模糊的规格、没有存量测试、散落在代码库各处的隐式业务规则——通过率只会进一步下降。原文档给出的场景非常典型:智能体运行 20 分钟然后自信地告诉你"全部完成",你打开代码却发现它加了功能却弄坏了测试、修了一个 bug 却引入两个新 bug,甚至产出的根本不是你要的东西。
这就是 Capability Gap(能力落差):模型在基准测试上的表现与在真实任务中的表现之间存在巨大鸿沟。SWE-bench Verified 上 50%–60% 的通过率意味着,几乎一半的真实 Issue 没有被解决。
大多数人的第一反应是"模型不够好,换个更贵的"。但在掏钱包之前,请先认真考虑一个可能性:问题根本不在模型。
同一匹马,不同的命运:Anthropic 的对照实验
原文档引用了一个极具说服力的对照实验(Anthropic 官方博客《Effective Harnesses for Long-Running Agents》所描述的实验):
- 提示词相同:"构建一个 2D 复古游戏编辑器";
- 模型相同:Opus 4.5;
- 第一次运行:裸奔、无任何支持——20 分钟、9 美元,游戏核心功能根本无法工作;
- 第二次运行:完整的 Harness(规划器 + 生成器 + 评估器的三智能体架构)——6 小时、200 美元,游戏完全可玩。
他们没有换模型。Opus 4.5 还是那个 Opus 4.5,改变的只是"马的装备"(tack)。OpenAI 2025 年关于 Harness 工程的文章说得更直接:在 Harness 良好的仓库中,Codex 从"不可靠"直接跃迁到"可靠"——注意措辞,不是"好了一点",而是质变。
这里的 Harness 定义是:模型权重之外的全部工程基础设施——指令、工具、环境、状态管理、验证反馈。凡不是模型权重,就属于 Harness。
智能体到底在哪里"卡住":五种典型失败模式
原文档把具体失败模式归结为五类,每一种都对应一个可以在本仓库中找到证据的工程问题。
1. 需求模糊——智能体只能靠猜
"加一个搜索功能"这句话几乎等于什么都没说。搜索什么?全文检索还是结构化数据?结果要不要分页?要不要高亮?你没说清楚,智能体就只能猜。猜对了是运气,猜错了返工成本是当初说清楚的好几倍。
本仓库用两个配套文件把这种失败具象化:
- underspecified-task.md 给出了一个典型的欠定义任务示例:"构建一个带 AI 问答的知识库桌面应用",限制条件一栏全是"未指定":未指定任何约束、未给出运行命令、未提供目录结构指引、未定义数据模型、没有显式的完成标准。文档明确列出这类 prompt 的典型结果:智能体自行发明结构、应用能编译却无法稳定启动、界面先于可用的导入/查询路径出现、智能体经常在"看起来成功"之后就停下。
2. 隐式约定没有写下来——智能体无从遵守
团队全员都在用 SQLAlchemy 2.0 语法,但智能体默认写 1.x 代码;所有 API 端点必须走 OAuth 2.0 认证,但这条规则只存在于你的头脑和三个月前的一条 Slack 消息里。智能体不知道——不是不想遵守,而是字面上从未见过这条规则。
3. 环境不完整——智能体把精力花在修环境上
开发环境不完整、依赖缺失、工具版本错误。智能体把宝贵的上下文窗口烧在 pip install 报错和 Node 版本冲突上,而不是解决你交给它的实际任务。原文档的比喻很贴切:你请了一位熟练的木匠,却忘了给他锤子、钉子和一张平整的工作台——无论他多熟练,都无法开工。
4. 没有验证手段——智能体"感觉完成"就算完成
没有测试、没有 lint,或者验证命令从未传达给智能体。它写完代码、看一眼、觉得没问题、宣布完成。Anthropic 还观察到一种有趣的现象:当智能体感知到上下文即将耗尽时,会匆忙收尾、跳过验证步骤、选择简单方案而非最优方案——他们把这种现象称为 context anxiety(上下文焦虑),就像考试快结束时开始蒙答案一样。
本仓库的 failure-pattern-demo.ts 用一段可运行的 TypeScript 把上述"能力足够却仍然失败"的路径模拟成了四步:
- Incomplete Context(上下文不完整):只有项目结构和路由定义,缺少 auth 中间件、限流策略、测试标准;
- Locally Reasonable Changes(局部合理的改动):加上 auth 后局部看起来完整,但全局看仍缺限流;
- No Global Verification(缺乏全局验证):功能表面完整,但没有测试,回归风险与标准违背;
- Premature Completion(过早完成):智能体输出"Done. Added search endpoint.",任务被标记完成,实际上不完整。
你可以直接用 npx tsx docs/ar/lectures/lecture-01-why-capable-agents-still-fail/code/failure-pattern-demo.ts 运行它,终端会打印每一步的"可用上下文 / 缺失上下文 / 采取动作 / 局部结果 / 全局影响",最后汇总一张对比表,展示 5 项必要上下文里缺了几项——这个脚本本身就是对"每一步孤立看都很合理"这一核心失败模式的演示。
配套的 failure-signals-checklist.md 则是一份复盘清单,用于审查弱 Harness 运行的产物:智能体是否问过或猜错了应用的运行方式?是否创建了与目标产品不匹配的目录或抽象?是否在搭好可见 UI 骨架后停步而没有完整工作流?是否留下便于后续会话接手的备注?一个新会话能否在五分钟内理解之前发生了什么?
5. 跨会话状态丢失——每个新会话从零开始
上一个会话的所有发现全部丢失,每个新会话都要重新探索项目结构、重新理解代码组织。原文档给出的经验阈值是:没有持久状态的智能体,在超过 30 分钟的任务上失败率急剧上升。
核心术语一览
原文档在描述完这些场景后,给出了六个不再只是"行话"的核心概念:
| 术语 | 含义 |
|---|---|
| Capability Gap | 模型在基准测试与真实任务之间的表现落差;SWE-bench Verified 50%–60% 意味着近半真实 Issue 无法解决 |
| Harness | 模型之外的一切——指令、工具、环境、状态管理、验证反馈;不是模型权重,就是 Harness |
| Harness-Induced Failure | 模型能力足够,但执行环境存在结构性缺陷;Anthropic 的对照实验已证明这一点 |
| Verification Gap | 智能体对输出的自信与实际正确性之间的差距;"说完成了其实没完成"是最常见的失败模式 |
| Diagnostic Loop | 执行 → 观察失败 → 归因到特定 Harness 层 → 修复该层 → 重新执行;这是 Harness 工程的核心方法论 |
| Definition of Done | 一组可用命令验证的条件——测试通过、lint 干净、类型检查通过;没有显式的完成定义,智能体就会发明它自己的定义 |
失败时先修 Harness:五层防御与实操步骤
原文档的核心原则只有一条:当事情失败时,不要先换模型——先检查 Harness。 如果同一个模型在类似的、结构良好的任务上能成功,那就假定问题是 Harness 的问题,而不是模型的问题。就像汽车抛锚,不要立刻假设发动机坏了,先检查是不是没油了。
把每个失败归因到具体层
不要说"模型不行"。问自己:任务不清晰吗?上下文不充分吗?没有验证手段吗?把每个失败映射到五个防御层之一:
- 任务规格(task specification)——任务是否被明确定义;
- 上下文供给(context provision)——智能体是否拿到了足够的相关信息;
- 执行环境(execution environment)——环境是否完整、工具版本是否正确;
- 验证反馈(verification feedback)——是否有测试、lint、类型检查并已传达给智能体;
- 状态管理(state management)——跨会话的发现是否被持久化。
养成这个习惯后,你会发现在日志里"模型不够好"这句话出现得越来越少。
为每个任务写显式的 Definition of Done
不要说"加一个搜索功能",要像原文档给出的示例一样写清楚:
Completion criteria:
- New endpoint GET /api/search?q=xxx
- Supports pagination, default 20 items
- Results include highlighted snippets
- All new code passes pytest
- Type checking passes (mypy --strict)
这份清单必须全部可由命令验证——这正是"Definition of Done 是一组可用命令验证的条件"这一术语定义在实践中的落点。
创建 AGENTS.md
在仓库根目录放置一个 AGENTS.md 文件,告诉智能体项目的技术栈、架构约定和验证命令。原文档强调:这是 Harness 工程的第一步,也是投入产出比最高的一步——一个 AGENTS.md 文件可能比升级到更贵的模型更有效,这并非玩笑。
本仓库的 projects/project-01/solution/AGENTS.md 就是一个可以直接对照学习的范本,它包含四个关键区块:
- Startup Rules(启动规则):写任何代码之前,按顺序完成——完整阅读本文件、阅读
docs/ARCHITECTURE.md理解 Electron 分层结构、阅读docs/PRODUCT.md理解功能需求、运行bash init.sh验证构建干净、读取feature_list.json查看全部功能状态; - Electron Layer Boundaries(分层边界):严格定义 main / preload / renderer / services 四层各自的职责与禁止事项(例如 renderer 永不导入 Node 模块、preload 是主进程与渲染进程之间的唯一桥);
- Conventions(约定):TypeScript 严格模式、只使用命名导出、IPC 频道名统一定义在
src/shared/types.ts、渲染进程禁用同步 I/O; - Definition of Done:TypeScript 编译通过(
npm run check)、应用可启动且窗口可见(npm run dev)、功能在feature_list.json中标记为"pass"并附证据、遵守分层边界、正常运行无控制台错误。
配套的 feature_list.json 展示了"以功能清单作为进度的唯一事实源"的实践:每个功能有 id、name、description、status(pass / fail / not-started)、evidence(验收证据)与 testedAt 时间戳。例如 window-launch 的 evidence 写明了"npm run dev 以 1200x800 启动窗口,contextIsolation=true 且 nodeIntegration=false"——这就是可验证的完成标准。
建立诊断循环并度量改进
不要把失败当作"智能体又犯蠢了",而要当作 Harness 暴露缺陷的信号。每次失败:识别层 → 修复 → 确保不再以同样方式失败。几轮之后 Harness 变强、智能体表现趋于稳定。同时维护一个简单日志:每个任务成功还是失败、是哪个层导致的失败。几轮之后你就会看出哪个层是瓶颈,把精力集中在那里。
百万行实验:OpenAI 的 Codex 实践
原文档记录了 OpenAI 2025 年的一个大胆实验:人类绝不直接写代码,只由 Codex 写。从空的 Git 仓库开始,五个月后仓库里有了约一百万行代码——应用逻辑、基础设施、工具、文档、内部开发工具,全部由智能体生成。三名工程师共开合并约 1,500 个 PR,平均每人每天 3.5 个。
初始进展出奇地慢——不是因为 Codex 不行,而是环境不够完整:智能体缺少朝高层目标推进所需的工具、抽象和内部结构。三名工程师逐渐摸索出模式:把大目标拆成小积木(设计、编码、评审、测试),让智能体逐个拼装,再用这些积木去组合更复杂的任务。当某件事失败时,问题几乎从来不是"不够努力",而是"智能体还缺什么能力,以及如何让这个缺失的能力变得可理解、可执行"。
这个实验直接证明了本讲的核心论点:同一个模型,在裸环境与完整 Harness 环境中,产出的结果有本质差异。模型没变,环境变了。
更接地气的例子:Claude Sonnet 与一个中型 Python 项目
原文档还给出了一个更贴近日常的场景:一个团队用 Claude Sonnet 给一个中型 Python Web 应用(FastAPI + PostgreSQL + Redis,约 15,000 行代码)添加新的 API 端点。
- 第一轮:只给一句话——"add user preferences endpoints under
/api/v2/users"。结果:智能体把 40% 的上下文窗口烧在探索仓库结构上,产出的代码看似合理但不遵循项目的错误处理模式、使用了过时的 SQLAlchemy 语法,还宣布完成——实际上端点存在运行时错误,下一个会话不得不重做全部探索工作; - 第二轮:加入
AGENTS.md(描述项目架构与技术栈版本)、显式验证命令(pytest tests/api/v2/ && python -m mypy src/)和架构决策记录。同一个模型在三次独立运行中全部成功,上下文效率提升约 60%。
他们没有换模型。他们换了 Harness。
项目实战印证:Project 01 对照实验
这一讲在仓库中对应的实操项目是 projects/project-01-baseline-vs-minimal-harness/index.md(Project 01: Baseline vs Minimal Harness),其设计意图与本讲内容严丝合缝:对比弱 Harness(仅 prompt)与显式 Harness(规则文件 + 验证机制)对智能体任务完成率的影响。
starter/是起点:只有一个模糊的 task-prompt.md,全文只有一句话——"Build an Electron app that can show documents and answer questions.",没有AGENTS.md、没有feature_list.json。这就是"弱 Harness"版本;solution/是参考实现:同样的应用代码,但配齐了完整 Harness 文件(AGENTS.md、feature_list.json、init.sh、claude-progress.md、docs/ARCHITECTURE.md 与docs/PRODUCT.md)。
对照实验的进行方式(只读地运行与对比,无需修改仓库):
# 1. 先用 starter(弱 Harness)跑一次
cd starter
npm install
# 把 task-prompt.md 的内容作为 prompt 交给 Claude Code / Codex
# 要求智能体完成:窗口启动、文档列表、QA 面板、数据目录四项功能
# 本次运行不要给智能体 solution 的文件
# 2. 用 solution(显式 Harness)跑同一任务
cd ../solution
npm install
# 让智能体在碰代码之前先读 AGENTS.md、init.sh、feature_list.json、claude-progress.md
# 3. 对比两次结果
# - 任务是否完成?
# - 需要多少次重试?
# - 智能体是否过早宣布"完成"?
其中 init.sh 用 set -euo pipefail 严格地按顺序执行三步:npm install → npm run check(类型检查)→ npm run build(构建),任何一步失败立即中断——这正是"写代码之前先验证环境干净"的工程化落地,也对应原文档五层防御中的"执行环境"与"验证反馈"两层。
项目用一张功能对照表来度量完成度:窗口启动对应 src/main/main.ts 与 feature_list.json 的 window-launch、文档列表面板对应 src/renderer/components/DocumentList.tsx 与 document-list、问答面板对应 QuestionPanel.tsx 与 question-panel、数据目录对应 src/services/persistence-service.ts 与 data-directory。项目的明确定位是"对照实验而非普通填空作业",学习成果就是测量出 prompt-only 运行与"从显式仓库规则和验证产物出发"运行之间的可量化差异——把本讲的核心论点变成一次可重复的实验。
核心结论
- 模型能力与执行可靠性是两回事。即便是纯血统的赛马,也需要好的装备;
- 失败时先查 Harness,再查模型。换模型是最贵的选择,而且多数时候根本不是模型的问题;
- 每个失败都是一个信号:你的 Harness 有结构性缺陷,找到它、修复它;
- 系统地走完五层防御:任务未明确定义、上下文不足、环境配置错误、缺少验证、会话间状态丢失——十有八九问题出在这五层中的某一层;
- 一个
AGENTS.md文件可能比升级到更贵的模型更有效。
练习建议
-
对照实验:选一个你熟悉的代码库和一个非平凡的修改任务。先不带任何 Harness 支持运行智能体并记录失败;然后加入
AGENTS.md和显式验证命令,用同一个智能体再跑一次。对比两次结果,把每个失败归因到五层防御之一。 -
度量 Verification Gap:选 5 个编程任务。每个任务完成后记录智能体是否声称完成,再用独立测试验证真实正确性。计算"声称完成但实际未完成"的比例——这就是你的 Verification Gap。然后思考:什么验证命令能降低这个比例?
-
训练诊断循环:在项目里找一个智能体反复失败的任务。跑一次并记录失败,归因到五层之一,修复那一层,再跑一次。重复 3–5 轮,逐轮记录改进。
-
阅读配套代码:运行 failure-pattern-demo.ts 观察四步失败模式,对照 failure-signals-checklist.md 审查一次你最近的智能体运行产物,再结合 Project 01 完成一次完整的强弱 Harness 对比实验。