news 2026/10/22 14:19:47

从单体Agent到多Agent编排:Routa架构设计与实践记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从单体Agent到多Agent编排:Routa架构设计与实践记录

做了一年多的单体 Agent 工具链,也就是我们内部代号叫 AutoDev 的那套东西,我一度觉得它已经够用了:一个大的 Agent 实例,背上几十个工具,读完仓库上下文,按指令干活。直到有一天它在一段跨模块任务上连续翻车——先是工具调用列表互相干扰,模型把“查询用户权限”的工具误当成了“写入配置”的工具,接着上下文窗口被十几个中间结果塞爆,生成的代码张冠李戴。那次故障让我彻底想通了一个事:一个脑子管太多事,早晚要出问题。也是从那时候起,我开始动手做 Routa——一个把任务切给多个专职 Agent、靠事件协作、对模型和工具都保持开放的编排引擎。这篇文章就是我从 AutoDev 一路走到 Routa 的完整记录,包括核心设计、迁移步骤、以及我在实测中被现实教育过的几个坑,希望对正在做多 Agent 编排、或者已经被单体 Agent 气到过的团队有点参考价值。

1. 单体 AutoDev 的瓶颈,出在“一个脑子管太多事”

1.1 工具清单膨胀,上下文稀释到失真

AutoDev 单体方案最直观的问题,是工具描述本身就在吃上下文。我做过一个统计:单个 Agent 挂了 38 个工具,每个工具按规范写 description 和参数 schema,平均要占 200 个 token,光工具描述就接近 8000 token。再算上 system prompt、历史消息、检索回来的代码片段,一轮任务还没开始干正事,窗口已经见底。

后果很直接:模型在工具选择上的准确率肉眼可见地下降。不是模型变笨了,而是它在一个超长的列表里找正确工具,本身就接近“大海捞针”。有一次它成功把git_commit调用成了git_push,还有一次它把枚举参数里的pending错误传成了pendding,这类低级错误在工具少的时候几乎没有出现过。

比工具误选更隐蔽的,是上下文稀释。单体模式下,所有任务的历史都挤在同一个上下文里。如果你同时让它重构一个后端服务、写一个前端组件、再跑一遍测试,这三个任务的状态就会互相污染。模型经常会在这个任务的末尾,突然去处理另一个任务的残留指令。我在日志里看到过一段非常经典的混乱输出:代码生成到一半,模型开始解释数据库表结构,还附了一句“上一步已经执行了迁移”。实际上迁移那步它根本没做。

这种“一个脑子管所有事”的架构,在任务单一、工具不超过二十个时是高效的。但一旦工具数量上来、任务变复杂,效率就会断崖式下跌,而且问题不是换个更强的大模型就能解决的,它更像结构性问题:单体 Agent 的注意力是有限的,而你强迫它同时关注太多东西。

1.2 多步调用链的脆弱性,一步错步步错

AutoDev 模式下比较典型的失败场景是一个跨步骤的调用链。比如“扫描代码、定位 bug、修复、跑测试、提交”,这五个步骤在一个 Agent 内部串行执行,每一步的结果都直接拼接到历史里,作为下一步的依据。

这个设计的链子很长,任何一个环节出幻觉,后续所有环节都会跟着错。我有一次让它修一个并发问题,它第一步扫描时把项目里的“锁的粒度”理解错了,第二步定位直接找错了对象,第三步的修复方案自然就歪了,第四步测试因为方案错而失败,它没有回头反思,反而认为是测试写错了,开始“修”测试用例。整个过程看起来每一步都在干活,实际上是沿着错误方向一路狂奔,而且因为上下文里塞满了错误中间结果,纠正的成本极高,几乎等于推倒重来。

我还注意到一个现象:一旦 Agent 意识到自己前面的结论有问题,它倾向于“修补叙述”,而不是“纠错”。它会在后续总结里淡化前面步骤的问题,甚至编造一个能自圆其说的故事。这种情况下,单体 Agent 的日志看起来是完整的,但内容是失真的,排查起来非常痛苦。

