Harness 工程:在智能体优先的世界里利用 Codex

在过去五个月里,我们团队一直在做一个实验:用 0 行人工编写代码来构建并交付一个软件产品的内部 beta 版本。

这个产品已经有内部日活用户和外部 alpha 测试者。它会发布、会部署、会出故障,也会被修复。不同之处在于,其中每一行代码,不论是应用逻辑、测试、CI 配置、文档、可观测性还是内部工具,全部都由 Codex 编写。我们估计,整个项目的构建时间大约只有手写代码所需时间的 1/10。

Humans 负责掌舵。Agents 负责执行。

我们是刻意给自己加上这个约束的,因为只有这样,才会逼着我们去构建那些真正能把工程效率提升一个数量级所必需的东西。我们只有几周时间,却最终交付了一个拥有上百万行代码的系统。为了做到这一点,我们必须弄清楚:当一个软件工程团队的主要工作不再是写代码,而是设计环境、明确意图、构建反馈回路,让 Codex Agents 能够稳定工作时,工程实践究竟会发生什么变化。

这篇文章讲的是:我们用一支智能体团队从零做出一个全新产品后学到了什么,哪些东西会失效,哪些东西会产生复利,以及如何最大化利用我们唯一真正稀缺的资源:人的时间和注意力。

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

这个空仓库的第一次提交发生在 2025 年 8 月下旬。

最初的脚手架,包括仓库结构、CI 配置、格式规则、包管理器设置和应用框架,都是由 Codex CLI 配合 GPT‑5、在少量既有模板的引导下生成的。甚至连那个用来告诉 Agents 如何在仓库中工作的最初版 AGENTS.md 文件,也是 Codex 自己写的。

当时没有任何既有的人类代码能给这个系统提供锚点。从一开始,这个仓库就是由智能体塑造出来的。

五个月后,这个仓库已经拥有约一百万行代码,涵盖应用逻辑、基础设施、工具、文档以及内部开发者实用工具。在这段时间里,大约有 1,500 个 pull request 被打开并合并,而驱动 Codex 的工程师团队最初只有 3 个人。这意味着平均每位工程师每天产出 3.5 个 PR;更令人意外的是,随着团队扩展到如今的 7 位工程师,吞吐量反而还在提升。更重要的是,这不是为了产出而产出:这个产品已经被数百位内部用户使用,其中包括每天都在重度使用的内部 power user。

在整个开发过程中,人类从未直接贡献过任何代码。这逐渐成为团队的一条核心理念:不写人工代码

重新定义工程师的角色

缺少亲手写代码这件事,引入了另一种工程工作,它聚焦的是系统、脚手架和杠杆效应

早期进展比我们预期得慢,不是因为 Codex 做不到,而是因为环境定义得还不够充分。智能体缺少推进高层目标所需要的工具、抽象和内部结构。于是,我们工程团队的主要工作,变成了让智能体具备做有用工作的能力。

在实践中,这意味着要按深度优先的方式推进:把更大的目标拆成更小的构件,比如设计、编码、评审、测试等;提示智能体先把这些构件造出来,再用这些构件解锁更复杂的任务。当某件事失败时,解决方案几乎从来不是“再努力一点”。因为唯一的前进方式,就是让 Codex 去做这件事,所以人类工程师总会回到任务本身,问一句:“到底缺了什么能力?以及我们怎样才能让这个能力既对智能体可见,又能被强制执行?”

Humans 与系统的交互,几乎完全是通过 prompt 进行的:工程师描述任务,运行智能体,让它去打开一个 pull request。为了把一个 PR 推到完成,我们会指示 Codex 先在本地评审自己的改动,再发起额外的、有针对性的智能体评审,本地和云端都做;然后响应人类或其他智能体给出的反馈,循环迭代,直到所有智能体评审者都满意为止(本质上就是一个 Ralph Wiggum Loop⁠(opens in a new window))。Codex 会直接使用我们标准的开发工具,比如 gh、本地脚本以及嵌在仓库里的 skills,来收集上下文,而不是让人类把信息复制粘贴进 CLI。

Humans 也可以评审 pull request,但不是必须的。随着时间推移,我们已经把几乎所有评审工作都推向了 agent-to-agent 的处理方式。

提高应用的可读性

随着代码吞吐量提升,我们的瓶颈变成了人工 QA 的容量。由于真正固定不变的约束一直是人的时间和注意力,我们开始努力给智能体增加更多能力,方法是让应用 UI、日志和应用指标这些东西本身,都能够直接被 Codex 读懂。

