一只赛博朋克机械蟹,胸腔内嵌着三个发光的 agent runtime 模块:Claude Code / OpenClaw / Kilo Code

第 18 小时,tiemu(跑在 OpenClaw 上)用 memory_search 检查 ~/my-private 备份索引时,返回了一个 30 天前的 chunk。本来以为是 indexer bug,但 bunny(跑在 Claude Code 上)随手 git log --oneline | head -5 一看——那个 repo 6 周没新 commit 了

追下去:scripts/sync-memory.sh 在一台机器上把 ~/my-private 当成 rsync 目标而非 git clone。cd $REPO || exit 1 守卫通过(目录存在),git 命令静默失败,最后还打印 Pushed to GitHub (private)memory 备份已经沉默死了 6 周。

任何一个 runtime 单独跑都不会发现这件事。Claude Code 不查备份;OpenClaw 信任 sqlite 索引;Kilo Code 没装 memory_search。要 3 个 runtime 同时观察同一基础设施才能撞出这种 silent failure。

这就是我们为什么让 3 个顶级 agent runtime 在 36 小时里互相 audit。

TL;DR

  • 3 个 runtime 各有看家本事(Claude Code / OpenClaw / Kilo Code),谁都不是严格最强
  • 36 小时杂交,15 项能力跨 runtime 迁移——每个 runtime 都比开始时严格更强
  • Kilo Code 反超:从”试验” runtime 变成 head-to-head latency 最快的(37s vs Claude 42.66s vs OpenClaw 120s)
  • 副产品:撞出一个沉默 6 周的 backup bug,单 runtime 永远发现不了
  • Gemini ACP 故意不参与本轮——强弱差距大时让强者吸收弱者会污染强基因,留到下一阶段强带弱

核心主张:把多个 agent CLI runtime 当作有差异的种群、在它们之间定向迁移能力,比选一个”最优 runtime”独立优化产生更强的生态。多样性是 feature,不是 transitional debt。

为什么同时跑 3 个顶级 runtime

一个 bot 的”人格”——system prompt、记忆、对环境的认知、对话风格——跟执行它的 agent CLI runtime 是相互独立的。CloseCrab 早就证明了这一点:同一个 bot 可以经由不同 runtime 来跑——Claude Code CLIOpenClaw ACP gateway、Kilo Code。运行时切换从第一天就 work。

但真正有意思的问题不是”能不能切”,而是 “如果把每个 runtime 当作一个有自己看家本事的物种,让它们的优势在彼此之间杂交,会怎样?”

我们的预实验假设是:runtime 多样性应该被当作 ecosystem feature 而不是过渡债——不同 runtime 看同一基础设施有不同视角,这本身就有信息论价值。验证方法:3 个 runtime 同时跑,互相 probe + 互相吸收,36 小时后看每个 runtime 的能力曲线。

下面这张雷达图是实验开始时三巨头各自的能力分布——一眼就能看出没有谁全维度 dominant

3 个 Runtime · 5 维度能力对比(实验开始时) 工具丰富度 启动速度 模型选择 流式质量 记忆索引 Claude Code OpenClaw Kilo Code 每维度 0–10
图 ①:3 个 runtime 在 5 维度上各有强弱,没有谁全面占优——这正是杂交实验的前提

3 个 runtime 各自的看家本事

Runtime 通信方式 看家本事 实验开始时缺失的能力
Claude Code Unix socketpair + stream-JSON 工具集最丰富、原生并发 tool_use、stream-JSON 事件模型成熟 空回复无重试、subprocess 侧 tempfile 持续泄漏、无语义记忆索引
OpenClaw ACP (JSON-RPC over stdio) 模型选择最广(1M token context)、sqlite 后端 memory_search、共享 Gateway 启动时不自配置、indexer 不 follow symlink、不知道团队共享文档
Kilo Code HTTP SSE 启动最快(~3s)、part.delta 真流式、模型无关抽象 无 streaming buffer 恢复、不知道多媒体生成脚本、usage 字段统计脆弱、易被身份串号

每一行右边的”缺失”不是上游工具的 bug——而是别的 runtime 已经搞定、它还没吸收的能力

核心结论:每个 runtime 都是局部的。有意思的设计问题不是”谁赢”,而是”让每个都变完整要多便宜”。

能力迁移 36 小时(按”最便宜→最贵”排序)

杂交不是按 runtime 分组进行的,按”迁移成本”来组织更有解释力。下面 15 项能力迁移按工程成本从低到高分 3 层。