这让我意识到,多步骤任务需要一个更结构化的机制:每步独立、可校验、可回滚,而不是靠上下文自然地“接力”。这就是 Routa 编排模型出现的直接原因。

2. Routa 的编排内核:先切任务,再连消息

2.1 角色划分:编排器、规划器、执行器与审查器

Routa 的核心设计理念很简单:把原来一个 Agent 的“全知视角”,拆成一组有明确分工、有独立上下文的角色。我最早只设计了四个角色,后来根据实践加了两个,目前稳定在六个:

角色职责说明
Orchestrator接收用户请求,拆任务,下发、回收、裁决不直接写代码,只负责调度
Planner把任务展开成任务图(DAG),标注依赖关系在复杂任务上格外有价值
Executor Worker执行具体单元任务,调用工具一个 Worker 只配一组相关工具
Reviewer对 Worker 产物做静态检查、测试校验、接口对齐在合并之前拦截问题
Tool Agent连接外部系统,做数据库、CI、消息等操作从 Worker 中剥离出来,统一收口
Human Gateway人工审批通道,权限敏感操作必须走这里关键安全阀门

一开始我犯过一个小错误:把 Planner 和 Orchestrator 合并成一个角色。结果它一边拆任务一边调度,逻辑混乱,任务拆得特别粗糙——经常把一个完整的“改日志级别”的任务拆成四个子任务。后来我强行分开了它们:Orchestrator 只做“拆”和“收”,Planner 只做“策略设计”,各管一段,问题明显好转。

这里有个设计原则值得分享:角色不是越多越好,每个角色都要有独立存在的理由,要么是上下文隔离需要,要么是安全边界需要,要么是校验职责需要。如果一个角色只是“把代码从 A 文件挪到 B 文件”,它就不该存在,直接让 Worker 做就行。

2.2 消息协议与任务生命周期

角色之间不共享内存,不共享上下文,它们之间只有一种交互方式:事件消息。这是 Routa 和 AutoDev 最大的架构差异。AutoDev 靠“同一个人记住整件事”,Routa 靠“一群人交换标准格式的纸条”。

我们定义了一套最小化的事件协议,核心字段包括:

  • event_id:全局唯一事件 ID
  • task_id/correlation_id:关联同一次请求的所有任务
  • event_type:task.assigned、task.started、artifact.produced、task.completed、task.failed、approval.requested、context.query
  • producer/consumer:来源角色和目标角色
  • payload:不超过一定大小的结构化数据,超出部分落库,只传引用

任务生命周期也做了严格定义,状态机是:

created → scheduled → running → completed,以及running → awaiting_approval → running,失败则进failed,可重试则回scheduled,人工取消则进canceled。

为什么要搞得这么正式?因为我吃过多 Agent 协作不定义协议的亏。早期版本里 Worker 之间直接互相调 API,结果 A 等 B 的回调,B 又等 A 的结果,形成死锁,排查到后面只能靠重启。改成事件驱动加状态机之后,任何一个任务卡住,定位到状态就完了:要么是running太久,要么是awaiting_approval没人批,秩序感完全不同。

2.3 上下文策略:按需注入,而不是全量共享

上下文管理是这轮改造里最值得说的一块。Routa 的指导方针是“四不传”:

  • 不传全量历史:Worker 之间只传必要输入,不传对方的聊天记录
  • 不传无关工具:我们做了一个工具路由表,每个任务只注入关联度高的几个工具描述
  • 不传原始代码大块:超过阈值就存到工作记忆库,传引用
  • 不传未收敛结论:Planner 的早期草案不进执行上下文,只有定稿任务图才下发

对应地,Routa 引入了两个辅助设施。一个是工作记忆(Working Memory),本质是一个带向量索引的知识库,每个执行的中间产物、总结、关键决策都会异步写入,Worker 需要时可以“查询”而不是“等待上一棒传递”。另一个是检查点(Checkpoint)机制,每隔一段任务进度,把当前状态落盘。一旦某个 Worker 崩溃或者产出被判无效,可以直接从最近的检查点重建上下文,而不是重跑整个链路。

