Agent 工程 - 长时运行之 Ralph Agent

2 39.5~50.8 min

最近 AI 圈子有点疯,一个叫 Ralph Wiggum 的东西突然就火了,火到什么程度呢?有人说它是 “最接近 AGI 的玩意儿”,有人用它通宵干活,第二天早上醒来发现几个代码仓库都妥妥地建好了。甚至还有人专门为它发了个加密货币 $RALPH。

这名字听着是不是有点耳熟?没错,就是《辛普森一家》里那个脑子不太灵光,但总是蜜汁自信,坚持不懈的小胖子 Ralph Wiggum。

INsm1_lvrE4rQPmTkgC_N.jpg

一个动画里的憨憨角色,怎么就成了 AI 圈的当红炸子鸡?

一、 Ralph Wiggum 是个啥?

简单来说,Ralph Wiggum 不是一个具体的模型或软件,而是一种工作方法、一种技术理念。它的核心思想就一个字:循环

具体点说,就是用一个脚本把 AI 编程助手(比如 Anthropic 的 Claude Code)框在一个循环里,给它一个任务,让它不停地尝试、失败、再尝试,直到任务完成为止。它不会因为一次失败就停下来等你,而是会把失败的日志、错误信息作为下一次尝试的“养料”,自己琢磨怎么解决。

这个看似简单的想法,在社区的演化中,其实分裂成了两个版本,我称之为“两个拉尔夫的故事”。

1.1 野生 Ralph

故事的起点,在 2025 年的澳洲乡下。一位名叫 Geoffrey Huntley 的老哥,他是个资深开源开发者,后来跑去养山羊了。在用 AI 助手写代码的时候,他被一个问题搞得不胜其烦——“人在环路中”(human-in-the-loop)的瓶颈。

当时的 AI 助手,更像个听话的实习生。你让它干活,它干砸了,就会停下来,把一堆错误甩给你,等你分析完,再给它新的指令。这个过程极其打断心流,效率很低。

Huntley 老哥一琢磨,能不能让 AI 自己“死磕”呢?于是,他写了一个极其简单粗暴的 5 行 Bash 脚本,并用他最喜欢的《辛普森一家》角色 Ralph Wiggum 来命名。这个脚本的核心长这样:

while :; do cat PROMPT.md | claude-code ; done

这行代码的意思就是:

  1. while :; do ... done:创建一个无限循环,也就是俗称的“死循环”。

  2. cat PROMPT.md:读取一个叫做 PROMPT.md 的文件,这个文件里写着要 AI 干的活儿。

  3. |:这是管道符,把前面命令的输出,作为后面命令的输入。

  4. claude-code:调用 AI 编码助手。

连起来就是:不断地把任务指令喂给 AI,让它执行,执行完了再把指令喂给它,周而复始,永不停止。

1768731295499_3ea367ed-cae3-454a-840f-134531dea1fd.jpg

这套打法的精髓,Huntley 称之为“上下文压力锅”(contextual pressure cooker)。它不做任何花里胡哨的处理,直接把 AI 的所有输出——无论是成功的代码、失败的日志、还是胡言乱语(hallucinations)——全部通过命令行历史再次塞回给 AI。AI 在下一次迭代中,就能“看到”自己上次干的好事,包括完整的错误堆栈。

这种感觉,就像你把一个犯了错的人关进小黑屋,屋里只有他自己和一堆他搞砸的烂摊子,不收拾干净就不准出来。AI 被迫直面自己的失败,没有任何“保护措施”。Huntley 和其他人认为,这种“天真的坚持”(naive persistence)和未经过滤的反馈,会给模型施加巨大的压力,逼着它在绝望中“悟”出正确的解决方案,只为了能跳出这个该死的循环。

这就是最初的、野生的 Ralph,它的哲学是:大力出奇迹,用坚持和蛮力压倒一切困难。

1.2 Anthropic Ralph

Huntley 的这个“骚操作”很快就在开发者社区传开了。到了 2025 年末,Anthropic 公司(Claude 的开发商)的 Claude Code 负责人 Boris Cherny,将这个黑客方法正式化,做成了一个官方插件 ralph-wiggum

