Skip to content

驾驭(Harness)工程,在“智能体优先”的世界中利用 Codex #100

@coderPerseus

Description

@coderPerseus

以下是 https://openai.com/index/harness-engineering/ 博客正文的完整中文精翻,绝对是一篇高质量并且改变软件工程游戏规则的文章

原文标题:驾驭(Harness)工程,在“智能体优先”的世界中利用 Codex

作者:Ryan Lopopolo,发布时间 :2026年2月11日

翻译官:🥥 椰子翻译浏览器插件

在过去的五个月里,我们的团队一直在进行一项实验:构建并发布一个内部软件产品的测试版,且0 行代码由人工编写

该产品目前拥有内部日常用户和外部 alpha 测试人员。它正常发布、部署,会出现故障,也能被修复。不同之处在于,每一行代码(包括应用逻辑、测试、CI 配置、文档、可观测性配置以及内部工具)均由 Codex 编写。据估算,我们完成这项工作的时间仅为人工编写所需时间的十分之一。

人类掌舵,智能体执行

我们特意选择了这一约束条件,以此迫使自己构建必要的体系,从而将工程速度提升数个数量级。我们只有几周时间来交付最终达到一百万行代码的项目。为了实现这一目标,我们需要理解当软件工程团队的主要工作从编写代码转变为设计环境、明确意图以及构建反馈循环(让 Codex 智能体能够可靠地工作)时,一切会发生怎样的变化。

这篇文章讲述了我们利用智能体团队构建全新产品过程中学到的经验(包括哪些地方出了问题,哪些产生了复利效应),以及如何最大化利用我们唯一的稀缺资源:人类的时间和注意力。

我们从一个空的 git 仓库开始

第一次提交进入这个空仓库是在 2025 年 8 月下旬。

初始脚手架(包括仓库结构、CI 配置、格式化规则、包管理器设置和应用程序框架)是由 Codex CLI 使用 GPT-5 生成的,并由少量现有模板作为引导。甚至最初指导智能体如何在仓库中工作的 AGENTS.md 文件本身也是由 Codex 编写的。

系统中没有任何预先存在的人工代码作为锚点。从一开始,这个仓库就是由智能体塑造的。

五个月后,该仓库包含大约一百万行代码,涵盖了应用逻辑、基础设施、工具、文档和内部开发实用程序。在此期间,仅由三名工程师组成的小团队驱动 Codex 开启并合并了大约 1500 个拉取请求(Pull Request)。这意味着每位工程师每天平均处理 3.5 个 PR,令人惊讶的是,随着团队扩大到现在的七名工程师,这一吞吐量还在增加。重要的是,这并非为了产出而产出:该产品已被数百名内部用户使用,其中包括每天都在使用的深度用户。

在整个开发过程中,人类从未直接贡献过任何代码。这成为了团队的核心哲学:杜绝人工编写代码

重新定义工程师的角色

缺乏人工手动编码引入了一种不同类型的工程工作,重点在于系统、脚手架和杠杆作用。

早期的进展比我们预期的要慢,原因并非 Codex 能力不足,在于环境定义不明确。智能体缺乏朝着高层目标推进所需的工具、抽象和内部结构。我们工程团队的首要工作变成了赋能智能体去做有用的工作

在实践中,这意味着采用深度优先的工作方式:将较大的目标分解为更小的构建块(设计、编码、审查、测试等),提示智能体构建这些模块,并利用它们来解锁更复杂的任务。当出现故障时,修复方法几乎从不是“再试一次”。由于取得进展的唯一途径是让 Codex 完成工作,人类工程师总是会介入任务并询问:“缺少什么能力,我们如何让这种能力对智能体既清晰可见又可强制执行?”

人类几乎完全通过**提示词(prompts)**与系统交互:工程师描述任务,运行智能体,并允许它开启一个拉取请求。为了推动 PR 完成,我们指示 Codex 在本地审查自己的更改,在本地和云端请求额外的特定智能体进行审查,响应任何人类或智能体给出的反馈,并循环迭代直到所有智能体审查员都满意(实际上这就是一个 Ralph Wiggum 循环)。Codex 直接使用我们的标准开发工具(gh、本地脚本和仓库内嵌技能)来收集上下文,无需人类在 CLI 中进行复制粘贴。

人类可能会审查拉取请求,但这并非强制要求。随着时间的推移,我们将几乎所有的审查工作都推向了智能体对智能体的模式。

提高应用的可读性

随着代码吞吐量的增加,瓶颈变成了人类的 QA(质量保证)能力。由于固定的约束条件是人类的时间和注意力,我们致力于通过让应用 UI、日志和应用指标等内容对 Codex 直接可见,来为智能体增加更多能力。

