在过去五个月里,我们团队一直在做一项实验:从零构建并交付一款软件产品的内部 Beta 版,其中没有一行代码是人工手写的。

这款产品拥有内部日活用户和外部 Alpha 测试者,完整经历了交付、部署、故障与修复的全过程。与以往不同的是,里面的每一行代码——从应用逻辑、测试、CI 配置、文档、可观测性到内部工具——全都是由 Codex 编写的。按我们的估算,完成这一切所花的时间,大约只有纯手工写代码的 1/10。

人类掌舵,Agent 执行。

我们有意给自己设下“不手写一行代码”的限制,以此倒逼自己去构建那些真正能让工程效率提升几个数量级的基础设施。我们只用几周时间,就交付了一个最终规模达到 100 万行代码的项目。为此,我们必须弄清楚:当软件工程团队的主要工作不再是亲手写代码,而是设计运行环境(Harness)、明确意图并构建反馈回路,从而让 Codex Agent 能够可靠地工作时,软件工程究竟会发生怎样的变化。

这篇文章将分享我们与 Agent 团队一起从零打造一款新产品时学到的经验与教训——哪些地方踩了坑、哪些问题会产生复利效应,以及如何最大化利用我们唯一真正稀缺的资源:人类的时间与注意力。

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

向空代码仓库提交的第一个 commit 发生在 2025 年 8 月下旬。

最初的脚手架——包括仓库目录结构、CI 配置、代码格式化规则、包管理器设置以及应用框架——都是在少量现有模板的指导下,由 Codex CLI 调用 GPT-5 生成的。甚至连指导 Agent 如何在仓库里工作的初始 AGENTS.md 文件,也是由 Codex 自己写的。

整个系统没有任何预先存在的人工代码。从第一天起,这个代码仓库就是由 Agent 塑造的。

五个月后,这个仓库已经包含约 100 万行代码,涵盖应用逻辑、基础设施、工具、文档和内部开发者工具。在这段时间里,一个最初只有 3 名工程师的小团队推动 Codex 发起并合并了大约 1,500 个 PR(Pull Request)。这相当于平均每位工程师每天合并 3.5 个 PR;更令人惊讶的是,随着团队规模扩大到现在的 7 名工程师,人均吞吐量不仅没有下降,反而还进一步提升了。而且这绝不是为了刷代码量而输出:该产品已经在数百名内部用户中投入使用,其中包括每天都在重度使用的核心内测用户。

在整个开发过程中,人类从未直接往仓库里手写过一行代码。这也成为了团队的核心准则:不手动写代码。

重新定义工程师的角色

当不再亲手写代码后,工程师的工作重心转向了系统、架构与杠杆效应。

项目早期的推进速度其实比我们预期的要慢——不是因为 Codex 能力不够,而是因为运行环境(environment)的规范不够明确。Agent 缺乏实现高层目标所需的工具、抽象层和内部结构,因此很难往前推进。于是,我们工程团队的核心任务,就变成了帮 Agent 具备做好工作的条件。

在实践中,这意味着采用“深度优先”的工作方式:先把宏大的目标拆解成更小的基础积木(设计、代码、审查、测试等),通过提示词让 Agent 先把这些积木搭建出来,再用它们去解锁更复杂的任务。每当事情推进不顺时,解决办法几乎从来不是“让模型再多试几次”。因为往前推进的唯一途径就是让 Codex 把活干完,所以人类工程师介入时总会追问同一个问题:“到底还缺什么能力?我们该怎样把这项能力变成对 Agent 既清晰可读、又能强制执行的机制?”

人类几乎完全通过提示词(Prompt)与系统交互:工程师描述任务、启动 Agent,并让它发起 PR。为了推动 PR 走完流程,我们会指示 Codex 先在本地审查自己的改动,再在本地和云端请求其他专项 Agent 进行审查,根据人类或 Agent 给出的反馈进行修改,如此循环往复,直到所有审查 Agent 都满意为止(这实际上就是一个 Ralph Wiggum 循环)。Codex 会直接调用我们的标准开发工具(gh、本地脚本以及内置在仓库里的 Skill)来收集上下文,完全不需要人工把内容复制粘贴到命令行里。