例如,我们让应用能够按 git worktree 启动,这样 Codex 就可以为每个变更单独拉起并驱动一个实例。我们还把 Chrome DevTools Protocol 接进了智能体运行时,并创建了用于处理 DOM 快照、截图和导航的 skills。这让 Codex 能直接复现 bug、验证修复,并推理 UI 行为。

标题为“Codex 通过 Chrome DevTools MCP 驱动应用以验证自己的工作”的图示。Codex 先选择一个目标,在触发某条 UI 路径前后分别做状态快照,通过 Chrome DevTools 观察运行时事件,应用修复、重启,再循环重复验证,直到应用恢复干净状态。

我们也用同样的思路处理可观测性工具。Logs、metrics 和 traces 都通过一套本地可观测性栈暴露给 Codex,而且这套栈对每个 worktree 都是临时的。Codex 操作的是该应用的一个完全隔离版本,包括它自己的日志和指标;任务完成后,这些都会被销毁。Agents 可以用 LogQL 查询日志,用 PromQL 查询指标。有了这些上下文,像“确保服务启动在 800ms 内完成”或“这四条关键用户路径里没有任何 span 超过两秒”这样的 prompt,就变得可操作了。

标题为“在本地开发中为 Codex 提供一整套可观测性栈”的图示。应用把日志、指标和追踪发送给 Vector,再由其扇出到一个包含 Victoria Logs、Metrics 和 Traces 的可观测性栈中,各自通过 LogQL、PromQL 或 TraceQL API 被查询。Codex 利用这些信号做查询、关联和推理,然后在代码库中实现修复、重启应用、重新运行负载、测试 UI 路径,并在反馈回路中不断重复。

我们经常看到单次 Codex 运行围绕一个任务持续工作六个小时以上,而且这通常发生在人类睡觉的时候。

我们把仓库知识变成系统记录

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

我们试过“一份超大的 AGENTS.md⁠(opens in a new window)”这种做法。结果失败得很典型:

  • 上下文是一种稀缺资源。 一个巨大的说明文件会挤占任务本身、代码和相关文档的上下文空间,结果就是智能体要么漏掉关键约束,要么开始朝错误的目标优化。
  • 过多的指导等于 没有指导。** 当所有内容都被标记为“重要”时,就没有什么是真正重要的。智能体最终只会在局部做模式匹配,而不是有意识地导航。
  • 它会立刻腐烂。 单体式手册很快就会变成一片陈旧规则的墓地。智能体分辨不出哪些还有效,人类也不再维护,最后它悄悄变成一个极具迷惑性的陷阱。
  • 很难验证。 一整块大文本不适合做机械化检查,比如覆盖率、新鲜度、归属关系和交叉链接,因此漂移几乎不可避免。

所以,我们不把 AGENTS.md 当百科全书,而是把它当作目录页

仓库里的知识库存在于结构化的 docs/ 目录中,它被当作系统记录。一个简短的 AGENTS.md(大约 100 行)会被注入上下文,主要承担地图的作用,指向其他地方更深入、更权威的真相来源。

Plain Text

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

仓库内知识存储的布局。

设计文档会被编目和索引,包括验证状态以及一组定义智能体优先工作原则的核心信念。架构文档⁠(opens in a new window) 提供了领域划分和包分层的顶层地图。质量文档则会对每个产品领域和架构层打分,并随着时间推移跟踪差距。

计划被当作一等工件。较小的改动使用轻量且短期的计划,而复杂工作会被记录在带有进度和决策日志、并提交进仓库的执行计划⁠(opens in a new window)中。活跃计划、已完成计划以及已知技术债都被版本化并放在一起,这样 Agents 就能在不依赖外部上下文的情况下继续工作。

这使得渐进披露成为可能:Agents 从一个小而稳定的入口点开始,再被教会接下来应该去哪里找信息,而不是一开始就被海量信息淹没。

我们通过机械化方式来强制执行这一点。专门的 linter 和 CI 任务会验证知识库是否是最新的、是否交叉链接正确、结构是否符合要求。还有一个周期性运行的“doc-gardening”智能体,会扫描那些陈旧或已经不能反映真实代码行为的文档,并自动打开修正文档的 pull request。

目标是让智能体可读

随着代码库演进,Codex 对设计决策的理解框架也必须随之演进。

因为这个仓库完全由智能体生成,所以它首先被优化的是 Codex可读性。就像团队会努力提升代码对新入职工程师的可导航性一样,我们的人类工程师目标,是让智能体能够直接从仓库本身推理整个业务领域

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

标题为“智能体知识的边界:Codex 看不见的,就等于不存在”的图示。Codex 的知识被画成一个有边界的泡泡。泡泡外侧是一些不可见知识示例,例如 Google Docs、Slack 消息以及隐性的人工经验。箭头说明,要让这些信息对 Codex 可见,必须把它编码进代码库里的 markdown。