然而,官方的实现和 Huntley 的野生版在哲学上有了微妙但关键的区别。如果说 Huntley 的版本是混乱、野蛮生长的,那 Anthropic 的版本就是“无菌化”、可控的企业级方案。

官方版的核心理念,从“大力出奇迹”转变成了“失败即数据”(Failures Are Data)。它不再是简单粗暴地把所有东西都怼回去,而是引入了一个更精巧的机制——“停止挂钩”(Stop Hook)。

这个官方插件的工作流程是这样的:

  1. 拦截退出(Intercept the Exit):当 Claude Code 认为自己已经完成了任务,尝试退出命令行时,ralph-wiggum 插件会像个门卫一样把它拦住。

  2. 验证承诺(Verify Promise):插件会检查 Claude 的输出里,是否包含一个特定的、预先定义好的“完成承诺”(Completion Promise),比如一个 XML 标签 <promise>COMPLETE</promise>。这个承诺就像是任务完成的“接头暗号”。

  3. 注入反馈(Feedback Injection)

    1. 如果找到了“完成承诺”,门卫放行,循环结束,任务成功。

    2. 如果没找到,说明任务还没搞定(比如,单元测试没通过)。这时,插件会把失败的结果(比如测试报告)格式化成一个结构化的数据对象,然后连同最初的提示,一起重新提交给 Claude,开始新一轮的迭代。

你看,区别就出来了:

  • 野生版:反馈是原始、混乱、无差别的。AI 看到的是一整个终端的输出历史。

  • 官方版:反馈是结构化、干净、有针对性的。AI 看到的是明确的失败原因。

这就引出了“两个拉尔夫”的关键选择:

  • 亨特利版 Ralph(Bash 脚本/社区 Forks):更适合混乱的、创造性的探索。你想让 AI 通过纯粹的、不受约束的“死磕”来解决问题,可能会有意外之喜。

  • 官方版 Ralph(Anthropic 插件):更适合企业级工作流。它被严格的 Token 限制和安全钩子约束,目标是可靠地修复一个构建失败的问题,而不用担心 AI 陷入无限的幻觉循环中。

一句话总结:Huntley 证明了循环是可行的;Anthropic 证明了循环可以是安全的。

二、 Ralph 如何实现「无人值守」?

光说理念有点虚,咱们直接上干货,看看一个典型的、社区驱动的 Ralph Wiggum 工作流到底是怎么搭建的。这套系统设计得相当精巧,充满了后端开发的智慧:通过简单的工具和约定,构建一个强大的自动化系统。

下面我们以 Ryan Carson 等社区贡献者完善的方案为例,来拆解它的核心组件和工作流程。

2.1 Ralph 的工具箱

要让 Ralph 跑起来,你需要在你的项目里准备一个专门的文件夹,通常是 scripts/ralph/,里面装着它全部的“作案工具”:

scripts/ralph/
├── ralph.sh          # 核心驱动脚本,那个无情的循环就在这里
├── prompt.md         # AI 的行动指南,每次循环都要读一遍的“圣经”
├── prd.json          # 任务清单 (PRD),AI 的 "Jira Board"
└── progress.txt      # 进度与学习日志,AI 的短期记忆

这四个文件各司其职,构成了一个最小但完整的自动化闭环:

  • ralph.sh引擎。负责启动和维持循环,调用 AI,检查结果,决定是继续还是收工。

  • prompt.md大脑。定义了 AI 在每一次迭代中的思考模式和行为准则,是整个系统里最重要的“提示工程”产物。

  • prd.json待办事项。以结构化的 JSON 格式定义了所有需要完成的子任务(User Stories),包括优先级和完成状态。

  • progress.txt记忆。记录了 AI 在之前迭代中都干了什么、学到了什么,避免重复犯错,并实现知识的“复利”增长。

接下来,我们把每个文件都拆开,仔细看看里面的门道。

2.2 ralph.sh 脚本详解

这是驱动一切的发动机。我们来看一个典型的 ralph.sh 实现:

#!/bin/bash
set -e
​
# 接收一个可选参数作为最大迭代次数,默认为 10 次
MAX_ITERATIONS=${1:-10} 
# 获取脚本所在的目录,方便引用其他文件
SCRIPT_DIR="$(cd "$(dirname \
  "${BASH_SOURCE[0]}")" && pwd)"