人类可以去审阅 PR,但并非必须这么做。随着时间推移,我们已经把绝大多数代码审查工作都调整为由“Agent 审 Agent”来完成。

提高应用的可读性

随着代码吞吐量不断上升,我们的瓶颈变成了人工 QA(质量测试)能力。既然人类的时间和注意力是固定的硬约束,我们就通过让应用的 UI、日志和性能指标对 Codex 直接可读,来为 Agent 赋予更多验证能力。

例如,我们让应用程序支持按 git worktree 独立启动,这样 Codex 就可以为每一次代码改动单独拉起并驱动一个应用实例。我们还将 Chrome DevTools 协议(MCP)接入了 Agent 运行时,并编写了专门用于获取 DOM 快照、屏幕截图和页面导航的 Skill。这让 Codex 能够自己复现 bug、验证修复效果,并直接对 UI 行为进行推理。

Codex 使用 Chrome DevTools MCP 驱动应用来验证自己的工作

我们对可观测性(Observability)工具也做了同样的改造。日志、指标和调用链追踪(Traces)会通过一套本地可观测性技术栈暴露给 Codex,且这套技术栈对每个 git worktree 都是独立、临时的。Codex 会在完全隔离的应用实例上运行测试;一旦任务完成,该实例的所有数据(包括日志和指标)都会被销毁。Agent 可以使用 LogQL 查询日志,使用 PromQL 查询指标,使用 TraceQL 分析调用链。有了这些上下文,像“确保服务启动在 800ms 内完成”或“这四个核心用户路径中的任何一个 Span 耗时都不能超过 2 秒”这样的指令,就真正变得可执行、可验证了。

在本地开发中为 Codex 提供完整的可观测性技术栈

因此,我们经常看到单次 Codex 运行在一个任务上持续自主工作超过 6 个小时(通常是在人类睡觉的时候)。

把代码仓库设为唯一事实来源

如何管理上下文(Context),是让 Agent 在大型复杂任务中发挥作用的最大挑战之一。我们最早学到的经验之一非常简单:给 Codex 一张地图,而不是一本 1,000 页的说明书。

我们也曾尝试过“写一个巨型 AGENTS.md”的做法。不出所料,这种做法彻底失败了:

  • 上下文是稀缺资源。一个庞大的指令文件会挤占具体任务、代码和相关文档的空间——结果 Agent 要么漏掉关键约束,要么开始针对错误的约束做过度优化。
  • 指导太多等于没有指导。当所有事情都被标成“重要”时,就没有什么是重要的了。Agent 最终只会在局部做机械的模式匹配,而不会有意识地全局导航。
  • 它会迅速腐烂过时。一本大而全的手册很快就会变成过时规则的坟场。Agent 无法判断哪些信息依然有效,一旦人类停止维护,这份文件就会悄然变成误导 Agent 的陷阱。
  • 难以做自动化校验。揉成一团的单文件不适合做机械检查(如覆盖率、时效性、负责人、交叉引用),因此必然会随着时间推移而偏离实际代码。

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

仓库的知识库存放在结构化的 docs/ 目录下,作为整个项目的唯一事实来源(System of Record)。注入到初始上下文里的只有一份约 100 行的简短 AGENTS.md,它主要充当导航地图,指向其他目录中更深层的真实信息源:

AGENTS.md                                  # 约 100 行的全局目录索引与导航入口
ARCHITECTURE.md                            # 业务域与包分层的顶层架构地图
docs/
├── design-docs/                           # 经过编目的设计文档库
│   ├── index.md
│   ├── core-beliefs.md                    # 定义“Agent 优先”原则的核心工程理念
│   └── ...
├── exec-plans/                            # 一等公民执行计划(含进度与决策日志)
│   ├── active/                            # 正在推进中的执行计划
│   ├── completed/                         # 已完成的归档计划
│   └── tech-debt-tracker.md               # 已知技术债追踪表
├── generated/
│   └── db-schema.md                       # 自动生成的数据库表结构文档
├── product-specs/                         # 产品功能规格说明书
│   ├── index.md
│   ├── new-user-onboarding.md
│   └── ...
├── references/                            # 面向 LLM 优化的第三方库与设计规范参考文本
│   ├── 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 优先”原则的核心理念。架构文档提供了业务域(domain)与包分层的顶层地图。质量评分文档(QUALITY_SCORE.md)则会对每个产品领域和架构层级进行打分,并持续追踪差距。