例如,我们让应用可以基于每个 git 工作树(worktree)启动,这样 Codex 就可以为每个更改启动并驱动一个实例。我们还将 Chrome DevTools 协议接入智能体运行时,并创建了处理 DOM 快照、截图和导航的技能。这使得 Codex 能够重现错误、验证修复并直接推断 UI 行为。

Gemini_Generated_Image_ofn3ntofn3ntofn3.webp
(图表说明:Codex 使用 Chrome DevTools MCP 驱动应用以验证其工作。)

我们对可观测性工具也做了同样的处理。日志、指标和追踪信息通过一个本地可观测性堆栈暴露给 Codex,该堆栈对于任何给定的工作树都是临时的。Codex 在该应用的完全隔离版本上工作(包括其日志和指标),任务完成后这些内容会被销毁。智能体可以使用 LogQL 查询日志,使用 PromQL 查询指标。有了这些上下文,像“确保服务启动在 800ms 内完成”或“在这四个关键用户旅程中没有跨度(span)超过两秒”这样的提示词就变得可执行了。

image.png
(图表说明:在本地开发中给予 Codex 完整的可观测性堆栈。)

我们经常看到单个 Codex 运行在单个任务上持续工作长达六个小时以上(通常是在人类睡觉的时候)。

我们将仓库知识作为记录系统

上下文管理是让智能体在处理大型复杂任务时保持高效的最大挑战之一。我们学到的最早教训之一很简单:给 Codex 一张地图,而不是一本 1000 页的说明书。

我们尝试过“一个巨大的 AGENTS.md”的方法。它以可预见的方式失败了:

  1. 上下文是稀缺资源。 一个巨大的指令文件会挤占任务、代码和相关文档的空间,导致智能体要么错过关键约束,要么开始针对错误的约束进行优化。

  2. 过多的指导变成了无指导。 当一切都“重要”时,就没什么东西是重要的了。智能体会陷入局部模式匹配,无法进行有意识的导航。

  3. 它会立即腐烂。 庞大的手册会变成过时规则的坟墓。智能体无法分辨什么是真实的,人类停止维护它,这个文件悄然变成了一个诱人的麻烦。

  4. 难以验证。 单个大文件不适合机械检查(覆盖率、新鲜度、所有权、交叉链接),因此偏离是不可避免的。

所以,我们不把 AGENTS.md 当作百科全书,将其视为目录。

仓库的知识库存在于一个结构化的 docs/ 目录中,被视为记录系统(System of Record)。一个简短的 AGENTS.md(大约 100 行)被注入到上下文中,主要作为一个地图,包含了指向其他地方更深层真理来源的指针。

1  AGENTS.md
2  ARCHITECTURE.md
3  docs/
4  ├── design-docs/
5  │ ├── index.md
6  │ ├── core-beliefs.md
7  │ └── ...
8  ├── exec-plans/
9  │ ├── active/
10  │ ├── completed/
11  │ └── tech-debt-tracker.md
12  ├── generated/
13  │ └── db-schema.md
14  ├── product-specs/
15  │ ├── index.md
16  │ ├── new-user-onboarding.md
17  │ └── ...
18  ├── references/
19  │ ├── design-system-reference-llms.txt
20  │ ├── nixpacks-llms.txt
21  │ ├── uv-llms.txt
22  │ └── ...
23  ├── DESIGN.md
24  ├── FRONTEND.md
25  ├── PLANS.md
26  ├── PRODUCT_SENSE.md
27  ├── QUALITY_SCORE.md
28  ├── RELIABILITY.md
29  └── SECURITY.md

(仓库内知识存储布局。)

设计文档已被分类和索引,包括验证状态和定义智能体优先操作原则的一套核心信念。架构文档提供了领域和包分层的顶层地图。质量文档对每个产品领域和架构层进行评分,跟踪随时间推移的差距。

计划(plan)被视为一等公民(first-class artifacts)。临时的轻量级计划用于小的更改,而复杂的工作则被记录在执行计划中,包含进度和决策日志,并提交到仓库中。活跃计划、已完成计划和已知的技术债务都被版本化并放在一起,允许智能体在不依赖外部上下文的情况下操作。

这实现了渐进式披露:智能体从一个小的、稳定的入口点开始,被教导下一步去哪里看,避免一上来就不堪重负。

我们通过机械方式强制执行这一点。专用的 linter(代码扫描工具)和 CI 任务验证知识库是否是最新的、交叉链接是否正确以及结构是否合理。一个定期运行的“文档园丁”智能体会扫描那些不能反映真实代码行为的陈旧或过时文档,并开启修复性的拉取请求。

