Harness Engineering:当人类不再写代码,软件工程反而更"工程"了

Harness Engineering:当人类不再写代码,软件工程反而更"工程"了

导语

2026 年 2 月,OpenAI 发了一篇博客,标题是 Harness engineering: leveraging Codex in an agent-first world

文章讲了一件听起来挺疯狂的事:他们用 Codex Agent 从零构建了一个软件产品,五个月交付上线,代码量约一百万行——没有一行是人写的。

不是"大部分由 AI 生成",不是"人写框架 AI 填细节",而是从第一个 git commit 开始,应用逻辑、测试、CI 配置、API 文档、内部工具、可观测性堆栈——全部由 Codex 自主产出。人类工程师全程只做一件事:设计环境,然后看着 Agent 干活。

这篇文章在技术圈引发了剧烈讨论。有人说这是"软件工程的终结",有人说这是"新一轮炒作"。

我觉得都不对。这是软件工程的一次范式迁移——工程师不是消失了,而是从"写代码的人"变成了"设计笼子的人"。

OpenAI 管这套方法论叫 Harness Engineering。Harness 这个词很妙——它既有"驾驭"的意思(驾驭一匹烈马),也有"挽具"的意思(套在马身上的缰绳和鞍具)。一个词同时说清了两件事:Agent 是干活的马,工程师是造缰绳的人。

这篇文章拆解这套方法论的核心思路、技术细节和工程哲学。

原文:https://openai.com/index/harness-engineering/


一、先看数据:3 个人,5 个月,100 万行代码

在聊方法论之前,先感受一下这个实验的规模:

指标 数据
开发周期 5 个月
起始团队 3 名工程师
后期团队 7 名工程师
代码规模 ~100 万行
人工编写的代码 0 行
处理的 PR 数量 ~1,500 个
人均日处理 PR 3.5 个
效率提升 相比手写代码快 ~10 倍
产品状态 已上线,有内部 DAU 和外部 Alpha 测试者

3 个人,每天处理 3.5 个 PR,5 个月干了 1500 个 PR。 如果是传统开发模式,这至少需要 20-30 人的团队。

但更值得注意的是后面发生的事:团队从 3 人扩展到 7 人后,吞吐量不但没有因为沟通成本增加而下降,反而继续增长。这违反了布鲁克斯法则("向进度落后的软件项目增加人手,只会使其更加落后"),说明这套工作方式的扩展性和传统团队完全不同。

为什么?因为人和人之间几乎没有代码层面的依赖了。每个工程师面对的不是"我在写的代码和你在写的代码会不会冲突",而是"我设计的环境和你设计的环境是否兼容"。后者的耦合度天然更低。


二、核心理念:人类掌舵,Agent 划桨

Harness Engineering 的核心观点可以浓缩为一句话:

工程师的工作不再是编写代码,而是设计环境、明确意图、构建反馈回路。

这三个词值得展开说。

2.1 设计环境

传统软件工程中,"环境"是指开发环境——IDE、编译器、依赖管理。在 Harness Engineering 中,"环境"的含义被极大扩展:你的整个代码仓库、架构约束、Linter 规则、文档结构、CI 管线,都是 Agent 的运行环境。

Agent 不是在"你的代码库里帮你写代码",而是"在你设计的环境里自主完成任务"。环境设计得好,Agent 写出的代码自然规范;环境设计得烂,Agent 就会四处漂移。

这有点像城市规划:你不需要亲自盖每一栋楼,但你需要划定道路、规定容积率、制定建筑规范。楼是开发商盖的,但城市的样子是规划师决定的。

2.2 明确意图

传统开发中,PM 写 PRD,工程师理解需求后写代码。在 Harness Engineering 中,工程师做的事更接近 PM:把模糊的大目标拆解成 Agent 能执行的小任务序列。

OpenAI 团队称之为"深度优先工作法":

  1. 把一个大功能拆成多个独立模块
  2. 每个模块进一步拆成设计 → 编码 → 测试 → 评审
  3. 每一步都明确输入、输出和验收标准
  4. 然后提示 Agent 逐个构建

注意:这不是"写更好的 Prompt"。这是把软件架构的思维方式从"我怎么实现这个功能"迁移到"我怎么描述这个功能,使得 Agent 能独立实现它"。两者的抽象层次完全不同。

