Harness Engineering:在 Agent 优先的世界中利用 Codex
来源: OpenAI Blog 作者: Ryan Lopopolo,技术团队成员 日期: 2026 年 2 月 11 日 标签: openai codex ai-engineering agent harness-engineering
概述
过去五个月,我们团队进行了一项实验:构建并发布了一个软件产品的内部测试版,0 行手动编写的代码。
该产品拥有内部日常用户和外部 alpha 测试人员。它会发布、部署、崩溃、修复。不同的是每一行代码——应用逻辑、测试、CI 配置、文档、可观测性和内部工具——都由 Codex 编写。我们估计这大约只用了手动编写代码所需时间的 1/10。
人类引导。Agent 执行。
从一个空的 git 仓库开始
2025 年 8 月下旬,第一次提交落入了空仓库。
初始脚手架——仓库结构、CI 配置、格式化规则、包管理器设置和应用框架——由 Codex CLI 使用 GPT‑5 生成,由一组现有模板指导。甚至指导 agent 如何在仓库中工作的初始 AGENTS.md 文件本身也是由 Codex 编写的。
没有预先存在的人工编写的代码来锚定系统。从一开始,仓库就是由 agent 塑造的。
五个月后,仓库包含大约一百万行代码,涵盖应用逻辑、基础设施、工具、文档和内部开发人员实用程序。在此期间,大约有 1,500 个 pull request 被打开和合并,只有一个小团队的三名工程师驱动 Codex。这转化为平均每位工程师每天 3.5 个 PR 的吞吐量,令人惊讶的是,随着团队增长到现在七名工程师,吞吐量实际上增加了。重要的是,这不是为了输出而输出:该产品已被数百名用户在内部使用,包括日常的高级用户。
在整个开发过程中,人类从未直接贡献任何代码。这成为了团队的核心哲学:没有手动编写的代码。
重新定义工程师的角色
缺乏人工编码引入了不同类型的工程工作,专注于系统、脚手架和杠杆。
早期的进展比我们预期的慢,不是因为 Codex 无能,而是因为环境未被充分指定。agent 缺乏工具、抽象和内部结构来朝高层目标取得进展。我们工程团队的主要工作变成了让 agent 能够做有用的工作。
在实践中,这意味着深度优先的工作:将更大的目标分解为更小的构建块(设计、代码、审查、测试等),提示 agent 构建这些块,并使用它们来解锁更复杂的任务。当某些东西失败时,修复几乎绝不是”再努力一点”。因为取得进展的唯一方法是让 Codex 做这项工作,人类工程师总是进入任务并问:“什么能力缺失了,我们如何使它对 agent 既可理解又可执行?”
人类几乎完全通过提示与系统交互:工程师描述任务,运行 agent,并允许它打开 pull request。为了驱动 PR 完成,我们指示 Codex 在本地审查自己的更改,请求额外的特定 agent 审查(本地和云端),响应任何人类或 agent 给出的反馈,并在循环中迭代,直到所有 agent 审查者满意(实际上这是一个 Ralph Wiggum Loop)。Codex 直接使用我们的标准开发工具(gh、本地脚本和嵌入式仓库技能)来收集上下文,而无需人类复制粘贴到 CLI。
人类可以审查 pull request,但不是必需的。随着时间的推移,我们将几乎所有的审查工作推向 agent 对 agent 的处理。
提高应用可读性
随着代码吞吐量的增加,我们的瓶颈变成了人工 QA 能力。因为固定约束是人类时间和注意力,我们努力通过让应用 UI、日志和应用指标本身直接对 Codex 可读来为 agent 添加更多功能。
例如,我们使每个 git worktree 可以启动应用程序,以便 Codex 可以为每个更改启动并驱动一个实例。我们还将 Chrome DevTools Protocol 连接到 agent 运行时,并创建了处理 DOM 快照、屏幕截图和导航的技能。这使 Codex 能够重现错误、验证修复并直接推理 UI 行为。

