
我一直觉得单个 AI Agent 的智商远没有多个 Agent 的组织方式重要。这个观点在我完整做完 OpenRig 这套多智能体编排内核之后彻底变成了工作信条——我们手上那批离散的 AI Agent单独跑每一个都很正常可一旦要让它们互相传递结果、共享一份状态、在同一个业务流程里接力系统的复杂度和脆弱程度立刻指数级上升。OpenRig 这个项目解决的就是这件事不做新的 Agent而是把已有的、各干各的 Agent 编织成一个可持久化、可恢复、可观测的协作系统。这篇文章我会把为什么需要这种编排、编排内核怎么设计、持久化怎么做、并发容错怎么扛以及两个真实业务场景里的落地复盘一次性讲透。适合正在搭 AI Agent 中台、或者已经发现单个 Agent 扛不动真实业务复杂度的朋友参考。1. 单体 Agent 的失控现场OpenRig 想要解决的那个真实痛点我最早也是从做一个全能 Agent入手的后来发现这个路线在生产环境里走不通。这不是模型能力的问题而是组织形态的问题。1.1 三个 Agent 各自为战我差点被在线事故压垮当时团队里已经有几个跑得不错的 Agent一个知识库问答 Agent、一个数据检索 Agent、一个工单处理 Agent。各自独立跑的时候效果都挺好用户反馈也不错。直到产品经理提了个需求用户问一个问题之后需要自动查数据、自动分析、自动开一个工单然后由另一个 Agent 去执行最后再回报结果。听起来只是串起来但真正连的时候问题全冒出来了。第一个 Agent 分析完要传给第二个 Agent传的不是结构化数据而是一段对话文本第二个 Agent 解析不了就开始胡编字段。更头疼的是状态第一个 Agent 已经完成了一件审批动作第二个 Agent 不知道又重复做了一遍线上用户收到了两遍通知。最崩溃的一次是进程重启之后工单 Agent 把自己之前已经处理过的会话上下文全丢了从中间节点又开始重跑差点把一笔已经审批通过的单子再提交一次。这次事故之后我意识到单个 Agent 的推理能力再强它也只是系统里的一个节点。真正难的从来不是节点而是节点之间的关系。1.2 为什么生产环境最终都会走到多体系统后来我复盘发现业务一旦复杂一点单体 Agent 必然是撑不住的原因很朴素上下文窗口是硬约束。一个 Agent 把所有历史对话、工具返回、中间推导全塞进自己的上下文里很快会被截断。更合理的方式是每个人只保留和自己职责相关的上下文。角色冲突真实存在。让同一个 Agent 既写方案又审方案它会倾向于自说自话很难做出真正的批判。把审查拆成独立 Agent用不同的模型、不同的 prompt 约束效果会好很多。数据权限不允许打通。有些 Agent 要访问财务数据有些只能碰运维日志。单体 Agent 一旦都要访问权限边界就很难控制。业务天然有并行。一次告警排查可能要同时查网络、查日志、查监控指标。单体 Agent 只能串行做时间拖到不可接受。这些约束其实和软件工程里微服务替代单体应用的理由一模一样。AI Agent 也是一样的道理单体到一定程度拆开是唯一出路。但拆开之后随之而来的就是编排问题——谁来调度、怎么通信、状态放哪、挂了怎么恢复。这就是 OpenRig 切入的地方。1.3 现成编排框架很好但缺的是持久化底座我并不是一开始就想自己写框架的。LangGraph、CrewAI、AutoGen 我都认真调研过说实话每个都有值得抄的设计框架编排思路强项生产短板LangGraph状态图 条件边循环控制流、状态管理思路清晰偏会话级状态长时间任务的持久化恢复要靠自己补CrewAI角色协作 任务队列写 Demo 很快角色抽象自然抽象偏上层复杂分支和人工介入场景比较别扭AutoGen对话式多 Agent调试直观Agent 间通过对话交换信息对话流松散结构化状态和审计链不够强MetaGPTSOP 流水线内容生产场景很顺手偏单链路流水不适合动态分支业务这些框架共同缺一个东西持久化的协作底座。所谓持久化不只是把聊天记录存下来而是指整个协作过程的状态——每个 Agent 跑到哪一步了、中间结果是什么、等待哪个外部输入、进程重启之后能不能从断点接上。Demo 里不怕进程挂生产环境里这是最基本的生存需求。我评估一圈之后决定基于现有框架的启发自己写一个偏底层的编排内核核心诉求就三个可编排、可持久化、可观测。OpenRig 就这么立项了。2. 编排内核怎么设计图执行器、状态机和 Agent 之间的接口契约OpenRig 的第一版我花了两周时间只做了一件事定清楚一个流程怎么被描述、被调度。这个阶段想不清楚后面所有功能都会歪。2.1 用图表达流程用状态机控制流转我把一个业务流程建模成一张有向图节点是执行单元边是流转关系。节点类型有五种Agent 节点调用大模型完成一个认知任务比如读取工单内容并判断紧急程度。工具节点调用外部系统比如查询库存 调用支付 API一般没有 LLM 参与。人工节点流程停下来等人确认或填信息人回来之前一直挂着。决策节点根据当前共享状态走哪条分支。网关节点做扇出fan-out和汇聚fan-in比如并行让三个 Agent 各自查一类数据然后等全部返回。在 OpenRig 里图的表达很直接但执行并不是跑完一个节点就走下一条边这么简单。我在执行器上层叠了一个基于事件的状态机每个节点跑完之后输出一个事件调度器根据当前状态和事件内容决定下一步动作。比如重试事件会回到当前节点已确认事件会从人工节点走向下一个节点。图上负责描述有哪些可能路径状态机负责决定当前这次运行具体走哪条路径。这两层分开代码不会糊成一团。为什么不用纯 DAG真实业务里大量存在分析完发现信息不够回去再查一次这种循环。DAG 表达不了回边硬建模会很别扭。所以我允许图里有环但给每个回边加了退出条件约束。比如最多重试三遍达到上限直接走 fallback 分支防止 Agent 陷入死循环。2.2 别让 Agent 直接对话消息总线与共享状态的分工最开始我把 Agent 之间的交互设计成直接传消息就是 AgentA 把一段话推给 AgentB。实践了两周就放弃了——文本消息太随意字段对不上、语义理解有偏差调试像在解谜。后来 OpenRig 采用了双通道通信模型通信方式适用场景我的用法事件总线通知时机、触发下一步动作AgentA 完成后发一个data_ready事件订阅者感知并开始工作共享状态区传递结构化数据、中间结果AgentA 把结果写入状态槽位slotAgentB 从对应槽位读取事件总线管什么时候共享状态管数据是什么。Agent 之间不直接知道彼此存在只管自己对哪些事件敏感、需要读写哪些槽位。槽位的定义是带 Schema 的。Agent 节点声明input_slots和output_slots调度器在执行前校验上游数据是否满足下游的 Schema不满足就报错。这样做的好处是多个 Agent 协作的本质变成了读写共享结构化状态而不是互相猜自然语言。数据从 A 到 B 的过程里谁写入的、什么时间写入的、版本号是多少全部有记录出了错可以直接查。2.3 能力注册与动态发现拓扑不再写死OpenRig 里的每个 Agent 上架时必须提交一份 Manifest描述三件事能干什么、输入输出是什么、需要哪些权限。Manifest 长这样简化的 YAMLid: risk-scoring-agent name: 风控评分 Agent capabilities: - risk_scoring - fraud_check input_slots: - name: application_profile type: schema://credit/application output_slots: - name: risk_score type: schema://credit/risk_result permissions: - credit_db_read - risk_model_invoke执行器看到一个新 Agent 接入不关心它内部是用 LangChain 还是直接调 API只关心它的 Manifest 和能力。流程定义里写需要一个能做风险评分的节点执行器自己找符合条件且输入槽位匹配的 Agent 来绑定。新增一个 Agent 的时候旧节点一行不用改。这个机制帮我躲过很多次加一个 Agent 就要动所有上下游的连锁修改。2.4 一个编排定义的最小例子空谈概念不如看实例。下面是一段 OpenRig 的流程定义 DSL对应一个客服工单自动分级并转派的场景workflow: ticket-triager nodes: - id: parse-ticket type: agent agent: ticket-parser output_slots: [ticket_profile] - id: decide-severity type: decision input_slots: [ticket_profile] conditions: - if: ticket_profile.severity high then: notify-manager - else: assign-agent - id: notify-manager type: agent agent: manager-notifier requires_confirmation: true - id: assign-agent type: tool tool: assign_ticket input_slots: [ticket_profile] edges: - from: parse-ticket to: decide-severity - from: decide-severity to: notify-manager - from: decide-severity to: assign-agent recovery: snapshot_interval_events: 50 resume_strategy: from_checkpoint这段 DSL 编译之后会被执行器加载成一张可运行图。decide-severity是一个纯逻辑节点走哪条边由共享状态里的severity字段决定。notify-manager带requires_confirmation: true意思是它做完之后流程会挂起等人工在页面上点确认已通知然后才继续。这就是持久化协作系统里最典型的人机接力节点。3. 持久化是分水岭状态、事件与断点续跑如果说编排模型解决的是Agent 怎么协作那持久化解决的就是协作到一半天塌了怎么办。OpenRig 从第一版开始就把持久化当一等公民不然后面全是在给线上事故填坑。3.1 为什么我放弃了纯快照选了事件溯源第一版我偷懒用的纯快照方案每隔一段时间把流程实例的完整状态存一份到数据库恢复时候直接读最近快照。跑了一周就发现问题快照只能恢复当时的结果恢复不了当时为什么这么做。比如一个风控 Agent 拒绝了一笔贷款我们要解释给用户听结果快照里只有最终拒绝状态中间的推理过程、引用数据、调用过哪些模型全丢了。后来我换成事件溯源Event Sourcing思路一个流程实例的生命周期里所有有意义的变化都记录成一条不可变事件追加写入事件存储。节点完成、消息传递、工具返回、人工确认、Agent 决策全部是事件。恢复的时候从最近的一次快照开始把增量事件一条条重放就能精确重建出当时的全部上下文。这个切换带来的额外收益是审计能力。多 Agent 协作里出了业务事故你要能回答哪个 Agent 当时看到了什么、做了什么决定、手抖改了哪个字段。没有事件流这个问题永远回答不了。3.2 恢复流程快照重放增量Agent 失忆也能接话OpenRig 里每个流程实例都有一个生命周期状态恢复流程的伪代码大概是这样的def resume(workflow_instance_id): snapshot load_snapshot(workflow_instance_id) # 加载最近快照 events load_events_after(snapshot.version) # 查增量事件 state snapshot.state for event in events: # 重放时只更新内存状态不触发真实副作用 if event.type slot_updated: state.slots[event.slot_name] event.value elif event.type node_completed: state.node_status[event.node_id] completed elif event.type awaiting_human_input: state.pending_human_nodes.append(event.node_id) # 其他事件类型同理 # 根据恢复后的状态把未完成节点重新入队 pending_nodes compute_ready_nodes(state) scheduler.enqueue(pending_nodes)关键点是重放过程不能触发真实副作用。也就是说重放时不能真去调支付接口、不能真发消息。副作用操作必须单独走一层幂等执行器Run 的时候记录一个幂等键恢复时检查这个键是否已经执行过执行过就直接跳过。这样就不会出现进程重启Agent 把已发出的转账又发了一遍这种事故。3.3 人工介入节点怎么持久化生产环境里几乎每个正经流程都有人工节点。审批要人点一下工单要人确认一下。人工介入给持久化带来的麻烦是等待状态不能放在 Agent 进程里。如果流程正等着人点击确认这时候进程重启了难道人点的那一下就没了吗OpenRig 的解法是把等待也状态化。每当流程执行到人工节点执行器向事件存储写入一个awaiting_human_input事件然后流程实例进入waiting状态执行器把它从调度队列里撤下来不再占用任何线程。人工在系统里确认之后回调 API 往事件流里追加一条human_confirmed事件调度器被唤醒重新加载该实例的状态从等待节点继续往下走。整个流程里人点的那个动作也是一条持久化事件。进程重启与否对流程来说完全无感。持久化做到这个程度我才敢说这套系统是协作系统而不是在一堆 Agent 外面套了层流程控制。4. 并发与容错Agent 真正一起干活之后才遇到的坑单 Agent 跑的时候并发和容错都很好处理一个请求进来处理完返回就行。多 Agent 同时跑一个流程、多个流程实例共享一组 Agent 资源的时候才真正理解了什么叫AI Agent 怎么扛并发。4.1 共享状态写冲突版本号、CAS 与幂等两个 Agent 同时更新同一个槽位是最常见的并发事故。我们线上踩过一次告警恢复 Agent 和值班提醒 Agent 同时改同一个告警单一个要把级别调低一个要调高最后最后写入的覆盖了前一个级别被改错了。OpenRig 的共享状态槽位从一开始就带了版本号更新用 CASCompare-And-Swap语义只有当前版本号等于我读到的版本号这次写入才成功不相等就说明有人抢先改了需要读取最新值重新计算再提交。这个机制成本很低但能挡住大多数并发写冲突。比写冲突更隐蔽的是副作用幂等。Agent 调用外部工具是天然非幂等的——发消息发两次就重复通知调扣款接口调两次就重复扣款。OpenRig 里所有工具调用在进执行器之前都要生成一个幂等键事件存储里记录这个键的执行状态。重试的时候先查幂等键查到了直接返回已记录的结果不二次执行。这一条是所有 AI Agent 上生产环境的底线没有它重试机制就不是兜底而是事故放大器。4.2 Agent 跑飞、超时与重试熔断要设计在哪个层级LLM 调用超时不是异常是常态。OpenRig 对每种节点都设了不同的执行 deadlineAgent 节点默认 90 秒工具节点默认 10 秒人工节点没有 deadline 但受挂起时间上限约束。超时之后进入退避重试间隔 1 秒、4 秒、16 秒最多三次。三次还失败就走 fallback要么降级到更简单的模型 Agent要么转人工处理要么直接结束本次流程并输出需要人工介入的说明。循环节点要防死循环。我在 2.1 提过图里允许有环但必须配终止条件。OpenRig 的做法是给每个环附带最大迭代数配置例如分析-再分析的环最多转 3 圈超过之后强制跳出并标记needs_human_review。别指望 LLM 自己知道什么时候该停它很容易在循环里越滚越深。熔断要放在两个层级。第一层是外部依赖某个模型供应商的错误率超过阈值执行器短暂熔断不再调度新的调用。第二层是单个 Agent 节点连续 N 次执行失败的 Agent 会被标记为 unhealthy后续流程动态改绑到备用 Agent 或直接走人工兜底。这些边界在写流程定义时就要想清楚等线上出事再补已经晚了。4.3 可观测性把思考-行动-观察变成可回放的轨迹多 Agent 系统的调试难度比单 Agent 高一个数量级。问题可能发生在第三个 Agent 的中间推理里而它读到的数据是被第一个 Agent 写错的。没有完整的执行轨迹只能靠猜。OpenRig 给每个节点每次执行都产生一条执行追踪记录包含流程实例 ID、节点 ID、Agent ID、输入槽位摘要、输出槽位摘要、执行的起止时间、重试次数、调用的模型名与 token 数。我常用的一张关键指标表长这样指标作用节点成功率区分是模型问题还是链路问题节点平均耗时 / P99定位瓶颈最常见的是超长 Agent 节点Token 消耗分布发现某个 Agent 反复读超大上下文重试分布重试集中在哪个节点往往就是不稳定源头等待队列深度判断并发调度是否积压但指标只是辅助真正让我在事故里脱身的是事件回放打开一条流程实例的时间线能像看录像一样看到每个 Agent 在哪个时刻看到了什么、做了什么决定、改了什么槽位、触发了哪个工具。这套能力其实是在事件溯源基础上白捡的。所以我说可观测性和持久化不是两件事是同一件事的两面。5. 两个真实落地场景审批流与告警排查的编排复盘OpenRig 在内部接手了不少业务流我挑两个最有代表性的复盘一下能看得更清楚这套编排到底怎么工作。5.1 信贷审批串行风控链路与人工复核的接续信贷审批是一个典型的串行链路申请解析 Agent → 反欺诈 Agent → 风控评分 Agent → 人工复核节点 → 放款执行 Agent。链路上最怕两件事第一风控 Agent 和评分 Agent 对同一份申请数据各自处理写槽位时互相覆盖第二人工复核之后如果进程重启Agent 忘了已复核通过这回事又走一遍放款。第一件靠槽位版本号解决第二件靠事件溯源解决。人工复核节点的确认动作本身就是一条事件进程重启后重放事件流流程实例会精确恢复到人工复核通过、放款执行之前的等待态放款 Agent 从该节点重新被唤醒。而且放款执行带了幂等键就算因为网络抖动重复调度也不会重复放款。这套链路在 OpenRig 上线之前我们是靠人工在数据库里改状态来续接的每周都有那么一两次状态改错。上线之后最直观的变化是出错之后可以回放完整链路几分钟就能定位是哪个 Agent 的判断问题而不是翻日志翻到怀疑人生。5.2 告警响应多路并行排查与决策后的执行告警响应则是另一个极端并行多、时效强。流程大概是一条告警进来 → 并行触发网络排查 Agent、日志排查 Agent、指标分析 Agent → 汇总 Agent 整合三路结果 → 决策 Agent 给出处理方案 → 人工确认 → 执行 Agent 落地操作。并行排查的难点在汇聚节点。OpenRig 的网关节点支持两种汇聚语义AND等待所有上游完成和 OR任一上游完成即继续。告警排查必须用 AND三路结果缺一路就下结论容易误判。三个排查 Agent 各写各的槽位汇总 Agent 是第一个读它们的节点所以读之前要做完整性检查——三路槽位都 ready 才允许执行。另一个优化是槽位冲突的优先级策略。告警场景里规则 Agent基于预设规则判断的结论比统计 Agent基于指标异常判断的结论更硬所以同一个槽位两边同时写时规则 Agent 的版本优先被保留。这个是在线上踩过坑之后加的代价是不过是想让两个结论并存结果互相覆盖的那份恰恰是关键证据。5.3 复盘时发现的两个性能和存储问题事件溯源好用但存储是真贵。第一个问题是事件量过大Agent 节点的内部推理过程如果逐步入库一天能攒几千万条事件。我们的解法是分两级热事件先写 Redis异步批量落库到 PostgreSQL同时定期做事件压缩把旧事件聚合成快照超过审计保存期的直接清理。第二个问题是恢复时间变长。事件多了之后从快照位置重放到最新需要几分钟这对于告警响应场景不可接受。后来我们按租户和流程类型做了分区存储并且调了快照生成频率——高频运行的流程类型每 50 个事件就出一份快照低频的流程类型可以放宽。恢复时还做了并行加载不同槽位状态分线程重建整体恢复时间从分钟级降到了秒级。6. 写在最后的工程心得OpenRig 做到现在我最大的体会是多智能体编排的核心不在调度本身而在契约。Agent 与 Agent 之间读什么槽位、写什么事件、遵循什么 Schema这些契约定清楚了后面的一切都顺。反过来如果只盯着流程引擎怎么调度、节点怎么并发Agent 之间还是会互相踩脚。另外持久化这件事一定要从第一天就做。我第一版偷懒用快照第二周就被审计需求打脸后来切事件溯源才真正体会到失忆的 Agent 能接上话对协作系统有多关键。很多团队做多 Agent 系统先把流程跑通再补持久化结果跑通那天就是事故开始那天。最后分享三个我自己用下来最值的小技巧第一设计任何多 Agent 流程之前先拿人脑把流程走一遍画一张人肉流程图再转成机器流程图人脑都理不顺的流程机器更别想跑顺第二所有 Agent 可能触发的副作用操作一律加幂等键没有例外第三每个 Agent 节点上线前必须写清楚输入输出槽位的 Schema哪怕只写一句话的注释也不能省。OpenRig 这套编排内核我自己还打算继续演进方向是把执行器拆成独立库Agent 节点的插件化做得更彻底再往下一层做一些支持不同模型路由的抽象。如果你们团队也在被多 Agent 的协作和持久化问题折磨我的建议是从一个小小的、状态清晰、有人工节点的流程开始让事件溯源先跑起来你很快就会感受到能接上话、能说清楚为什么到底值多少钱。