我最初怀疑这套设计会引入额外的延迟,但实际跑下来发现:检索一次工作记忆的延迟,远小于把一个大上下文全量传给下一个 Worker 的 token 消耗和时间开销。尤其在工具多、代码量大的场景里,按需注入这套思路几乎是必须的。

3. 开放生态接入:工具注册表与模型无关的适配层

3.1 统一工具描述协议:任何工具,一层 Schema 接入

Routa 的“开放”首先体现在接入形式上。我们不想为每个新工具写定制代码,而是定义了一个统一工具注册协议,所有工具只要按这个 Schema 描述自己,就能被编排体系使用:

{ "tool_name": "query_order_status", "version": "1.2.0", "description": "根据订单号查询订单当前状态,只读操作,无副作用", "category": "order_system", "input_schema": { "type": "object", "properties": { "order_id": {"type": "string", "pattern": "^ORD_[0-9]{10}$"}, "env": {"type": "string", "enum": ["staging", "production"]} }, "required": ["order_id", "env"] }, "output_schema": { "type": "object", "properties": { "status": {"type": "string", "enum": ["pending", "paid", "shipped", "canceled"]}, "updated_at": {"type": "string"} } }, "execution_endpoint": "internal://order_system/query_status", "execution_mode": "sandbox", "auth_scope": "read_only", "timeout_seconds": 10, "rate_limit": 50 }

这套协议看起来简单,真正落地时才感受到它的威力。模型侧不需要知道工具的实现语言,执行侧不需要关心模型怎么理解工具,两边通过这个 Schema 解耦。任何一个业务团队,只要按这个格式提供一份 JSON,Routa 不出一行新代码就能让 Agent 群里所有角色用上这个工具。

我还给注册表加了一个自动“工具相关性索引”——用工具名和 description 的向量,跟当前任务的语义做匹配。比如任务是“确认用户订单是否已发货”,工具路由模块就会把query_order_status、get_user_info这一类工具列为高相关,优先注入执行 Worker 的上下文。这样做以后,单个 Worker 上下文里的工具描述从几十个降到了五六个,模型选工具的准确率明显回升。

3.2 沙箱执行与权限收口:开放不等于裸奔

开放生态最大的担忧永远是安全。一个工具接入进来,模型什么能调、什么不能调、调用范围有多大,必须在注册表层面说清楚。

Routa 每个执行器都跑在独立沙箱进程里,遵循最小权限原则。对外部命令,我们做了三件事:

  • 文件系统只读映射,执行器默认看不到宿主机真实目录,只有显式声明的目录会被映射
  • 网络白名单,外部工具访问域名或内部接口需要注册时申报,未申报的一律拦截
  • 超时与重定向,任何执行器最多运行 N 秒,输出大小限制在指定范围内,超过直接截断并记一条异常

权限这块我们还做了一个分级:read_only、read_write、requires_approval。像查订单这种只读操作,Worker 可以直接执行;像推送分支、删除表这种有副作用的操作,必须经过 Human Gateway 审批,审批界面会展示操作对象和影响范围,人工同意后任务才继续。

这套机制跑到现在,最大的收获不是“防住了多少攻击”,而是让业务方敢于把内部接口接进来。以前接一个工具要层层审批,现在只要 Schema 声明清楚、权限级别合规,几分钟就能接入,安全可控。

3.3 模型无关适配:不要让编排层绑死某个模型

多 Agent 编排不能绑死某一个模型,这是 Routa 能持续演进的另一个原因。不同模型在工具调用上的行为差异很大:有的模型严格遵循 JSON Schema,有的模型会略做修改;有的模型擅长长文本规划,有的模型短指令响应更好。