​
echo "🚀 Starting Ralph"
​
# 启动一个从 1 到 MAX_ITERATIONS 的循环
for i in $(seq 1 $MAX_ITERATIONS); do
  echo "═══ Iteration $i ═══"
  
  # 这是整个脚本最核心的一行
  OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" \
    | amp --dangerously-allow-all 2>&1 \
    | tee /dev/stderr) || true
  
  # 检查输出中是否包含“完成承诺”
  if echo "$OUTPUT" | \
    grep -q "<promise>COMPLETE</promise>"
  then
    echo "✅ Done!"
    exit 0 # 成功退出
  fi
  
  # 短暂休眠,避免 API 调用过于频繁
  sleep 2
done
​
echo "⚠️ Max iterations reached"
exit 1 # 达到最大迭代次数,异常退出

这段脚本虽然不长,但对于后端同学来说,里面有很多熟悉的味道。我们来逐行分析一下:

  • set -e:这是一个非常重要的安全设置。它告诉 Bash,如果脚本中任何一个命令执行失败(返回非零退出码),就立即终止整个脚本。这可以防止在某个环节出错后,后续的命令继续执行,造成更大的混乱。

  • MAX_ITERATIONS=${1:-10}:这是 Ralph 的“保险丝”。它设置了一个最大迭代次数,防止 AI 陷入死循环,把你的 API 账单烧穿。你可以通过命令行参数传入这个值,比如 ./scripts/ralph/ralph.sh 25

  • SCRIPT_DIR="...":一个健壮的脚本必备的写法,确保无论你在哪个目录下执行这个脚本,它总能正确地找到 prompt.md 等同级文件。

  • for i in $(seq 1 $MAX_ITERATIONS):主循环体,控制 Ralph 的工作轮次。

下面是最关键的一行命令,我们把它拆解开:

OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true

这是一个经典的 Unix/Linux 管道链,数据像流水一样从左向右处理:

  1. cat "$SCRIPT_DIR/prompt.md":读取 prompt.md 文件的内容,并将其输出到标准输出(stdout)。

  2. |:管道符,将左边命令的 stdout 连接到右边命令的标准输入(stdin)。

  3. amp --dangerously-allow-all:这是调用 AI 助手的地方(amp 是一个流行的 Claude Code 命令行工具)。它从 stdin 读取 prompt.md 的内容作为指令。--dangerously-allow-all 参数授予 AI 执行任意终端命令的权限,这是 Ralph 能够修改文件、运行测试、提交代码的前提,但也是巨大的安全风险,我们后面会详细说。

  4. 2>&1:这是一个重定向操作。它把标准错误(stderr,文件描述符为 2)合并到标准输出(stdout,文件描述符为 1)。这意味着,无论 AI 在执行过程中是正常输出还是报错,所有的信息都会被送到同一个输出流里。这对于“上下文压力锅”至关重要,因为我们希望 AI 看到所有的结果,尤其是错误结果

  5. | tee /dev/stderrtee 命令像一个三通管。它从 stdin 读取数据,然后做两件事:一份数据写到指定的文件(这里是 /dev/stderr,也就是标准错误流,所以你能在屏幕上实时看到 AI 的输出),另一份数据再原样输出到 stdout。

  6. OUTPUT=$(...)$(...) 是命令替换语法,它会捕获整个管道链最终的 stdout 输出,并将其赋值给变量 OUTPUT

  7. || true:这是一个容错处理。|| 表示如果前面的命令失败(返回非零退出码),则执行后面的命令。true 命令永远成功返回 0。这里加它的原因是,如果 AI 执行的某个命令(比如 npm test)失败了,会导致 amp 工具退出并返回一个错误码,如果没有 || trueset -e 就会让整个脚本立即中止。我们不希望这样,我们希望即使单次迭代失败,循环也能继续下去,所以用 || true 来“欺骗” set -e,让脚本总能继续执行。

循环的最后,通过 grep -q "<promise>COMPLETE</promise>" 来判断 AI 是否已经发出了“任务完成”的信号。如果找到了,就正常退出;如果循环次数用尽还没找到,就异常退出,提醒你去检查问题。

2.3 prompt.md 设计