2.3 构建反馈回路

这是最关键的一环。Agent 会犯错,会漂移,会产生低质量代码。Harness Engineering 的核心信仰不是"Agent 不会出错",而是"出错了能快速被发现和纠正"。

当进展受阻时,OpenAI 团队不再追问"怎么让 Agent 更努力",而是追问一个完全不同的问题:

"究竟需要什么样的能力(工具、抽象层),以及如何让这个能力对 Agent 清晰可读?"

这个思维转变至关重要——你不是在调教 Agent,你是在升级 Agent 的运行时环境。


三、五大工程实践:让 Agent "看得见、跑得稳、不走偏"

方法论好理解,但魔鬼在细节里。OpenAI 团队在文中详细披露了五个核心工程实践,每一个都值得拆开说。

3.1 让应用对 Agent 可读(Application Legibility)

问题:Agent 能写代码,但它"看不见"自己写的代码跑起来是什么样。你让它写一个 UI 组件,它能把 JSX 写对,但它不知道渲染出来的按钮是不是歪了、颜色是不是错了、交互是不是卡了。

传统流程中,这靠人工 QA——工程师亲自打开浏览器看一眼。但在一天要处理 3.5 个 PR 的节奏下,人工 QA 直接成为瓶颈。

解决方案:让 Agent 自己"看见"应用。

OpenAI 团队做了几件事:

  • Git Worktree 集成:每次代码变更,Codex 可以在独立的 worktree 中启动一个完整的应用实例。不同 PR 之间互不干扰。
  • 接入 Chrome DevTools Protocol(CDP):让 Codex 能操作浏览器——截图、读取 DOM 快照、模拟点击和导航。这意味着 Agent 可以自己复现 bug、验证修复、录制演示视频
  • 本地可观测性堆栈:部署了完整的日志和指标系统,Agent 可以用 LogQL 查日志、用 PromQL 查指标。出了问题,Agent 自己看日志排查。

这三个能力叠加在一起,实现了一个关键跃迁:Agent 不再是"盲写代码",而是"写代码 → 跑起来看效果 → 自己改 → 再看"的闭环。

一个有意思的细节:Codex 经常在单个任务上持续工作超过 6 小时。通常发生在人类睡觉的时候——工程师晚上提交一批任务,早上起来收 PR。这大概是"异步协作"的终极形态了。

3.2 代码仓库即记录系统(Repo as Record)

问题:Agent 没有记忆。每次启动一个新任务,它对项目的理解从零开始。你得告诉它"这个项目用什么框架、什么约定、什么架构",否则它写出来的代码风格五花八门。

最直觉的解决方案是写一个超长的 AGENTS.md,把所有规则、约定、架构决策全塞进去。

OpenAI 明确否决了这种做法。

原因有三:

  1. 挤占上下文:一个几千行的指令文件会吃掉大量上下文窗口,留给"正事"的空间就少了
  2. 容易腐烂:代码在变,但指令文件没人维护,很快就过时
  3. 难以核实:你怎么验证 Agent 真的按照文件里的规则做了?

他们的替代方案是"地图模式"

AGENTS.md(约 100 行)
├── 项目概述:一句话说清楚这是什么
├── 架构入口:指向 docs/architecture/
├── 设计文档:指向 docs/design/
├── 编码规范:指向 docs/conventions/
├── 执行计划:指向 docs/plans/
└── 参考资料:指向 docs/reference/

AGENTS.md 只是一张"目录"——大约 100 行——告诉 Agent "你要找什么信息,去哪个目录看"。

具体的知识分散在结构化的 docs/ 目录中:

文档类型 内容 特点
设计文档 每个功能的设计方案 包含验证状态(Draft/Approved/Implemented)
架构文档 系统整体架构和模块边界 稳定,很少变化
执行计划 当前迭代的任务清单 频繁更新
产品规格 功能需求和验收标准 与 PM 同步
参考文档 API 约定、错误码、数据模型 自动生成

这叫"渐进式披露"(Progressive Disclosure):Agent 从稳定的入口点开始,按需获取信息,而不是一开始就被海量指令淹没。

更精妙的是维护机制:

  • Linter 和 CI 作业会自动验证文档是否过时
  • 定期运行"doc-gardening"Agent——专门负责扫描并清理过时文档

