文章

从一行 Bash 到一级原语:Ralph Loop 与 AI 编程代理的自主循环演进

从一行 Bash 到一级原语:Ralph Loop 与 AI 编程代理的自主循环演进

2025 年 7 月,Geoffrey Huntley 写了一篇博客,提出了一个荒谬得不像正经方案的想法:把 AI 编程代理塞进一个 bash 死循环里,让它不停地跑,直到完成为止。

八个月后,OpenAI 和 Anthropic 先后在自己的 CLI 工具中内置了这个模式,命名为 /goal

从一个社区玩笑到两大厂商的一级原语,这个故事值得讲清楚。

一行 Bash 改变一切

Ralph Loop 的全部实现,长这样:

1
while :; do cat PROMPT.md | claude -p --dangerously-skip-permissions; done

就这么一行。把一个 prompt 文件反复喂给 AI 代理,让它每次退出后重新开始,直到目标达成。名字来自《辛普森一家》的 Ralph Wiggum——那个不太聪明但永远不放弃的小孩。

第一次看到的人反应都一样:就这?但恰恰是这个”就这”,才是精髓。

问题出在哪里

用 AI 编程代理干过活的都知道,它会越聊越蠢。你描述需求,它尝试实现,你说”不对,改成这样”,它再改,你指出边界情况,它修了这个又坏了那个。十几个来回之后,代理开始忘记你十分钟前说过的话,或者凭空编造三个版本前的函数签名。

Steve Kinney 给这个现象起了个精准的名字:Context Rot(上下文腐烂)。Steve Kinney 在他的博客里写道:

每次失败尝试、每个跑偏的纠正都留在对话历史里。模型在处理当前问题之前,得先消化所有这些噪音——好的、坏的、以及”算了忘了我刚才说的”。

除了上下文腐烂,还有两个稳定故障模式:

  • 目标漂移:模型开始解决和你最初描述的略有不同的任务
  • 代理信号坍塌:模型在只满足表面指标时(比如测试通过),就宣布”大功告成”

Ralph Loop 的回答

Ralph Loop 用一种粗暴但有效的方式解决了所有三个问题:不要一个长会话,用很多个短会话

每次迭代都从干净的上下文窗口开始。代理不知道自己上一轮的挣扎、挫败和弯路。它只看到:

  • 相同的 PROMPT.md(稳定输入)
  • 已经被修改过的代码库(收敛中的世界)
  • git log(完整的修改历史)
  • 一个进度文件(当前状态)

Huntley 把这个策略叫做 “deterministically bad in a nondeterministic world”——在非确定性的世界里确定性地糟糕。每次迭代都平庸地可靠,但时间一长,代码库就会被打磨到正确。

四个组件

一个完整的 Ralph Loop 只需要四样东西:

  1. Bash 脚本——编排器,管循环、检查完成信号、设迭代上限
  2. PROMPT.md——大脑,定义做什么、怎么验证、什么是”完成”
  3. 文件系统——记忆层,进度文件 + git 历史
  4. 完成信号——退出条件,通常是代理输出一个特定标记如 <promise>COMPLETE</promise>

最难写好的是 PROMPT.md,也最重要。它本质上是在定义”代理的整个行为规范”。

YC 黑客松的成名战

Ralph Loop 真正出圈是在 Y Combinator 的一场黑客松上。参与者在 GCP 实例上跑起循环,然后去睡觉。第二天早上醒来:

  • 6 个仓库,1,100 个 commits
  • Browser Use 库几乎完全从 Python 移植到了 TypeScript
  • 总花费 $800,相当于 $10.50 一小时雇了个开发者

还有一个工程师分享的实案:他用 Ralph Loop 以 $297 的 API 成本交付了价值 $50,000 的合同项目,已测试、已审查、已交付。

这笔账算得太清楚了。

从社区模式到厂商内置

Ralph Loop 在社区传播得很快。snarktank/ralph 拿到 9,200+ stars,各种工具都有了适配版本。但真正标志性的转折是两大厂商的官方采纳。

Codex /goal:先发制人

2026 年 1 月,OpenAI 发布了技术博文 Unrolling the Codex Agent Loop,详细拆解了 Codex 的代理循环架构。三个月后,4 月 30 日,Codex CLI 0.128.0 直接把 Ralph Loop 做成了内置命令:

1
codex /goal "Make all tests pass and commit each green checkpoint"

Codex 的 /goal 本质上把社区用 bash 手搓的那套东西全部搬进了运行时:

  • 目标状态机:pursuingachieved / paused / unmet / budget-limited
  • 暂停/恢复/清除的完整生命周期管理
  • 内部由 continuation.mdbudget_limit.md 两个 prompt 模板驱动
  • token 预算作为硬性上限(比迭代次数更实用)

