生产扩展——队列、检查点、持久性


文档摘要

生产扩展——队列、检查点、持久性 本节摘要:把多智能体系统扩到数千并发运行需要持久执行(durable execution)——工作队列加检查点,使任何工作器在任何崩溃后都能恢复任何运行,前提是租约(lease)处理、幂等副作用、确定性回放就位。LangGraph 的运行时是参考范例:它在每个超步(super-step)后写一个以 为键的检查点(默认 Postgres);工作器崩溃则释放租约,另一工作器恢复。Agent 可无限期睡眠等待人类输入。MegaAgent(arXiv:2408.09955)跑每 Agent 的生产者-消费者队列,含三态(空闲/处理/响应)与两层协调(组内聊天 + 组间管理聊天)。

生产扩展——队列、检查点、持久性

本节摘要:把多智能体系统扩到数千并发运行需要持久执行(durable execution)——工作队列加检查点,使任何工作器在任何崩溃后都能恢复任何运行,前提是租约(lease)处理、幂等副作用、确定性回放就位。LangGraph 的运行时是参考范例:它在每个超步(super-step)后写一个以 thread_id 为键的检查点(默认 Postgres);工作器崩溃则释放租约,另一工作器恢复。Agent 可无限期睡眠等待人类输入。MegaAgent(arXiv:2408.09955)跑每 Agent 的生产者-消费者队列,含三态(空闲/处理/响应)与两层协调(组内聊天 + 组间管理聊天)。Fiber/异步胜过每任务一线程做 LLM 流式:线程 99% 时间在等 token 时空转,fiber 在 I/O 上协作让出。反方观点:Ashpreet Bedi 的「扩展 Agent 软件」主张FastAPI + Postgres、别的不要,直到负载证明否则——简单架构比预期走得更远。本节搭一个持久检查点日志、一个每 Agent 工作队列带状态转移、一个异步 vs 线程 demo,并落实「先简单」的务实规则。

学习目标

阅读完本节,你应当能够:

  1. 说明持久执行的三条要求(可序列化状态、确定性恢复、幂等副作用),以及 LangGraph/Temporal/Restate 如何实现同一模式。
  2. 复述 MegaAgent 的每 Agent 三态队列与两层协调(组内聊天 + 组间管理聊天),并指出它如何在数千 Agent 上保持成本线性。
  3. 解释为何 LLM 调用是 I/O 密集、线程为何在等 token 时浪费 99%、fiber/异步为何在 Agent 规模下「是架构而非优化」。
  4. 复述 Bedi 的反方论点——FastAPI + Postgres 在约 100 并发 Agent 运行内常常够用——并说明「何时该采用持久执行框架、何时该推迟」。
  5. 应用「有效一次」语义的三步工程动作(每次运行去重键、发件箱模式、补偿事务)与彩虹部署,为长程有状态工作负载设计生产加固。

一、问题与直觉

一个原型多智能体系统在一台笔记本上、三个 Agent、一个内存事件循环里跑得好好的。你上生产:

  • Agent 有时跑数小时(长研究、人在环等待)。
  • 工作器进程会崩,重启丢状态。
  • 峰值负载是均值的 10 倍,你要水平扩展。
  • 用户按 Agent 运行付费,你要对计费做到「有效一次」语义。

内存事件循环这些都不做。你需要在底层加一个持久执行层。 2026 的标准选项是:带检查点的工作流引擎(Temporal、LangGraph 运行时);带状态库的消息队列(Postgres + SQS/RabbitMQ);Actor 模型框架(MegaAgent 的每 Agent 生产者-消费者);手搓 FastAPI + Postgres(Bedi 的主张)。本节各搭一个微缩版。

持久执行,这个模式

一个持久执行引擎在每个「步」(LangGraph 的超步)后持久化全程序状态。崩溃时:

工作器在步中途崩溃 -> 租约超时 -> 另一工作器接起 thread_id -> 从最近检查点恢复 -> 无重复副作用

要它工作需要:状态可序列化(带活数据库连接的函数闭包活不下来);确定性恢复(给定相同状态与输入,Agent 产出相同动作,或对 LLM 调用 defer 到外部确定性预言机);副作用幂等(外部调用如工具、付款,必须幂等或用去重键)。