我们逐渐认识到,随着时间推移,必须把越来越多的上下文推入仓库。比如那场帮助团队在某个架构模式上达成共识的 Slack 讨论,如果智能体无法发现它,那它对智能体来说就不可读,这跟一个三个月后加入的新员工不知道这件事,本质上是一样的。

给 Codex 更多上下文,并不意味着用临时指令把它淹没,而是要组织并暴露正确的信息,让智能体能对这些信息做推理。就像你会给一位新同事介绍产品原则、工程规范和团队文化(包括 emoji 偏好)一样,把这些信息交给智能体,会让它产出更对齐的结果。

这种 framing 帮我们厘清了很多权衡。我们会优先选择那些可以在仓库内部被完整吸收和推理的依赖与抽象。那些常被称为“boring”的技术,往往因为可组合性强、API 稳定、在训练集中表征充分,而更容易被智能体建模。在某些场景下,让智能体自己重写一部分功能,比起绕过公共库的黑盒行为还更便宜。例如,我们没有引入通用的 p-limit 风格包,而是自己实现了一个带并发控制的 map helper:它和我们的 OpenTelemetry 埋点深度集成,测试覆盖率达到 100%,而且行为完全符合运行时预期。

把更多系统以一种智能体可以直接检查、验证和修改的形式拉进来,会提高杠杆,而且受益的不只是 Codex,也包括其他正在这个代码库上工作的 Agents(例如 Aardvark)。

强制架构与品味

光靠文档,并不能让一个完全由智能体生成的代码库保持一致。我们通过强制不变量,而不是事无巨细地干预实现,来让智能体既能快速交付,又不伤害基础。 举例来说,我们要求 Codex 在边界处解析数据形状⁠(opens in a new window),但并不规定具体必须怎么做(模型似乎很喜欢 Zod,但这并不是我们指定的)。

Agents 在拥有严格边界和可预测结构⁠(opens in a new window)的环境里最有效,所以我们围绕一个刚性的架构模型来构建应用。每个业务领域都被划分成一组固定层次,依赖方向必须严格验证,而且允许出现的边也是有限的。这些约束通过定制 linter(当然也是 Codex 生成的)和结构性测试来机械执行。

下图展示了这条规则:在每个业务领域内部(例如 App Settings),代码只能沿着固定层级“向前”依赖(Types → Config → Repo → Service → Runtime → UI)。跨领域关注点,例如 auth、connectors、telemetry、feature flags,只能通过一个明确的单一接口进入:Providers。除此之外的一切都被禁止,并被机械强制执行。

标题为“具有显式跨领域边界的分层领域架构”的图示。业务逻辑领域内部包含 Types → Config → Repo,以及 Providers → Service → Runtime → UI 模块,底部是 App Wiring + UI。领域边界外有一个 Utils 模块,它向 Providers 输入能力。

这类架构通常要等到你有了几百名工程师后才会去认真做;但有了编码智能体之后,它反而成了前期必需品:正是这些约束,让你能在不腐坏、不发生架构漂移的情况下保持速度。

在实践中,我们通过定制 linter 和结构性测试,再加上一小组“品味不变量”来落实这些规则。例如,我们会通过定制 lint 静态强制结构化日志、schema 与类型的命名规范、文件大小限制,以及平台特定的可靠性要求。因为 lint 是定制的,所以我们还会把修复指令直接写进错误消息里,把 remediation 信息注入智能体上下文。

在以人为中心的工作流里,这些规则可能显得过于教条、过于束缚。但对智能体来说,它们会变成乘数效应:一旦被编码进去,就会同时作用于所有地方。

与此同时,我们也会明确区分哪些地方必须受约束,哪些地方不必。这很像领导一个大型工程平台组织:边界由中心统一强制,本地实现保留自治。你会非常在意边界、正确性和可复现性;而在这些边界之内,你会允许团队,或者智能体,在表达解决方案的方式上拥有相当大的自由。

最终生成的代码不一定符合人类的审美偏好,这没关系。只要输出是正确的、可维护的,并且对未来的智能体运行仍然可读,它就达标了。

人类的品味会被持续反馈回系统。评审评论、重构 pull request 和面向用户的 bug,都会被转化成文档更新,或者直接编码进工具里。当文档不够用时,我们就把规则升级成代码。

吞吐量改变了合并哲学

随着 Codex 吞吐量提高,很多传统工程规范开始变得适得其反。