执行计划(Plans)被视为一等公民工件。 小改动使用临时的轻量计划,而复杂工作则会记录在执行计划中,附带进度与决策日志并直接提交到代码仓库。进行中的计划、已完成的计划以及已知的技术债全都纳入版本控制、集中存放,让 Agent 无需依赖任何外部上下文就能独立推进。

这套结构实现了渐进式披露(Progressive Disclosure):Agent 从一个小而稳定的入口出发,按图索骥去读取下一步需要看的文件,而不是一上来就被海量文本淹没。

我们还用自动化手段严格保障这套文档体系:专门的 Linter 和 CI 任务会校验知识库的时效性、交叉链接和目录结构是否正确;一个定期运行的“文档园丁(doc-gardening)”Agent 会自动扫描那些已经与真实代码行为脱节的过时文档,并直接发起修复 PR。

目标是面向 Agent 的可读性

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

因为整个代码仓库完全由 Agent 生成,所以我们首先针对 Codex 的可读性(Agent legibility) 进行优化。就像传统团队会努力让新入职的工程师更容易读懂代码一样,我们人类工程师的目标,是让 Agent 能够直接从代码仓库本身推理出完整的业务逻辑。

从 Agent 的视角来看,任何在运行时无法通过上下文访问到的东西,就等于不存在。存放在 Google Docs、聊天记录或者装在某个人脑子里的知识,系统全都看不到。唯有保存在代码仓库本地、受版本控制的工件(代码、Markdown、Schema、可执行计划),才是它能看到的全部世界。

Agent 知识的局限性:Codex 看不到的东西就等于不存在

随着时间推移,我们意识到必须把越来越多的上下文沉淀进代码仓库里。比如团队在 Slack 上讨论并达成了一项架构共识——如果 Agent 在仓库里找不到这段信息,那它就像三个月后才入职的新员工一样,对此一无所知。

给 Codex 提供更多上下文,并不是用临时指令把对话框塞满,而是把正确的信息组织好、暴露出来,让 Agent 能够基于这些信息做推理。就像你会向新队友介绍产品原则、工程规范和团队文化(甚至包括常用 Emoji 偏好)一样,把这些信息清晰地交给 Agent,就能换来更加一致的产出。

这一认知也帮我们理清了许多技术选型上的取舍:我们更倾向于选择那些可以在仓库内部完全内化、便于推理的依赖项与抽象。那些常被称为“无聊(boring)”的技术,往往因为可组合性强、API 稳定且在模型训练集里出现充分,反而最容易被 Agent 准确建模。在某些情况下,让 Agent 在仓库内自己实现一个精简的功能子集,比引入一个行为黑盒、难以调试的第三方公共库划算得多。例如,我们没有引入通用的 p-limit 类并发库,而是写了一个自己的并发 map 辅助函数:它与我们的 OpenTelemetry 埋点紧密集成,拥有 100% 测试覆盖率,且行为完全符合我们的运行时预期。

把系统的更多部分转化为 Agent 可以直接检查、验证和修改的形式,能够直接放大杠杆效应——不仅惠及 Codex,也惠及同样在参与代码库工作的其他 Agent(例如 Aardvark)。

约束架构与工程品味