对,你没看错:用 Agent 来维护给 Agent 看的文档。 这是一个自指的系统。

3.3 用架构约束规范 Agent 的"品味"(Codifying Architecture and Taste)

问题:100 万行代码,由 Agent 在 5 个月内写成。如果不加约束,代码风格会极度混乱——同一个功能,Agent 可能用三种不同的方式实现。

传统团队靠 Code Review 统一风格。但在 Agent 每天产出 3.5 个 PR 的节奏下,你不可能逐行审查。

解决方案:不靠审查,靠约束。让 Agent 只能在规定的"赛道"里跑。

严格的分层架构

每个业务域被强制分为六层,依赖关系严格单向:

Types → Config → Repo → Service → Runtime → UI
  • Types 层:纯类型定义,不依赖任何其他层
  • Config 层:只依赖 Types
  • Repo 层:数据访问,只依赖 Types 和 Config
  • Service 层:业务逻辑,只依赖 Types、Config、Repo
  • Runtime 层:运行时编排,可以依赖上面所有层
  • UI 层:只依赖 Runtime 暴露的接口

依赖方向由 CI 自动验证。如果 Agent 写了一段 UI 代码直接调用了 Repo 层,CI 直接报错,PR 过不了。

这种严格程度在人类团队中很难推行——总有人觉得"这次绕一下没关系"。但 Agent 不会抱怨,不会走捷径,不会"下次再改"。只要 CI 不过,它就改。

Providers 模式

横切关注点(认证、遥测、日志、错误处理)不允许在代码中随意引入。只能通过一个统一的 Provider 接口注入:

// ✅ 正确:通过 Provider 获取
const auth = useProvider('auth');

// ❌ 错误:直接 import
import { getSession } from '../auth/session';

这确保了每个横切关注点都有单一入口,Agent 不会在不同模块里搞出五种不同的认证方式。

自定义 Linter