LangGraph 每个超步后写检查点,Temporal 每个活动后写,Restate 用事件溯源日志。三者实现同一模式。

每步检查点的运行时

LangGraph 的运行时是范例:每个 Agent 有一个 thread_id,状态是类型化字典,每个超步写一行到检查点表。恢复时从最近检查点回放,不从头来。Agent 可 interrupt() 等人类输入,运行时持久化并释放工作器;输入到达时,任何工作器都能恢复。

这是 2026 年 4 月的参考生产设计。

MegaAgent 的每 Agent 队列

arXiv:2408.09955 描述了一个规模实验:单集群数千并发 Agent。架构:

agent i: state ∈ {Idle, Processing, Response} in_queue <- 发给 agent i 的消息 out_queue -> 回复 + 副作用 coordinators: 组内聊天 (同组 Agent 间) 组间管理聊天 (高层路由)

两层协调让组内对话密集发生、组间保持稀疏——这是在数千 Agent 上保持成本线性的模式。

异步 vs 每任务一线程

LLM 调用是 I/O 密集的。一个等下一个 token 的线程 99% 时间空转。线程每个约 1MB 内存;1 万并发调用就是 10GB 仅栈。

Fiber(Python asyncio、Go goroutine、Rust tokio)在 I/O 上协作让出。同样 1 万调用轻松装进一个进程。在 LLM-Agent 规模下,异步不是优化,而是架构。

例外:CPU 密集的后处理(嵌入、tokenizer 技巧)仍要线程或进程。把 I/O 层与 CPU 层分开。

Bedi 的反方观点

「扩展 Agent 软件」(Ashpreet Bedi, 2026)主张多数团队在测量负载前就过度工程。务实默认:FastAPI + Postgres;每个 Agent 运行是一行,状态带乐观并发就地更新;后台作业经 pg_notify 或简单 Celery worker;重试策略在应用代码里。对可管理任务、约 100 并发 Agent 运行以内,这常常就是你需要的。 测出它失败时再升级。

规则:当你撞上一个简单架构解决不了的具体问题时,才采用持久执行框架。过早采用把时间烧在不回报的仪式上。

⚠️ 这是「协调难点在状态」的生产版本——多智能体系统的全部状态(消息、计划、检查点)都在持久层里,这一层任何崩溃都让协调成果化为乌有。「先简单」不是偷懒,而是避免在没测量到痛点前就背上一套你不需要的持久执行引擎。

有效一次语义

对付费的 Agent 运行,你要「有效一次」(至少一次交付 + 幂等消费)。工程动作:

  • 每次运行一个去重键。 把它放进每个副作用调用里。
  • 发件箱模式(outbox)。 副作用先写一张表,再由独立进程执行,两步都幂等。
  • 补偿事务。 当副作用成功但其跟踪写入失败时,调度一个补偿。

这些是数据库工程模式,非 LLM 专属。LLM 的税只是调用慢;其余都是标准分布式系统。

彩虹部署

Anthropic 的多智能体研究系统用「彩虹部署」:多个版本的 Agent 运行时并发跑,这样长程 Agent 不必在每次代码部署时被杀。金丝雀新版切一部分流量;旧版等其 Agent 跑完再退役。这对长程有状态系统是标准的;2026 的适配是 Agent 可活数小时,部署周期必须适应。

标准生产清单

可序列化的持久状态(检查点、快照、或发件箱 + 可回放日志);幂等副作用;LLM 调用的异步 I/O 层;至少一次交付 + 去重;有状态工作负载的彩虹/金丝雀部署;可观测性(逐 Agent 轨迹、超步审计、重试计数)。

二、从零实现

code/main.py 实现:

  • CheckpointStore——SQLite 支撑、以 thread_id 为键的检查点日志,每个超步追加一行。
  • run_with_checkpoint(agent, thread_id)——模拟运行中途崩溃,第二个工作器从最近检查点恢复。
  • AgentQueue——每 Agent 的 空闲/处理/响应 状态机,带小型工作队列。
  • demo_async_vs_threads()——用 asyncio 和线程各跑 500 个并发模拟「LLM 调用」,报告墙钟与峰值内存(近似)。