我们对可观测性工具做了同样的事情。日志、指标和跟踪通过本地可观测性堆栈暴露给 Codex,该堆栈对于任何给定的工作树都是短暂的。Codex 在该应用程序的完全隔离版本上工作——包括其日志和指标,一旦任务完成就会被拆除。Agent 可以用 LogQL 查询日志,用 PromQL 查询指标。有了这个上下文,像”确保服务启动在 800ms 内完成”或”这四个关键用户旅程中的任何 span 都不超过两秒”这样的提示变得可行。
我们经常看到单个 Codex 运行在单个任务上工作长达六小时(通常在人类睡觉时)。
使仓库知识成为记录系统
上下文管理是使 agent 在大型和复杂任务上有效的最大挑战之一。我们学到的最早的教训之一很简单:给 Codex 一张地图,而不是一本 1000 页的说明书。
我们尝试了”一个大 AGENTS.md”方法。它以可预测的方式失败了:
- 上下文是稀缺资源。 一个巨大的指令文件挤占了任务、代码和相关文档——所以 agent 要么错过关键约束,要么开始为错误的约束优化。
- 太多的指导变成了无指导。 当一切都是”重要的”时,什么都不重要。Agent 最终在本地进行模式匹配,而不是有目的地导航。
- 它立即腐烂。 一个单一的百科全书式手册变成了陈旧规则的墓地。Agent 无法分辨什么仍然是真的,人类停止维护它,文件悄悄地变成了一个诱人的麻烦。
- 它难以验证。 单个 blob 不适合机械检查(覆盖率、新鲜度、所有权、交叉链接),所以漂移是不可避免的。
因此,我们不是将 AGENTS.md 视为百科全书,而是将其视为目录。
仓库的知识库位于结构化的 docs/ 目录中,被视为记录系统。一个简短的 AGENTS.md(大约 100 行)被注入上下文,主要作为地图,指向其他地方的更深层真相来源。
知识存储结构
AGENTS.md
ARCHITECTURE.md
docs/
├── design-docs/
│ ├── index.md
│ ├── core-beliefs.md
│ └── ...
├── exec-plans/
│ ├── active/
│ ├── completed/
│ └── tech-debt-tracker.md
├── generated/
│ └── db-schema.md
├── product-specs/
│ ├── index.md
│ ├── new-user-onboarding.md
│ └── ...
├── references/
│ ├── design-system-reference-llms.txt
│ ├── nixpacks-llms.txt
│ ├── uv-llms.txt
│ └── ...
├── DESIGN.md
├── FRONTEND.md
├── PLANS.md
├── PRODUCT_SENSE.md
├── QUALITY_SCORE.md
├── RELIABILITY.md
└── SECURITY.md
设计文档被编目和索引,包括验证状态和定义 agent 优先操作原则的一组核心信念。架构文档提供了域和包分层的顶层地图。质量文档对每个产品域和架构层进行分级,随着时间的推移跟踪差距。
计划被视为一等公民。临时轻量级计划用于小的更改,而复杂的工作在执行计划中捕获,其中包含进度和决策日志,这些日志被检入仓库。活动计划、已完成计划和已知的技术债务都是版本控制的并位于同一位置,允许 agent 在不依赖外部上下文的情况下操作。
这实现了渐进式披露:agent 从一个小的、稳定的入口点开始,并被教导接下来在哪里寻找,而不是一开始就被淹没。
我们通过机械方式强制执行这一点。专用的 linter 和 CI 作业验证知识库是最新的、交叉链接的和正确结构的。一个定期的”文档园艺”agent 扫描不反映真实代码行为的陈旧或过时的文档,并打开修复 pull request。
Agent 可读性是目标
随着代码库的发展,Codex 的设计决策框架也需要发展。
因为仓库是完全 agent 生成的,它首先针对_Codex 的_ _可读性_进行了优化。就像团队旨在为新工程雇员提高代码可导航性一样,我们人类工程师的目标是使 agent 能够直接从仓库本身推理完整的业务域。
从 agent 的角度来看,它在运行时无法在上下文中访问的任何东西实际上都不存在。存在于 Google Docs、聊天线程或人们头脑中的知识对系统来说是不可访问的。仓库本地的、版本化的制品(例如,代码、markdown、模式、可执行计划)就是它能看到的所有东西。

