Ralph 实验:构建 SQLite UI


摘要

  • 我使用 Ralph 技巧配合 Claude Code,根据生成的 PRD 自主构建了一个基于浏览器的 SQLite UI。
  • Claude 逐个处理需求,在极少指导且不使用框架的情况下,生成了一个简单的静态应用。
  • 这种方法效果出奇地好,但速度慢、消耗 token 多,且在没有测试或版本控制的情况下存在风险。
  • 更强的护栏、更短的冲刺周期和更好的项目结构将显著提高可靠性和效率。
  • 总的来说,当时间和 token 成本可接受时,Ralph 被证明对这种无需人工干预的从零开发是有效的。

最终结果可以在 lochie.dev/sqlite-ui 找到。

简介

首先 —— Ralph Wiggum 和软件开发有什么关系?

Geoffrey Huntley 在 2025 年 7 月首次创造了这个术语

Ralph 是一种技巧。在其最纯粹的形式中,Ralph 就是一个 Bash 循环。

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

Ralph 可以取代大多数公司在全新项目上的大部分外包工作。它有缺陷,但这些缺陷是可以识别的,并且可以通过各种风格的提示词来解决。

本周早些时候,Matt Pocock 关于他的 Ralph 工作流的精彩 视频 出现在我的视野中。Matt 采纳了 Geoffrey 的最初想法,并将其调整以适应他自己的开发风格。

我强烈建议观看该视频,但为了总结核心概念,Matt 实际上重建了一个完全由 Claude 驱动的敏捷风格工作流。

  1. 创建需求(用户故事),包含名称、描述、验收标准和实施状态。
  2. 这些需求被分组到一个列表中(一个“Sprint”/冲刺)。
  3. Claude 获取单个需求,独立工作直到完成,然后更新其状态。
  4. 重复步骤 3,直到没有未满足的需求。

Claude 本身被用来定义需求列表。没有显式的依赖映射或优先级排序,这完全留给 Claude 在冲刺期间去弄清楚。

冲刺作为一个 JSON 文件存在,包含所有需求及其进度。更新会同时持久化在文本文件和每次迭代后写入的 git 提交中。

实验

看到我的推送里充满了对 Ralph 的热议,我感到有点“错失恐惧症”(FOMO),并开始构思一个足够复杂有趣,但又可实现的项目。

一个用于与 SQLite 数据库交互的基于浏览器的 Web 应用感觉很合适。更棒的是,如果效果好,我将来还能自己用它。

这是我的第一个提示词(prompt),使用了 plan 模式:

create a product requirements document for a browser based sqlite DB viewer, it should allow opening a sqlitedb file, show the contents and be interactive. No frontend JavaScript framework just keep it simple, lightweight and static.
(译:创建一个基于浏览器的 SQLite 数据库查看器的产品需求文档,它应该允许打开 sqlite 数据库文件,显示内容并且是交互式的。不要使用前端 JavaScript 框架,保持简单、轻量和静态。)

这产生了一个相当大的需求列表,存储在 PRD.md 中。

然后我将这些需求转换为 PRD.json,遵循 Matt 在视频中使用的方法:

use @PRD.md to create PRD.json

the file should contain an array of feature objects, the schema of an object is as follows. Break the PRD into many small features.

field description
category functional, non functional, etc
description a brief 1 sentence description of the feature
steps array of strings, eg. when button is clicked, colour changes
passes boolean representing if the feature is complete and working as intended

总共产生了 62 个需求。