Routa 在模型层和编排层之间加了一个适配层,统一转换成内部“动作指令”。适配层负责几件事:

  • 把模型的自然语言工具调用解析成标准工具调用,参数类型强校验,不匹配的尝试按 Schema 做最近匹配纠正
  • 把多个模型提供商返回结果统一成标准ActionResult,带上 token 消耗、耗时、置信度字段
  • 处理不同模型对“拒绝任务”的表达差异,统一转换成task.refused

有了这层适配后,我们可以针对不同角色选不同模型:Planner 用一个擅长推理思考的强模型,普通 Worker 用延迟更低的轻快模型,Reviewer 用一个专注代码审查的模型。整体成本比原来“一个大脑全包”下降了不少,效果反而更稳。

4. 从 AutoDev 迁移到 Routa 的三步实操清单

4.1 先横向切:一个调度器加多个专职执行者

如果你现在也在用单体 Agent,我的建议是不要一步到位搞成六角色大编排,那太激进,容易崩。我们当时的迁移分了三步,每步都是独立可验证的。

第一步是横向切分:把 AutoDev 按领域拆成多个 Worker Agent——代码生成、测试执行、数据库操作、Git 操作各一个。拆完以后保留一个简单的 Router Agent,根据用户请求的语义把任务分发给对应 Worker。这一步本质上还是“一个人派活给几个人干”,没有真正的规划环节,但效果已经立竿见影。

原因在于,每个 Worker 的工具清单从三十多个降到了十个以内,上下文干净了,错误率立刻下降。我统计过迁移第一步后的准确率,工具误选类错误几乎清零,这还不算 token 成本上的节省。

4.2 再纵向切:引入 Planner 和 Reviewer

第二步才是真正进入编排:加 Planner 做任务图拆解,加 Reviewer 做产物校验。从这一步开始,任务不再是一个 Action 到底,而是变成一个 DAG——有并行的地方并行,有依赖的按依赖串行。

Planner 的输入是用户请求加初始上下文,输出是一份结构化的任务图,包含每个子任务的角色、输入引用、预期产出、依赖关系。Reviewer 拿到 Worker 产物后,会做静态检查、编译验证、接口对齐检查,不合格直接打回并附上原因。这一步以后,质量问题不再靠“最后人工看一遍代码”,而是形成了执行与校验的闭环。

这里有一个坑提醒一下:Reviewer 不要设计成“全知全能的裁判”,它必须有明确的校验清单。早期我们把 Reviewer 设计得太开放,让它“全面检查产物质量”,结果它每次都能挑出一堆风格问题,把 Worker 逼疯。后来把校验清单收敛成接口对齐、类型正确、测试通过、无硬编码这四项核心指标,其他风格问题不阻塞合并,流程顺畅多了。

4.3 最后补可观测性:trace、回放与成本分析

第三步是配套工程。多 Agent 编排链路长了,如果没有可观测性,出了问题只能胡乱猜测。Routa 从上线第一天就要求每个事件必须带 trace 上下文,我后来又补了两样东西:回放机制和成本看板。

回放机制用于失败排查:某个任务失败了,系统把该任务从输入到输出的所有消息、决策、工具调用原始记录收集起来,按时间轴重新播放。排查问题的效率提升了非常多,以前在单体里是“靠日志和猜”,现在是可以精确地看到“Planner 在哪一步把约束丢了”或者“Worker 在哪个工具调用上返回了异常结果”。

成本看板则跟踪每个任务的 token 消耗,按任务类型、角色、模型来源聚合。我看了几周的数据后发现,Reviewer 的 token 消耗远超预期,因为它每次校验都重新读一遍代码。后来给它加了“变更 diff 优先”策略,只看改动部分,成本直接掉了一半。这种优化没有看板靠拍脑袋是拍不出来的。

4.4 迁移前后对比:到底改了什么

维度AutoDev 单体Routa 编排
工具承载量30+ 工具全量入上下文按任务注入,每个 Worker 5 个左右
上下文管理全量历史共享按需注入 + 工作记忆检索
任务执行模型单链串行DAG 并行,依赖可控
失败恢复靠重新生成检查点重建 + 任务重试
质量保障看模型心情Reviewer 闭环校验
可观测性日志难还原决策过程全链路 trace 回放
模型选型一个模型全包按角色选不同模型