如果说 ralph.sh 是骨架和肌肉,那么 prompt.md 就是灵魂和大脑。这个文件定义了 AI 在每一轮迭代中应该遵循的思维框架和行为准则。它的好坏,直接决定了 Ralph 的工作效率和质量。

这是一个精心设计的 prompt.md 示例:

# Ralph Agent Instructions
​
## Your Task
​
You are an autonomous AI software engineer. Your goal is to complete user stories by writing and modifying code. Follow these steps precisely in each iteration.
​
1.  **Read Context**:
    *   Read the task list in `scripts/ralph/prd.json`.
    *   Read the progress log in `scripts/ralph/progress.txt`. Pay close attention to the "Codebase Patterns" section at the top.
​
2.  **Select Task**:
    *   Check you are on the correct git branch defined in `prd.json`.
    *   Pick the ONE story from `userStories` with the highest priority (lowest `priority` number) where `passes: false`.
​
3.  **Implement**:
    *   Implement the changes required for that single story.
    *   Focus only on fulfilling the acceptance criteria for that story.
​
4.  **Verify**:
    *   Run the type checker (e.g., `npm run typecheck`).
    *   Run the unit tests (e.g., `npm test`).
    *   If both pass, proceed. If not, analyze the errors and fix them in the next attempt. This entire loop is one attempt.
​
5.  **Document Learnings**:
    *   If you discovered any reusable patterns, gotchas, or important conventions, update the `AGENTS.md` file in the relevant directories.
    *   Append a summary of your work and key learnings to `scripts/ralph/progress.txt`.
​
6.  **Commit & Update Status**:
    *   Commit your changes with a clear message, like: `feat: [Story ID] - [Story Title]`.
    *   Update `prd.json` to set the `passes` field for the completed story to `true`.
​
## Progress Format
​
When appending to `progress.txt`, use this format:
​
---
## [Date] - [Story ID]
- **What was implemented**: A brief description.
- **Files changed**: List of modified files.
- **Learnings**:
  - Patterns discovered that might be useful later.
  - Gotchas or tricky parts encountered.
---
​
## Codebase Patterns
​
Add any reusable patterns you find to the TOP of `progress.txt` under this heading. This is your long-term memory for this session.
​
## Stop Condition
​
**If and only if ALL stories in `prd.json` have `passes: true`, you MUST end your response with the following line and nothing else:**
<promise>COMPLETE</promise>
​
Otherwise, end your response normally without the promise tag.

这个 Prompt 的设计堪称典范,它把一个复杂的软件开发流程,拆解成了一个 AI 可以理解和执行的、确定性的算法:

  • 角色设定:开宗明义,告诉 AI 它的身份是“自主 AI 软件工程师”。

  • 指令清晰:用编号列表给出了一个严格、有序的操作流程(SOP)。这对于 LLM 来说至关重要,它减少了 AI 自由发挥导致的不确定性。

  • 上下文感知:指令 1 强制 AI 在每次开始工作前,必须先“阅读”任务清单和历史记录,确保它知道“要做什么”和“已经做了什么”。

  • 任务聚焦:指令 2 和 3 强调一次只做一个任务,避免范围蔓延(scope creep),这与敏捷开发的原则不谋而合。

  • 自我验证:指令 4 把测试和类型检查内化为工作流程的一部分,创建了一个紧密的反馈闭环。

  • 知识沉淀:指令 5 要求 AI 主动记录和总结,这是实现“知识复利”的关键。它不仅要完成任务,还要提炼经验。

  • 状态管理:指令 6 让 AI 负责维护自己的任务状态(更新 prd.json)和代码版本(git commit),形成了一个完整的、可追溯的工作流。

  • 明确的终止条件:最后,“Stop Condition”部分用极其明确和强硬的语气,定义了循环的退出信号。这种确定性的信号,是连接自动化脚本和 AI 推理的桥梁。

2.4 prd.json 与 progress.txt 设计

这两个文件是 Ralph 工作流的“数据平面”,它们将状态和记忆外部化,从而解决了 LLM 本身无状态、上下文窗口有限的问题。

prd.json

prd.json 文件本质上是一个简化的产品需求文档(Product Requirements Document)。它把一个大功能,拆分成一个个小的、可执行的“用户故事”(User Stories)。