智能体可读性是目标

随着代码库的演变,Codex 的设计决策框架也需要随之演变。

因为仓库完全由智能体生成,它首先针对 Codex 的可读性进行了优化。就像团队致力于提高代码的可导航性以方便新入职的工程师一样,我们人类工程师的目标是让智能体能够直接从仓库本身推断出完整的业务领域。

从智能体的角度来看,任何它在运行时无法在上下文中访问的东西实际上都不存在。存在于 Google Docs、聊天线程或人们头脑中的知识对系统来说是不可访问的。仓库本地的、版本化的工件(例如代码、markdown、schema、可执行计划)是它能看到的全部。

image.png
(图表说明:智能体知识的局限性:Codex 看不到的就不存在。)

我们了解到,随着时间的推移,我们需要将越来越多的上下文推入仓库。如果在 Slack 上的讨论对齐了团队关于某种架构模式的看法,如果这些信息对智能体不可发现,那么它就像对三个月后加入的新员工一样是未知的,因此是不可读的。

给予 Codex 更多上下文意味着组织和暴露正确的信息,以便智能体可以对其进行推理,这优于用临时指令压倒它。就像你会就产品原则、工程规范和团队文化(包括表情符号偏好)对新队友进行入职培训一样,向智能体提供这些信息会产生更一致的输出。

这种框架澄清了许多权衡。我们倾向于那些可以在仓库中被完全内化和推理的依赖项和抽象。通常被描述为“无聊”的技术往往更容易被智能体建模,因为它们具有可组合性、API 稳定性以及在训练集中的广泛存在。在某些情况下,让智能体重新实现部分功能比绕过公共库中不透明的上游行为成本更低。例如,我们没有引入通用的 p-limit 风格的包,而是实现了我们自己的并发映射助手:它与我们的 OpenTelemetry 仪表紧密集成,拥有 100% 的测试覆盖率,并且行为完全符合我们运行时的预期。

将更多的系统部分转化为智能体可以直接检查、验证和修改的形式,这增加了杠杆作用(这不仅针对 Codex,对于其他正在代码库上工作的智能体,如 Aardvark,同样如此)。

强制执行架构与品味

仅靠文档无法保持完全由智能体生成的代码库的连贯性。通过强制执行不变量,不微观管理实现细节,我们让智能体在不破坏基础的情况下快速发布。例如,我们要求 Codex 在边界处解析数据形状,但并不规定具体的发生方式(模型似乎喜欢 Zod,但我们没有指定那个特定的库)。

智能体在具有严格边界和可预测结构的环境中最为有效,因此我们围绕严格的架构模型构建了应用程序。每个业务领域都被划分为一组固定的层,具有严格验证的依赖方向和有限的一组允许边缘。这些约束通过自定义 linter(当然是由 Codex 生成的!)和结构测试进行机械强制执行。

下图展示了该规则:在每个业务领域(例如应用设置)内,代码只能通过一组固定的层(Types → Config → Repo → Service → Runtime → UI)“向前”依赖。横切关注点(认证、连接器、遥测、功能标志)通过一个单一的显式接口进入:Providers。其他任何依赖都是不允许的,并且会被机械地强制禁止。

image.png
(图表说明:具有显式横切边界的分层领域架构。)

这是那种你通常会等到拥有数百名工程师时才会推迟实施的架构。对于编码智能体来说,这是一个早期的先决条件:这种约束使得在不发生腐烂或架构漂移的情况下实现速度成为可能。

在实践中,我们通过自定义 linter 和结构测试,加上一小部分“品味不变量”来强制执行这些规则。例如,我们静态地强制执行结构化日志记录、schema 和类型的命名约定、文件大小限制以及特定于平台的可靠性要求。因为 linter 是自定义的,我们将错误消息编写为向智能体上下文注入补救指令。

在人类优先的工作流中,这些规则可能会让人觉得迂腐或受限。而在智能体看来,它们成为了倍增器:一旦编码,它们就会同时应用于所有地方。

同时,我们明确区分约束重要的地方和不重要的地方。这类似于领导一个大型工程平台组织:在中心强制执行边界,在局部允许自治。你深切关注边界、正确性和可复现性。在这些边界内,你允许团队(或智能体)在表达解决方案方面拥有极大的自由度。

生成的代码并不总是符合人类的风格偏好,这没关系。只要输出正确、可维护且对未来的智能体运行可读,它就符合标准。

人类的品味会不断反馈到系统中。审查评论、重构拉取请求和面向用户的错误被捕获为文档更新或直接编码到工具中。当文档不足时,我们将规则提升为代码。

吞吐量改变了合并哲学

随着 Codex 吞吐量的增加,许多常规的工程规范变得适得其反。