我们了解到我们需要随着时间的推移将越来越多的上下文推入仓库。那个在架构模式上对齐团队的 Slack 讨论?如果 agent 无法发现它,它就像三个月后加入的新雇员不知道一样不可读。
给 Codex 更多上下文意味着组织和暴露正确的信息,以便 agent 可以对其进行推理,而不是用临时的指令压倒它。就像你会向新团队成员介绍产品原则、工程规范和团队文化(包括表情符号偏好)一样,给 agent 这个信息会导致更好的一致的输出。
这个框架澄清了许多权衡。我们倾向于可以在仓库内部完全内化和推理的依赖和抽象。通常被描述为”无聊”的技术由于可组合性、api 稳定性和在训练集中的表示,往往更容易让 agent 建模。在某些情况下,让 agent 重新实现功能子集比解决来自公共库的不透明上游行为更便宜。例如,我们没有拉入通用的 p-limit 式包,而是实现了我们自己的带并发映射的助手:它与我们的 OpenTelemetry 集成紧密集成,具有 100% 的测试覆盖率,并且行为完全符合我们的运行时期望。
将系统的更多部分拉入 agent 可以检查、验证和直接修改的形式会增加杠杆——不仅对 Codex,而且对也在代码库上工作的其他 agent(例如 Aardvark)也是如此。
强制架构和品味
仅靠文档不能保持完全 agent 生成的代码库连贯。**通过强制不变量而不是微管理实现,我们让 agent 快速发布而不破坏基础。**例如,我们要求 Codex 在边界解析数据形状,但不规定如何发生(模型似乎喜欢 Zod,但我们没有指定那个特定库)。
Agent 在具有严格边界和可预测结构的环境中最有效,所以我们围绕刚性的架构模型构建应用程序。每个业务域分为一组固定的层,具有严格验证的依赖方向和一组有限的允许边。这些约束通过自定义 linter(当然是 Codex 生成的!)和结构测试机械地强制执行。
下图显示了规则:在每个业务域内(例如,App Settings),代码只能通过一组固定的层”向前”依赖(Types → Config → Repo → Service → Runtime → UI)。跨域关注点(auth、connectors、telemetry、feature flags)通过单个显式接口进入:Providers。其他任何东西都被禁止并通过机械方式强制执行。

