
上一集家庭日历暴露的是方向决策问题——AI 选了训练数据里最常见的界面模式,人来扳方向。那个项目 23 小时就结束了,$92,范围(scope)不大。这一集换了一个更重的产品——47 小时、40 个执行单元、五次部署才上线——暴露的是另一类问题:基础设施迁移和自主循环的边界。当项目足够复杂、运行时间足够长,AI 会撞上一面墙:上下文溢出、环境差异、以及停不下来。
凌晨三点,我的 terminal 还在跳。OPC 循环(自动化 AI 构建流水线)每 20 分钟醒一次,读 loop-state.json,发现 status: pipeline_complete,打一行 “No-op.”,再睡回去。
这已经是它第 50 次这么做了。流水线跑完了,但循环停不下来——这是整个项目最讽刺的 bug。
从单用户工具到多用户架构
起点是一个单用户的垂直工具——功能完整但只为一个人设计。原型极简:SQLite 单文件数据库、Flask 后端、内容库是硬编码 JSON、没有用户系统。代码散落在四个 GitHub repo 里,没有人确定哪个是最新版本——典型的”个人工具”状态:功能能用,但架构完全不具备扩展性。没有用户系统意味着无法追踪不同用户的数据;没有数据库迁移意味着 schema 改了就得手动改 SQL;没有部署流程意味着只能在开发者自己的机器上跑。
目标:从单用户工具升级到可售卖的多用户架构。PostgreSQL 多租户、OAuth(开放授权)认证、Stripe 支付、AI Coach(用大语言模型生成个性化内容)、国际化(i18n)、管理后台、Fly.io 云端部署。
OPC 循环接到任务后,Claude 看了一眼范围(scope),第一反应是:“i18n、OAuth 多 provider——单用户工具不需要这些。”
我直接否定了这个判断:“我需要多用户支持。”
智能体有自己的判断,但用户的战略意图它无法推测。
这和 S2E01 的日历网格是同一类问题——AI 会根据当前代码状态和训练数据中的常见模式做推理(“这是个单用户工具,不需要多用户基建”),但它无法获知用户的长期战略规划。方向必须人来定。
47 小时,40 个 Tick
14 个范围(SCOPE),分解成 40 个执行单元(unit,每个 unit = 一个执行周期 tick = 一个执行步骤:plan → build → test → review),分 5 个阶段推进。阶段顺序是刻意安排的——先打地基(数据库、认证),再建核心产品,最后安全审计和部署。如果顺序反了——比如先做前端再做数据库——后续的 schema 变更会连锁打碎已经完成的前端代码。
| Phase | 内容 |
|---|---|
| Foundation | PG schema → 数据库迁移(migration) → 多租户 → Auth → 安全加固 |
| Core Product | 核心功能 → 前端重构 → AI Coach → 内容管理 |
| Infrastructure | i18n → Admin 后台 |
| Security + Deploy | 安全审计 → 云端部署 |
| E2E Verification | 端到端验收 |
每个 unit 的 plan 里写了 verify:(怎么跑测试)和 eval:(谁来评审)。比如:
verify: pytest tests/test_stripe.py -v && curl localhost:8000/api/healthz
eval: security, backend
这两行看起来无关紧要,但它们是抗失忆的关键——后面会讲为什么。
上下文溢出三次,流水线没断
S1E08 讲过上下文压缩(context compaction)的机制和文件系统桥梁的设计。这个项目是那套设计的实战压力测试——47 小时,1189 条 message,37M input tokens,上下文溢出三次。
每次溢出后智能体丢失了代码细节和决策原因,但流水线没断——因为 loop-state.json + plan.md 是持久化的事实基准。
Loop state 是唯一的事实源。 上下文炸了,智能体失忆了,但只要
loop-state.json+plan.md还在磁盘上,流水线就能继续。
OPC 循环协议的设计哲学不是围绕”智能体怎么执行”——而是围绕”智能体失忆后怎么恢复”。每个 tick 启动时强制读取 loop-state 和 plan,从不依赖对话历史。这就是为什么每个执行单元都有 verify: 和 eval: 两行——失忆后的智能体读到这两行就知道怎么验证自己的工作,不需要回忆之前的上下文。前面说”后面会讲为什么”,原因就在这里:这两行不是文档,是失忆保险。
三次溢出,三次恢复,流水线没断过。代价是词元暴涨——3.09 亿词元里 2.7 亿是缓存读取,大部分来自溢出后重建上下文的重复读取。
部署:另一种 Debugging
本地验证全部通过后进入部署阶段。前后尝试五次才成功。
第一次:psycopg2-binary 在 Alpine Linux 上编译不过。换 Debian slim + libpq-dev。
第二次:云平台给的数据库 URL 是 postgres://,但 SQLAlchemy 要 postgresql+asyncpg://。写了一个 normalize_database_url() 一劳永逸。
第三次:初始 SQL dump 已经包含后续 migration 的表结构,alembic upgrade head 试图重复建表。写了 migrate.sh 检测 fresh database,只跑初始 migration + alembic stamp head。
第四次:加了 /api/healthz 端点 + 平台健康检查(health check)配置。
第五次:终于跑起来了。但 CORS(跨域资源共享)只允许 localhost:5173——生产域名忘了加。流水线结束后我手动加了一行 config fix,但这个 bug 本身说明一个问题:AI 在 localhost 上测试通过就认为任务完成,不会主动检查生产环境域名配置。
五次部署尝试里,每一次失败都是同一类问题:本地开发环境和云端生产环境之间的差异。 编译器版本不同(Alpine vs Debian)、URL scheme 不同(postgres:// vs postgresql+asyncpg://)、数据库状态不同(fresh vs pre-seeded)、网络配置不同(localhost vs 公网域名)。AI 在本地跑通所有测试,认为”完成了”——但部署不是”跑通测试”,部署是让代码在一个完全不同的环境里活过来。这就是为什么部署要当独立的 iteration loop 对待,不是流水线的”最后一步”。
部署不是”最后一步”——它是另一种 debugging,要当成独立的 iteration loop 对待。
停不下来的循环
S1E10 用一句话讲过这个 bug——循环停不下来,是五个介入信号里的第五个。那是事后的一行总结。下面是完整的调试过程。
流水线全部完成,生产环境就绪——但 OPC 循环不肯停。
试了所有已知的停止方法:CronList → 空(dynamic mode 不用 cron);CronDelete → 没有 job ID;甚至试了 /ralph-loop:cancel-ralph → “No active Ralph loop found”。
唯一的办法:我手动按了 Ctrl+C。
问题的根源很清楚:循环的启动条件(“有新任务”)和终止条件(“任务完成”)不对称。启动是显式触发——我给了一个 task;但终止没有对应的显式机制。pipeline_complete 是流水线层面的状态,不是循环层面的终止信号。循环只会问”我该不该执行下一个 tick”,它不会问”我该不该停止存在”。
这不是一个可以靠”加个 if 判断”解决的问题。一个自主运行的系统,如果它自己不知道什么时候该停,那外部必须有人或有机制来告诉它。在这个项目里,这个”外部机制”是我的手指和 Ctrl+C。
自主循环需要终止守卫(termination guard)。 “流水线完成”不等于”循环停止”。任何自主系统都需要明确的终止条件和终止机制。
开场讲的那个凌晨三点的 “No-op.” ——它消耗了 270M 缓存读取词元,零产出。每 20 分钟醒一次,重新读取整个项目上下文,确认没有新任务,然后睡回去——如此循环 50 次。这是整个项目最大的浪费,也是最好的教训。
47 小时的账
| 指标 | 数据 | 备注 |
|---|---|---|
| 总耗时 | 47 小时 | 含空转轮询浪费 |
| API 费用 | ~$347 | 309M tokens,缓存读取占 87% |
| 执行单元 | 40 tick | 14 个 SCOPE,5 个阶段 |
| 子智能体 | 76 个 | 每个独立上下文,词元成本高 |
| 上下文溢出 | 3 次 | 自动恢复,流水线未中断 |
| 部署尝试 | 5 次 | 最终:Fly.io 生产环境 |
| 遗留 bug | CORS(手动修复) | 一行配置 |
$347 API 费用,309M tokens——但其中 270M 是缓存读取,大部分来自空转轮询和上下文溢出后的重建。如果循环有终止守卫,成本估计能砍掉至少三分之一。
这个范围(PG 多租户 + OAuth + Stripe + AI Coach + i18n + Admin + 部署)让一个熟悉 Python 但没做过全栈 SaaS 的开发者来做,大约需要 1-3 周全职工作。$347 换来的不是省钱——是把”周”压缩成”天”。
这 $347 也只是 API 费用。我自己花了大约 5-6 小时做范围划分、方向决策和部署调试——加上人工时间的机会成本,真实总成本大约 $700-$1100。
76 个子智能体是一把双刃剑:审查者真的不知道实现细节,反馈不受实现偏见影响——它不会因为”我花了两小时写这段代码”而放过质量问题;但每个子智能体都要重新理解项目上下文,词元消耗巨大。40 个 tick 意味着至少 40 次上下文重建,再加上 review 子智能体各自独立的重建——加在一起远超主循环本身的消耗。这是”做的人不评自己”的代价——独立性和效率之间的取舍,目前没有两全其美的方案。
终止守卫的缺失在后续迭代中得到了部分解决——maxLoopsPerEdge 限制了单个节点的循环次数(S1E03 引入),防止单个 tick 陷入无限重试。但这是节点级别的保护,不是会话级别的。更高层的问题——“整个 loop session 该什么时候停”——需要一个不同层次的机制:session 预算(总词元上限)、idle 检测(连续 N 次 no-op 自动退出)、或者显式的 done 信号从流水线传递到循环。到 S2 结束,这个问题仍未完全机械化。这个问题在 EP08 还会出现。
如果这篇文章只记一件事:任何自主循环都需要一个明确的退出条件。 没有终止守卫的自主系统,完成任务之后会变成一台空转的发动机——不会停,只会烧钱。
硅基团队 S2: 在实战中进化工具链 ← S2E01: 扳回方向之后,产品才刚开始 | S2E03: 所有人都说通过的时候该担心了 →