带上我的 Ralph 脚本,我准备好让它开跑了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 {iterations}"
exit 1
fi
for ((i=1; i<=$1; i++)); do
echo "Iteration: $i"
echo "---------------------------------------"
result=$(claude --permission-mode acceptEdits -p "@PRD.json @progress.txt \
1. Find the highest-priority feature to work on and work only on that feature. \
2. Check that the tests pass. \
3. Update the PRD (PRD.json) with the work that was done. \
4. Append your progress to the progress.txt file. \
ONLY WORK ON A SINGLE FEATURE
If, while implementing the feature, you notice the PRD is complete, output <promise>COMPLETE</promise>. \
")
echo "$result"
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo "PRD COMPLETE"
exit 0
fi
done

我一次运行 10 次迭代的脚本。每次迭代都会阻塞且没有输出,所以我每次都急切地等待完成。

我的方法没有 git 历史记录。我在“危险边缘试探”。我依靠每次迭代后的 progress.txt 文件来查看发生了什么变化,并刷新浏览器查看是否有东西坏了。令我惊讶的是,一切都维持得很好,应用程序很快开始看起来像一个功能齐全的数据库 UI。

我无法一次性完成所有需求。随着功能的积累,token 使用量显著攀升,我最终花了大约两天时间进行实验。

一旦所有原始需求都实现了,我忍不住开始要求更多功能,包括:

  • 显示表存储大小
  • 对查询运行 EXPLAIN
  • 显示查询执行时长
  • 交互式行编辑
  • 保存更新后的数据库
  • 索引管理
  • 架构图
  • ……还有更多

在那一刻,我从工程师变成了一个过度兴奋的产品经理,刚刚发现了一个 24/7 工作、从不拒绝、并且没有自尊心去拒绝需求膨胀的软件工程师。

最终结果

你可以在这里试用:lochie.dev/sqlite-ui

schema

data

反思

仅用极简的提示词,我就能生成完整的需求列表并让 Claude 直接开始工作。结果在很大程度上符合我的预期,考虑到我有限的提示词,这仍然令人印象深刻。

我要求一个简单的静态实现,而这正是我得到的——单个 HTML、CSS 和 JavaScript 文件。唯一的外部依赖是 sql-wasm。Claude 构建了一个坚实的基础,可以进一步扩展。

显示表存储大小最初不起作用。Claude 指出,由于 sql-wasm 使用的编译标志,这在实现过程中可能无法工作。我自己用 SQLITE_ENABLE_DBSTAT_VTAB 重新构建了它,更新了 HTML 以指向我的构建版本,用 HTTP 服务器(加载 WASM 所需)提供文件服务,然后一切正常,无需进一步的代码更改。

一些缺点变得很明显:

  • Claude 运行时没有可见的中间输出,所以很难判断它是卡住了还是只是慢。
  • 没有编写测试。(没有提示它写,Claude 认为没有必要)。
  • 项目没有版本控制。
  • 我很幸运,没有什么灾难性的崩溃。

不过,这种成功并不令人惊讶。Ralph 本质上将 Claude 变成了一个专注的软件工程师,按照项目经理定义的冲刺计划工作。没有上下文切换,只有一次一个故事。

也就是说,这种方法并不快,而且消耗大量的 token。这是速度和准确性之间的明显权衡。如果你有时间并且能承担成本,像这样自主构建项目是有意义的,尤其是当它在你睡觉时还在埋头苦干的时候。

未来改进

根据以往的经验,Claude 在有更强的护栏和更清晰的指导下表现会好得多。虽然 Ralph 循环在极少设置下效果出奇地好,但这次实验突显了几个更刻意的结构会带来回报的领域。

护栏和项目结构

下次我会做不同的事情:

  • 创建一个定义良好的 CLAUDE.md,记录项目惯例、架构、约束和期望。
  • 使用结构化的多文件项目布局,而不是单个不断增长的 JavaScript 文件。
  • 从一开始就添加 git 以持久化进度,通过提交历史启用检查,并允许在 Claude 走上不可恢复的道路时回滚。
  • 添加测试 —— 大量的测试。对于 Web 应用,Playwright 将是一个强有力的选择,可以自动捕获功能和视觉回归。

因为每次 Ralph 迭代都在一个新的 Claude Code 会话中运行,大量的时间和 token 花在了重新加载和重新理解项目上下文上。更好的结构、更清晰的惯例和自动化测试将显著减少这种开销并提高迭代质量。

更小的冲刺和提前规划

冲刺的大小也很重要。在一个冲刺中有 62 个功能实在是太多了,Claude 仅仅为了决定下一步做什么就要进行大量的推理。

一种选择是预先手动界定故事范围、定义关系并分配优先级。然而,一个更有趣的工作流是让 Claude 自己处理那个计划步骤:

  • 从完整的需求集开始。
  • 要求 Claude 将它们分解为多个较小的冲刺,并提前计划几个冲刺,就像现实世界的敏捷流程一样。
  • 独立执行每个冲刺。

通过这种方法,Ralph 脚本可以扩展为使用嵌套循环:遍历冲刺,然后遍历冲刺中的故事。这将减少因重复扫描完整需求集而浪费的上下文,并允许 Claude 自主运行更长时间。

进一步来说,这种模式甚至可以扩展到多个“软件工程师”(多个 Claude 实例)并行工作,由更高级别的编排层协调。

结论

我认为这次实验是成功的。

我构建了一些真正有用的东西,不会被丢弃,在实践中学到了很多关于 Ralph 技巧的知识,现在对将其应用于未来的项目充满信心。

我期待着在有更好的护栏、测试和版本控制的情况下再次尝试。