核心思想

当工程团队的主要职责从”写代码”变为”让智能体可靠地写代码”时,新工作集中在四件事:把 AGENTS.md 当目录索引而非百科全书;把仓库知识做成”对智能体可读”的事实来源;用 lint 和不变量从机制上强制架构;把”AI 垃圾输出”清理也变成持续的智能体任务。人类掌舵,智能体执行。

过去五个月里,我们团队一直在做一个实验:用 0 行手写代码构建并发布一款软件产品的内部 beta 版本。

这款产品有内部日活用户和外部 alpha 测试者。它会上线、部署、出问题、被修复。不同寻常的是,每一行代码——应用逻辑、测试、CI 配置、文档、可观测性、内部工具——都是由 Codex 写的。我们估计,相比手写代码,整个项目只用了大约十分之一的时间。

人类掌舵,智能体执行。

我们故意给自己加上了这个约束,这样才能逼自己去做那些真正能把工程速度提升一个数量级的事情。我们只有几周时间来交付最终规模达百万行的代码。要做到这一点,就必须搞清楚:当一支软件工程团队的主要职责不再是写代码,而是设计环境、明确意图、构建反馈循环来让 Codex 智能体可靠地工作时,到底有什么变了。

这篇文章讲的就是我们带着一支智能体团队从头开始构建一款新产品时学到的东西——什么崩了、什么形成了复利效应,以及如何最大化我们唯一真正稀缺的资源:人类的时间和注意力。

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

第一次提交落到这个空仓库上,是在 2025 年 8 月底。

最初的脚手架——仓库结构、CI 配置、格式化规则、包管理器设置、应用框架——都是用 Codex CLI 在 GPT-5 驱动下生成的,参考了一小组已有模板。连最初那份指导智能体如何在仓库里工作的 AGENTS.md 文件,本身也是 Codex 写的。

没有任何预先存在的人类手写代码作为系统的锚点。从一开始,仓库就是由智能体塑造出来的。

五个月后,仓库已经包含约百万行代码,覆盖了应用逻辑、基础设施、工具链、文档和内部开发者工具。在此期间,一支仅有三名工程师驱动 Codex 的小团队,开起并合入了大约 1500 个拉取请求(PR)。这相当于平均每位工程师每天 3.5 个 PR 的吞吐量,而令人惊讶的是,随着团队扩张到现在的 7 人,吞吐量还在上涨。重要的是,这并不是为了产出而产出:这款产品已经被数百名内部用户使用,包括内部的日活重度用户。

整个开发过程中,人类从未直接贡献过任何代码。这成了团队的核心信条:不手写任何代码

重新定义工程师的角色

没有人类亲自动手写代码这一事实,催生出一种不同的工程工作,它聚焦于系统、脚手架与杠杆

早期的进展比我们预想的要慢,并不是因为 Codex 能力不行,而是因为环境本身没有被充分定义。智能体缺少推动高层目标所需的工具、抽象和内部结构。我们工程团队的首要任务,变成了”让智能体能做有用的事”。

实际操作中,这意味着我们采用深度优先的工作方式:把更大的目标拆成更小的构建块(设计、编码、评审、测试等),提示智能体去构造这些块,然后用它们来解锁更复杂的任务。当某个环节失败时,解决办法几乎从来不是”再努把力”。因为推动进展的唯一方式就是让 Codex 把活干完,所以人类工程师每次接手任务时,问的都是同一个问题:“缺了什么能力?我们怎么把它做得既对智能体清晰可读、又能机械强制执行?”

人类与系统的交互几乎完全通过提示词进行:工程师描述一个任务,运行智能体,让它去开 PR。要把一个 PR 推进到完成,我们指示 Codex 在本地评审自己的修改,请求额外的本地和云端智能体评审,回应任何来自人类或智能体的反馈,循环迭代直到所有智能体评审者都满意为止(这本质上就是一个 Ralph Wiggum 循环 ⁠)。Codex 直接使用我们的标准开发工具(gh、本地脚本、嵌入仓库的技能模块)来收集上下文,不需要人类把内容复制粘贴到 CLI 里。

人类可以评审 PR,但不是必须。随着时间推移,我们已经把几乎所有评审工作都推到智能体之间互评。

