13 · M4c:cell 级分布式运行时
状态:核心切片完成(2026-11)。这是从“整 flow 调度”走到“agent cell 归属 + 远程消息 + 迁移/接管”的一步。
13.1 完成了什么
新增 crates/ain-cluster:
| 模块 | 内容 |
|---|---|
protocol.rs |
Envelope / Outbound;spawn/deliver/snapshot/remove/ping/stats |
wire.rs |
NDJSON/TCP 一行请求/响应 |
worker.rs |
CellHost:持有多个 AgentState,执行 behavior + effects,ack 携带 state/sends/usage |
cluster.rs |
cell 归属(placement)、消息队列、seq、worker 端 dedupe、迁移、failover、audit |
| CLI | ain cell serve、ain cluster demo/run/echo100 |
关键语义:
- cell 归属:每个 agent 恰好属于一个 worker;coordinator 维护
placement: cell -> node。 - 远程消息:每次消息是
Envelope { id, from, to, port, message };worker ack 里带sends, coordinator 按placement路由到目标 worker。 - seq/dedupe:coordinator 单调
id;workerseen集合丢弃重复 envelope。 - effect:worker 本地执行
EffectBus(mock LLM/search + HTTP/shell),返回 usage;coordinator 汇总 ledger。 - 迁移:
snapshot→spawn到目标 →remove源;更新 placement;auditMigrate。 - 故障接管:节点失联时,用
last_states(最后一次 ack 的 cell 状态)在存活节点重新 spawn;auditFailover。 - 审计:
Spawn/Deliver/Send/Effect/Migrate/Failover/NodeDown事件。
13.2 实测
3 个 cell worker:
ain cell serve --bind 127.0.0.1:7281 --id n1
ain cell serve --bind 127.0.0.1:7282 --id n2
ain cell serve --bind 127.0.0.1:7283 --id n3
ain cluster demo --nodes n1@127.0.0.1:7281,n2@127.0.0.1:7282,n3@127.0.0.1:7283
结果:
cluster scenario=pipeline nodes=3 steps=3 effects=4 usd=0.000322 state=58cedc39...
audit={"deliver": 3, "effect": 3, "send": 3, "spawn": 3}
output writer.out=[{"tag":"final", ...}]
placement=critic@n2,researcher@n1,writer@n3
迁移:
ain cluster demo --nodes ... --migrate critic:n3
# audit 增加 migrate: 1;placement critic@n3, writer@n3
故障接管:
ain cluster demo --nodes ... --kill n1
# audit: failover:1, node_down:1;researcher 迁到 n2,仍输出 writer.out
100 个 cell(echo 链):
ain cluster echo100 --nodes n1@127.0.0.1:7281,n2@127.0.0.1:7282,n3@127.0.0.1:7283
# steps=100, deliver=100, send=100, spawn=100, output c99.out
ain cluster run examples/research.flow --nodes ... 也支持任意 FlowSpec。
13.3 测试
crates/ain-cluster/tests/cluster.rs(16 个):
- 3 节点 pipeline:最终
writer.out,3 个节点都参与。 - 迁移 critic 后结果不变。
- kill 节点后从最后 ack 状态 failover,仍输出。
- 重复 envelope 被 dedupe。
- 100 个 echo cell 跨 3 节点跑完(steps=100)。
- 错误 token 拒绝 spawn。
总计:82 个测试(27 core/golden + 9 language + 19 runtime + 8 sched + 19 cluster)。
13.4 与“真正分布式交互网”的差距
| 项目 | M4c 实际 |
|---|---|
| cell 归属 | ✅ agent 级 |
| 远程消息 | ✅ TCP NDJSON + seq/dedupe |
| 迁移 | ✅ snapshot/spawn/remove |
| 故障接管 | ✅ last-ack 状态,at-least-once |
| 传输安全 | ❌ 仅共享 token;无 mTLS |
| 背压 | ❌ 无界队列 |
| 精确一次 | ❌ at-least-once;重复消息靠 id dedupe |
| session 协议 | ❌ 只有 FlowSpec protocol label |
| worker 自动心跳/再入 | ❌ 需手动 kill/health |
| 100 agents 单 flow | ✅ echo 链(非 LLM) |
| LLM agents 单 flow 100 | ❌ 未做 |
13.5 M4d 计划(已实现,见 13.6)
- backpressure:每 cell/每节点有界队列 + credit。
- mTLS + capability token:每个 cell 一条 capability;撤销。
- worker watch:自动心跳、摘除、重新加入。
- at-least-once → exactly-once:effect idempotency + dedupe 持久化。
- 100 个真实 LLM agents 单 flow 与 kill-node 5s 恢复演练。
- remote wire 的图级语义:跨节点 wire 也进入 trace/可视化。
13.6 M4d 追加(安全/通道/幂等/LLM 100)
per-cell capability
Spawn携带capability;Envelope携带capability;worker 对 deliver/snapshot/remove 校验。Cluster::revoke(cell)→ 生成新 capability →SetCapability→ 旧 capability 立即失效。- 测试:错误 capability 拒绝;revoke 后旧 cap 拒绝、新 cap 可用;cluster revoke 后仍输出。
- 注意:capability 是
run_id-cell-seq字符串,不是密码学 token;仍依赖共享 token + 受信网络。
per-edge channels
Envelope增加channel = "<from>:<port>"与seq。- worker 按
(run, channel)维护last_seq:seq == last+1接受;seq <= last视为重复(ack duplicate);seq > last+1拒绝(gap)。
- 测试:gap 拒绝;旧 seq dedupe;跨 run 复用 worker 正常(channel key 含 run)。
effect 幂等缓存
- worker 缓存
run:cell:op:input_hash -> output。 - 同一 effect 输入再次出现(不同 envelope id)不会重复调用 handler,也不会重复计费。
- 测试:counting handler 只被调用 1 次;
effect_cache_len=1。 - 限制:缓存是 per-worker;跨节点 failover 后缓存丢失,仍可能重复执行(需持久化幂等键)。
worker watch / rejoin
Cluster::watch_once():ping 所有节点;失联 → failover;恢复 →NodeUp。Cluster::watch(rounds, interval);CLIain cluster watch。Cluster::add_node(id, address)可把恢复的 worker 重新挂到同一 node id。- 测试:kill 后
down包含节点;新 worker + add_node 后rejoined+NodeUp。
100 LLM-cell 链
demo::llm_chain(100):每个 cell 执行一次 mock LLM effect,再发给下一个。ain cluster llm100 --nodes ...:3 节点、100 cells、100 effects、100 steps。--kill-after 30 --kill n1:34 个 cell failover,最终仍 100 effects、同 output。--kill-after 30 --kill n1:34 个 cell failover,最终仍 100 effects、同 output。
audit 落盘
ain cluster ... --save-audit <path>输出完整 audit JSON(Spawn/Deliver/Send/Effect/Migrate/Failover/NodeUp/Revoke)。
仍然没做
- mTLS / 密码学 capability / capability 撤销的持久化。
- 背压(coordinator 队列无界)。
- 跨 worker exactly-once:M5 已用 coordinator ledger 复用 effect 结果;provider 调用成功但 ledger 未落盘的窗口仍存在。
- wire 级(同一 cell 多端口)通道与重连窗口。
- 100 个真实 LLM provider(当前 mock)。
测试总数:82。