这是你通常推迟到有数百名工程师时才做的架构。有了编码 agent,它是一个早期的先决条件:约束是允许速度而不衰变或架构漂移的关键。
在实践中,我们通过自定义 linter 和结构测试以及一小组”品味不变量”来强制执行这些规则。例如,我们通过自定义 lints 静态强制结构化日志、模式和类型的命名约定、文件大小限制和平台特定的可靠性要求。因为 lints 是自定义的,我们编写错误消息以将补救指令注入 agent 上下文。
在人类优先的工作流中,这些规则可能感觉迂腐或受限。有了 agent,它们成为倍增器:一旦编码,它们立即到处应用。
同时,我们明确约束重要和不重要的地方。这类似于领导一个大型工程平台组织:中央强制边界,允许局部自治。你深切关心边界、正确性和可重现性。在这些边界内,你允许团队——或 agent——在如何表达解决方案方面有重大自由。
生成的代码并不总是符合人类风格偏好,这没关系。只要输出是正确的、可维护的,并且对未来的 agent 运行可读,它就达到了标准。
人类品味不断反馈到系统中。审查评论、重构 pull request 和面向用户的错误被捕获为文档更新或直接编码到工具中。当文档不足时,我们将规则提升为代码。
吞吐量改变合并哲学
随着 Codex 吞吐量的增加,许多传统的工程规范变得适得其反。
仓库以最小的阻塞合并门运行。Pull request 是短暂的。测试片断通常通过后续运行来解决,而不是无限期地阻塞进展。在一个 agent 吞吐量远超人类注意力的系统中,纠正是廉价的,等待是昂贵的。
这在低吞吐量环境中是不负责任的。在这里,这通常是正确的权衡。
“Agent 生成”的真正含义
当我们说代码库是由 Codex agent 生成的时,我们的意思是代码库中的所有内容。
Agent 产生:
- 产品代码和测试
- CI 配置和发布工具
- 内部开发人员工具
- 文档和设计历史
- 评估工具
- 审查评论和响应
- 管理仓库本身的脚本
- 生产仪表板定义文件
人类始终保持循环,但在与我们过去不同的抽象层工作。我们确定工作的优先级,将用户反馈转化为验收标准,并验证结果。当 agent 挣扎时,我们将其视为信号:识别缺失的东西——工具、护栏、文档——并将其反馈到仓库中,总是通过让 Codex 本身编写修复。
Agent 直接使用我们的标准开发工具。他们拉取审查反馈,内联回复,推送更新,并且经常压缩和合并自己的 pull request。
增加自主权级别
随着更多的开发循环直接编码到系统中——测试、验证、审查、反馈处理和恢复——仓库最近跨越了一个有意义的阈值,Codex 可以端到端地驱动新功能。
在单个提示下,agent 现在可以:
- 验证代码库的当前状态
- 重现报告的错误
- 录制演示失败的视頻
- 实现修复
- 通过驱动应用程序验证修复
- 录制演示解决方案的第二段视頻
- 打开 pull request
- 响应 agent 和人类反馈
- 检测并修复构建失败
- 仅在需要判断时升级给人类
- 合并更改
这种行为严重依赖于此仓库的特定结构和工具,不应假设在没有类似投资的情况下推广——至少,还没有。
熵和垃圾回收
**完全的 agent 自主权也引入了新问题。**Codex 复制仓库中已存在的模式——即使是不均匀或次优的模式。随着时间的推移,这不可避免地导致漂移。
最初,人类手动解决这个问题。我们团队曾经每个星期五(一周的 20%)花在清理”AI slop”上。不出所料,这无法扩展。
相反,我们开始将所谓的”黄金原则”直接编码到仓库中,并建立了一个定期的清理过程。这些原则是固执的、机械的规则,使代码库对未来的 agent 运行保持可读和一致。例如:(1) 我们更喜欢共享实用程序包而不是手工编写的助手,以保持不变量集中;(2) 我们不”YOLO 风格”探测数据——我们验证边界或依赖类型化的 SDK,这样 agent 就不会意外地基于猜测的形状构建。在定期的节奏中,我们有一组后台 Codex 任务扫描偏差,更新质量等级,并打开有针对性的重构 pull request。大多数这些可以在一分钟内审查并自动合并。
这就像垃圾回收一样。技术债务就像高息贷款:几乎总是最好以小的增量连续偿还,而不是让它复合并在痛苦的突发中解决。人类品味被捕获一次,然后在每行代码上连续强制执行。这也使我们能够每天捕获和解决不良模式,而不是让它们在代码库中传播几天或几周。
我们仍在学习的东西
到目前为止,这种策略在 OpenAI 的内部发布和采用方面运作良好。为真实用户构建真实产品帮助我们将投资锚定在现实中,并指导我们实现长期可维护性。
我们还不知道在完全 agent 生成的系统中,架构一致性如何在多年中发展。我们仍在学习人类判断在哪里增加最多的杠杆,以及如何编码这种判断以便它复合。我们也不知道这个系统将如何随着模型继续变得更有能力而发展。
变得清楚的是:构建软件仍然需要纪律,但纪律更多地出现在脚手架而不是代码中。保持代码库连贯的工具、抽象和反馈循环变得越来越重要。
我们现在最困难的挑战集中在设计环境、反馈循环和控制系统,以帮助 agent 完成我们的目标:大规模构建和维护复杂、可靠的软件。
随着像 Codex 这样的 agent 承担软件生命周期的更大一部分,这些问题将变得更加重要。我们希望分享一些早期教训有助于你推理在哪里投入你的努力,这样你就可以构建东西。
相关文章
标签
openai codex ai-engineering agent harness-engineering cybernetics automation software-architecture