多智能体编排实战:OpenRig如何用持久化协作系统驯服Agent雪崩
做OpenRig这套多智能体编排方案之前我在生产环境被一群AI Agent折磨了很久。单个Agent跑Demo的时候人人都说好一旦把三五个Agent放进同一个业务流程里问题就全冒出来了消息乱序、状态互不可见、一个Agent改了结果另一个还在用旧上下文、协作流程跑到一半进程挂掉前面所有对话全部归零。OpenRig就是在这些协作雪崩里逼出来的——一个围绕离散AI Agent与持久化协作系统两个核心诉求设计的多智能体编排运行时。这篇文章会把它的架构拆解、核心抽象、端到端实操、故障恢复手段以及我在真实流量下踩过的坑一次讲清楚。如果你正在做多Agent应用或者被workflow编排、Agent并发、任务断点续跑这类问题卡住这篇应该能给你一套可以直接参考的工程范式。1. 从协作雪崩说起为什么多Agent编排必须持久化1.1 单Agent的成熟方案放进多Agent就失效了单Agent场景下工程问题其实很好解决。LLM调用、上下文管理、工具调用这些都有相当成熟的轮子LangChain、Spring AI、各类SDK都能收拾明白。你维护一个会话历史数组把用户消息、LLM回复、工具返回都push进去再在需要时交给模型流程是线性的状态边界很清晰。但多Agent协作不是线性的它是网状的。Agent A要把结果交给BB要等C的判断才能往下走C又需要A早前说过的一句话来做参考。状态变为了图结构任何一个节点的失败都会导致整张图死掉。我第一次在真实业务里跑三个Agent组成的流程时其中一只模型超时结果整个Task在内存里只剩一团乱麻——日志还在但会话状态已经对不上了。这时候我才意识到多Agent的复杂度根本不是模型能力问题而是分布式系统问题。1.2 持久化协作系统到底要持久化什么标题里持久化协作系统这几个字刚开始我自己也没想透。做过一轮崩溃复盘之后我把它拆成了四类必须落盘的东西工作流定义这个协作静态拓扑是这样的起点在哪、有哪些分支、超时怎么兜底。任务实例状态当前跑到了哪一步、每个Agent的输入输出快照、谁在等谁。Agent间的消息历史虽然单条消息是结构化的但消息之间的因果顺序、依赖关系才是协作的关键。执行痕迹Event Log每次状态迁移、每一条决定、每一回工具调用都要记录下来用于回放和审计。你会发现这几乎就是一个事件溯源 状态快照的组合和电商订单系统、工作流引擎没什么本质区别。区别只在于这里的执行单元不是确定的函数而是Token消耗高、延迟不可控、输出可能不稳定的AI Agent。所以持久化的意义就更重了——崩溃之后你要能把整张协作图拉起来而不是重跑一遍。1.3 反直觉的一点编排的第一要务是让Agent变笨如果说持久化解决的是崩溃问题那编排层解决的就是失控问题。OpenRig里一个很重要的设计准则是编排层不做任何智能判断Agent也不应该持有全局视角。大多数人的第一反应是让Agent互相知道对方的状态、共享一个大上下文觉得这样更能协作。我实测下来的结论恰好相反Agent的上下文越全越容易产生莫名其妙的自信和自作主张消息中大量无关信息还会膨胀Token费用。正确的做法是让每个Agent只知道自己这个环节该做什么、输入的Schema是什么、输出的契约是什么全局流程交给一个确定性的运行时去把控。Agent负责干活编排底座负责记住活干到哪了。这个认知贯穿了后面所有设计。OpenRig本质上就是一层确定性的胶水把一堆不确定的Agent粘成一个可预期、可恢复、可观测的系统。2. 先拆边界再谈协作OpenRig的模块划分要撑起持久化协作系统OpenRig的架构没有搞成大单体。我把它拆成了四个职责清晰的模块Agent接入层、消息交换层、状态持久层、执行引擎。每一层都解决一类特定问题。2.1 Agent接入层让Agent退化成无状态WorkerAgent接入层的目标是让Agent变得无状态化。注意这里的无状态不是说Agent没有记忆而是说Agent不持有集群级的状态比如其他Agent的运行情况、整个Task的全局进度。它只需要暴露两样东西一个能力声明它能做什么、输入输出字段是什么用JSON Schema描述。一个消息处理函数接收任务消息干完活返回结构化结果。from openrig import Agent, RigMessage class StockAgent(Agent): name stock_agent input_schema { type: object, properties: { order_id: {type: string}, sku_id: {type: string}, quantity: {type: integer} } } async def handle(self, msg: RigMessage) - dict: # 只有一条消息进来不需要关心是哪个Task的哪个环节 # 查库存、锁定库存然后返回结果 return {stock_status: ok, locked: True}这种设计带来的直接好处是Agent可以被轻量水平扩展重启一个Agent不会影响其他Agent。因为所有会话级状态都在持久层Agent重启后只要把这个Task的下一段消息投递给它它就能继续干活。如果你更喜欢用LangGraph这类的Agent框架来做单个Agent内部的思考循环也没问题。OpenRig的Agent适配层允许你把LangGraph、LangChain、甚至裸的OpenAI SDK包进来Workflow级的编排和Agent内部的思考是两个层面的事情不用绑死。2.2 消息交换层异步总线是协作的血液Agent之间怎么传话是编排系统里最容易被低估的模块。我最早用过同步HTTP调用Agent A直接调用Agent B的接口简单粗暴但问题很快暴露一旦B卡住或超时A的所有线程全部被占用系统吞吐直接掉到地板而且HTTP的同步语义强烈暗示着你等我出结果这在协作流里不成立——很多情况下A发完消息就该退场等B完成后通过事件继续触发后面的节点。OpenRig的消息交换层基于Redis Streams实现API兼容的组件也可以替换成NATS JetStream或Kafka。核心是两点消息发布后不等待任何Agent的同步确认而是写入Stream由调度器异步投递。每条消息带有精确的元信息task_id、from_agent、to_agent、seq_no。seq_no是后面做乱序治理的关键。# broker配置 broker: type: redis_streams url: redis://127.0.0.1:6379/5 consumer_group: openrig这里多说一句不要为了省事把消息总线和业务数据库混在一起。消息总线是流形的存储是状态形的。你如果把它们放同一个库等到你要对消息做回放、做延迟分析的时候SQL查询会痛苦得让你怀疑人生。2.3 状态持久层事件日志加快照持久层是OpenRig里最重的部分。每个TaskInstance的完整生命周期都以事件日志的形式追加存储。事件类型包括TaskCreated、AgentInvoked、AgentCompleted、WorkflowPaused、TaskRecovered等等。同时系统周期性为每个Task生成状态快照快照记录了当前进行到哪个节点、各Agent结果是什么、待投递消息队列有哪些。这里我选择了PostgreSQL的JSONB字段来存事件和快照而不是引入专门的Event Store。理由很简单团队不需要多维护一套基础设施PostgreSQL的可靠性足够JSONB对半结构化的事件数据支持得很好而且历史事件可以通过SQL直接排查。只有当单个Task的事件量每天超过几十万条时我才会考虑迁移到专门的Event Store。持久层确定了之后崩溃恢复的方案就顺势定下来了从最近一次快照恢复状态重放快照之后发生的事件直到状态回到崩溃前的最后一刻。2.4 执行引擎把静态定义变成动态运行执行引擎是OpenRig里唯一有控制权的模块。它读取Workflow定义解析成可执行的状态机再根据事件日志推进状态机当下一个要执行的节点是Agent时把任务消息投递到消息总线。它和Agent之间的交互永远是发布消息异步等待所以执行引擎自身不能被Agent的长耗时阻塞。整个引擎是用Rust实现的核心循环基于Tokio异步运行时单机可以支撑非常高的消息吞吐。Agent侧你用Python、TypeScript、Java都无所谓只要遵循接入层的契约就行。为什么用Rust写引擎而不是直接用Node或Python不是我追求性能噱头而是执行引擎这种底座的稳定性要求值得。Rust的类型系统在状态机迁移上能提前拦住很多非法状态变化而且内存占用可控部署时一个二进制文件就搞定这比任何容器镜像都轻。3. 静态工作流与动态协作图核心编排抽象3.1 一个订单售后流程的Workflow定义编排系统绕不开工作流定义。OpenRig使用YAML定义静态拓扑。下面这个例子是典型的订单售后场景用户发起退货先查库存锁定再判断是否符合退款条件条件满足走退款否则转人工客服。workflow: order_after_sale version: 1.0 start: ingestion nodes: - id: ingestion type: agent agent: service_agent next: check_conditions - id: check_conditions type: condition conditions: - if: payload.eligible true next: refund - if: payload.eligible false next: human_review - id: refund type: agent agent: refund_agent next: done - id: human_review type: human description: 客服确认退款资格 next: refund - id: done type: end这个定义看起来很像传统的BPMN工作流但运行时的表现和传统工作流完全不一样。传统工作流的每个节点是确定逻辑而这里的service_agent和refund_agent输出什么内容引擎并不知道引擎只负责把消息送进去、等结果、根据结果走分支。所以我把这个模型称作静态骨架 动态神经Workflow定义是静态骨架Agent的每一次实际推理是动态神经。定义负责保证不会迷路Agent负责在节点内做任意复杂的判断。3.2 TaskInstance、Session与上下文作用域多Agent系统里上下文作用域是最容易踩坑的设计点。OpenRig把上下文分成三个层级严格限制可见性TaskInstance一次完整业务请求的实例例如一个售后单、一次数据分析请求。它包含Workflow ID、当前状态、事件日志、快照引用。Session一个Agent与协作系统的交互会话。Session中存放Agent输入输出、工具调用记录。同一任务关联多个Session。ContextToken级上下文是Agent真正能看到的Prompt内容。关键规则是Agent A可以读取Task级别的共享信息比如订单号、用户身份但不能直接修改Agent A的输出会作为事实写入Task的记录中后续Agent可以引用但只能读引用不能原地改。这个只读引用的约束很重要。没有它Agent B可能会偷偷篡改Agent A已经提交的结果最后业务对不上账。共享是只读的变更走事件——这套规则和数据库的MVCC思路同源。3.3 记忆与反思上下文该从哪里来多Agent的协作过程中Agent经常需要回想之前聊过什么。OpenRig提供了两层记忆机制短期工作记忆最近N轮会话消息的全文。这部分直接拼进Prompt保证Agent对最近上下文有完整的感知。长期向量记忆历史任务的关键结论沉淀成向量通过相似度检索把最相关的几条记忆注入Prompt。这部分不是每次都要查只有当前Agent声明需要时才挂上。我一开始还设计了反思Agent定期把历史任务整理成经验摘要。后来发现反思Agent本身也会消耗大量Token还可能因为管得太多而产生错误的经验总结。目前的做法是只有在工作流明确包含reflective: true标记时才开启反思环节默认保持轻量。关于Token消耗这里必须提醒一句多Agent的Token消耗是呈指数级增长的不是线性。三个Agent、每个上下文1万Token单次完整任务就可能吃掉十几万Token。你要是连上下文作用域都不控制一个复杂的售后流程跑下来成本会让你怀疑人生。OpenRig里每个Agent的Context都是摘要最近对话当前输入的组合而不是把所有历史无脑灌进去。3.4 人工介入不是Bug而是特性多Agent系统最容易让人担心的就是让AI自己从头跑到尾。OpenRig在设计上把人工介入当成了一个一等公民节点。每个human节点会生成一个独立的审批任务通过REST接口暴露。Engine会把该Workflow暂停在等待状态直到人工提交决定才继续推进状态机。这个等待是有超时保护的超时后会走降级路径——比如自动延迟、重新分配客服、或者终止并发送告警。另外一个细节人工介入窗口产生的决定也会被写入事件日志。这样后期如果业务上有争议你可以明确知道这个退款是哪个客服在哪个时间点确认的。审计链路是完整的。4. 从零实现三Agent协作端到端实操理论讲再多不如动手跑一遍。这里用一个最小可运行案例带你完整走通OpenRig的部署、Agent编写、Workflow触发和结果观察。4.1 环境准备与部署最小运行时OpenRig运行时是一个Rust二进制加上Redis和PostgreSQL两个依赖。本地开发我推荐用Docker Compose一把拉起version: 3.9 services: redis: image: redis:7-alpine ports: [6379:6379] postgres: image: postgres:15-alpine environment: POSTGRES_DB: openrig POSTGRES_USER: openrig POSTGRES_PASSWORD: openrig ports: [5432:5432] openrig: image: openrig/runtime:0.4.2 ports: [8080:8080] depends_on: [redis, postgres] environment: BROKER_URL: redis://redis:6379/5 STORAGE_URL: postgres://openrig:openrigpostgres/openrig运行时启动后会做两件事一是监听REST API用于提交新任务、查询任务状态、人工审批二是作为调度器消费消息总线中的事件并把消息正确投递给Agent。Agent进程可以单独跑在别的机器上通过SDK注册到运行时。两边注册我的方式是Agent进程启动后向运行时发送一个/agents/register请求带上Agent名和Schema。运行时校验通过后这个Agent就具备接收任务消息的资格。4.2 编写三个Agent并注册能力下面用Python SDK写三个极简Agent。注意Agent本身没有任何Redis或PostgreSQL的连接它只和运行时通信。import asyncio from openrig import Agent, RigMessage class ServiceAgent(Agent): name service_agent async def handle(self, msg: RigMessage) - dict: order_id msg.payload[order_id] reason msg.payload.get(reason, ) is_eligible bool(reason) and breakage not in reason return { order_id: order_id, eligible: is_eligible, summary: 受理售后请求初步判断是否符合退款条件 } class RefundAgent(Agent): name refund_agent async def handle(self, msg: RigMessage) - dict: # 模拟退款处理 return { refund_id: fRF-{msg.payload[order_id]}, status: refunded }这两个Agent非常简单但已经足够演示核心流程。第三个Agent如果走人工分支则不需要写代码运行时直接通过human节点把任务挂起来由人工在管理端操作。写完Agent后启动进程并注册python service_agent.py --runtime http://127.0.0.1:8080 --name service_agent python refund_agent.py --runtime http://127.0.0.1:8080 --name refund_agent4.3 通过事件触发Workflow并观察运行轨迹向运行时提交一个新任务只需一个HTTP请求curl -X POST http://127.0.0.1:8080/tasks \ -H Content-Type: application/json \ -d { workflow: order_after_sale, correlation_id: order-10086, payload: { order_id: 10086, sku_id: SKU-A100, quantity: 1, reason: 尺寸不合适 } }运行时收到请求后会创建TaskInstance并写入初始事件然后按照Workflow定义把第一条消息投递给service_agent。你可以立刻查询任务状态curl http://127.0.0.1:8080/tasks/order-10086返回的数据会包含当前节点、Last Event、各Agent的完成状态。你还应该通过OpenRig的Web UI或CLI查看事件流。我自己的习惯是每次调试都开着事件流观察看Agent的返回结果和分支走向比看最终结果有用得多因为你可以明确看到是哪个节点的哪个判断把流程带到了错误分支。4.4 调试心得先从事件流入手不要只盯结果初学阶段最常见的错误是只看最终结果比如退款成功了没有。一旦结果不对就疯狂改Agent的Prompt。但很多问题其实出在协作拓扑和时间线上不是Agent能力问题。我的建议是先看事件日志确认每个节点是否按预期执行。再看条件分支的判断依据确认是哪个字段导致了错误流向。最后才回到Agent的Prompt和输出Schema上。这个排查顺序能帮你节约大量Token和口舌尤其是当系统里有5个以上Agent协作的时候。5. 故障恢复与断点续跑把崩溃当成正常事件5.1 崩溃现场复盘有一次我在压测中故意kill掉一个Agent进程想看看系统反应。场景是这样的一个三Agent协作处理退款任务service_agent刚完成任务把消息发到总线紧接着refund_agent进程崩了还没有消费消息。如果编排层不做持久化接下来会发生service_agent的结果丢失在内存里Task状态停留在等待refund_agent再也无法推进前端永远在转圈。OpenRig在这种情况下做了什么它什么都没做——这正是正确的行为。因为service_agent的结果已经作为事件持久化了refund_agent这边的消息还在Redis Stream里未消费。我重启refund_agent后消费组自动把之前未ACK的消息再次投递给它Task继续执行一切就好像什么都没发生过一样。5.2 事件回放与补偿节点上面说的是消息投递层面的恢复。还有更严重的情况整个运行时进程崩溃Redis和PostgreSQL都存活但内存中的状态全部丢失。恢复流程是这样的运行时启动时检查PostgreSQL中是否有未完成的TaskInstance。对每个未完成任务读取最近快照。重放快照之后的新增事件恢复到最新状态。检查每个Agent节点的执行状态把需要重投的消息重新投递到总线。这里有一个必须注意的点重放逻辑要设计成幂等的。如果refund_agent其实已经完成了退款事件也写了只是ACK响应还没发出来系统恢复后往总线上再投一次消息就会导致重复退款。解决方法是给每个事件和消息都带上event_id唯一IDAgent的Handler要实现幂等判断——同一event_id只处理一次重复接收时直接返回上次结果。OpenRig内置了一个幂等缓存默认缓存最近10万条已处理消息的ID和结果。Agent侧如果接到重复消息SDK会先查缓存命中就直接返回结果不再调用LLM。5.3 消息乱序与重复投递的处理异步消息总线有一个经典问题消息不是按严格顺序投递的。两个Agent A和B并发向C发送消息C收到两条消息的顺序可能和发送顺序不一致。AI Agent又是顺序敏感的——你给它看A确认了退款再让它分析和让它先看到库存扣减失败再看到退款确认结论完全不同。OpenRig的解法不是去追求全局全序那在分布式场景下代价太高。它只保证同一个TaskInstance内的消息全序。具体做法是运行时为每个Task维护一个自增的seq_no分配器消息在写入总线前先去取号。Consumer端的Agent SDK会维护一个expected_seq如果到来的消息比期望值大就放入pending缓冲区等待前面的seq补齐后再按序交给Handler。# Agent SDK内部维护的乱序缓冲伪代码 class SequencedMessageBuffer: def __init__(self): self.expected_seq 1 self.pending {} def accept(self, msg): if msg.seq_no self.expected_seq: self.expected_seq 1 self._flush() else: self.pending[msg.seq_no] msg def _flush(self): while self.expected_seq in self.pending: msg self.pending.pop(self.expected_seq) self.expected_seq 1 yield msg这个机制极其简单但很好地解决了Agent侧因乱序产生的幻觉和误判。代价是内存中需要维护一个pending map任务量大时要注意清理超时的pending消息避免内存泄漏。5.4 恢复验证的标准动作清单我每次在测试环境验证恢复能力都固定跑下面这套动作启动一个包含3个以上Agent的Workflow停在一个中间态。杀掉其中一个Agent的进程观察消息总线中该Agent的消息是否积压未ACK。重启Agent确认消息被重新投递、任务能继续推进到终点。杀掉整个运行时进程观察PostgreSQL中Task状态。重启运行时确认它自动发现未完成任务、执行快照恢复。对比恢复前后的事件日志是否连续确认没有丢事件、没有重复执行。这套清单我强烈建议所有做Agent编排的同学都在自己的系统里跑一遍。持久化方案如果不具备可验证性那就等于没有。6. 并发与性能从能跑到扛得住6.1 先分清放大的瓶颈在哪AI Agent怎么扛并发这个问题经常被问但很多人上来就调框架参数方向错了。多Agent系统的吞吐瓶颈通常不在编排层而在三个地方LLM API的速率限制、Agent内工具调用的外部系统延迟、以及消息总线自身的吞吐。我之前做过一次压测编排引擎的CPU占用不到10%消息总线也很空闲但整个系统的任务完成率直线下降。查下来发现是LLM API的TPS限额被打爆了所有Agent都在排队等模型响应。所以OpenRig在设计上明确了一点编排引擎不承担模型调用模型调用全部发生在Agent进程内部由每个Agent自己管理。这样如果某个Agent依赖的外部模型限流只会拖慢它自己不会把整个编排系统拖死。6.2 调度器如何避免成为瓶颈OpenRig的执行引擎是异步的这是它能扛住高并发的根本原因。引擎线程从不会阻塞在等待一个Agent返回上。当Workflow推进到Agent节点时引擎只是发布一条消息并立即返回继续处理其他事件。Agent完成后的返回事件会再次唤醒引擎推进状态机。这也意味着即便同时有几千个TaskInstance处于等待Agent响应状态引擎也只需要维护非常轻量的事件循环内存中不会为每个Task保留完整上下文。Task的上下文要么已经持久化要么在Agent侧。事件驱动模型在实现的时候有一个细节不要每个TaskInstance单独起一个状态机协程那样会在大量挂起状态下产生数万个空转协程。OpenRig的做法是用一个全局事件循环事件携带task_id循环时按task_id查状态机当前节点。虽然多了一次状态读取但内存占用和CPU开销都更可控。6.3 背压、并发度与Token预算并发压上来了必须思考三个控制参数per_agent_max_concurrency每个Agent实例允许同时处理多少条消息。超过后消息继续留在Stream里不投递。global_active_tasks全局允许同时处于活跃状态的TaskInstance数量超过则新任务先排队。queue_high_watermark每个Stream的积压阈值。超过阈值时引擎停止接收新任务靠这个机制实现背压。这三个参数不是拍脑袋定的。我建议按单Agent处理一条消息的平均时间来倒推。假如一个Agent处理一条消息要5秒你有10个Agent实例期望单Agent吞吐是每秒2条那并发上限可以定在20左右再粗略乘以Agent的数量就是全局合理的active task数。Token预算的控制同样关键。OpenRig会在Task创建时通过一个轻量估算器预估整条链路需要消耗的Token总量并把预算细分到每个Agent。Agent处理完消息之后实际消耗会回写一旦超过了预算上限的120%系统会强制将后续动作切换成摘要模式或转人工。这不是抠门而是防止一个失控的Agent把一个月的预算在半天里烧光。6.4 实测数据与调优建议我在一个模拟订单处理的场景里用30个Agent实例、16核服务器跑过一轮压测。任务类型是混合的一部分走纯业务Agent一部分走带向量检索的Agent。结果大致如下场景并发任务数平均任务完成时间引擎CPU消息总线积压纯业务Agent链路50032秒8%0带检索Agent链路30068秒12%少量瞬时积压混合链路80055秒16%0这个数据说明编排引擎的调度开销其实非常小瓶颈完全在Agent侧的LLM调用和外部检索上。所以如果你的系统扛不住并发优先排查Agent进程的连接池、LLM API的限流参数不要急着给编排层加机器。另一个调优建议是Agent进程的横向扩容比增加单实例并发更有效。多开几个Agent进程消息总线会自动在消费组里做负载均衡同时per_agent_max_concurrency也要相应调低让每个进程保持轻载、快速响应能有效降低超时概率。7. 踩坑实录那些设计图上不会告诉你的问题7.1 状态快照的写冲突持久化方案在理论上是完美的真正跑起来才暴露了问题。OpenRig早期版本对整个TaskInstance做整份快照序列化后直接写PostgreSQL用task_id作为主键。结果某次压测中系统频繁报出更新行数不匹配的错误——两个Agent几乎同时完成各自向引擎提交结果引擎对同一个Task做了两次并发快照更新后写入的覆盖了先写入的。这个问题的根因是快照粒度太粗没有做并发控制。解决方案有两个我最终用了版本号乐观锁快照表里加version字段每次更新时WHERE task_id ? AND version ?版本不匹配就重试一次。如果重试还冲突说明同一个Task里确实有多个协作分支在同一瞬间结束这时候就需要检查Workflow设计是否真的合理——通常说明条件分支之后又意外汇合了拓扑上存在并发汇合点且没有做合并处理。7.2 互相踢皮球的消息死循环这个坑现在想起来还觉得好笑。两个Agent在同一个Workflow里A发现信息不足就把任务消息抛给BB也发现自己无法处理又把消息抛回给A。由于引擎根本不管消息内容只按Workflow拓扑推动这两条消息就在A和B之间无限循环直到把Token预算烧空。你可能会说我的Workflow又不是循环图。但实测告诉我动态协作场景里循环很容易在运行时涌现出来——条件分支的判断结果可能反复在两条路径间横跳。OpenRig的兜底措施是一硬一软两项硬兜底每个TaskInstance有全局hop_limit默认50条消息左右超过立即终止并转人工。软兜底每个Agent节点记录被调用的次数当同一个Task里某个Agent被连续调用超过阈值时引擎会在事件日志里打上疑似循环的标记并让消息走一个专门的人工审查分支。没有这两个兜底我还真不敢把多Agent系统放到生产环境。7.3 上下文膨胀与Token失控这是我踩得最贵的一个坑。早期版本为了让Agent全面掌握信息把Task的完整事件历史都塞进每个Agent的Prompt。三个Agent协作时每跑一轮后续Agent看到的上下文就多出一大截到了第10轮单次调用的Token已经上万费用肉眼可见地飙升而且模型反而开始犯低级错误——因为信息太多关键信息被淹没。后来我强制推行了一条规则Agent默认只接收任务描述 最近3轮相关消息 结构化中间结果。完整历史永远不要直接进Prompt需要的时候通过Session API按需拉取并且由Agent在返回结果时明确说明我引用了第几轮的什么内容。这个改动之后Token消耗降了40%任务准确率反而上升了。多Agent协作里的信息隔离很多时候不是限制而是保护。7.4 什么场景请别用OpenRig说了这么多优点我也得说说它不适用的地方避免你拿着锤子找钉子。如果你只需要单个Agent不需要编排直接用LangChain或Spring AI就好OpenRig反而引入不必要的复杂度。如果你的协作流程极其短比如就是两个Agent各回答一个问题用消息总线加持久化属于杀鸡用牛刀。如果你追求极致的实时交互延迟比如要求毫秒级响应事件驱动加持久化的开销会拖后腿这种场景更该关注Agent自身速度。如果你的业务对账务一致性要求极高比如Agent直接参与资金流操作除非你愿意投入大量精力在幂等机制和审计上否则我不建议让Agent直接动账。让Agent生成建议人工或确定性系统执行资金操作更稳。OpenRig的舒适区是多个Agent需要在分钟级或小时级的流程里持续协作、中途可能出错、事后要求可追溯的场景。比如售后处理、复杂客服工单、多部门知识协作、研发自动化流水线等这类业务才是它真正发光的地方。我个人在实际使用中的体会是多Agent编排这个方向最大的敌人不是技术复杂性而是边界模糊——Agent之间的职责边界、上下文信息边界、状态恢复边界任何一处模糊都会在后面变成生产事故。OpenRig的所有设计本质上都是在把这些边界画清楚。如果你也想做类似的项目建议先画一个最小的业务闭环比如客服判断 一个操作型Agent 一个兜底人工节点把持久化、消息时序、幂等恢复这几个底座能力跑通再去加Agent数量。先把地基打牢再让Agent们上去跳舞这是我在OpenRig上踩了一堆坑之后最想跟你说的一句话。