检查点恢复与发件箱

def run_with_checkpoint(agent, thread_id, store): state = store.load_latest(thread_id) or initial_state() while not state.done: checkpoint = store.save(thread_id, state) # 每超步先存 try: state = agent.step(state) # 可能在此崩溃 outbox.enqueue(state.pending_side_effects, dedup=thread_id) except Crash: state = store.load(checkpoint) # 从检查点恢复,副作用靠 dedup 去重 continue return state

设计要点:store.saveagent.step 之前写,是崩溃可恢复的承重顺序——先持久化意图、再执行,这样即使执行中途崩溃,恢复后靠 dedup 键保证副作用「有效一次」。把顺序反过来(先执行后存),崩溃就可能在恢复后重放副作用,造成重复计费/重复发送。发件箱模式把「想做」与「去做」物理分离,是这条顺序纪律的工程落地。

运行 python3 code/main.py,期望输出:模拟崩溃后检查点恢复成功;异步版在 < 1 秒内处理 500 并发调用;线程版耗时数秒,且每并发单元内存高一到两个数量级。

三、框架对比

方案 承重机制 适用规模 何时采用
FastAPI + Postgres(Bedi) 行级状态 + 乐观并发 ~100 并发运行 默认,先上
LangGraph 运行时 每超步检查点 + thread_id 中~大 长程、人在环、复杂重试
Temporal 每活动检查点 + 工作流引擎 大、跨区 跨区协调、复杂补偿
MegaAgent 每 Agent 队列 三态机 + 两层协调 数千 Agent 超大规模、成本线性

💡 心法:先简单,测出失败再升级。 Bedi 的 FastAPI + Postgres 在 100 并发内常常够;LangGraph/Temporal 的检查点机制在你撞到「小时级人在环等待」或「跨区协调」时才值回票价。颠倒顺序——一上来就上 Temporal——是本节最常见、最昂贵的过度工程。

四、可复用产物

outputs/skill-scaling-advisor.md:为持久执行选型——FastAPI + Postgres、LangGraph 运行时、Temporal、还是自研。按负载、状态保留需求、部署频率校准。

五、练习

  1. 跑基线:运行 code/main.py,确认检查点恢复工作;测异步 vs 线程的并发差异。
  2. 实现发件箱:每条工具调用先写发件箱,再由独立 goroutine/任务执行。跑两次工具调用验证幂等。
  3. 彩虹部署:模拟两个并发运行时版本,各路由一半新 thread_id,确认旧版在途线程不被打断。
  4. 读 LangGraph 运行时:读其文档,识别哪个功能在手搓 FastAPI + Postgres 上复制最久。是该采用的理由,还是可以推迟?
  5. 读 MegaAgent:读 arXiv:2408.09955 第 3 节,两层协调(组内 + 组间管理聊天)是显式的。草拟如何把它映射到两个队列族的消息队列。

本节要点回顾

  1. 持久执行三要求:状态可序列化、确定性恢复、副作用幂等;LangGraph/Temporal/Restate 实现同一模式。
  2. 每步检查点是参考设计:LangGraph 每个 thread_id 每超步写一行,崩溃靠租约超时 + 另一工作器恢复。
  3. MegaAgent 三态 + 两层:每 Agent 空闲/处理/响应,组内密集 + 组间稀疏,数千 Agent 成本线性。
  4. 异步是架构不是优化:LLM 调用 I/O 密集,线程 99% 空转,fiber 在 I/O 让出,Agent 规模下必选异步。
  5. Bedi 反方论点:FastAPI + Postgres 在 ~100 并发内常够,测出失败再升级。
  6. 有效一次三步:每次运行去重键、发件箱模式、补偿事务——数据库工程模式,非 LLM 专属。
  7. 彩虹部署:多运行时版本并发,金丝雀新版,旧版等在途 Agent 跑完再退役。
  8. 标准生产清单:持久状态、幂等副作用、异步 I/O、至少一次 + 去重、彩虹/金丝雀、可观测性。

下一节,我们直面本章最沉重的话题——失败模式:MAST 分类、群体思维、单一文化、级联错误,看多智能体系统为何崩、以及如何在崩之前堵住。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U