OpenAI 让 Codex 自己生成了一套自定义 Linter,强制执行:

  • 结构化日志(不允许 console.log
  • 命名约定(文件名、变量名、函数名)
  • 文件大小限制(单文件不能超过一定行数)
  • Import 规则(不允许跨层依赖)

最妙的一点:每条 Linter 规则的错误信息中,直接包含修复指令。

ERROR: File exceeds 300 lines limit.
FIX: Split into smaller modules. Move helper functions to utils/.
     See docs/conventions/file-size.md for guidelines.

这样 Agent 看到报错后,不需要额外的上下文就知道怎么改。错误信息本身就是 Prompt。

技术选型偏好

团队在选择依赖库时,有一条特殊的原则:倾向于选择 API 稳定、可组合、且在训练数据中表现良好的库。

这意味着:

  • 选 Express 而不是最新的某个小众框架
  • 选 PostgreSQL 而不是刚出的 NewSQL
  • 选社区庞大、Stack Overflow 问答丰富的工具

原因很简单:Agent 的能力和它在训练时见过的代码高度相关。用一个训练集中有大量示例的库,Agent 更不容易犯错。

3.4 高吞吐量改变合并理念(Throughput Changes Merge Philosophy)

问题:当 Agent 的产出速度远超人类的审查能力时,传统的合并流程就成了瓶颈。

传统模式:写代码 → 提 PR → 等 Review → 改 → 再等 → 合并。整个 PR 生命周期可能持续数天。

但在 Agent 一天出 3.5 个 PR 的节奏下,如果每个 PR 都要两三天才能合,积压会呈指数增长。

OpenAI 的应对方式是:降低合并门槛,接受更高的纠错频率。

核心逻辑:

在 Agent 产出远超人类注意力的系统中,等待的成本高于纠错的成本。

具体做法:

  • 缩短 PR 生命周期:减少不必要的阻塞门。如果自动化测试全过,CI 全绿,就可以合。
  • 测试偶发失败不阻塞:偶尔的 flaky test 通过重跑解决,而不是无限期阻碍进展。
  • 快速回滚优于严格审查:出了问题,让 Agent 再开一个 PR 修复,而不是在合并前花大量时间预防所有可能的问题。

这和 Google 的"Trunk-Based Development"有相似之处,但更极端——因为代码的"作者"本身就是可以随时叫回来修 bug 的 Agent,修复的边际成本几乎为零。

3.5 对抗熵增:垃圾收集(Entropy and Garbage Collection)

问题:完全自主的 Agent 会引入"漂移"——随着时间推移,代码库会逐渐积累不一致的风格、冗余的工具函数、过时的注释、重复的实现。

这不是 bug,这是熵增。就像一间没人打扫的房子,不是某个东西坏了,而是整体越来越乱。

解决方案:建立持续的"清理循环"。

编码"黄金原则"

把主观的代码品味转化为可机械执行的规则。比如:

主观规则 机械化翻译
"代码要简洁" 单函数不超过 30 行
"不要重复造轮子" 偏好使用 shared/utils/ 中的现有工具
"命名要有意义" 函数名必须以动词开头,变量名必须是名词短语
"错误处理要规范" 所有错误必须通过 ErrorProvider 上报

主观规则无法被 CI 验证,机械规则可以。 Harness Engineering 的一个核心工作就是:持续把前者翻译成后者。

后台清理 Agent

团队定期运行专门的 Codex 任务:

  1. 扫描代码库中偏离约定的地方
  2. 生成重构 PR
  3. 自动运行测试验证重构是否安全
  4. 提交 Review

这些"清理 Agent"就像代码库的"保洁阿姨"——不写新功能,专门收拾乱摊子。

OpenAI 在文中用了一个精妙的类比:

技术债务像高息贷款,应该小额常还,而不是累积后痛苦偿还。

传统团队的做法是"先欠着,等有空了搞一次大重构"。Harness Engineering 的做法是"每天自动还一点,永远不让债务累积到需要大重构的程度"。


四、端到端自主流程:Agent 从"工具"变成"同事"

把上面五个实践拼在一起,OpenAI 实现了一个完整的端到端自主功能开发流程:

1. 验证代码库当前状态
       ↓
2. 复现 Bug 并录制视频
       ↓
3. 实施修复
       ↓
4. 启动应用,自行验证
       ↓
5. 录制演示视频
       ↓
6. 打开 PR,回应评审反馈
       ↓
7. 检测并修复构建故障
       ↓
8. 仅在必要时交由人工处理
       ↓
9. 合并更改

注意第 8 步:"仅在必要时交由人工处理"。这不是"人类审查每个 PR",而是"只有 Agent 搞不定的才找人"。人类从审查者变成了兜底者

这个流程中,Agent 不仅写代码,还:

  • 自己启动应用看效果
  • 自己录视频证明功能正常
  • 自己回应代码评审意见
  • 自己检测并修复 CI 故障

如果不看署名,你很难区分这个工作流和一个普通工程师的日常有什么不同。


五、Harness Engineering 的哲学内核

聊完了技术细节,我想把这套方法论的哲学内核提炼出来。

5.1 控制论,而不是管理学

传统软件工程本质上是一个管理学问题:怎么组织人、分配任务、协调进度、管理复杂度。

Harness Engineering 本质上是一个控制论问题:怎么设计一个系统,使得 Agent 在其中运行时,输出收敛于期望目标,而不是发散到混乱。

控制论的核心要素——传感器(可观测性)、执行器(代码生成)、反馈回路(CI + Linter + 文档验证)——在 Harness Engineering 中都有直接对应物。

控制论概念 Harness Engineering 对应
传感器 Chrome DevTools、LogQL、PromQL、DOM 快照
执行器 Codex Agent(代码生成和修改)
控制器 工程师(设计环境和约束)
反馈信号 CI 结果、Linter 报错、测试覆盖率、截图对比
设定点 架构约束、编码规范、验收标准
干扰 熵增、漂移、偶发错误

5.2 "不可能三角"的突破

传统软件工程有一个"不可能三角":速度、质量、成本——三选二

Harness Engineering 声称可以同时改善三者。这听起来像营销话术,但逻辑上有一定道理:

  • 速度:Agent 7×24 工作,单任务可持续 6+ 小时,不需要休息、开会、对齐
  • 质量:严格的架构约束 + 自动化 Linter + 持续的清理 Agent,从环境层面保障质量
  • 成本:3 人团队干了 30 人的活,算上 Codex 的 API 费用,总成本仍然大幅下降

当然,这有一个前提:你得先投入大量时间设计环境。这是一次性的固定成本,但一旦环境搭建好,边际成本趋近于零。

5.3 "纪律"的新含义

OpenAI 在文章结尾写了一句耐人寻味的话:

软件工程需要的纪律不再体现在代码本身,而是体现在支撑结构、工具、抽象和反馈回路上。

以前,"有纪律的工程师"意味着:代码写得干净、测试覆盖充分、文档及时更新、PR 描述清晰。

现在,"有纪律的工程师"意味着:架构约束设计得严密、Linter 规则覆盖得全面、文档结构维护得新鲜、反馈回路闭合得快速。

代码质量从"个人修养"变成了"系统属性"。就像工厂的产品质量不取决于某个工人手艺多好,而取决于生产线设计得多精密。


六、这对普通开发者意味着什么?

如果你不在 OpenAI 工作,没有 Codex 的内部版本,Harness Engineering 对你有什么启发?

6.1 立刻可以做的事

重新审视你的 AGENTS.md(或 .cursorrules.clinerules

大多数人的做法是把所有规则塞进一个大文件。OpenAI 的经验告诉我们:这不好使。改成"地图模式"——入口文件只做目录,具体知识分散在结构化的文档中。

给 Linter 规则加上修复指令

如果你的团队已经在用 ESLint / Pylint / golangci-lint,试着在每条自定义规则的报错信息里写清楚"应该怎么改"。这不仅帮 Agent,也帮新入职的同事。

开始用分层架构约束 Agent

不需要像 OpenAI 那样搞六层。哪怕只是简单地把"数据层"和"业务层"的边界用 import 规则锁死,就能显著减少 Agent 的代码漂移。

6.2 需要转变的思维

从"怎么写"到"怎么描述"

当你遇到一个需求,不要第一时间想"我该怎么实现",而是想"我该怎么描述这个需求,使得 Agent 能独立实现"。这两种思维方式训练的是完全不同的能力。

从"预防错误"到"快速纠错"

不要试图在 Agent 写代码之前预防所有可能的错误。那是不可能的。把精力放在"出错后能多快发现和修复"上。好的反馈回路比好的 Prompt 重要一百倍。

从"审查代码"到"审查环境"

以后 Code Review 的重点不再是"这行代码写得对不对",而是"我们的架构约束是不是有漏洞?Linter 是不是漏了某种 pattern?文档是不是过时了?"。审查环境比审查代码更高杠杆。


七、挑战与局限

Harness Engineering 很诱人,但也有明显的局限性:

1. 环境设计本身就是一项极高的技能

OpenAI 团队的工程师不是普通工程师——他们对软件架构、系统设计、可观测性有极深的理解。"设计一个好环境"比"写好代码"的门槛可能更高,而不是更低。

2. 对 Agent 能力有强依赖

Codex 是 OpenAI 的旗舰产品,普通开发者用的开源模型或其他 Agent 能达到同等水平吗?如果 Agent 的能力不够,再好的环境设计也无法弥补。

3. 适用场景可能有限

文章中的产品是一个 Web 应用——这恰好是 LLM 训练数据中最丰富的领域。如果你做的是嵌入式系统、高频交易、或者冷门语言的项目,Agent 的表现可能大打折扣。

4. "零人工代码"可能有误导性

虽然没有人工编写的"代码",但工程师编写了大量的"环境代码"——架构文档、Linter 规则、CI 配置、约束定义。这些东西本质上也是"代码",只是换了一种形态。


八、结语

Harness Engineering 不是"AI 取代程序员"的故事,也不是"未来的程序员只需要写 Prompt"的童话。

它是一个更深刻的命题:当代码的生产可以被自动化时,软件工程的"工程"到底是什么?

答案似乎是:工程从来不是关于"写"的,而是关于"控制"的。 控制质量、控制复杂度、控制变化的方向。以前我们通过亲自编写代码来实现控制,现在我们通过设计环境来实现控制。

手段变了,本质没变。

这对工程师来说,既是挑战,也是机遇。那些真正理解"工程"而非仅仅擅长"编码"的人,在 Agent 时代的价值不是下降了,而是上升了。

因为在一个 Agent 都会写代码的世界里,"知道该写什么"和"知道怎么确保写对",才是真正稀缺的能力。


参考资料

posted @ 2026-03-19 18:45  warm3snow  阅读(42)  评论(0)    收藏  举报