多智能体协作的语义路由与上下文编排:统一通道设计与工程落地

发布时间:2026/10/7 17:22:07
多智能体协作的语义路由与上下文编排:统一通道设计与工程落地 我们团队半年前启动了一个叫 Agent-Reach 的内部项目。起因挺简单的大模型落地到业务之后第一批智能体跑得挺好但第二批、第三批接进来就乱了。Agent 之间互相不知道各自提供什么能力、不知道消息该往哪里投递、也不知道一个任务经过多个 Agent 流转时状态怎么同步。结果是流程越扩越乱排查问题要翻十个系统。所以 Agent-Reach 的目标就是一件事给多智能体协作装上统一的任务触达与编排通道让 Agent 彼此“够得着、传得通、接得住”。这篇文章把我们从设计到落地的完整思路打开讲一遍包括核心架构、路由策略、会话上下文管理、异常兜底这些层面过程中踩过的坑也一并写出来给正被多 Agent 协作问题卡住的朋友做个参考。1. 内容整体设计与思路拆解1.1 Agent-Reach 要解决什么问题多智能体系统听起来很酷但工程上真正磨人的是协作链路。我们内部原来的模式是点对点直连A 调用 BB 再调 C每个 Agent 自己维护一份“我该找谁”的清单。这种模式在 Agent 数量小于五个时很高效肉眼可见的简单直接。可一旦超过这个数问题就集中爆发了。能力发现靠开会新 Agent 上线要从头通知一遍周边系统漏掉一个就断一路流程。参数语义各写各的一个 Agent 发出去的“用户ID”接的 Agent 以为是另一个字段数据解析报错是常态。回调链难以跟踪请求在多个 Agent 之间跳转某一个环节失败了拉起的告警链条叉开六七条线谁先出错根本定位不了。会话状态散落用户在多轮交互里被不同 Agent 接力服务上文处理到哪一步下一位接手的完全不清楚。Agent-Reach 的设计出发点就是把这些问题集中收口。我们不再让 Agent 之间互相知道对方的地址、字段、流程细节而是统一走一个“注册、路由、递送、跟踪”的线性通道。Agent 变成插线板上的设备插上去就能收发消息谁在另一端处理、中间经过哪几跳全部由框架接管。这个思路在电商订单流转里很好类比。订单路由的本质是极速物流集散中心的分拣和配送而不是让商家雇人一家店一家店地送货。Agent 协作有了统一集散能力链路的调度与追踪才可能可控。1.2 为什么不能直接套用消息队列听到“统一通道”四个字第一反应可能是拿 RabbitMQ 或 Kafka 来做。我们最初也这么想过评估后放弃了因为 Agent 协作对消息内容的依赖深度远高于传统消息中间件能处理的层次。传统消息队列擅长处理的是“内容透明”的异步事件支付成功、用户注册、库存变动。生产者和消费者各自关心事件本身的载荷不需要理解内容的含义。Agent-Reach 的场景不同它要传递的是“意图、上下文、契约版本、策略约束、状态快照”这些复合信息。消息体本身是业务可感知的投递过程涉及根据消息内容做语义匹配这是传统消息组件干不动的。所以我们最终定位 Agent-Reach 是一个轻量级语义路由层消息队列只是它底层传输的其中一种可选实现而不是全部。它必须理解 Agent 的入参契约、出参契约、当前负载状态和历史协作质量基于这些信息决定把任务交到谁手里。这个决策逻辑普通 MQ 是给不出来的。1.3 核心抽象与总体框架Agent-Reach 整体落在四个抽象层上域Domain一组能力可互相替换的 Agent 构成的逻辑集群。比如“订单查询域”下面挂了三套实现有的走 CRM、有的走仓储、有的走客服旧系统。域的意义在于路由收口调用方只知道找“订单查询域”不需要知道具体找谁。契约ContractAgent 暴露能力时的 JSON Schema 描述。包含入参类型、必填项、返回格式、调用语意和版本号。路由核心依赖就是契约而不是人工配置的 XML 映射表。路由策略Routing Policy域内挑选目标实例的规则集合。支持全量广播、加权随机、最少调用、粘滞匹配、跨域外部委派等策略。策略执行发生在每次消息入站时动态计算计算开销控制在入站整体时延的 3% 以内。会话锚点Session Anchor跨 Agent 流转时的上下文服务。用全局唯一的 Reach-ID 串联一场会话中的所有消息块后续所有 Agent 都能在同一锚点下读写共享状态避免“上文在 A、下文在 B、两边互不认识”的断档问题。这四个抽象层构成 Agent-Reach 的骨架下文章节里我会逐一展开实现细节。一次性讲完架构理解门槛偏高建议你拿到设计图后先把域和契约两个概念吃透后面都是在这两个底座之上长出来的东西。2. 核心细节解析与实操要点2.1 服务网格内嵌的九种能力可以把 Agent-Reach 理解为服务网格思想在智能体场景的变体Agent 之间不再互相直连所有流量都走一层与业务逻辑解耦的通道这一层拎出来包含九种可插拔能力能力说明业务意义服务注册Agent 启动时上报自身元信息与契约新上线无需通知任何人发布即被感知心跳保活周期上报健康状态、负载与延迟指标路由决策实时避开亚健康节点意图解析入口消息先做归一化解析识别真实意图调用方不用纠结按哪个接口名发起请求动态路由结合契约、策略和运行时指标选目标节点工作量从人肉维护变成自动化决策会话编织多轮消息关联到同一 Reach-ID 上下文复杂流程跨 Agent 不丢失状态协议转换格式、类型、版本差异在框架层抹平新旧 Agent 换版时调用方无感知可靠性递送同步调用与异步回调的统一重试与补偿临时故障不直接导致整个会话终断跟踪日志每个跨 Agent 步骤输出结构化轨迹链路排查从几小时缩短到分钟级策略执行在框架层统一做限流、熔断、降级业务代码不需要关心流量保护细节这些能力都做成了可插拔插件。默认全部开启如果你只想先解决“互相发现”这个单一痛点也可以只开注册和路由两个插件降低资源开销。我们内部最开始就是最小插件的方案起步跑通之后再逐步打开协议转换和跟踪日志。插件交互完成之后消息链路通和稳的问题就算关卡过了。但是要真正让协作可控路由细节才是关键下面重点讲路由这块。2.2 路由的核心——契约驱动匹配契约驱动的核心数据类型就是 JSON Schema。每个 Agent 在上报注册信息时必须携带一份精确到字段级语义的 Schema 声明比如“订单信息查询”的入参声明中包含字段 orderId类型 string语义是“系统内唯一订单号”。Agent-Reach 只拿这份声明做匹配和校验这样消息才能找到最合适的接收域。Schema 匹配有两种粒度。粗粒度匹配面向意图关键词调用方发起消息时只要写上一段自然语言描述“帮我查一下最近一笔未发货订单”入口解析层负责把意图归类直接投递给订单查询域。细粒度匹配在域内完成域内挂着多个实现消息进来之后会结合当前每个实现登记的出参覆盖度和运行时健康指标进行打分选出具体实例。这个机制我用一个很直观的效果总结调用方写的是一份“我要什么”的描述框架负责从“谁能给”的注册表里找到对应的人而不需要硬编码知道谁是应答方。实际用下来Schema 的专注度和约束力比传统的接口文档强太多。传统文档靠人读Schema 直接可以被框架解析错误率低一个量级。2.3 参数语义标准化方案字段粒度的语义统一是路由落地的基础。没有标准化之前Agent A 用 userId 传用户标识Agent B 用 user_id 接收中间断层。Agent-Reach 规定所有跨 Agent 字段必须映射到标准的工业语义字典上。映射分三种层级全等映射名称一致语义一致直接透传。别名映射名称不同语义一致框架层做字段转换。函数映射文本 id 到数值 id 这类需要计算转换的注册时声明转换函数框架自动执行。第一版我用的是字段级映射配置维护量非常大每接一个 Agent 都得人工对一遍字段。后来改进为语义词典自动识别法对历史日志里的字段做了语义词向量聚类命中率能到 85%剩下 15% 走人工确认。跑通这个机制后一个新 Agent 接进来应该能缩短到十分钟以内完成路由验证。参数语义标准化的相关原理涉及语义对齐的模型细节篇幅关系就不在此展开需要模板或样例集的可以直接找我聊。2.4 会话状态传递机制多 Agent 协作最容易踩的坑是“上文断在上一个 Agent 手里”。用户和客服 Agent 聊了三轮最终被转给售后 Agent售后完全不知道前面聊了什么用户又得从头说起。Agent-Reach 的会话锚点机制是这样工作的入口处生成一个全局 Reach-ID所有后续消息块都带这个 ID。同一个 ID 之下框架自动维护一个可读写的共享上下文字段。任何被本会话调用到的 Agent都允许在锚点下写入自己处理的结果片段。后接的 Agent 在路由到达时可以主动读取锚点下的完整上下文也可以只读取与自己契约字段相关的子集。这个实现的关键要点是写入冲突控制。两个 Agent 同时写同一个字段会产生竞争我们的策略是字段分区 版本化写入。每个 Agent 注册时声明自己的写入域两段不同的写入域物理隔离同一写入域内通过版本号覆盖旧值。这个设计同时解决了同步更新和一致性冲突这两个高频问题。做过多人协同文档的人应该明白这块的逻辑字段分区等于给每个人发了不同的编辑区版本化等于最后一次保存覆盖旧版本区别只是对象从人换成了 Agent。3. 实操过程与核心环节实现3.1 环境准备与依赖选型Agent-Reach 的核心运行环境基于 Python 3.11 构建依赖 FastAPI 作为入口框架底层消息传输可选 Redis Stream、RabbitMQ 或者 Kafka 适配器。我们先说说环境怎么搭。基础组件清单Python 3.11虚拟环境工具 uv 或 poetryRedis 7.x用于注册表缓存和会话锚点存储PostgreSQL 15用于契约 Schema 和链路轨迹落库容器运行时 Docker / K8s用于 Agent 实例编排可观测组件 Prometheus Grafana用于运行时指标采集开发调试阶段最低配置只需要一个 Redis 和一个 PostgreSQL其它组件可以按需后补。需要注意 Redis 的持久化策略要调成 appendonly yes避免会话上下文因为重启丢掉这个细节后面展开说明。3.2 快速注册一个 Agent 到域现在我们实操接一个用于电商订单场景的“订单查询Agent”到 Agent-Reach 网格。第一步是写一份契约描述声明它能干的事。from agent_reach import Agent, Contract, SchemaField order_query_contract Contract( domainorder.center, namequery_recent_unshipped, version1.0.0, input_schema{ user_id: SchemaField( field_typestring, semantic_taguser.id, requiredTrue, description用户唯一标识 ), limit: SchemaField( field_typeinteger, semantic_tagresult.limit, requiredFalse, default10, description返回条数上限 ) }, output_schema{ orders: SchemaField( field_typearray, semantic_tagorder.list, requiredTrue, description订单列表 ) } ) order_agent Agent( nameorder-query-impl-v2, domainorder.center, contracts[order_query_contract], transportredis.stream ) order_agent.register()这段代码核心就做了两件事声明“我会查最近未发货订单”并且把入参和出参的语义标签挂上了工业语义字典。注册成功后框架会把 agent 信息和契约写入 Redis 与 PostgreSQL 双写存储RabbitMQ 等开放接口不用单独再配。实测下来注册成功后约 1.2 秒便可对外提供服务前提是启动注册进程之前 Redis 和数据库连接都处于健康状态。曾经遇到过 registry 写入成功但 agent 起不来原因是 Redis 里存了过期的 QoS 状态后来在注册前加入一次 preflight 检查这类问题就消失了。3.3 配置路由策略与调用示例注册完成之后第二步配置域内路由策略。我们把 order.center 域的策略配置为“最少调用优先 粘滞回退”。domain: order.center strategy: primary: least_calls fallback: sticky sticky_key: user_id fallback_ttl: 120 load_threshold: 0.75这段配置的逻辑是一条查询请求进来框架优先选择当前时段被调用次数最少的 Agent 实例。但如果同一 user_id 已经在会话中命中过某个实例比如正在会话进行时那么保持命中不变的优先级比最少调用更高避免一个用户被轮询散到不同实例导致会话割裂。负载超过 75% 的节点会从候选池临时剔除。实际调用侧代码如下。from agent_reach import ReachClient client ReachClient() resp client.invoke_sync( intent查询最近未发货的订单, domainorder.center, context{ user_id: U-88231, limit: 5 }, timeout_s8 )注意调用侧根本不需要写“我要调用 order-query-impl-v2”只写一句意图框架经过意图解析和策略路由找到合适的实现再把响应原样回传。这就是 Agent-Reach 对调用方彻底屏蔽实现细节的核心体现。上线至今路由延迟中位数在 3.1 毫秒P99 延迟在 21 毫秒左右可以满足绝大多数同步交互场景。3.4 会话锚点实操第三步处理跨 Agent 会话。上面的订单查询 Agent 返回订单列表只走了第一步紧接着用户说“我要把其中一单改成地址”这一步若交给另一个地址变更 Agent 处理就必须先确认两者读写上下文不冲突。# 订单查询 Agent 处理结果写入锚点区 session reach.get_anchor(reach-anchor-U-88231-163) session.write_block( ownerorder-query-impl-v2, data{returned_orders: [SO-2024-0521, SO-2024-0623]}, version1 ) # 地址变更 Agent 读取锚点区 addr_session reach.get_anchor(reach-anchor-U-88231-163) allowed_orders addr_session.read_block( ownerorder-query-impl-v2, keys[returned_orders] )这里最有价值的细节是“anchor block 的 owner 隔离”。地址变更 Agent 只能读取订单查询 Agent 写过的部分不能改写。否则会出现订单查询 Agent 刚记录的订单列表被地址变更 Agent“顺手”覆盖掉一半整场会话从这一刻开始失真。我们第一版没有做 owner 隔离线上出过两次数据串写的事件后来把读写权限管理加进锚点模块才把这个坑彻底填平。3.5 协议转换快与稳的平衡不同 Agent 的技术栈差异在协议层一直是一个大问题。有的 Agent 原生用 REST有的 Agent 只支持 gRPC还有旧系统只有 XML 接口。Agent-Reach 的协议转换层统一为“入口标准化 出口适配化”。意思是入口所有消息统一转成框架内部的中间表示格式出口侧根据目标 Agent 注册时声明的协议自动封装成目标格式。请求到达前转换完成目标 Agent 拿到的就是自己熟悉的格式用起来可以说是无感接入。这个方案换来的是新老 Agent 无缝混跑的能力。不过也不是没有代价协议转换本身有时延和 CPU 开销平均单次转换增加 10 到 30 毫秒开销。我们因此做了一条优化策略同一会话内同一个 Agent 的多次调用第一次转换后缓存转换模板后续直接套模板执行整体转换耗时降了四成。如果场景是高并发低延迟敏感类业务建议评估是否给协议转换层单独扩容而不是直接在框架内塞更多计算资源。4. 常见问题与排查技巧实录4.1 注册失败但服务无感知Agent 注册失败最典型的表现是Agent 进程日志没有报错但网格第一个消息的调用方开始超时。原因大多有两类一类是 PostgreSQL 写入超时一类是契约 Schema 校验不过框架拒绝入库但进程 launch 流程没有感知错误被吞掉了。我们的排查习惯是先查 registry 落库记录再看契约校验日志。落地兼容方案则是在注册入口加水印日志将注册阶段每一步都串上 trace_id同时加布尔返回值进程端断言的逻辑必须有明确的结果响应而不是静默容忍。4.2 路由不均衡策略配的是最少调用但实际流量还是集中到了一个节点。排查后最常见的元凶是 Redis 中存储的调用计数到期时间不一致导致计数器没有被清理。其次是 Agent 实例的时钟偏差导致框架读到的心跳时间最新节点被优先选中掩盖了最少调用策略。解决方式是在路由选择打点里加入双维度排序主键用计数次键用最近心跳时间。这样既保证了最小调用优先又兼顾了活性感知。两个维度取交集比单纯依赖一个维度稳得多。4.3 上下文跨会话污染污染的症状是一个会话里偶尔出现上一个会话残留的字段特别是拉订单列表这种常见动作。原因会有两类一是 Reach-ID 生成时随机数碰撞极端极端场景很罕见但不是零二是错误复用了同一个 anchor block没有按 session 维度重新开 block。这里有两个硬性规范是我们吃了亏之后总结出来的Reach-ID 一定要加入随机性和时间戳直接用 UUID v7 方案避免理论碰撞窗口。每次新会话必须新建 Anchor不允许一个 Anchor 被多个会话共用。调用方侧定期清理短时锚点如果衰减周期设置过长Redis 内存会被历史会话堆满。4.4 调用超时堆积超时堆积是指一个上游 Agent 响应慢导致同一域内大量后续消息排队。Agent-Reach 有背压保护默认机制但需要调整队列大小和线程池比例。我们发现默认配置在高峰期容易触发“你拉我推”的堆雪球效应即 A 超时重试增加了 B 的负载B 变慢又加重 A 的等待最终整条链路雪崩。经验值是将线程池上限设为域内可用实例数的 60%每个实例的并发线程数限制为 200多余请求直接进入降级队列。若降级队列也满了则新请求直接返回“繁忙稍后再试”让上游感知到压力而调整自己的节奏。4.5 链路追踪数据快速定位最后分享一个平时不起眼但排查时极其救命的能力Agent-Reach 会在每个消息进入网格时生成一个全局链路标识每个跨 Agent 跳转都会把这个标识透传。如果一个请求经历了订单查询 → 地址变更 → 库存扣减这三跳日志里会把三段流水全部串在一个 trace 下。排查问题不必再去三个平台割裂看日志地图视图上直接能看到三段调用的耗时、状态码和参数摘要哪个环节延迟高、哪个环节报错扫一眼就能锁定。写在最后Agent-Reach 一路落地下来我最大的体会其实是多智能体协作真正难的并不是每个 Agent 里的模型能力而是它们之间那层既透明又有序的运输网络。把服务注册、路由策略、会话锚点这些基础工程问题处理好业务的复杂性才有机会被真正释放出来。后面我们还准备做基于历史路由质量数据的自学习路由策略、跨数据中心多活部署、以及把会话锚点数据升级为向量记忆池。目前版本里的泛化和网络依赖问题我们也在同步优化。如果你正在搭自己的多 Agent 体系欢迎拿这套设计做参考也期待看到你的落地实践。