Greg Brockman 在 X 上用一句话总结:“codex now has a built in Ralph loop++.”

目前 Codex 的 /goal 仍是实验性功能,需要手动启用:

1
2
3
# ~/.codex/config.toml
[features]
goals = true

有用户已经在设备驱动项目上跑出了 14 小时不间断的记录。

Claude Code /goal:评估者分离

Anthropic 紧随其后,在 5 月 12 日通过 Claude Code v2.1.139 发布了 /goal。但实现路径和 Codex 有一个关键区别。

Claude Code 的 /goal 引入了独立的评估者模型

1
/goal all tests in test/auth pass and the lint step is clean

工作流程是这样的:

  1. 你设置完成条件
  2. Claude 开始第一轮工作(条件本身即为指令)
  3. 每轮结束后,一个小而快的模型(默认 Haiku)独立评估条件是否满足
  4. 评估器返回 yes/no + 理由
  5. 如果 no → 理由作为下一轮的指导
  6. 如果 yes → 目标清除

执行者和评估者是两个不同的模型。 这意味着正在干活的 Claude 说了不算,得另一个模型点头才算完成。这个设计直接对付了 Ralph Loop 最怕的”代理信号坍塌”问题——干活的人不能自己给自己打分。

本质上,Claude Code 的 /goal 是一个 session-scoped 的 prompt-based Stop hook 的封装。但 Anthropic 把它封装得很干净,开箱即用。

两条路线,同一个终点

虽然都叫 /goal,但 Codex 和 Claude Code 的设计哲学有微妙差异:

Codex /goal 更像一个”给当前线程附加一个长期目标”。目标是持久化的,可以跨越会话恢复,偏重于目标的存续和追踪。目前仍是实验性功能,文档措辞相对保守。

Claude Code /goal 更像一个”为当前会话设置可验证的停止条件”。每轮自动评估、自动继续,条件未满足就不停下来,直到评估器说”够了”。已经是正式文档页,不用额外启用。

一个更有意思的对比是在 /goal 出现之前,社区已经在做的事情。Ralphable 有一篇深度分析把事情说得很透彻:

/goal 是 OpenAI 的赌注——整个 bash 循环那一层应该放进运行时,而不是留在用户空间的 shell 脚本里。

但 Thomas Wiegold 在他的分析中也指出了一个关键分歧:bash 版 Ralph Loop 每次迭代都有干净上下文,而 /goal(无论 Codex 还是 Claude Code)都在单一会话内累积上下文。对于过夜运行这种超长任务,原始的 bash 版本可能反而更可靠。

怎么用好 /goal

不管用哪个工具,写好一个 /goal 条件需要三个要素:

  1. 可衡量的终点——测试退出码 0、文件计数为 N、队列为空
  2. 明确的验证方法——npm test 输出包含 PASS、git status 干净
  3. 不可变的约束——不能修改某些文件、不能超过 N 轮

一个好条件的例子:

1
2
/goal all tests in test/auth pass, npm run lint exit code is 0,
no files in src/legacy/ are modified, stop after 20 turns

反过来,”把项目优化一下”这种条件只会让代理在黑暗中乱撞。

适合 /goal 的场景:

  • 测试修复,直到全部通过
  • API 迁移,直到所有调用点更新
  • 批量 lint/类型错误清理
  • Issue 队列处理

不适合 /goal 的场景:

  • 需求本身模糊
  • 需要频繁人工判断
  • 涉及高风险的删除或数据变更

一个简单的判断标准:如果你能写出”运行什么命令、看到什么结果、哪些文件不能碰”,那就是 /goal 的好候选。

智能在循环中,不在代理中

Ralph Loop 最大的启发不是技术上的,而是思维方式上的。

整个 2025-2026 年,AI 编程工具的核心叙事一直是”让模型更聪明”。但 Ralph Loop 证明了另一条路:让循环更聪明,代理可以笨一点。

代理每次迭代都犯同样的错误类型,都犯同样的认知偏差,但没关系——因为上下文是干净的,代码库是收敛的,git 历史是诚实的。循环本身才是那个”记住一切、不断推进、知道什么时候该停”的智能体。代理只是一个可替换的执行器。

从一行 bash 到两大厂商的一级原语,这个过程只用了不到一年。但我觉得故事的终章还没有到来——当 /goal 成为标配,当”条件工程”取代”提示工程”成为新的核心技能,当编程代理真正可以overnight交付一个完整功能……那个时候我们回头再看 Ralph Wiggum 这个名字,会觉得再贴切不过了。

它不够聪明,但它真的能帮忙。

参考

本文由作者按照 CC BY 4.0 进行授权