让应用对智能体更”可读”

随着代码吞吐量增加,瓶颈变成了人类的 QA 容量。由于固定不变的约束就是人类的时间和注意力,我们一直在想办法给智能体加更多能力——比如让应用 UI、日志、应用指标本身对 Codex 直接可读。

举个例子,我们让应用支持按 git worktree 启动,这样 Codex 就能为每个变更启动并驱动一个独立实例。我们还把 Chrome DevTools Protocol 接入了智能体运行时,并打造了用于操作 DOM 快照、截图和页面导航的技能模块。这让 Codex 能够直接复现 bug、验证修复、对 UI 行为进行推理。

可观测性工具也是一样的做法。日志、指标和 trace 通过一个本地可观测性栈暴露给 Codex,这套栈对每个 worktree 来说是临时的。Codex 在一个完全隔离的应用版本上工作——包括它的日志和指标,任务完成后这些都会被销毁。智能体可以用 LogQL 查询日志,用 PromQL 查询指标。有了这些上下文,“确保服务启动在 800ms 内完成”或”这四条关键用户旅程中没有一个 span 超过 2 秒”这样的提示词,就变得可执行了。

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

我们让仓库知识成为唯一的事实来源

上下文管理是让智能体在大规模复杂任务上保持高效的最大挑战之一。我们最早学到的一课很简单:给 Codex 一张地图,而不是一本千页的操作手册。

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

  • 上下文是稀缺资源。 一个巨大的指令文件挤占了任务、代码和相关文档的位置——所以智能体要么漏掉关键约束,要么开始为错误的东西做优化。
  • 过多的指引会变成*“非指引”*** 当一切都”重要”时,就没有什么是真正重要的。智能体会在局部做模式匹配,而不是有意识地导航。
  • 它会瞬间腐烂。 单体式的手册会变成过时规则的坟场。智能体分不清哪些还有效,人类不再维护它,文件悄然变成一种”招致麻烦的诱饵”。
  • 它难以验证。 单一的内容块不适合做机械检查(覆盖率、新鲜度、所有权、交叉链接),漂移因此变得不可避免。

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

仓库的知识库存放在一个结构化的 docs/ 目录中,作为唯一的事实来源(system of record)。一份简短的 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

仓库内部知识存储的目录布局。

设计文档被编目和索引,包含验证状态以及一组定义”智能体优先操作原则”的核心信念(core beliefs)。架构文档 ⁠提供了领域和包分层的顶层地图。一份质量文档为每个产品领域和架构层评分,持续追踪缺口。

计划被视为一等公民。轻量级的临时计划用于小变更,复杂工作则被记录在执行计划 ⁠中,进度和决策日志一并提交到仓库。活跃计划、已完成计划和已知的技术债务都被版本化、就地共置,让智能体不必依赖外部上下文就能工作。

这就实现了渐进式披露(progressive disclosure):智能体从一个小而稳定的入口开始,被引导着去探索下一步该看哪里,而不是一开始就被海量信息淹没。

我们用机械手段强制执行这一点。专门的代码检查器(linter)和 CI 任务校验知识库是否保持最新、相互链接、结构正确。一个周期性运行的”文档园丁”(doc-gardening)智能体会扫描那些过时或不再反映真实代码行为的文档,主动开 PR 去修。

“对智能体的可读性”才是目标

随着代码库不断演进,Codex 做设计决策时所依赖的那套框架也必须演进。

因为整个仓库完全由智能体生成,它的优先优化目标是 Codex 自己的可读性。就像团队会努力让代码对新员工更易于上手一样,我们人类工程师的目标,是让智能体能够直接从仓库本身对完整的业务领域进行推理。

从智能体的视角看,运行时它在上下文中拿不到的东西,实际上等于不存在。存在 Google Docs、聊天群里、人脑里的知识,对系统而言是不可访问的。仓库本地、版本化的产物(代码、markdown、schema、可执行计划)才是它能看见的全部。

我们学到的是:必须越来越多地把上下文推到仓库里。那段在团队里就架构模式达成共识的 Slack 讨论?如果智能体没办法发现它,那它的”不可读”程度,就和一个三个月后才入职的新人对它的陌生程度是一样的。

