朋友问出的问题:怎么交付一个手写代码为 0 行的产品
有人问这篇博文的作者 Shir Meir Lador:怎么才能交付一个手写代码为 0 行的软件产品?她当时的回答是给不出简单答案,于是回头查了一圈。
这个问题听着像噱头,指向的却是一个很具体的工程困境。AI 已经能一口气写出成百上千行代码,但没有人愿意逐行读完再决定要不要合并。读不完,就等于没有验证;没有验证的代码不能靠近生产环境。可如果每次都要人把代码通读一遍,自动化的好处又被抵消掉了。
她找到的答案不在模型身上,而在包着模型的那层东西:harness。这个词原本指马具——套在马身上控制方向的一整套东西,缰绳、眼罩、赛道。模型是那匹力气很大的马,harness 是让它别冲进看台的结构。
类比harness套在赛马身上的缰绳、眼罩和赛道:不改变马的力气,只决定力气往哪里使。
这层“马具”到底由哪些零件组成,一个更极端的实验给了清单。
0 行手写代码的实验里,人写的是什么
Google 的这篇博文转述了 OpenAI 的一篇博客:一个 3 人工程师团队构建并交付了一个软件产品的内部 beta,手写代码是 0 行。按这份转述,应用逻辑、测试、CI 配置、文档、可观测性和内部工具,全部由 Codex 写出。他们没有写应用,写的是 harness。
harness 的准确含义,博文引述了作者同事 Arthur Thompson 的说法:对 Agent(智能体)来说,harness 由包裹 LLM(大语言模型)的全部确定性组件构成。Balaji Subramaniam 在一篇博客里把这些组件分成四类:编排层、执行沙箱、状态持久化、验证工具。
模型的输出是概率性的:同样的输入,这次给出正确答案,下次未必。这四类组件的共同点是不靠概率做事。编排层决定先跑什么、后跑什么、失败时走哪条分支;沙箱决定进程能碰到哪些文件;状态持久化把中间结果留下来;验证工具负责判断这一步算不算过。把不确定的部分关进一个确定性的框架里,这是 harness 工程的全部野心。
需要说清楚的是,“0 行手写代码”这条信息经过了两次转述:Google 的博文转述 OpenAI 的博客。OpenAI 那篇博客本身、“内部 beta”的定义、产品规模、交付周期、人力到底介入多少小时,以及 0 行这个数字按什么口径审计,都没有随这条说法一起公开。
包住模型的四类确定性组件
| 组件 | 它替模型决定的事 | 在博文示例里的对应物 |
|---|---|---|
| 编排层 | 先跑哪一步、失败后走哪条分支 | Workflow 图:START → agent → 测试节点 |
| 执行沙箱 | 进程能访问哪些文件 | 工作区限定为 sandbox 目录,策略只放开沙箱内访问 |
| 状态持久化 | 中间结果与历史轨迹留在哪里 | 轨迹目录 save_dir,以及状态里的迭代计数 |
| 验证工具 | 这一步算不算通过 | execution_test_node 读取测试结果并决定路由 |
3 人团队用 0 行手写代码交付内部 beta,但验收口径没有公开
一个 3 人工程团队能否在不出手写任何代码的情况下,构建并交付软件产品的内部 beta?
- 实验设置
- 团队不直接编写应用,而是围绕 Codex 设计 harness:编排层、执行沙箱、状态持久化与验证工具;由 Codex 生成应用逻辑、测试、CI 配置、文档、可观测性与内部工具。
- 样本与轮次
- 未知。博文只称团队 3 人、产出 1 个内部 beta,未给出迭代轮数、提交量或测试次数。
- 模型
- Codex
- 判定指标
- 手写代码行数(声称 0 行)以及是否交付内部 beta。
- 对照或基线
- 无明确对照。
结果:按博文转述,团队以 0 行手写代码构建并交付了内部 beta,全部代码由 Codex 写出。
边界:信息来自 Google 博文对 OpenAI 博客的转述,那篇 OpenAI 博客本身没有随这条转述一起提供;没有样本量、验收标准、人力介入小时数和环境细节,无法评估可复现性与外推范围。
四类组件里,最容易讲清也最容易被忽略的是沙箱:它决定了 agent 能碰什么。
先划边界:为什么沙箱比“别删数据”这句叮嘱可靠
边界不能靠叮嘱。博文给出的示例代码把 agent 限制在一个工作区里运行:配置里指定 workspaces=[sandbox_dir],策略用 policy.allow_all()——沙箱里面放手做,沙箱外面去不了。配套配图的标题写得很直白:这条策略让 agent 只能访问 Sandbox 文件夹。
同一段配置里还有 save_dir="./trajectories",把 agent 走过的轨迹存到磁盘。这一步容易被忽略,但它解决的是第二个问题:agent 下一次开工时,不必从零猜测自己上次做过什么。
为什么不用一句“不要删生产数据”的提示词代替?因为提示词是给模型的输入,模型可能忽略;文件系统权限是操作系统层面的判断,不经过模型的意愿。这也是 harness 这个说法的关键:约束不是建议,是结构。
边界解决了“别碰坏东西”,但真正让 agent 能连着干活的,是出错之后发生什么。
把失败变成输入:测试通过就结束,失败就带着报错回去
出错之后,报错要回到 agent 手里。博文里的示例用 Google 的 ADK 2.0 搭了一个最小工作流:START 先进 agent,再由 execution_test_node 这个测试节点判断结果。测试通过,路由到 END,工作流结束;测试没通过,就把原始报错拼成一条新消息,通过 loop_back 路由送回 agent,让它拿着报错重写。
这套结构常被称为修复循环,它把过去由人做的动作变成了程序:复制报错、重新描述问题、再让模型试一遍。博文里有一句话说得直接:在普通聊天窗口里,人本身就是 harness,负责贴错误日志、盯着模型别跑偏;把这件事写成代码,系统才能自己照看自己。
循环旁边必须站着一个能喊停的东西。示例里的测试节点每次执行都会把 iteration_count 加一,一旦超过 5 就直接结束工作流,代码注释把它叫做 kill switch,也就是急停开关,理由是 agent 可能卡在反复改坏的循环里。同样的护栏在配套的参考仓库里用的是 10 次。5 和 10 都是示例里随手定下的值,没有给出调参依据,最好把它们读成“这里应该有个上限”,而不是“上限就是 5”。
类比kill switch跑步机上的急停拉绳:不是为了让人跑得更快,而是为了在摔倒前让机器停下来。
一次失败的修复回环走哪几步
agent 在沙箱里改代码
agent 按任务写新代码,或修改沙箱里已有的文件。
测试节点判断结果
execution_test_node 递增迭代计数,读取测试是否通过与反馈内容。
通过就结束,失败就回环
通过时路由到 END;失败时把报错作为消息经 loop_back 送回 agent。
超过上限强制结束
示例在迭代次数超过 5 时直接结束;配套仓库的同类上限是 10。
依据博文示例与配套仓库整理的流程示意;顺序不代表实际耗时比例,也不代表真实运行数据。
一个被限制在 ./sandbox 的 agent,怎么靠测试节点自我修复
用 LocalAgentConfig 把 Antigravity agent 的访问范围限制在 ./sandbox 工作区,轨迹写入 ./trajectories,再把遗留代码放进沙箱,用单元测试驱动修复。
- 定义围栏与记忆目录
配置 policies、workspaces=[sandbox_dir] 与 save_dir=save_dir,把 agent 的访问范围限定在沙箱内,并把走过的轨迹存到磁盘。
LocalAgentConfig( ... workspaces=[sandbox_dir], ... save_dir=save_dir,)
- 测试节点校验
execution_test_node 读取测试是否通过与反馈内容,并把迭代计数加一。
iteration_count = ctx.state.get("iteration_count", 0) + 1
- 失败回环
测试未通过时,把报错内容作为新消息回传,并以 loop_back 路由回到 agent。
actions=EventActions(route="loop_back")
- 急停
迭代次数超过 5 时直接结束循环。
if iteration_count > 5: # The Kill Switch: The agent is stuck. Stop the loop.
- 成功收束
测试通过时返回 END 路由,工作流结束。
if test_passed: # Success! End the workflow.
结果:工作流在“agent 写码 → 测试节点校验 → 失败回环或成功结束”之间循环,并由迭代上限强制终止。
不能据此证明:这是博文里的教学示例代码,没有给出运行结果、通过率、耗时或成本数据,不能证明这套 harness 在真实项目中的可靠性;迭代阈值 5 也没有调参依据。
基于证据重建事件顺序,非逐字对话
可这套自洽的循环能保证的东西,比看上去少得多。
这套循环的三处软肋:谁验证、循环多久、口径多细
第一处软肋是停止条件。同一发布方的循环工程材料把“失控循环”列为失败模式第一名,理由很实际:token 是要花钱的。没有硬性上限的自主循环,只是把不确定性换成了账单。那份材料还列了目标不可检验、复杂度溢出两条,并认为复杂度溢出正是从循环升级为图的时机。
第二处软肋是验证者。让 agent 给自己打分,在那份材料里被比作“让幼儿园小朋友批改自己的作业”;给出的办法是让 agent A 检查 agent B 的工作。但这个办法本身也受到质疑:同一模型家族的评审者可能带着相似盲点,评论区的反驳正指向这一点,而材料里没有给出这个问题已经解决的证据。
第三处软肋是可用数据太少。沙箱示例、测试节点、急停开关都是教学代码,没有提供测试通过率、平均迭代轮数、耗时或 token 成本;ADK 2.0 与 Antigravity SDK 的正式发布状态、版本号和许可信息也没有一并给出;“在本机跑通这套自愈循环不到五分钟”是博文作者的自我陈述,没有环境、网络条件和计时方法。整套方法的技术细节来自 Google 官方账号及其配套仓库,目前没有独立第三方复现:能确认的是这套结构讲得清,而不是效果被验证过。
同一个回环模式也被用在不写代码的任务上:标题生成
用户给一个主题,工作流先由 generate_headline agent 生成标题。
- 生成
generate_headline agent 根据用户给出的主题写出标题。
- 评估
evaluate_headline agent 把标题评为与科技相关或不相关,并在不相关时给出反馈。
- 条件路由
route_headline 依据评分决定是否回跳;结果为不相关时,回到 generate_headline 重试。
If the headline is "unrelated", the workflow loops back to the generate_headline agent, passing the feedback so it can try again.
- 结束
生成与科技相关的标题后,循环结束。
结果:工作流在生成与评估之间迭代,直到产出符合条件的结果。
不能据此证明:这是官方示例文档,没有运行数据与迭代次数统计,不能说明该模式的收敛速度或失败率;它用在标题任务上,不能直接类推到代码修改任务的效果。
基于证据重建事件顺序,非逐字对话
把这些限制放在一起看,人在这套体系里的位置反而更清楚了。
0 行手写代码之后,人负责的是环境而不是逻辑
回到开头那个问题:0 行手写代码的产品靠什么交付?靠的是把工作从写逻辑挪到设计环境。人不再决定每一行代码长什么样,转而决定 agent 能碰哪些文件、失败时拿到什么信息、跑到第几步必须停、什么叫做完。
博文把这件事总结成三条。第一,设定严格边界,不让 agent 去猜自己能碰什么。第二,构建修复循环,把构建或测试失败转成干净的日志喂回去。第三,给 agent 一张地图而不是一本说明书:不要用巨大的指令文件把它压垮,而是把仓库结构理清楚,让它自己逐步找到上下文。
对刚进入这个领域的人来说,这个转向有一层不太直观的含义。讨论 agent 时,最容易被拿来比较的是模型能力:谁的代码写得好、谁的上下文更长。但在这套结构里,模型是跑在赛道上的那匹马,赛道、眼罩和缰绳是另一回事。同一个模型配一套糟糕的 harness,结果可能是反复改坏、烧掉额度、最后一无所获;配一套边界清楚、反馈干净、有硬停止条件的 harness,它才能连续工作而不需要人守在旁边。
所以“0 行手写代码”这句话的重心不在 0。手写代码确实归了零,但设计、审查和终止条件的位置反而变多了,只是这些工作不再以代码行的形式出现。能不能安全地放开自主循环,取决于这些看不见的工作做得多细——这个判断不会因为模型变强而自动成立。



