Skip to content
Touchskyer's Thinking Wall
S2E02
9 min read

硅基团队 S2E02: 40 个 Tick 推上线

硅基团队 S2E02

上一集家庭日历暴露的是方向决策问题——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内容
FoundationPG schema → 数据库迁移(migration) → 多租户 → Auth → 安全加固
Core Product核心功能 → 前端重构 → AI Coach → 内容管理
Infrastructurei18n → 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 费用~$347309M tokens,缓存读取占 87%
执行单元40 tick14 个 SCOPE,5 个阶段
子智能体76 个每个独立上下文,词元成本高
上下文溢出3 次自动恢复,流水线未中断
部署尝试5 次最终:Fly.io 生产环境
遗留 bugCORS(手动修复)一行配置

$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: 所有人都说通过的时候该担心了 →

留言