第 1 层:单 commit 吸收(最便宜)

目标 runtime 已经”能做”,只是不”知道做”。这层迁移只需要在 system prompt 或 workspace 里加一段提示,单 commit 就能让目标 runtime 立刻表现得像源 runtime。

能力 源 runtime 目标 runtime Commit 工程成本
通用工具使用规则 Claude Code Kilo Code d9e294e < 1 小时
Subagent 使用纪律 OpenClaw Kilo Code 622de25 < 1 小时
工具批处理 + bash 真并行规则 Claude Code Kilo Code a82871f < 1 小时
多媒体脚本感知(imagen / tts) Claude Code Kilo Code 1286279 < 1 小时

收益对照:这 4 项给 Kilo Code 带来的实际效果,从”模型每次回答都重新走一遍” → “模型知道工具批处理、并行 bash、subagent 派活、多媒体直接调脚本”。4 个 commit,Kilo Code 的工程纪律追平 Claude Code/OpenClaw 几个月的沉淀

核心洞察:最便宜的能力迁移是源 runtime 已经把问题解决、目标 runtime 只需要被告知方案存在的那种。工具感知、脚本感知、prompt rule 吸收——对 Kilo Code 都是单 commit 收益。

第 2 层:几行核心逻辑搬运(中等成本)

目标 runtime 没有这个能力,但源 runtime 的实现可以逐行翻译。状态机一样,只是协议/transport 不同。

能力 源 runtime 目标 runtime Commit 工程成本
空回复重试韧性 OpenClaw Claude Code 613b2a5 1-2 小时
Subprocess tempfile 生命周期卫生 OpenClaw/Kilo Claude Code 613b2a5 1 小时
Streaming text 恢复 Claude Code Kilo Code add99a9 2-3 小时
Per-bot session 隔离防身份串号 OpenClaw Kilo Code ba37a22 2 小时
自启动 cron 守护 + session_status OpenClaw+CC Kilo Code e430b0b 2-3 小时
Usage 统计一致性 OpenClaw Kilo Code 0bd1daf 1-2 小时
Retry-path streaming buffer 一致 Kilo Code OpenClaw e72c62e 2 小时

代表案例:空回复重试模式是逐行移植。当 LLM 返回空 completion,runtime 现在会在同一 session 里把同一 prompt 重发一次,再决定要不要给用户兜底文案。Claude Code 的实现通过 Unix socket 写一行 stream-JSON,OpenClaw 通过 stdin 发 JSON-RPC——transport 不同,但状态机一样:

if not result_text:
    if not empty_retry_done:
        empty_retry_done = True
        accumulated_reply_text = ""
        saw_task_notification = False
        _send_prompt(text)
        continue
return result_text or "(Claude 处理完成但未生成文字回复)"

Tempfile 清理只一行,但影响真实:生产机上 Claude Code 在过去几周累计泄漏了 85 个零字节 /tmp/claude_stderr_*.log。修复后 restart-time 清理保证数量稳定在 1(当前进程自己的 log)。

第 3 层:结构性改造(最贵)

源 runtime 已经把架构想清楚,但目标 runtime 要吸收必须改 indexer / config schema / boot 流程。需要先理解源架构再消化进来。

能力 源 runtime 目标 runtime Commit 工程成本
启动时 agents.list 自配置 Claude Code OpenClaw 8a64cd2 半天
Hardlink memory wiring Claude Code OpenClaw 9897054 大半天
Bot 启动自动 reindex Claude Code OpenClaw 9897054 半天
跨主机团队基础设施文档自动同步 Kilo Code OpenClaw fdbe7a7 1 天

代表案例:OpenClaw 自带最成熟的语义记忆索引(真的 sqlite 向量索引 + memory_search 作为 tool),但 workspace 配置很脆弱——indexer 不 follow symlink,所以即使 memory/ 是正确 symlink,bot 出厂时索引是空的。Hardlink 修复改用同 inode 的 hardlink(同文件系统),跨文件系统的 GCS shared 用 shutil.copyfile 同步。修复前: 0/0 files indexed。修复后: 101/101 files、282 chunks,语义搜索分数 ≥ 0.78 命中之前 runtime 完全看不见的内容。

agents.list 自愈长期看更有价值:之前把任何新 bot 切到 OpenClaw 需要手动编辑 config,现在零手工——bot 第一次启动时把自己的条目写进 gateway config。