给 Codex 更多上下文,意味着把正确的信息组织好、暴露出来,让智能体能基于这些信息推理,而不是用临时指令把它淹没。就像你给一个新队友介绍产品理念、工程规范、团队文化(包括偏爱的 emoji)一样,给智能体提供这些信息,输出就会更对齐。

这个视角让很多权衡变得清晰。我们偏好那些可以被完整内化、能在仓库内推理的依赖和抽象。那些常被叫作”无聊”的技术,往往因为可组合性、API 稳定性、训练集中代表性充足,更容易被智能体建模。在某些情况下,让智能体重新实现某些功能子集,反而比绕过公开库的不透明上游行为更划算。比如,与其引入一个通用的 p-limit 风格的包,我们让 Codex 实现了自己的”带并发控制的 map”辅助函数:它和我们的 OpenTelemetry 埋点深度集成、有 100% 的测试覆盖率、行为完全符合我们运行时的预期。

把更多系统拉到一种”智能体可以直接检视、验证、修改”的形式中,能直接放大杠杆——不只是对 Codex,也对其他正在这个代码库上工作的智能体(比如 Aardvark)。

强制架构与品味

光靠文档不足以让一个完全由智能体生成的代码库保持一致。通过强制不变量(invariants),而不是事无巨细地管理实现细节,我们让智能体既能快速交付,又不会破坏地基。 比如,我们要求 Codex 在边界处解析数据形状 ⁠,但不规定具体怎么做(模型似乎喜欢用 Zod,但我们没指定这个特定的库)。

智能体在拥有严格边界与可预测结构的环境 ⁠中最有效,所以我们围绕一个刚性的架构模型来构建应用。每个业务领域被划分为一组固定的层,依赖方向被严格校验,允许的边数也被限定。这些约束通过定制的代码检查器(当然是 Codex 生成的!)和结构性测试机械地强制执行。

下面这张图展示了这条规则:在每个业务领域内(比如”应用设置”),代码只能通过一组固定的层”向前”依赖(Types → Config → Repo → Service → Runtime → UI)。横切关注点(鉴权、连接器、遥测、特性开关)通过单一的显式接口进入:Providers。除此之外的依赖一律不允许,并由机械手段强制执行。

这种架构通常要等到团队有几百名工程师时才会去搞。在编码智能体的语境下,它是早期就必须具备的前提:正是这些约束让你能在不腐化、不发生架构漂移的前提下保持速度。

实际操作中,我们用定制 linter、结构性测试,加上一小组”品味不变量”来强制这些规则。比如我们用定制 lint 静态强制结构化日志、schema 和类型的命名规范、文件大小上限、平台特定的可靠性要求。因为 lint 是定制的,我们会把修复指引(remediation instructions)写进错误信息,注入到智能体的上下文中。

在以人类为先的工作流中,这些规则可能让人觉得迂腐或束手束脚。但在智能体的语境下,它们就成了倍增器:一旦被编码,就会一次性应用到所有地方。

与此同时,我们对”哪里需要约束、哪里不需要”是有明确态度的。这有点像在领导一个大型工程平台组织:在中心层强制边界,在局部允许自治。你深切关心的是边界、正确性、可复现性。在这些边界之内,你给团队——或智能体——在解决方案表达上留出充分的自由。

由此产出的代码并不总是符合人类的风格偏好,这没关系。只要输出是正确的、可维护的、对未来的智能体运行依然可读,就达标了。

人类的品味是被持续反馈进系统的。评审意见、重构 PR、面向用户的 bug,都被捕获为文档更新或直接编码进工具链。当文档不够用时,我们就把规则升格为代码。

吞吐量改变了合并哲学

随着 Codex 的吞吐量上升,许多传统的工程规范开始适得其反。

仓库以最少的阻塞性合并门槛运转。PR 是短命的。测试 flake(偶发失败)通常通过补一次重跑来解决,而不是无限期地阻塞进度。在一个智能体吞吐量远超人类注意力的系统里,纠错是廉价的,等待才是昂贵的。

这种做法在低吞吐量环境下是不负责任的。但在这里,它通常是正确的权衡。

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