光靠文档本身,并不足以让一个完全由 Agent 生成的代码库保持连贯。通过强制执行不变式(invariants),而不是对具体实现过程做微观管理,我们既能让 Agent 快速交付,又不会侵蚀系统根基。例如,我们强制要求 Codex 在边界处解析数据形态(Parse, don't validate),但不死板规定必须用哪个具体写法(模型自己似乎偏爱 Zod,但我们并没有强行指定库)。

Agent 在拥有严格边界和可预测结构的环境中效率最高,因此我们围绕一套严格的架构模型构建了整个应用。每个业务领域(domain)都被划分为一组固定的层级,依赖方向受到严格校验,只允许有限的合法调用边。这些约束全部通过自定义 Linter(当然也是由 Codex 编写的!)和结构化测试来机械地强制执行。

下图展示了这套规则:在每个业务领域内部(例如应用设置),代码只能沿着固定层级“向前”依赖(Types → Config → Repo → Service → Runtime → UI)。横切关注点(身份认证、连接器、遥测、功能开关)则只能通过唯一的显式接口 Providers 进入。任何违反该依赖方向的调用都是被禁止的,并会被自动化检查直接拦截。

具有明确横切边界的分层领域架构

在传统团队里,这类严格的架构约束往往要等到团队扩张到几百名工程师时才会推迟引入。但对于编码 Agent 来说,这是从一开始就必须具备的先决条件:只有先立好护栏,推进速度才不会掉下来,架构才不会逐渐腐化漂移。

在实践中,我们用自定义 Linter 和结构测试来强制执行这些架构规则,并配合一小套“品味不变式(taste invariants)”。例如,我们通过静态检查强制要求结构化日志记录、Schema 与类型的命名规范、单文件大小上限,以及特定平台的可靠性要求。而且因为这些 Linter 都是我们自己定制的,我们在编写报错信息时,会直接把修复指南注入到报错文本里,让 Agent 看到报错就能立刻知道怎么改。

在以人为主的工作流里,这么细的静态规则可能会让人觉得教条、束手束脚;但在 Agent 工作流里,它们成了效率倍增器:一旦编码成规则,就能瞬间在所有地方同时生效。

与此同时,我们也明确区分了“哪些地方必须管死”和“哪些地方应该放权”。这很像管理一个大型平台工程组织:在全局层面严格把控边界,在局部层面充分赋予自主权。你死守的是边界、正确性与可复现性;而在这些边界之内,无论是团队还是 Agent,在具体解法的表达上都拥有充分的自由。

Agent 生成的代码未必总是完全贴合人类的个人代码风格偏好,但这没关系。只要产出是正确的、可维护的,并且对后续运行的 Agent 清晰易读,就已经达标了。

人类的工程品味则会持续不断地反馈回系统中:代码审查评论、重构 PR 和用户遇到的 bug,都会被沉淀为文档更新,或者直接编码进检查工具里。每当发现仅靠文档管不住时,我们就把规则提升为代码。

吞吐量改变了合并理念

随着 Codex 的产出吞吐量大幅增加,许多传统的工程惯例反而成了阻碍。

我们在这个仓库中尽可能减少了阻塞式的合并关卡。PR 的生命周期非常短;偶发的测试抖动(flaky tests)通常会通过后续重跑来解决,而不是让它无限期卡住主干推进。在一个 Agent 产出吞吐量远超人类注意力的系统里,事后快速修补的成本很低,而排队等待的成本极高。

在低吞吐量的传统开发环境里,这样做或许是不负责任的;但在高频 Agent 环境中,这往往才是正确的权衡。

“由 Agent 生成”到底意味着什么?

当我们说整个代码库是由 Codex Agent 生成的,我们指的是代码库里的所有一切。

Agent 的产出涵盖了:

  • 产品业务代码与测试
  • CI 配置与发布工具
  • 内部开发者工具
  • 文档与设计演进历史
  • 评估框架(Eval frameworks)
  • 代码审查评论与回复
  • 管理代码仓库本身的自动化脚本
  • 生产环境监控仪表盘的定义文件

人类始终在环(in the loop),但工作所处的抽象层级已经和过去截然不同。我们负责排列优先级、把用户反馈转化为验收标准,并对最终结果进行验收。每当 Agent 卡壳时,我们会把它视为一个重要信号:找出当前环境里缺了什么——是缺工具、缺护栏约束,还是缺文档?然后把缺失的一环补回代码仓库里,并且始终让 Codex 自己去写出对应的修复。

Agent 可以直接操作我们的全套标准开发工具:它们自己拉取审查意见、在代码行内回复评论、推送更新提交,甚至经常自己执行 Squash 并合并自己的 PR。

不断提升的自主水平

随着软件开发中越来越多的环节被直接编码进系统——包括测试、验证、审查、反馈处理与故障恢复——这个代码仓库最近跨过了一个重要的临界点:Codex 已经能够端到端地独立驱动一个完整的新功能开发。

现在,只需要给出一句提示词,Agent 就可以自主完成以下全流程:

  • 验证代码库当前状态
  • 复现已报告的 bug
  • 录制一段演示故障现象的视频
  • 编写并实施修复方案
  • 实际运行应用程序来验证修复效果
  • 录制第二段视频,演示修复后的效果
  • 发起 Pull Request
  • 响应来自其他 Agent 和人类的审查反馈
  • 检测并修复构建失败
  • 只有在真正需要主观判断时才请求人工介入
  • 合并改动

当然,这种高度自主的表现很大程度上依赖于该仓库特定的架构与工具链投入;如果没有类似的Harness(运行环境与护栏)建设,不应默认它在任意仓库都能直接复现——至少目前还不行。

熵增与“垃圾回收”

完全自主的 Agent 也带来了新的问题。Codex 会忠实地复制代码仓库里已经存在的模式——包括那些写得参差不齐或不够理想的模式。随着时间推移,这不可避免地会导致架构漂移与熵增。

起初,我们是靠人工来解决这个问题的:团队以前每周五(占一周工作时间的 20%)都要专门花一天时间来清理“AI 留下的残渣(AI slop)”。可想而知,这种做法根本无法扩展。

于是,我们转而把一套被称为**“黄金原则(Golden Principles)”**的规范直接编码进仓库,并建立了一套周期性的自动清理流程。这些原则是带有明确主观品味、可机械执行的规则,目的是让代码库始终保持整洁一致,方便后续 Agent 继续在上面工作。例如:

  1. 我们优先使用共享的工具包(utility packages),而不是到处手写零散的辅助函数,以便将核心不变式集中管理;
  2. 我们禁止“盲猜式(YOLO-style)”地探测数据结构——要求必须在边界处做校验,或者依赖强类型的 SDK,防止 Agent 基于臆测的数据结构写出脆弱的代码。

我们会定期在后台运行一组 Codex 巡检任务,自动扫描偏离规范的代码、更新质量评分,并发起针对性的重构 PR。这类重构 PR 绝大多数都能在 1 分钟内完成审查并自动合并。

这套机制就像编程语言里的垃圾回收(Garbage Collection)。技术债就像一笔高息贷款:与其让债务不断复利累积、最后不得不停下所有业务痛苦地大重构,不如每天持续地小额偿还。人类的工程品味一旦被捕捉下来,就能持续不断地作用在每一行代码上,让我们能够按天发现并消灭不良模式,而不是任由它们在代码库里蔓延数周。

我们仍在探索的问题

到目前为止,这套策略在 OpenAI 内部产品的发布与落地过程中表现得非常好。为真实用户打造真实产品,帮我们把工程投入牢牢锚定在现实需求上,并引导我们走向长期的可维护性。

但也有一些问题我们还没有完整答案:在一个 100% 由 Agent 生成的系统里,架构的连贯性在更长的时间尺度上会如何演变?人类的判断力究竟在哪些环节能发挥最大杠杆作用?又该如何把这种判断力更好地编码进系统?当底层模型的能力随着时间继续大幅跃升时,这套 Harness 体系又会如何演化?

但有一点已经非常清晰:构建软件依然需要严格的工程纪律,只不过这种纪律不再体现在手写每一行代码上,而是体现在支撑 Agent 运行的脚手架与护栏(scaffolding)上。 那些用来保持代码库一致性的工具、抽象层和反馈回路,正变得前所未有地重要。

我们当下最核心的挑战,都集中在如何设计环境、反馈回路和控制系统,去帮助 Agent 实现我们的目标:大规模地构建并维护复杂、可靠的软件。

随着像 Codex 这样的 Agent 在软件生命周期中承担起越来越大的比重,这些问题只会愈发关键。希望分享这些早期的实战经验,能帮你看清该把精力投入在哪里,让你也能直接开始构建自己的 Agent 优先工程体系。