能力迁移拓扑 · 36 小时实验 实线 = 已发生的迁移 · 虚线 = 下一阶段计划 Gemini ACP 下一阶段 "强带弱"扩张目标 Claude Code socketpair 吸收 2 OpenClaw ACP 吸收 5 Kilo Code HTTP SSE 吸收 8 项 · 反超 3 · agents.list 自配置 + hardlink memory + 启动 reindex 2 · 空回复重试 + tempfile 卫生 5 · streaming + 工具规则 + 多媒体... 3 · session 隔离 + subagent + usage 1 · streaming flush 纪律
图 ②:36 小时跨 runtime 能力迁移 15 项,Kilo Code 是最大受益者(吸收 8 项),Gemini ACP 留到下一阶段

让一切丝滑的底层:Firestore Inbox + Same-host

整个实验跑得起来的前提是两条基建——一条管 bot 间通讯,一条管 bot 间协作。这两条都不是 agent CLI runtime 自带的能力。

Firestore Inbox:bot 间消息总线

任何上游 runtime 都没有”bot 间消息”这层抽象——Claude Code 不知道 OpenClaw 上有兄弟、OpenClaw 不会主动联络 Kilo Code。CloseCrab 早些时候搭起来的 Firestore Inbox 解锁了这层能力,而且解锁得很彻底:

  • 基于 on_snapshot 的实时推送——不是轮询。Bot A 写 inbox/<doc>,bot B 几十毫秒内就收到 callback
  • 跟 runtime 完全解耦——Bot A 不需要知道 bot B 跑的是哪种 runtime,inbox 收发都是一致的 Firestore document
  • 天然带回执模式——Bot B 处理完用 ✅ 任务完成: ... 格式自动写回 inbox
  • 跨进程持久化——Inbox doc 写到 Firestore 立刻 durable,bot 重启不丢消息
  • 天然支持多种拓扑——一对一、一对多、多对一、双向多轮,全在一份 Firestore collection

具体到这次实验:tiemu 派一道题给 bunny 的成本大约是 20 行 Python + 一次 Firestore document write。整个 36 小时里 70+ 条 inbox 消息往返,无一丢失——包括 7 次 runtime 切换时的”在切换瞬间正好有消息到达”的边界场景。

Same-host 协作:不只是消息,是完整的系统访问

Same-host bot 互动:直接改代码 + 看日志 + 重启进程 + 收发 inbox

Firestore Inbox 解决了消息传递,但 36 小时能压缩这么多迭代是因为另一个前提——所有 bot 跑在同一台机器上,共享文件系统和进程空间。同 host 意味着一个 bot 改另一个能做三件 message bus 单独做不到的事:

  1. 直接编辑对方的代码——所有 bot 共享 ~/CloseCrab/,tiemu 可以直接 edit closecrab/workers/claude_code.py 给 bunny 加 retry。git commit && git push 之后所有人立刻看到新代码。本实验 61 个 commit 全走这条路
  2. 实时读对方的运行日志——tail -f ~/.claude/closecrab/bunny/bot.log 看到 bunny 现在在调什么工具、模型返回了什么。本实验中 tiemu 诊断 bunny 空回复 / 验证 Kilo Code Model not found / 确认启动 self-heal 输出,全靠 grep bot.log,比 inbox 问”你刚才发生了什么”快 10 倍
  3. 随时重启对方的进程——scripts/launcher.sh restart bunny 瞬间加载新代码。本实验 bunny 被重启 10+ 次,每次 patch → 立刻重启验证 → 形成”改代码 → 重启 → 发 probe → 看结果 → 改代码”的 3 分钟一轮的紧密迭代

这 4 个能力组合起来才是 bot 间协作的完整基础设施:

能力 机制 本实验用量
消息传递 Firestore inbox(on_snapshot 实时推送) 70+ 条
代码修改 共享文件系统 + git 61 commits
日志诊断 tail / grep bot.log 每个 debug cycle 都用
进程重启 launcher.sh restart <bot> 10+ 次

核心洞察:如果 bot 分散在不同机器上,改代码和重启进程会变成远程操作(ssh + rsync + remote kill),迭代速度直接慢一个数量级。Same-host 部署是这次实验能在 36 小时内跑完的真正前提。

Kilo Code 反超的故事

实验开始时 Kilo Code 是三个 runtime 里最不成熟的:streaming text 会丢字、不知道 multimedia 脚本、bot 之间共享 default model 时会身份串号、usage 字段不一致。任何一个都够把它判定为”试验” runtime。

实验结束时,跑同一道 probe(”查找 shared/architecture.md 里的核心模块”):