这张表我给很多团队讲过,每次都要强调:迁移的价值不是“用了某种框架”,而是把不可控的“全知上下文”变成了可控的“任务流”。认知原理下的投资回报,比工具的换新重要得多。

5. 实测翻车现场:上下文漂移、过度编排与工具幻觉

5.1 上下文漂移:摘要没传,约束就丢了

多 Agent 编排最常见的一个坑,是我称之为“上下文漂移”的现象。它发生在任务链路较长时:Planner 把任务切了八个子任务,Worker A 做完,把关键信息压缩成摘要传给 Worker B。摘要当然是简洁的,但简洁意味着要丢弃细节,而模型偏偏可能把不该丢的约束丢掉了。

我们真踩过一次:Planner 拆了两个任务,一个是“按接口文档生成订单模块”,一个是“为订单模块写单元测试”,生产者和消费者之间通过摘要传递,摘要里有接口字段包含金额、状态、时间,但丢了金额必须是分单位整数这个关键约束。下游 Worker B 写测试时按“元单位小数”去断言,结果整体校验通过,部署上去才发现数据错位。

这让我总结出一个硬规矩:关键约束不能靠摘要传递,要落成结构化断言。我们在工作记忆里增加了一个“约束卡片”类型,Planner 在拆任务时如果识别到关键约束,会单独写成卡片存进工作记忆,下游 Worker 必须引用卡片 ID,执行完还要附带“已满足/未满足”的逐项检查。把约束从“自然语言描述”变成“结构化检查项”之后,这类问题基本绝迹。

5.2 过度编排:一个改注释的任务被拆成了五步

如果你把编排做得太细,会走到另一个极端:过度编排。有一次为了验证新架构,我强行让一个“修改 README 描述文字”的任务走了完整编排链路。Planner 拆了五个子任务、调度了三个角色、开了四次事件传递、模型跑了三轮,最后为了加一行字,消耗的 token 比原来单体模式翻了三倍,耗时增加了十几秒。

这个教训很深刻:编排是有固定开销的,必须让任务复杂度配得上编排复杂度。Routa 后来加了一个“复杂度评估器”:在 Orchestrator 收到请求后先做一个快速判断,如果是低复杂度任务,直接走“快路径”——用一个 Worker 一次性完成,不经过 Planner,不生成 DAG。只有当任务被认定确实需要多角色协同时,才会进入完整的编排链路。这个设计看似是“退步”,其实是让编排系统的适用范围更合理了。

5.3 工具调用幻觉:模型调了一个不存在的工具

开放生态接入后,工具数量会快速增加,模型又会出现一种新问题:明明注册表里没有这个工具,它却倾向于“认为”自己可以用。有一次模型直接请求调用send_email_to_admin,实际上注册表里只有send_notification。还有一次模型调search_code时,给一个枚举字段传了不存在的值。

这个问题靠模型本身很难彻底避免,我选择在适配层加一道“工具调用校验+修正”机制:先按注册 Schema 强校验,不合格的调用不会真正执行,而是生成一条修正建议返回给模型。枚举值错时会做最近匹配;工具名不存在时会返回“你手上有这些相关工具”的参考列表,引导模型选择正确的工具。这套拦截机制上线后,工具调用类异常下降了绝大部分。

我还把工具执行改为幂等优先的设计:执行器会为每个调用生成幂等键,重试不会产生重复副作用。这对于多 Agent 场景尤其重要,因为在重试机制下,同一个工具调用可能被执行多次,如果没有幂等保护,就会出现重复下单、重复发通知这类事故。

5.4 多 Agent 协作死锁:事件驱动的另一个隐藏坑

事件驱动虽然解决了单体上下文污染问题,但它引入了一个新问题:无界等待。早期版本里,如果 Worker A 发布了事件,没人消费,任务就挂在running状态一动不动。我们花了很大力气排查,发现是消费者注册顺序出了问题——新加的角色没有订阅正确的事件。