当我们说代码库由 Codex 智能体生成,意思是代码库里的所有东西

智能体产出:

人类始终在闭环之中,但工作在和过去不同的抽象层。我们排优先级、把用户反馈翻译成验收标准、验证结果。当智能体陷入困境时,我们把它当作一个信号:识别出缺了什么——工具、护栏、文档——然后把它反馈进仓库,永远是让 Codex 自己写出修复。

智能体直接使用我们的标准开发工具。它们拉取评审反馈、就地回应、推送更新,而且经常会自己 squash and merge 自己的 PR。

不断提高的自治水平

随着越来越多的开发循环——测试、验证、评审、反馈处理、恢复——被直接编码进系统,仓库最近跨过了一个有意义的门槛:Codex 可以端到端地驱动一个新功能。

只需一个提示词,智能体现在就能:

  • 校验代码库的当前状态
  • 复现一个被报告的 bug
  • 录制一段演示故障的视频
  • 实现修复
  • 通过驱动应用来验证修复
  • 录制第二段演示问题已解决的视频
  • 开一个 PR
  • 回应智能体和人类的反馈
  • 检测并修复构建失败
  • 仅在需要判断时升级给人类
  • 合并这个变更

这种行为高度依赖本仓库的具体结构和工具链,不能假设它在没有类似投入的情况下就能直接泛化——至少现在还不能。

熵与垃圾回收

完全的智能体自治也带来了新问题。 Codex 会复制仓库中已经存在的模式——哪怕是参差不齐或次优的模式。久而久之,这必然导致漂移。

最初,我们靠人来手动处理这件事。我们团队曾经每周五(一周里的 20% 时间)都用来清理”AI slop”(AI 生成的垃圾输出)。意料之中,这种方式无法 scale。

后来,我们开始把所谓”黄金原则”(golden principles)直接编码进仓库,并构建了一套周期性运行的清理流程。这些原则是有立场、机械化的规则,让代码库对未来的智能体运行保持可读和一致。比如:(1)我们偏好共享工具包(shared utility packages)而不是手写的辅助函数,以集中维护不变量;(2)我们不允许”YOLO 式”地探测数据——要么在边界处校验,要么依赖类型化 SDK,让智能体不可能不小心基于一个猜出来的数据形状写代码。我们以固定节奏运行一组后台 Codex 任务,它们会扫描偏差、更新质量分级、开针对性的重构 PR。这些 PR 大多能在一分钟内评审完并自动合并。

这套机制功能上类似垃圾回收。技术债务就像一笔高利贷:与其让它复利累积、然后在痛苦中集中偿还,几乎总是更好的做法是持续小额还款。人类的品味只需被捕获一次,然后就在每一行代码上被持续强制执行。这也让我们能够每天发现并消除糟糕的模式,而不是任由它们在代码库里蔓延数日数周。

我们仍在学习的事

到目前为止,这套策略一直运转良好,支撑产品完成了内部上线和在 OpenAI 内部的采用。为真实用户构建一款真实产品,让我们的投入有了现实的锚点,把我们引向了长期可维护性。

我们还不清楚的是:在一个完全由智能体生成的系统里,架构一致性会如何随多年时间演化。我们仍在摸索:人类的判断在哪些位置能产生最大的杠杆,以及如何把这种判断编码下来,让它能产生复利。我们也不知道,随着模型能力持续提升,这套系统会如何演进。

但有一点已经清晰:构建软件依然需要纪律,但纪律越来越多地体现在脚手架上,而不是代码本身。让代码库保持一致的工具、抽象和反馈循环,正变得越来越重要。

我们眼下最难的挑战,集中在如何设计环境、反馈循环和控制系统,让智能体能完成我们的目标:在规模上构建并维护复杂、可靠的软件。

随着 Codex 这样的智能体承担起软件生命周期中越来越大的部分,这些问题只会变得更重要。我们希望分享这些早期教训,能帮你思考该把精力投到哪里,让你能直接动手做事

作者

Ryan Lopopolo

致谢

特别感谢为本文做出贡献的 Victor Zhu 和 Zach Brock,以及构建这款新产品的整个团队。

相关笔记