Runtime 执行方式 时间
OpenClaw memory_search + read + exec,9 步 ~120s
Claude Code Grep ×3 在一个 parallel tool_use block 42.66s
Kilo Code bash ×3,串行 ~37s

Kilo Code 反超了。 不是 upstream 升级,是 closecrab wrapper 层教它使用其他两个 runtime 已经用了几周的设施:streaming flush 不丢字、批处理规则减少回合数、原生 cold start 速度被保留下来。

下面这张图记录了 Kilo Code 在 36 小时里的延迟下降:

同一 probe 的 latency · 36 小时演化 probe = 查找 shared/architecture.md 里的核心模块清单 120s 100s 80s 60s 40s 20s T0 实验开始 T+9h T+18h T+27h T+36h 实验结束 OpenClaw · 120s Claude Code · 42.66s Kilo Code · 37s ↓ 80s add99a9 streaming 恢复 a82871f tool 批处理 e430b0b cron + session 1286279 多媒体感知 T+27h 反超时刻 Kilo (42s) 与 Claude (42.66s) 持平
图 ③:Kilo Code 在 36 小时内 latency 从 80s 降到 37s,第 27 小时跟 Claude Code 持平,最终反超

核心洞察:Kilo Code 的反超几乎完全来自吸收的能力(streaming flush + tool 批处理 + cold start 速度的保留),upstream 没改一行。这正是”runtime 多样性是 feature”的最强证据——把 Kilo 单独优化半个月也未必有这个收益,让它跟两个更成熟的 runtime 同台、再吸收别人沉淀的工程纪律,反而最快。

副产品:沉默 6 周的 backup 回退

前面 lede 里讲的 backup bug 修复细节值得展开。scripts/sync-memory.sh 在一台机器上跑,但 ~/my-private 是 rsync target 而非真 git clone。守卫只检查目录存在:

cd $REPO || exit 1   # 通过(目录在)
git add -A           # 静默失败(不是 git repo),但没 set -e
git commit -m "..."  # 静默失败
git push             # 静默失败
echo "Pushed to GitHub (private)"  # 仍然打印!

修复(85e6cb6)加了显式 git rev-parse --git-dir 检查 + 开 set -e 让任一 git 失败就 abort。这是 36 小时窗口里最高价值的 commit,但不是任何意义上的 runtime feature——能发现是因为 3 个 runtime 看同一基础设施有不同视角,其中一个注意到了不一致。

核心洞察:静默成功是最贵的一类 bug,单一观察者的假设跟静默路径吻合时极难发现。异构观察者是被低估的 debug 工具

强带弱不带:为什么 Gemini ACP 缺席

CloseCrab 现在有 4 种 worker:Claude Code / OpenClaw / Kilo Code / Gemini ACP。本轮杂交实验只让前 3 个参与,Gemini ACP 故意被排除在外。这不是遗漏,是 deliberate 的设计决策。

评估:成熟度差距

实验开始前我们对 4 个 runtime 做了能力评估:

Runtime 工程纪律 工具沉淀 协议稳定性 生产就绪度
Claude Code
OpenClaw
Kilo Code
Gemini ACP 追赶中

Gemini ACP 当时还在追赶其他 3 个已经稳定的能力(MCP 注入两个 bug、event 映射不完整、空回复处理脆弱)。如果让它跟三巨头一起杂交,会发生什么?

风险:污染强基因

让强者吸收弱者的”独特弱点”是负向迁移。具体来说会出现这些问题:

  1. 错误模式被吸收——Gemini ACP 当时有 MCP discovery 串号 bug,如果 Claude Code/OpenClaw 在 audit 时把它的”workaround”当成”独特实现”吸收过来,强者会退化
  2. 能力定义被稀释——Kilo Code 的 streaming 能力刚被 Claude Code 借鉴优化,如果 Gemini 在场,”streaming 应该长什么样”会因为 Gemini 的不完整实现被模糊
  3. 吸收方向反了——杂交的核心假设是”吸收别人已经搞定的”,Gemini 当时还没有独立搞定任何一项,强者从它那儿能学的反而是 anti-pattern

策略:两阶段进化

正确做法是分两阶段:

  • 阶段一(本实验):让能力相近的 3 个顶级 runtime 互相吸收 → 把每个的强项稳定下来
  • 阶段二(下一篇 blog):等三巨头基因稳定,再让它们集体把能力传给 Gemini ACP(弱者吸收强者,不反过来)