我不能确认这个问题是否普遍,但我的经验是:每个任务都必须有超时,每条事件都必须有死信路由。超时之后要么自动重试,要么标记失败并通知 Orchestrator;事件如果没有消费者,必须进入一个可检查的死信队列,而不是无声无息地丢在空气里。没有这两样东西,编排系统的“失控方式”比单体更隐蔽,也更难排查。

6. 写给也想动手的人:最后几条经验

多 Agent 编排听上去是个很“新”的实践,真正把它落到生产环境时,重复踩的坑其实非常集中。这几条是我的真实感受,分享给你作为参考。

第一,先想清楚你到底需不需要编排。如果团队任务大多是单刀直入型——改个配置、写个文档、生成一段固定模板代码,单体 Agent 或直接调 API 可能更快更省。编排的价值只有在复杂任务频繁出现时才体现出来,为了用技术而用技术,只会徒增维护成本。

第二,启动时从最小的切分开始。我第一次规划 Routa 时设计了八个角色,上线后有一半在“等活干”,实际跑起来根本没有那么多种任务类型。后来砍到六个角色才算稳定。角色的增减应该由真实任务分布决定,而不是架构师纸上拍板。

第三,可观测性是一等公民,不是事后补丁。如果一个 Agent 系统你无法回答“它为什么做了这个决定”,你就没法对它做任何可靠的上线。我会建议从第一天就把 trace、事件日志、成本记录都建好,多花半天配置,能省下未来好几天排查的功夫。

第四,保留人工审批的闸门。无论技术做得多成熟,涉及权限、付费、删数据的操作,都应该留一个 Human Gateway。它不只是安全机制,也是让业务方对这套体系建立信心的关键。看到风险操作需要确认,很多团队才敢把更多任务交出来。

从 AutoDev 到 Routa,与其说是一次工具重写,不如说是我对“怎么让多个模型可靠地协作”这个问题的一次系统思考。单体像一张大桌子,所有东西都堆在一起,找起来迟早会乱;编排像一个运转有度的车间,每个工位做自己擅长的事,用标准化的工单流转。后者前期搭建设施要费些功夫,但跑顺之后,扩建产能、接入新工具、换用新模型,都变得比想象中轻松。如果你也在摸着石头过河,希望这篇实践记录能帮你少踩几个我已经踩过的坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 14:18:39

图片PDF文字提取全攻略:OCR与正则表达式实操指南

很多朋友在群里丢过来一张截图,说"帮我把里面的电话号码和金额弄成文本",或者自己整理资料时对着PDF干瞪眼,复制出来全是乱码。这种从图片、PDF、聊天记录里把文字和数字抠出来的需求,说实话天天都能碰到,但…

作者头像 李华
网站建设 2026/10/22 14:18:02

Spring Boot订餐系统源码解析:从.class反推生产级架构

简介:这是一套功能完整的类美团订餐系统源码,面向计算机相关专业学生及初级开发人员,适用于课程设计、毕业设计、项目实训与企业内部演示等场景。系统采用前后端分离架构,包含Web端管理后台(支持菜品、套餐、订单全生命…

作者头像 李华
网站建设 2026/10/22 14:17:12

AI Agent落地五大死亡陷阱与实操避坑指南

1. 这份报告不是PPT堆砌,而是AI Agent落地前必须摸清的“地形图”“AI Agent 深度调研报告”——这八个字最近在技术圈里出现的频率,已经快赶上咖啡机旁白板上写的“今天要发版”了。但说实话,我见过太多标题叫“深度调研”,打开后…

作者头像 李华
网站建设 2026/10/22 14:16:48

网络故障排查实战:从DNS解析到TCP握手的排障指南

前几天同事跑过来,说办公室一台电脑上不了网。现象很典型:聊天软件收发消息都正常,浏览器却打不开任何网页。我过去看了一眼,ping 网关是通的,ping 公网 IP 也通,但用域名访问就是不行。敲了一条 nslookup&…

作者头像 李华