这个仓库采用极少的阻塞式合并门槛。Pull request 生命周期很短。测试波动往往通过后续运行来修复,而不是无限期地阻塞进展。在一个智能体吞吐量远高于人类注意力的系统里,修正很便宜,而等待很昂贵。

如果是在一个低吞吐环境中,这样做会很不负责任。但在这里,这通常反而是正确的权衡。

“智能体生成”到底意味着什么

当我们说这个代码库是由 Codex Agents 生成的,我们的意思是:代码库中的一切都是如此。

Agents 负责产出:

  • 产品代码和测试
  • CI 配置与发布工具
  • 内部开发者工具
  • 文档和设计历史
  • 评估 harness
  • 评审评论与反馈回复
  • 管理仓库本身的脚本
  • 生产环境仪表盘定义文件

Humans 始终仍在环中,只是工作的抽象层已经和以前不同了。我们负责安排优先级,把用户反馈转译为验收标准,并验证结果。当智能体遇到困难时,我们把它看作一个信号:识别出缺的是什么,是工具、护栏还是文档,再把这些反馈回仓库,而且始终是通过让 Codex 自己编写修复来完成。

Agents 会直接使用我们标准的开发工具。它们会拉取评审反馈、逐条回复、推送更新,甚至经常自己 squash 并合并自己的 pull request。

自主程度不断提升

随着越来越多的开发循环被直接编码进系统,例如测试、验证、评审、反馈处理和恢复,这个仓库最近跨过了一个重要门槛:Codex 现在已经能够端到端地驱动一个新功能的开发。

给它一个单独的 prompt,现在智能体已经可以:

  • 验证代码库当前状态
  • 复现一个已报告的 bug
  • 录制一个展示故障的视频
  • 实现修复
  • 通过驱动应用来验证修复
  • 再录制一个展示问题已解决的视频
  • 打开一个 pull request
  • 响应智能体和人类的反馈
  • 检测并修复构建失败
  • 只在确实需要判断时才升级给人类
  • 合并变更

这种行为高度依赖这个仓库的具体结构和工具链,不应假设它无需类似投入就能普遍成立,至少目前还不行。

熵与垃圾回收

完全自治的智能体也会带来新的问题。 Codex 会复制仓库中已经存在的模式,即使这些模式并不均衡,甚至并不理想。随着时间推移,这必然会带来漂移。

起初,人类靠手工处理这些问题。我们的团队以前每周五都会拿出一天时间(占整周 20%)来清理所谓的 “AI slop”。很显然,这并不具备扩展性。

于是,我们开始把所谓的“黄金原则”直接编码进仓库,并构建了一个周期性清理流程。这些原则是带有明确立场、且可机械执行的规则,用来让代码库在未来的智能体运行中继续保持可读和一致。比如:(1) 我们更偏好共享工具包,而不是到处手搓 helper,以便把不变量集中管理;(2) 我们不会用“YOLO-style”的方式去试探数据,而是验证边界,或依赖带类型的 SDK,这样智能体就不会在猜测出来的数据形状上继续构建。在固定节奏下,我们会运行一组后台 Codex 任务,扫描偏差、更新质量评分,并打开有针对性的重构 pull request。大多数这样的变更,都可以在一分钟内完成评审并自动合并。

这就像垃圾回收。技术债就像高利贷:与其让它不断复利、最后在痛苦中集中偿还,几乎总是更好地选择持续、小步地把它还掉。人类的品味只需要捕获一次,之后就能在每一行代码上持续执行。这也让我们能按天发现并修复坏模式,而不是任由它们在代码库里扩散数天甚至数周。

我们仍在学习什么

到目前为止,这套策略在 OpenAI 内部上线和落地阶段运行得很好。为真实用户构建真实产品,帮助我们把投入锚定在现实问题上,也引导我们朝更长远的可维护性迈进。

我们还不知道的是:在一个完全由智能体生成的系统里,架构一致性能否在多年尺度上持续演化。我们仍在学习,人类判断力在哪些地方最有杠杆,以及怎样把这种判断编码进去,让它产生复利。我们也还不知道,随着模型能力继续增强,这套系统将如何演变。

但有一件事已经很明确:构建软件依然需要纪律,只不过这种纪律越来越多地体现在脚手架里,而不是代码本身。保持代码库一致性的工具、抽象和反馈回路,正变得越来越重要。

我们现在最困难的挑战,集中在如何设计环境、反馈回路和控制系统,来帮助智能体完成我们的目标:以规模化方式构建并维护复杂且可靠的软件。

随着 Codex 这样的智能体承担软件生命周期中越来越大的部分,这些问题只会变得更重要。我们希望分享这些早期经验,能帮助你判断应该把精力投入到哪里,这样你就可以专心把东西做出来