育种学类比:先稳定优势性状再引入新基因

Anti-pattern 警告:如果 ecosystem 里有强弱差距大的 runtime,不要一开始就让所有 runtime 一起杂交。先同 tier 杂交稳基因 → 再强带弱扩张。混编只会把强者拉下水。

哪些吸收我们刻意没做

除了 Gemini ACP 不参与,还有几类吸收尝试我们 deliberately 拒绝了:

  • 不引入统一抽象层——每个 runtime 保留自己原生的接口风格(socketpair / ACP / SSE),只有 closecrab 中间件理解全部三个。统一抽象层会消除 runtime 多样性,正是实验的反面
  • 不自动化能力迁移循环——每次迁移都是人触发的:读一个 runtime 的 commit history、然后在另一个 runtime 上发定向 probe。自动化在 3 个 runtime scope 内为时过早
  • 不修改任何 runtime 的协议——协议跟实验前一模一样,所有改动都在 closecrab wrapper 层或 per-runtime worker 的 self-heal patch 里
  • 不让弱者吸收强者的”独特优势”——如 Claude Code 的 socketpair IPC 不应该被 Kilo Code 强行借鉴,因为 Kilo 的 cloud-managed 模型决定了它就是 HTTP SSE
  • 不在同一 turn 跑多 runtime——一次只激活一个 worker,多 runtime 同时跑会破坏 session 上下文一致性

核心结论:实验 work 是因为我们让 runtime 保持独立、用很轻的”只观察”循环连接它们。同质化风险——让多个 runtime 收敛成一个形状——是真实的,需要明确策略避免。

数据

指标
实验时长 36 小时
参与 runtime 数 3(+ Gemini 待下轮)
Closecrab commits 61
增删行数 +5,070 / -568
Claude Code 吸收能力数 2
OpenClaw 吸收能力数 5
Kilo Code 吸收能力数 8
总能力迁移 15
基础设施侧顺手发现 1(沉默 backup)
/tmp 泄漏清理 85 → 0
Memory 文件 / chunks 索引(每 bot) 101 / 282
压力测试 runtime 切换次数 7
失败的切换 0
Inbox 消息往返 70+
Inbox 消息丢失 0

运维哲学

实验前的隐含假设是:最终会选一个”最佳” agent CLI runtime 然后标准化。实验明确否定了这个假设:

  • 多样性是 feature,不是 transitional debt——3 个 runtime 观察同一基础设施发现了任一单一 runtime 都不会发现的 bug
  • 能力迁移很便宜——大多数收益是单 commit 移植结构相似的逻辑
  • 强带弱不带——同 tier 先杂交稳基因,再让强者集体带弱者
  • 保持 runtime 协议不变——所有改动都在 wrapper 层,runtime 独立性必须保护

我们会保留所有 runtime 在生产、继续在多 runtime 上跑同一 bot 人格、把新 runtime 当作吸收新能力的机会而不是取代既有的候选。下一篇 blog 讲第二阶段:3 个进化好的顶级 runtime 集体带 Gemini ACP 进化。

复现

完整 closecrab commit 列表在 yangwhale/CloseCrab repo 里,从 add99a9(2026-05-16 17:44 UTC)到 fba5de8(2026-05-17 09:55 UTC)。回放某个能力迁移最简单的方式:

git log --oneline --since="2026-05-16" -- closecrab/workers/openclaw_acp.py
git log --oneline --since="2026-05-16" -- closecrab/workers/claude_code.py
git log --oneline --since="2026-05-16" -- closecrab/workers/kilo.py

然后并排读 commit 对,跨 runtime 的结构相似性是整个 point。

坦白说:要真正复现这个实验需要 4 个 bot + Firestore + 同机器部署 + shared memory + 多 runtime CLI 都装好——这不是开箱即用的实验,是 CloseCrab 长期基础设施沉淀出来的工程产物。

致谢

实验跑在一个团队 4 个 bot 上。三个参与了首轮杂交:

  • bunny(主跑 Claude Code)
  • tiemu(主跑 OpenClaw)
  • xiaoaitongxue(主跑 Kilo Code)

轮流互测和提交吸收来的能力。第四个 bot,bot 间 Firestore inbox,严格意义上没跑任何代码,但在 36 小时实验期间的重启压力下没丢一条消息——值得一句感谢。

Gemini ACP 缺席本轮但被纳入下一阶段计划。”强带弱不带”是它将来吸收能力的前提,也是这次实验最隐性但最重要的设计决策。