{
  "branchName": "ralph/feature-login",
  "userStories": [
    {
      "id": "US-001",
      "title": "Add login form component",
      "acceptanceCriteria": [
        "Create a React component for the login form.",
        "Component should include email and password input fields.",
        "Component should include a 'Login' button.",
        "typecheck must pass."
      ],
      "priority": 1,
      "passes": false,
      "notes": "Use the existing UI library for form elements."
    },
    {
      "id": "US-002",
      "title": "Implement email validation on front-end",
      "acceptanceCriteria": [
        "When user types in the email field, validate its format.",
        "Display an error message if the email format is invalid.",
        "The validation should be triggered on blur."
      ],
      "priority": 2,
      "passes": false,
      "notes": ""
    },
    {
      "id": "US-003",
      "title": "Create server action for authentication",
      "acceptanceCriteria": [
        "Create a new server action to handle login.",
        "It should accept email and password.",
        "For now, just log the credentials to the console.",
        "Return a success or error message."
      ],
      "priority": 3,
      "passes": false,
      "notes": ""
    }
  ]
}

这个 JSON 文件的设计非常清晰:

  • branchName:指定了本次系列任务工作的 Git 分支,实现了开发工作的隔离。

  • userStories:一个数组,包含了所有的子任务。

  • idtitle:任务的唯一标识和简短描述。

  • acceptanceCriteria验收标准,这是重中之重。它用自然语言告诉 AI,“完成”这个任务具体意味着什么。标准越明确、越可被机器验证(如 "typecheck must pass"),Ralph 的成功率就越高。

  • priority:决定了任务的执行顺序,AI 会从数字最小的开始做。

  • passes:一个布尔标志,初始为 false。AI 每完成一个任务,就会在 prompt.md 的指导下,把这个值更新为 true这个字段是整个系统的**状态机**,当所有 passes 都为 true 时,AI 就知道大功告成,可以发出 <promise>COMPLETE</promise> 了。

通过把任务状态外部化到这个文件里,即使 AI 在某次迭代中崩溃或者整个循环被中断,下次重启时,它也能准确地知道该从哪里继续,实现了工作的可续传。

progress.txt

如果说 prd.json 是计划,那 progress.txt 就是日志。它在每次循环中,都被 AI 读取和更新,扮演着“会话记忆”的角色。

一个 progress.txt 文件的内容可能长这样:

# Ralph Progress Log
Started: 2026-01-07
​
## Codebase Patterns
- All server actions should be defined in `app/actions/*.ts`.
- Use `zod` for all data validation.
- React state for timeouts should use `useRef<NodeJS.Timeout | null>(null)`.
​
## Key Files
- Database schema: `db/schema.ts`
- Authentication logic: `app/auth/actions.ts`
---
## 2026-01-07 - US-001
- **What was implemented**: Created the basic `LoginForm` React component with email/password fields and a button.
- **Files changed**: `components/LoginForm.tsx`, `app/login/page.tsx`
- **Learnings**:
  - The project uses Tailwind CSS for styling.
  - Discovered a reusable `Button` component in `components/ui/Button.tsx`. Added this pattern to `AGENTS.md`.
---
## 2026-01-07 - US-002
- **What was implemented**: Added client-side email validation using a simple regex.
- **Files changed**: `components/LoginForm.tsx`
- **Learnings**:
  - The initial regex was too strict. Found a better one from an online resource.
  - Realized form state management is getting complex, might need a library like `react-hook-form` for future stories. This is a gotcha.
---

这个文件的作用是双重的:

  1. 作为日志:它记录了每次迭代完成的工作,方便人类开发者追溯 AI 的“心路历程”。

  2. 作为记忆prompt.md 指示 AI 在每次开始时都要阅读这个文件。这意味着,在处理 US-003 时,AI 已经知道了它在 US-001US-002 中学到的东西(比如项目使用了 Tailwind CSS,有一个可复用的按钮组件等)。这种机制让知识在迭代中不断累积,形成“知识复利”。AI 解决后面任务的效率和准确性会越来越高,因为它对代码库的“理解”在不断加深。

顶部的 Codebase Patterns 部分尤其关键。AI 会把在实践中摸索出的最佳实践、代码规范、或者“坑”记录在这里,这相当于为当前这个项目动态生成了一份“开发规范”,让后续的所有工作都遵循统一的标准。

三、 怎样才能让 Ralph 好好「上班」?

看了上面的工作流,你可能会觉得这套系统太牛了,简直是程序员的“永动机”。但实际上,Ralph 的表现好坏,极度依赖于操作者——也就是你——为它搭建的工作环境。

想让 Ralph 高效、稳定地“上班”,而不是摸鱼或者搞破坏,你必须做好以下几件事。这几点,与其说是“提示工程”,不如说是“系统工程”。

3.1 小而美的 User Story

这是让 Ralph 工作的第一条,也是最重要的一条军规。你必须把一个大的功能需求,拆解成一系列非常小、非常具体的子任务。

为什么?因为 LLM 的“工作记忆”是有限的,这个“工作记忆”就是它的上下文窗口(Context Window)。如果一个任务太复杂,需要一次性修改十几个文件,思考多层逻辑,那它大概率会中途“失忆”,顾头不顾尾,最后输出一堆乱码。

来看一个对比:

  • ❌ 错误的拆分"id": "US-001", "title": "Build entire authentication system"

    • 这个任务太庞大了。它包含了数据库 schema 设计、API 路由、前端表单、状态管理、加密、Session/JWT 处理等等。AI 根本不可能在一个迭代里搞定,它会直接懵掉。

  • ✅ 正确的拆分

    • "Add user table to database schema"

    • "Create login form UI component"

    • "Add client-side email validation"

    • "Create server action for login"

    • "Implement password hashing with bcrypt"

    • "Generate and return JWT on successful login"

一个好的 User Story,应该能在一个原子性的 Git Commit 中完成。它的目标单一,验收标准明确。作为开发者,你的核心价值之一,就体现在这种将复杂问题分解为简单、可执行步骤的能力上。

3.2 不可或缺的反馈闭环

Ralph 的工作模式是“试错”。如果没有一个快速、可靠的机制来告诉它“你错了”,那它就会在一个错误的方向上狂奔,导致灾难性的后果。这个机制,就是自动化的反馈闭环

在软件开发中,我们最常用的反馈闭环就是:

  • 静态类型检查:比如 TypeScript 的 tsc 命令 (npm run typecheck)。它能在代码运行前就发现大量的类型错误。

  • 单元测试**/集成测试**:比如用 Jest, Vitest, Playwright 等工具编写的测试用例 (npm test)。它能验证代码的逻辑是否符合预期。

在 Ralph 的工作流中,这些工具不再是“锦上添花”,而是“生死攸关”。prompt.md 中明确要求 AI 在每次实现后都必须运行这些检查。

  • 如果测试通过,说明这步走对了,可以 git commit,然后继续下一个任务。

  • 如果测试失败,测试工具输出的错误报告,就成了最宝贵、最精确的反馈。AI 在下一次迭代中读到这个报告,就能知道“哦,原来是这个函数的返回值类型错了”,或者“这个边界条件没考虑到”,从而进行有针对性的修复。

没有**自动化测试的 Ralph,就像一匹脱缰的野马,你根本不知道它会把你带到哪里去。**

3.3 明确的 Acceptance Criteria

AI 不是你肚子里的蛔虫,它无法猜测你的意图。你必须在 prd.jsonacceptanceCriteria 字段里,用最清晰、最没有歧义的语言,告诉它“完成”的标准是什么。

来看一个对比:

  • ❌ 模糊的标准"Users can log in"

    • “可以登录”是什么意思?是通过账号密码?还是手机号?登录成功后会发生什么?失败了又该怎样?AI 完全没法干活。

  • ✅ 清晰的标准

    • "Email and password input fields must be present."

    • "The form must validate the email format on blur."

    • "An error message 'Invalid credentials' should be shown on login failure."

    • "On successful login, the user should be redirected to the '/dashboard' page."

    • "The npm run typecheckcommand must pass."

    • "The login-related tests in auth.test.tsmust pass."

最好的验收标准,是那些可以被机器自动验证的标准。比如“测试必须通过”、“类型检查必须通过”、“在 localhost:3000/login 页面上能看到某个元素”。你把这些写清楚,AI 就能自己对答案。

3.4 让 AI 持续学习

Ralph 的强大之处,在于它不是一个失忆的金鱼。通过 progress.txtAGENTS.md 这两个文件,它实现了知识的积累和传承。

  • progress.txt:这是会话级(Session-level)的**短期记忆**。它只在当前这次“上夜班”的任务中有效。AI 通过回顾它,知道自己刚刚踩了什么坑,发现了什么小技巧,从而在后续的几个小时里表现得越来越好。

  • AGENTS.md:这是项目级(Project-level)的长期记忆。这个想法非常酷。prompt.md 会指示 AI,如果发现了可以在整个项目中复用的模式、约定或“大坑”,就把它写到被修改文件所在目录下的 AGENTS.md 文件里。

举个例子,假设 AI 在修改 src/components/ 目录下的文件时,发现所有组件都遵循一个特定的 props 命名规范。它就会被指示在 src/components/AGENTS.md 文件里记上一笔。

# AGENTS.md
​
This document is for AI agents and humans to understand the conventions of this directory.
​
- **Props Naming**: All component props should be prefixed with `on` for event handlers (e.g., `onClick`, `onChange`).
- **Dependencies**: Do not introduce new UI libraries without prior approval in the main PRD.

这样一来,下一次不管是另一个 Ralph 会话,还是一个新的“人类”开发者加入项目,都可以通过阅读这些分布在代码库中的 AGENTS.md 文件,快速了解各个模块的“潜规则”。这相当于让 AI 参与到了项目文档的建设中,实现了知识的永久沉淀。

3.5 搞定前端 UI 测试

对于后端来说,API 的测试相对直接。但对于前端 UI 呢?AI 修改了一个按钮的样式,怎么知道改对了还是改丑了?

社区也给出了方案,比如集成 dev-browser 这样的工具。它可以让 AI 通过写脚本来控制一个无头浏览器(Headless Browser),执行诸如“打开页面”、“点击按钮”、“填写表单”等操作,最后截一张图

虽然 AI 目前还不能很好地“审美”,但它至少可以验证:

  • 页面是否成功加载,没有崩溃。

  • 点击按钮后,预期的元素(比如一个弹窗)是否出现。

  • 表单提交后,页面是否跳转到了正确的地方。

通过截图,AI 至少能确保 UI 在功能上是完整的,没有出现明显的布局错乱。这为 Ralph 应用于全栈开发提供了可能。

四、风险与注意事项

看到这里,你可能已经摩拳擦掌,准备让 Ralph 今晚就给你家的祖传代码库来一次“彻夜升级”了。且慢,天底下没有免费的午餐。Ralph Wiggum 这种强大的自动化工具,也伴随着同样巨大的风险。

4.1 失控的 API 调用

Ralph 的核心是循环,而且是一个在成功前永不停止的循环。这意味着,如果你给它的任务定义不清,或者任务本身就是不可能完成的(比如依赖一个不存在的外部服务),它就可能陷入真正的死循环。

LLM 的 API 调用是按 Token 计费的。一个不知疲倦的 AI 在那里通宵达旦地尝试,你的 API 账单可能一夜之间就从“尚可接受”飙升到“需要卖房”。有社区用户就警告说,要小心 Ralph 把你的 Token 预算给干爆了。

解决方案:

  • 永远设置 --max-iterations 参数! 这是你的救命稻草,是成本的“熔断器”。根据任务的复杂性,设置一个合理的上限,比如 20 或 50。如果达到上限还没完成,脚本会自动退出,提醒你介入调查。

  • 从小的迭代次数开始。先用 --max-iterations=3 跑一下,看看它的行为是否符合预期,然后再逐步增加。

  • 监控你的 API 使用量。在跑一个长时间的 Ralph 会话时,定期去云服务商的控制台看一眼费用,心里有数。

4.2 终端权限与安全沙箱

这是 Ralph 最大的安全隐患,没有之一。

为了能修改文件、安装依赖、运行测试、提交代码,我们通常需要给 AI 助手完全的终端访问权限,也就是 amp 工具的那个 --dangerously-skip-permissions--dangerously-allow-all 参数。

“dangerously”这个词可不是开玩笑的。它意味着 AI 可以在你的电脑上执行任何命令,包括但不限于 rm -rf /。虽然 LLM 本身没有“恶意”,但它可能会在解决问题的过程中“产生幻觉”,或者错误地理解了某个命令的含义,从而执行破坏性的操作。比如,它为了“清理构建缓存”,可能会把你的整个项目目录给删了。

解决方案:

  • 绝对不要在你的主开发机上直接运行!

  • 使用沙箱环境(Sandbox)。这是最专业、最安全的做法。你可以:

    • 使用 Docker 容器:为每个 Ralph 会话启动一个干净的 Docker 容器,把项目代码挂载进去。AI 在容器里怎么折腾都行,即使搞砸了,也只是毁掉一个容器,你的宿主机安然无恙。

    • 使用一次性的云**虚拟机(VM)**:在 AWS, GCP, Azure 等云平台上,开一个配置最低的虚拟机,把代码 clone 下去跑。跑完任务,直接把虚拟机销毁。成本低,隔离性好。

    • 使用 GitHub Codespaces 或类似服务:这些服务本身就提供了一次性的、基于云的开发环境,是运行 Ralph 的理想场所。

一句话:把 Ralph 当成一个你既不了解也不完全信任的实习生,把它关在安全屋里干活。

五、Warren's take

扒了半天 Ralph Wiggum,从它的野生起源到企业级实现,再到核心工作流和避坑指南,相信大家对它已经有了比较立体的认识。最后,我想聊聊我个人的一些思考。

  1. 从「对话式」到「委托式」的范式转移

Ralph 的出现,标志着我们与 AI 协作模式的一次深刻变革。过去,我们使用 Copilot 或其他 AI 助手,更多是“对话式”或“结对编程”模式。AI 像个坐在你旁边的副驾驶,你一句我一句,它提供建议,但最终的决策和执行还是由你来掌控。

而 Ralph 代表的是“委托式”模式。你不再是手把手地教,而是像一个项目经理或架构师,把一个大目标拆解成清晰的子任务(写好 prd.json),定义好验收标准和工作流程(设计好 prompt.md 和测试),然后把整个项目“委托”给 AI 去执行。你从一个执行者,变成了一个系统设计者和任务定义者。这是一种更高维度的抽象,也是对开发者能力提出的新要求。

  1. 「失败」不再是 Bug,而是**训练数据**

传统软件开发的理念是尽可能避免失败。我们写代码追求一次写对,Bug 是需要被消灭的敌人。但 Ralph 的哲学完全不同,它拥抱失败,甚至可以说,它依赖失败来学习

无论是 Huntley 的“上下文压力锅”,还是 Anthropic 的“失败即数据”,核心都是将错误信息、失败日志作为下一轮迭代的关键输入。每一次失败,都为 AI 提供了一次宝贵的学习机会,让它更接近正确答案。这套机制,把“试错”这个成本极高的过程,通过 API 调用变得廉价且高效。它提醒我们,在 AI 时代,构建一个能从失败中自动学习的系统,可能比追求单次交互的完美更重要。

  1. 系统的胜利,而非单一模型的胜利

很多人看到 Ralph 的惊人表现,第一反应可能是“哇,Claude Code 这个模型太牛了!”。模型当然是基础,但 Ralph 的成功,更多是系统的胜利

一个强大的 LLM,就像一颗高性能的 CPU。但你只有 CPU 是没法打游戏的,你需要主板、内存、显卡、操作系统和软件的协同工作。Ralph 就是这样一套精心设计的“主板”和“操作系统”:

  • ralph.sh 循环是主板上的总线,驱动着数据流动。

  • prd.jsonprogress.txt 是内存和硬盘,提供了状态管理和持久化记忆。

  • 自动化的测试脚本是显卡,提供了结果的“渲染”和验证。

  • prompt.md 是操作系统内核,调度和指挥着 CPU(LLM)该如何工作。

这个案例完美地诠释了,将软件工程的经典思想(如自动化、模块化、状态管理、反馈闭环)应用于与 LLM 的交互,能爆发出多大的能量。这已经超出了“提示工程”的范畴,进入了“AI 系统工程”的领域。

未来,CRUD boy/girl 的价值可能会被严重稀释,而那些既懂业务,又懂架构,还能为 AI 设计高效工作流的“AI 架构师”或“AI 系统工程师”,将变得炙手可热。

六、工程化

见:https://github.com/snarktank/ralph


0