该仓库在运行时保持最小的阻塞性合并门槛。拉取请求是短暂的。测试的不稳定(flakiness)通常通过后续运行来解决,不无限期地阻碍进度。在一个智能体吞吐量远超人类注意力的系统中,修正很便宜,而等待很昂贵。

在低吞吐量环境中,这样做是不负责任的。而在这里,这通常是正确的权衡。

“智能体生成”的真正含义

当我们说代码库是由 Codex 智能体生成时,我们指的是代码库中的一切

智能体生产:

  • 产品代码和测试

  • CI 配置和发布工具

  • 内部开发者工具

  • 文档和设计历史

  • 评估工具(Evaluation harnesses)

  • 审查评论和回复

  • 管理仓库本身的脚本

  • 生产仪表板定义文件

人类始终保持在循环中,但在与过去不同的抽象层面上工作。我们确定工作的优先级,将用户反馈转化为验收标准,并验证结果。当智能体遇到困难时,我们将其视为一个信号:识别缺失了什么(工具、护栏、文档),并将其反馈回仓库,始终通过让 Codex 自己编写修复程序来完成。

智能体直接使用我们的标准开发工具。它们拉取审查反馈,进行行内回复,推送更新,并经常压缩和合并它们自己的拉取请求。

不断提升的自主性水平

随着越来越多的开发循环被直接编码到系统中(测试、验证、审查、反馈处理和恢复),仓库最近跨越了一个有意义的阈值,即 Codex 可以端到端地驱动一个新功能。

给定一个单一的提示词,智能体现在可以:

  1. 验证代码库的当前状态

  2. 重现报告的错误

  3. 录制视频演示故障

  4. 实施修复

  5. 通过驱动应用程序验证修复

  6. 录制第二个视频演示解决方案

  7. 开启拉取请求

  8. 响应智能体和人类的反馈

  9. 检测并修复构建失败

  10. 仅在需要判断时升级给人类

  11. 合并更改

这种行为在很大程度上依赖于该仓库特定的结构和工具,如果没有类似的投资,不应假设这种能力可以普及(至少目前还不能)。

熵与垃圾回收

完全的智能体自主性也引入了新的问题。Codex 会复制仓库中现有的模式,甚至是不均匀或次优的模式。随着时间的推移,这不可避免地导致漂移。

起初,人类手动解决这个问题。我们的团队过去每周五(一周 20% 的时间)都要清理“AI 垃圾(AI slop)”。不出所料,这种方式无法扩展。

相反,我们要开始将所谓的“黄金原则”直接编码到仓库中,并建立一个定期的清理流程。这些原则是固执己见的机械规则,旨在保持代码库对未来智能体运行的可读性和一致性。例如:

(1) 我们更喜欢共享的实用程序包而不是手写的辅助函数,以保持不变量的集中化;

(2) 我们不进行“YOLO 风格”的数据探测,我们验证边界或依赖类型化的 SDK,以免智能体意外地建立在猜测的数据形状之上。按照固定的节奏,我们要有一组后台 Codex 任务来扫描偏差、更新质量评分并开启针对性的重构拉取请求。这些请求中的大多数可以在一分钟内完成审查并自动合并。

这就像垃圾回收一样起作用。技术债务就像高息贷款:持续以小增量偿还几乎总是比让其复利并在痛苦的爆发中解决要好。人类的品味被捕获一次,然后在每一行代码上持续强制执行。这也让我们能够每天捕捉并解决糟糕的模式,防止它们在代码库中蔓延数天或数周。

我们需要继续学习的内容

到目前为止,这一策略在 OpenAI 的内部发布和采用中效果良好。为真实用户构建真实产品有助于将我们的投资锚定在现实中,并引导我们走向长期的可维护性。

我们尚不知道的是,在一个完全由智能体生成的系统中,架构的一致性如何在数年内演变。我们仍在学习人类判断在何处能增加最大的杠杆作用,以及如何编码这种判断以使其产生复利。我们也不知道随着模型随时间变得更加强大,这个系统将如何演变。

目前已明确的是:构建软件仍然需要纪律,但这种纪律更多地体现在脚手架上,存在于代码之外。保持代码库连贯的工具、抽象和反馈循环变得越来越重要。

我们现在最困难的挑战集中在设计环境、反馈循环和控制系统上,以帮助智能体实现我们的目标:大规模构建和维护复杂、可靠的软件。

随着像 Codex 这样的智能体在软件生命周期中承担更大的份额,这些问题将变得更加重要。我们希望分享一些早期的经验教训能帮助你思考应该在何处投入精力,以便你可以专注于构建事物。

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI这是 人工智能Agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions