1. 项目概述:一个能“自己写代码”的AI代理循环
如果你和我一样,对AI辅助编程工具(比如Amp或者Claude Code)又爱又恨,爱的是它们能快速生成代码片段,恨的是它们经常“健忘”,上下文一长就逻辑混乱,或者在一个复杂的PRD(产品需求文档)面前束手无策,那么Ralph这个项目可能就是你要找的答案。它不是另一个AI代码生成器,而是一个自主运行的AI代理循环系统。你可以把它理解为一个不知疲倦、严格执行计划的“AI项目经理+初级工程师”组合体。
它的核心工作模式非常直接:你给它一份结构化的产品需求(prd.json),它就会启动一个循环。在每一次循环中,它都会创建一个全新的、上下文干净的AI工具实例,然后从需求列表中挑选出优先级最高且尚未完成的那一项,全力以赴去实现它。实现后,它会自动运行你预设的质量检查(比如类型检查、单元测试),只有通过了,代码才会被提交。接着,它会更新需求状态,将本次迭代的“经验教训”记录下来,然后毫不犹豫地终结当前AI实例,开启下一次干净的重生,继续攻克下一个任务。如此循环,直到所有需求项都被标记为完成,或者达到你设定的最大迭代次数。
这个模式最吸引我的地方在于它解决了AI工具在长上下文任务中的两大痛点:上下文污染和任务分解。传统的用法是我们在一个越来越长的对话里不断提出要求,AI很容易前后矛盾或遗忘早期设定。而Ralph的“每次迭代都是全新实例”策略,确保了每次AI都只专注于当前这个小任务,不受历史“杂念”干扰。同时,它强制要求你将PRD拆分成足够小的、能在单次上下文窗口内完成的“用户故事”,这本身就是一种极佳的需求管理实践。
2. 核心设计思路与工作原理解析
2.1 为什么是“循环”而非“对话”?
大多数开发者使用AI编程工具的方式是交互式的对话。我们提出问题,AI给出代码,我们指出错误,AI进行修正。这种方式在探索和调试时很有效,但对于实现一个明确的、多步骤的需求清单来说,效率低下且难以规模化。Ralph的设计哲学是将AI工具“物化”为可编程、可重复调用的函数。
想象一下,你把AI工具看作一个函数ai_coding_tool(task_description, context)。Ralph的工作就是管理这个函数的调用循环:
- 输入:一个待办任务列表(
prd.json)和积累的上下文(progress.txt, git历史)。 - 处理:在每次循环中,组合当前任务和上下文,调用一次
ai_coding_tool函数。 - 输出与反馈:检查函数输出(生成的代码)的质量,如果合格则提交结果,并更新任务列表和上下文。
- 循环:函数调用结束,其内部状态被清空。基于更新后的输入,开始下一次函数调用。
这种设计带来了几个关键优势:
- 状态隔离:每次调用都是独立的,避免了长期对话中的性能下降和逻辑混乱。
- 确定性增强:通过git提交和
progress.txt来持久化状态,整个流程变得可追溯、可重现。 - 自动化:整个循环可以由一个简单的Bash脚本(
ralph.sh)驱动,无需人工持续干预。
2.2 记忆的持久化:Git、文本与JSON
既然每次AI实例都是全新的,记忆如何传递?Ralph采用了三种朴实但极其有效的持久化机制,这构成了其“外部记忆系统”:
Git历史:这是最主要的代码记忆。每一次成功的迭代都会产生一个git commit。下一个迭代的AI实例可以通过
git diff或直接查看代码库来理解“之前已经做了什么”。这模拟了团队开发中,新成员通过阅读代码提交历史来熟悉项目的场景。progress.txt文件:这是一个只追加(append-only)的文本文件,充当“项目日志”或“学习笔记”。每次迭代后,AI会将其在本次任务中发现的重要模式、遇到的坑、总结的约定等,以自然语言的形式追加到这个文件中。例如:“发现项目使用react-query进行数据获取,所有API调用都应封装在useQuery钩子中。” 这个文件为后续迭代提供了宝贵的、浓缩的上下文。prd.json文件:这是项目的“任务清单”和“状态看板”。它是一个结构化的JSON文件,列出了所有需要完成的用户故事(User Stories),每个故事包含ID、标题、描述、验收标准和最重要的——passes布尔字段。Ralph的核心循环逻辑就是查找passes: false的最高优先级任务,完成后将其标记为passes: true。这个文件的更新是驱动循环前进的核心信号。
注意:
progress.txt和prd.json的更新必须由AI在迭代中自动完成,并作为提交的一部分。这意味着你的提示词(Prompt)必须明确指令AI去读写这两个文件,这是实现闭环自动化的关键。
2.3 质量门禁与反馈循环
没有质量检查的自动化代码生成是灾难性的。Ralph不是一个“黑盒魔法”,它的可靠性严重依赖于你建立的快速反馈循环。在每次迭代提交代码前,ralph.sh脚本会执行你预设的一系列检查,通常包括:
- 静态类型检查:如TypeScript的
tsc --noEmit,或Python的mypy。用于捕获接口不一致、类型错误等基础问题。 - 代码风格检查:如
eslint,prettier --check。确保生成的代码符合项目规范。 - 单元测试:运行相关的测试套件,如
jest,pytest。这是验证功能是否正确实现的核心。
只有所有这些检查都通过,代码才会被允许提交。如果检查失败,整个迭代被视为失败,代码不会被提交,prd.json中的任务状态也不会更新。这种“红灯停、绿灯行”的机制,是防止错误累积、确保代码库健康度的基石。
3. 从零开始配置与实战部署
3.1 环境与工具准备
在运行Ralph之前,你需要确保以下基础环境就绪:
AI编程工具二选一:
- Amp:这是Ralph默认且推荐的工具。你需要按照其官方文档安装CLI并完成认证。Amp的设计理念与Ralph的“工具化”思想非常契合。
- Claude Code:Anthropic提供的编程工具。需要通过npm全局安装 (
npm install -g @anthropic-ai/claude-code) 并进行认证。它同样支持技能(Skills)系统。
系统依赖:
jq:一个轻量级的命令行JSON处理器。Ralph脚本用它来解析和操作prd.json文件。在macOS上可以通过brew install jq安装,Linux发行版通常使用包管理器如apt-get install jq或yum install jq。
Git仓库:Ralph必须在Git仓库中运行,因为它重度依赖git进行版本管理和上下文传递。确保你的项目已经
git init,并且没有未提交的更改。
3.2 集成Ralph到你的项目
你有几种方式将Ralph集成到工作流中,我推荐第一种,因为它最透明、最易于自定义。
方式一:复制文件到项目内(推荐)这是最直接的方式,所有相关文件都在你的项目目录中,便于版本控制和定制。
# 在你的项目根目录下操作 mkdir -p scripts/ralph # 假设你将Ralph项目克隆到了 ~/code/ralph cp ~/code/ralph/ralph.sh scripts/ralph/ # 根据你使用的AI工具,复制对应的提示词模板 cp ~/code/ralph/prompt.md scripts/ralph/prompt.md # 如果你用Amp # 或者 cp ~/code/ralph/CLAUDE.md scripts/ralph/CLAUDE.md # 如果你用Claude Code # 赋予执行权限 chmod +x scripts/ralph/ralph.sh这种方式下,scripts/ralph目录会成为你项目的一部分,你可以随意修改ralph.sh脚本和提示词模板来适应项目的特殊需求。
方式二:全局安装技能(适用于Amp或Claude Code)如果你希望在所有项目中使用Ralph的技能(如生成PRD),可以将其安装到AI工具的全局技能目录。
# 对于Amp cp -r ~/code/ralph/skills/prd ~/.config/amp/skills/ cp -r ~/code/ralph/skills/ralph ~/.config/amp/skills/ # 对于Claude Code(手动安装) cp -r ~/code/ralph/skills/prd ~/.claude/skills/ cp -r ~/code/ralph/skills/ralph ~/.claude/skills/安装后,你可以在任何项目的对话中直接使用/prd和/ralph命令。
方式三:作为Claude Code市场插件如果你的团队使用Claude Code,可以将其添加为团队共享的插件。
/plugin marketplace add snarktank/ralph /plugin install ralph-skills@ralph-marketplace3.3 关键配置:Amp的自动切换(Auto-handoff)
对于使用Amp的用户,有一个强烈推荐的配置,能极大提升处理大型任务的能力。编辑Amp的配置文件~/.config/amp/settings.json,添加以下内容:
{ "amp.experimental.autoHandoff": { "context": 90 } }这个配置启用了“自动切换”功能。当AI的上下文窗口使用率达到90%时,Amp会自动将当前任务“移交”给一个新的、上下文干净的自身实例,并传递必要的状态。这对于Ralph处理那些稍微超出单次上下文限制的复杂故事非常有用,可以看作是在一次Ralph迭代内部的“微循环”,能有效避免因上下文不足导致的代码生成质量下降。
4. 核心工作流详解:从需求到代码
4.1 第一步:创建结构化的PRD
一切始于一份清晰的需求。Ralph配套的/prd技能能帮助你快速生成结构化的Markdown格式PRD。你不需要一开始就写出完美的prd.json,而是先用自然语言描述功能。
操作示例: 在Amp或Claude Code中,你可以这样开始:
加载prd技能,为“在用户仪表盘首页添加一个显示最近7天活跃用户数的统计卡片”创建一份PRD。AI会通过对话向你澄清一些细节,比如:
- 卡片的具体样式和位置?
- “活跃用户”如何定义?
- 数据从哪里获取(现有API还是需要新建)?
- 是否需要实时刷新?
对话结束后,技能会将输出保存到项目根目录下的tasks/prd-dashboard-active-users-card.md文件中。这份Markdown文档会包含背景、目标、用户故事、验收标准、技术考虑点等章节。
实操心得:不要跳过澄清问题!花时间与AI详细讨论需求细节,能极大减少后续迭代中的歧义和返工。把AI当成你的产品经理搭档,一起把需求打磨清楚。
4.2 第二步:将PRD转换为Ralph可执行的JSON格式
Markdown格式对人友好,但对机器(Ralph脚本)来说不易解析。接下来需要使用/ralph技能进行转换。
操作示例: 继续在AI工具中执行:
加载ralph技能,将 tasks/prd-dashboard-active-users-card.md 转换为 prd.json。这个技能会解析Markdown PRD,提取出其中的用户故事,并将其转换为Ralph所需的标准化JSON结构,输出到项目根目录的prd.json文件。
一个prd.json的示例片段如下:
{ "projectName": "Dashboard Enhancement", "branchName": "feat/dashboard-active-users", "userStories": [ { "id": "US-1", "title": "创建活跃用户统计卡片UI组件", "description": "在仪表盘网格的第三行第一列位置,创建一个与现有统计卡片风格一致的组件,用于展示‘最近7天活跃用户’标题和数字。", "acceptanceCriteria": [ "卡片使用 `Card` 组件,包含 `CardHeader`(带标题和图标)和 `CardContent`(显示数字)", "数字使用 `TrendingUp` 图标和绿色主题色", "响应式布局,在不同屏幕尺寸下正常显示", "在浏览器中使用dev-browser技能验证组件渲染正确" ], "priority": 1, "passes": false }, { "id": "US-2", "title": "从后端API获取活跃用户数据", "description": "在仪表盘页面加载时,调用 `/api/analytics/active-users?days=7` 接口获取数据,并填充到US-1创建的卡片中。", "acceptanceCriteria": [ "使用项目现有的 `useQuery` hook进行数据获取", "处理加载和错误状态(显示Skeleton和Alert)", "数据成功返回后,更新卡片中的数字显示", "编写针对该hook的单元测试,模拟API成功/失败场景" ], "priority": 2, "passes": false } ] }关键字段解析:
branchName: Ralph会自动基于此创建Git分支。userStories: 故事列表,每个故事必须足够小(“原子性”)。priority: 数字越小优先级越高。Ralph按此顺序处理。passes: 初始为false,完成并验证后由Ralph改为true。
4.3 第三步:启动Ralph自主循环
当prd.json准备就绪后,你就可以退后一步,让Ralph开始工作了。打开终端,进入项目根目录:
# 如果你使用Amp(默认工具) ./scripts/ralph/ralph.sh 15 # 最多运行15次迭代 # 如果你使用Claude Code ./scripts/ralph/ralph.sh --tool claude 15 # 你也可以显式指定Amp ./scripts/ralph/ralph.sh --tool amp 10脚本会开始运行,你将在终端看到类似以下的输出:
[RALPH] Starting iteration 1/15 on branch 'feat/dashboard-active-users' [RALPH] Selected story: US-1 - 创建活跃用户统计卡片UI组件 [RALPH] Spawning fresh AI instance... ... (AI工具输出日志) ... [RALPH] Running quality checks: npm run type-check && npm run test -- --testPathPattern=DashboardCard [RALPH] All checks passed! Committing changes. [RALPH] Updating prd.json: marking US-1 as passed. [RALPH] Appending learnings to progress.txt. [RALPH] Iteration 1 completed successfully. [RALPH] Starting iteration 2/15...Ralph会严格按照以下步骤执行每次迭代:
- 创建/切换分支:基于
prd.json中的branchName。 - 挑选任务:找到优先级最高且
passes: false的用户故事。 - 生成代码:启动一个新的AI实例,将故事描述、
progress.txt内容、相关代码上下文作为提示词输入,要求AI实现。 - 运行检查:执行你预设的构建、类型检查和测试命令。
- 提交与更新:如果检查通过,提交代码,将故事的
passes设为true,并将本次迭代的总结追加到progress.txt。 - 循环:回到步骤2,直到所有故事完成或达到迭代上限。
当所有故事的passes都变为true时,Ralph会输出<promise>COMPLETE</promise>并优雅退出,整个功能开发完成。
5. 高级技巧与定制化实践
5.1 如何设计“恰到好处”的用户故事
Ralph成功与否,一半取决于prd.json中用户故事的设计。故事太大,AI会在一次迭代中迷失;故事太小,会产生过多无意义的提交和上下文切换开销。
优秀故事的黄金法则:
- 可在一个上下文窗口内完成:这是硬性约束。评估AI工具的上下文长度(例如,Amp约8000 tokens),确保故事描述、相关代码上下文和预期输出不超过这个限制。
- 对应一个清晰的、可验证的产出:例如“创建X组件”、“在Y接口中添加Z参数”、“修复A文件中的B类型错误”。避免“改进用户体验”这类模糊描述。
- 具备独立的验收标准:每个故事的验收标准应该是明确、可测试的,最好能对应到一条具体的自动化测试或检查命令。
- 遵循依赖顺序:如果故事B依赖于故事A的产出(例如,需要先创建API端点才能连接前端),务必通过
priority字段正确排序。
拆分大型需求的示例:
- 糟糕的需求:“重写用户设置页面。”
- 拆解后的Ralph故事:
- (US-1) 创建新的设置页面路由和基础布局组件。
- (US-2) 实现“个人资料”选项卡的表单UI,包含姓名、头像字段。
- (US-3) 创建更新个人资料的后端API端点。
- (US-4) 连接US-2的表单到US-3的API,并添加提交逻辑和状态提示。
- (US-5) 实现“通知偏好”选项卡的UI(开关组)。
- (US-6) 创建更新通知偏好的后端API端点。
- (US-7) 连接US-5的UI到US-6的API。
5.2 定制你的提示词模板
项目自带的prompt.md或CLAUDE.md是一个通用的起点。要让Ralph在你的项目里发挥最大效能,必须对其进行深度定制。复制到本地后,打开并编辑它,重点修改以下几个部分:
项目特定的质量检查命令:将通用的
npm test替换成你项目实际的命令链。例如:## 质量检查 在实现代码后,你必须运行以下命令来验证: - `npm run lint:strict` # 严格的ESLint检查 - `npm run type-check` # TypeScript全项目类型检查 - `npm run test -- --bail --testPathPattern=<当前修改的文件相关模式>` # 运行相关测试,任何失败即停止 只有所有命令返回退出码0,才能认为任务成功。代码库约定与模式:在提示词开头部分,加入你项目的“宪法”。例如:
## 项目核心约定 - **状态管理**:一律使用Zustand,store定义在`/src/stores`下。 - **API调用**:使用`/src/lib/api-client`封装好的axios实例,不要直接使用fetch。 - **组件风格**:函数式组件 + TypeScript + Tailwind CSS。组件文件首字母大写。 - **错误处理**:使用`toast.promise()`显示操作反馈,错误信息记录到Sentry。常见的“坑”与解决方案:提前告诉AI容易出错的地方。例如:
## 常见陷阱 - 修改数据库模型后,**必须**同时创建Prisma迁移文件 (`npx prisma migrate dev --name xxx`)。 - 在Next.js中,服务端组件里不能使用`useState`或`useEffect`。 - 添加新的环境变量后,需要同时更新`.env.example`和Docker配置。
5.3 善用AGENTS.md文件积累知识
这是Ralph工作流中一个非常精妙的设计。项目中的AGENTS.md文件(或你指定的其他名称)是AI工具会自动读取的上下文文件。Ralph在每次迭代后,不仅更新progress.txt,也应该将最关键、最通用的“学习成果”更新到相关的AGENTS.md中。
例如,在实现“用户设置页面”时,第一个故事(创建路由和布局)的AI可能会发现:
“本项目使用
@shadcn/ui的Tabs组件来实现设置页面的选项卡切换,具体示例可参考/src/app/account/billing/page.tsx。”
它就应该把这个发现追加到项目根目录或组件目录下的AGENTS.md里。这样,后续在实现“通知偏好”选项卡时,新的AI实例就能自动读到这个模式,从而生成风格一致的代码。
你可以通过修改提示词模板,强制要求AI在每次迭代后必须审阅并更新AGENTS.md,将本次发现的项目模式、最佳实践、配置步骤等固化下来。这相当于为你的项目构建了一个不断增长的、AI可读的“开发手册”。
5.4 为前端故事添加浏览器验证
对于涉及用户界面的故事,仅仅通过类型检查和单元测试是不够的。Ralph支持通过“dev-browser”技能(如果AI工具支持)进行真实的浏览器交互验证。
在你的用户故事acceptanceCriteria中,明确加入:
"acceptanceCriteria": [ "...其他标准...", "使用dev-browser技能,导航到 `/dashboard`,确认新的统计卡片已正确渲染,并且数字显示区域不为空。" ]在提示词模板中,你需要指示AI在实现UI后,调用相应的浏览器自动化命令来执行验证。这能将前端开发的验证闭环做得更扎实。
6. 问题排查、调试与状态管理
即使流程设计得再完美,在实际运行中也可能遇到问题。Ralph提供了一套简单的命令行工具来帮助调试。
6.1 查看当前状态
当Ralph在运行时,或者你想了解中断后的进度,可以使用以下命令:
# 1. 查看所有用户故事的完成状态(最常用) cat prd.json | jq '.userStories[] | {id, title, passes}' # 输出示例: # { # "id": "US-1", # "title": "创建活跃用户统计卡片UI组件", # "passes": true # } # { # "id": "US-2", # "title": "从后端API获取活跃用户数据", # "passes": false # } # 2. 阅读积累的学习笔记 tail -f progress.txt # 动态查看最新追加的内容 # 或 cat progress.txt # 查看全部内容 # 3. 审查代码提交历史 git log --oneline --graph -20 # 查看最近20条提交的简约图形化历史6.2 常见失败场景与处理
质量检查失败:这是最常见的问题。AI生成的代码未能通过lint、类型检查或测试。
- 排查:查看Ralph运行的最后输出,找到失败的具体命令和错误信息。
- 处理:不要手动修复代码然后继续运行。这会导致上下文不一致。正确做法是:将当前失败的故事的
passes字段在prd.json中改回false,然后重新运行Ralph。AI会在新的迭代中看到之前的失败提交和错误日志,并尝试修正。如果多次失败,说明故事可能还是太大或太模糊,需要进一步拆分或澄清。
AI陷入循环或生成无关代码:AI可能误解了需求,开始反复修改同一段代码或添加无关功能。
- 排查:检查
progress.txt,看AI是否误解了某个关键点。检查git diff,看代码变更是否偏离主线。 - 处理:首先,停止Ralph脚本。然后,仔细审查当前迭代的提示词输入(结合故事描述和
progress.txt)。很可能需求描述存在二义性。你需要手动修改prd.json中该故事的描述和验收标准,使其更精确。然后,将故事的passes改回false,并考虑清空最近几次无意义的提交(使用git reset),再重新启动Ralph。
- 排查:检查
上下文不足导致代码不完整:AI在迭代中只完成了部分工作就停止了。
- 处理:这是故事过大的典型症状。你需要中断Ralph,然后回到
prd.json,将这个未完成的故事拆分成两个或更多更小的故事。确保每个新故事都能独立通过验收。
- 处理:这是故事过大的典型症状。你需要中断Ralph,然后回到
6.3 手动干预与流程控制
Ralph是自动化工具,但并非完全不可控。你需要知道何时以及如何介入。
- 暂停与继续:直接使用
Ctrl+C中断ralph.sh脚本即可。状态已保存在prd.json和git提交中。下次运行相同的命令,Ralph会从上次未完成的故事继续。 - 重置单个故事:如果某个故事的结果不满意,只需在
prd.json中将其passes改为false,Ralph下次就会重新处理它。注意,这可能会产生冲突,因为代码已经存在。你可能需要先手动回滚该故事相关的更改(git revert)。 - 完全重新开始:如果你想抛弃当前的所有Ralph工作,可以:
- 删除
prd.json和progress.txt。 - 使用
git checkout main && git branch -D <ralph-branch-name>删除Ralph创建的特性分支。 - 从头开始创建新的PRD和
prd.json。
- 删除
6.4 归档与知识管理
Ralph有一个很贴心的功能:自动归档。每当你开始一个全新的功能(即prd.json中的branchName与当前已存在的分支不同),并且存在之前运行的prd.json和progress.txt时,Ralph会自动将旧文件移动到archive/YYYY-MM-DD-<旧分支名>/目录下。
这保证了你的工作空间整洁,同时保留了完整的历史记录,方便日后回顾或审计某个功能的AI实现过程。养成定期查阅归档目录的习惯,你能从中总结出哪些类型的故事AI完成得好,哪些容易出问题,从而持续优化你的PRD编写技巧和提示词设计。
经过多个项目的实践,我发现Ralph并非要取代开发者,而是将开发者从重复性、模式化的编码任务中解放出来,扮演一个超级高效的执行者角色。它的价值在于强制推行了一种精细化、自动化、可追溯的开发流程。最大的挑战和乐趣,从“写代码”变成了“设计清晰的任务指令和验收标准”。当你看着Ralph一条条地将你规划好的任务从passes: false变为true,并最终输出COMPLETE时,那种感觉,就像是一位稳扎稳打的工程指挥官,看着自己的蓝图被精准无误地构建出来。