尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Agent-Reach:多Agent协作的智能触达层与消息总线实践
Agent-Reach这个名字是我在深夜写代码的时候随手敲的但现在回过头看它比我想象的更准确地描述了我们在做的事——让AI Agent真正够得着它需要的一切。团队里一直有句话单个Agent再聪明如果它触达不到外部工具、企业内部数据、甚至是另一个Agent的分析结果那它也就只能在聊天框里打转。Agent-Reach是我们内部落地的一个多Agent协作运行时核心定位是做一个“触达层”。它解决的问题很具体在大模型驱动的应用里不同的业务Agent比如客服Agent、财务Agent、运维Agent如何安全、高效地互相调用能力、访问工具而不是每一个Agent都各自接一堆API最后变成一张没人理得清的意大利面式的调用关系网。如果你也在做Agent平台、智能体中间件或者正在被“多个Agent之间怎么通信”这个问题折磨这篇文章应该能给你一些直接的参考。整个项目从立项到压测稳定跑通前后花了大概六周。我不想把它包装成一个理论框架所以下面写的都是我们在真实环境里踩出来的经验包括设计取舍、核心代码结构、以及几个让我连着加班到凌晨的坑。1. 为什么要有Agent-Reach智能体协作里的“最后一公里”1.1 单个Agent的能力边界先聊聊问题本身。现在大家做Agent本质上是在做“大模型 工具调用 状态管理”的组合。单个Agent可以很强给它几个工具让它自己写计划、调用工具、根据结果调整下一步这是标准的“ReAct”套路。但落到真实业务里麻烦立刻出现了——一个Agent解决不了所有问题。举个实际的例子我们当时有一个客服场景的Agent它需要查订单状态、查物流、处理退款。查订单要调内部订单系统查物流要调物流平台的API退款要调财务系统的接口。如果把这些能力全部塞给客服Agent会发生几件事第一提示词会被工具描述塞爆模型的注意力被稀释第二每个工具都需要独立的鉴权和流控Agent自身变成了运维噩梦第三一旦订单状态查询逻辑变了你除了改代码还得改Prompt否则Agent会用过时的工具描述去瞎猜。所以我们第一刀很明确把“会思考的Agent”和“能触达的Agent”分开。有些Agent是决策型的它们只负责推理有些Agent是执行型的它们专门负责调用特定系统。问题变成了——决策型Agent怎么知道自己能用哪些执行型Agent执行型Agent怎么安全地把结果传回来这时候就需要一个统一的触达层。1.2 多Agent协作的真实痛点当初我们也考虑过直接用市面上的成熟框架比如LangChain、AutoGen、CrewAI都试了一圈。坦白说它们在小规模demo里跑得很顺但一旦我们开始接入真实业务系统痛点就一个接一个通信模型太死板。大部分框架是“中心化编排”一个主Agent管所有子Agent。一旦你希望子Agent之间也能直接对话比如财务Agent查到账之后再让客服Agent去通知用户就得硬写N多自定义回调。工具绑定太紧。很多框架把工具定义直接塞给大模型靠模型自己选择。这听起来很美好但真实的企业系统可不会允许大模型随机调一个敏感接口。我们需要在调用链路上做硬拦截和审计框架级支持却很弱。上下文没有隔离。多个Agent共享同一个上下文窗口时A Agent产生的中间数据会污染B Agent的推理。尤其是一个Agent被A调用后又去调用C消息链一长上下文里全是其他Agent的“思维碎片”。还有一个被很多人忽略的问题可观测性。在调试多Agent系统时你要是拿不到每一次消息路由的完整轨迹出了问题基本只能靠猜。我们需要每一次触达都有迹可循任何一次失败都能回放整个过程。1.3 为什么最终选择了自研Agent-Reach回头复盘我们的选型经过了一个比较痛苦的过程。当时压力很大业务方等着要能力上线框架选不定代码不敢写。后来我们坐下来列了一个需求清单支持Agent之间的点对点调用支持把外部REST API包装成标准工具支持路由链路追踪支持同步请求也能支持异步回调权限模型要能精确到“某个Agent能不能调某个工具”部署要轻量不能又搞一个重中间件。对比下来现有框架没有一个能同时满足这几条。自研的成本看起来虽高但因为我们把范围卡得很窄——只做触达层不做Agent本身不做模型调度不做记忆管理——实际开发量是可以控制的。于是Agent-Reach立项目标是用三周把核心链路跑通再用两周压测和补坑。2. Agent-Reach的核心设计把“触达”变成可注册、可路由、可审计的消息总线2.1 三个核心抽象AgentHandle、ToolEndpoint、ReachRegistry在定义Agent-Reach前我们先定义了三个抽象概念后面所有代码都围绕这三个概念展开。第一个是AgentHandle。它代表一个可被触达的Agent实例。AgentHandle不关心底层是OpenAI的模型还是自建的开源模型也不关心Agent是用LangChain写的还是裸代码写的。它只暴露一个方法receive(message)。消息进来回复或者异步回传一个结果。第二个是ToolEndpoint。它代表一个能力入口可以是一段本地函数也可以是一个REST API的封装甚至可以是另一个系统里的消息队列。Agent-Reach不在乎背后是什么只在乎这样一个签名invoke(request) - response。为了能路由ToolEndpoint必须有一个明确的声明比如它属于哪个领域、需要什么参数、会返回什么。第三个是ReachRegistry。这是所有触达能力的注册表相当于一个“能力黄页”。Agent和ToolEndpoint都在ReachRegistry里登记包括它们的能力描述、地址、健康状态和权限标签。当Agent A需要某件事它向Registry询问“谁能做这件事”Registry返回符合条件的Agent或ToolEndpoint列表然后由路由器转发请求。这三个抽象之间不直接互相依赖全部通过消息驱动。这样一个Agent挂了只需要从注册中心摘除不影响其他Agent一个新的工具上线只需注册一下立刻可被发现。2.2 消息协议ReachMessage的字段与生命周期消息协议是Agent-Reach的血管。我们设计了一个最小但够用的消息结构叫ReachMessage核心字段如下字段类型作用message_idstring全局唯一消息ID用于链路追踪sourcestring发送方Agent IDtargetstring接收方Agent ID或者路由键如finance/refundintentstring意图例如CHECK_ORDER_STATUSpayloadobject业务数据JSON格式timeout_msint消息超时时间correlation_idstring关联ID用于请求/响应配对一个消息从发起到结束生命周期分成四个状态created、routed、processing、completed。Agent-Reach的跟踪中间件会把每个状态的时间戳和所在的节点记录下来。做完这一步我们终于能回答“谁在什么时候给谁发了什么”这个问题了。2.3 路由策略技能声明优先星号兜底路由决策是Agent-Reach里最需要谨慎的部分。一开始我们偷懒直接用正则匹配消息里的intent和工具名称测试时发现很脆。比如“CHECK_ORDER_STATUS”有的Agent会写“check_order_status”有的会写“查询订单状态”模型的中文和英文混杂根本不可能用字符串匹配。后来我们改为“技能声明优先星号兜底”的路由策略。每个Agent或ToolEndpoint在注册时声明自己处理哪一类的intent同时可以给出一个优先级。比如“订单Agent”声明它可以处理order.优先级10“通用问题Agent”声明处理优先级1。当消息intent是order.check时Registry把优先级最高的order.*选出来路由如果没有匹配到就落到星号兜底。这样我们在不引入复杂NLP匹配的情况下让路由足够灵活。这个设计还带来一个额外的好处可以动态调整路由权重。比如某个Agent负载太高可以把它的优先级临时调低或者某类新业务出来了不用改代码在注册中心加一条intent到新Agent的映射就行。3. 关键模块落地注册中心、请求-响应模型与工具网关3.1 注册中心Agent的上线、下线与心跳注册中心是Agent-Reach的神经中枢。我们使用了一个轻量的内存注册表加上可选的持久化存储。内存注册表保证毫秒级查询持久化存储用来在重启后恢复Agent信息。Agent实例启动时会调用Agent-Reach SDK的register方法from agent_reach import AgentReach, ReachAgent reach AgentReach(registry_urlhttps://registry.agent-reach.internal) order_agent ReachAgent( agent_idorder-agent-01, skills[order.query, order.refund], endpointhttp://agent-order:9000/receive ) # 自动带心跳每15秒上报一次健康状态 reach.register(order_agent)业务Agent进程不仅能注册自己还需要定期发送心跳。我们最初把心跳间隔设为5秒后来发现太频繁大量的心跳消息挤占了正常的业务消息导致消息总线上出现“心跳风暴”。最后把间隔调整为15秒同时加了一个tolerance连续3次心跳丢失才摘除节点。这个值在容器重启场景下还需要动态调整否则Agent服务慢启动时会被路由层错误摘除。摘除机制要注意不能只摘除最新心跳失败的那一次而要看时间窗。像K8s里Pod重新拉起需要时间如果Agent的启动速度慢那在这种场景下摘除太快会反复触发重试。我们通过“软状态”避免这个问题——先在注册表里标记为“suspect”只发给它非关键消息连续确认Unhealthy后才真正移除。3.2 请求-响应模型同步与异步的统一多Agent协作里有些调用必须等待结果有些则适合扔到后台。同步调用很好理解外层Agent发消息内层Agent处理完直接把响应返回。我们把这种调用封装成了RPC风格response await order_agent.call( intentorder.query, payload{order_id: SO-20231212-001} )但这会带来一个隐患如果内层Agent处理一个请求需要30秒外层Agent的HTTP连接早就超时了。所以Agent-Reach默认使用“异步消息 事件总线回传”的模式。调用方发出请求后可以选择挂起协程等待correlation_id对应的回执也可以注册一个callback让回调函数去处理结果。SDK底层把两种模式封装成同一个async接口开发者不需要关心底层是WebSocket还是HTTP轮询。异步模式下消息总线上会跑两种类型的消息一种是业务消息另一种是ResultAck消息。ResultAck里带有correlation_id和status我们把状态分成accepted、processing、success、failed。这样即使某个Agent在后面跑挂了调用方至少能拿到一个failed回执可以去做补偿逻辑而不是傻等超时。3.3 工具网关把内部API包装成ToolEndpoint业务系统里已经有大量API了Agent-Reach不可能要求所有系统都改用Agent协议。所以我们做了一个工具网关叫ReachGateway专门负责把REST API适配成ToolEndpoint。一个典型的适配过程是这样的from agent_reach import ToolEndpoint, HttpToolAdapter refund_api HttpToolAdapter( endpointhttp://finance-api/refund, methodPOST, headers{Authorization: Bearer internal-token}, request_mapping{order_id: orderId, reason: reason, amount: amount}, response_mapping{refund_id: refundId, status: state} ) refund_tool ToolEndpoint( tool_idfinance.refund, description对指定订单执行退款操作必须提供order_id和refund_reason, input_schema{...}, adapterrefund_api, timeout_ms10000 ) reach.register_tool(refund_tool)这里的重点在于request_mapping和response_mapping。因为内部API的字段命名跟Agent生态里习惯的命名可能完全不一样如果让每个Agent去适配外部系统的字段那耦合又回到了原点。Agent-Reach在网关层做了一层字段映射Agent发来的payload按照tool的声明格式进来网关负责转成目标系统能懂的样子。这一个设计直接省了Agent开发团队大量扯皮时间。工具网关还负责统一的超时控制和错误分类。我们把错误分为可重试如网络超时、5xx和不可重试如参数错误、4xx。分类结果写进ResultAck调用方Agent可以据此决定是换个参数重试还是换另一个工具。这个细节非常重要——模型层不需要理解HTTP状态码它只需要知道“这次调用失败了原因类别是参数问题还是临时故障”。3.4 安全与权限精确到Tool的IAM让Agent互相调用的风险很多人一开始低估了。想象一下客服Agent被用户提示词注入攻击了用户骗它去调用“给用户退款”工具而Agent本身是有权限调用这个工具的——这种是典型的越权链风险。Agent-Reach的安全模型借鉴了IAM最小权限原则每个Agent有一个ServiceIdentity并且只能调用它被授予的ToolEndpoint和同级的AgentSkill。权限检查发生在路由器上而不是靠Agent自律policy { order-agent: { can_call_agents: [customer-agent], can_call_tools: [finance.refund, order.query, logistics.track] }, finance-agent: { can_call_agents: [], can_call_tools: [finance.refund, finance.balance] } }路由器在处理message时会校验source agent是否有target的调用权限没有直接拒绝。这个校验放在最前面避免消息进入处理队列后才暴露问题。审计日志也会记录每一次因为权限被拒的请求这在合规角度上非常有用。我们当时的教训一开始权限控制只加在ToolEndpoint层Agent之间是互相开放的。结果一个Agent因为解析用户输入不当把一条“删除用户”的内部指令发给了另一个Agent差点造成事故。所以从第二天开始Agent之间的调用权限也收紧所有权限默认拒绝按需开放。4. 联调与实际运行踩掉的四个大坑4.1 Agent死锁两个Agent互相等待第一次联调时我们遇到一个特别隐蔽的问题Agent A调用Agent BB处理过程中又需要调Agent CC为了处理这个请求需要回过来调A的一个技能。如果消息总线是同步等待模型整条链路会形成一个A等B、B等C、C等A的循环等到超时后全体失败。我们把这类问题的根因归结为“没有 DAG 思维”。很多Agent在设计时习惯性地把协作看成链式调用但多Agent系统往往是图状的。Agent-Reach解决方式有两层第一层在路由参数里加上hop_limit一个消息最多能被转发多少次超过就拒绝并抛出“RouteLoop”错误。这是止损机制。第二层从架构上鼓励事件驱动。如果发现A和C之间可能出现成环团队里约定最高优先级的Agent只消费事件不回发请求需要结果时用HTTP回调而不是同步路由。这样虽然增加了一点编码量但彻底避免了死锁。4.2 上下文截断把几千字的中间结果塞进上下文我们的财务Agent调用退款工具后把整个工具Response里面带了几百行内部调试信息又塞到下一个Agent的Prompt里结果那个Agent直接糊涂了回复了一堆跟任务无关的内容。这个问题表面上是大模型上下文窗口限制实际上是我们在Agent-Reach消息设计里漏掉了“消息摘要化”的能力。后来我们给ReachMessage的payload加了一个选项summarize_within_reach对于大型响应网关层会调用一个小模型把长文本压缩成结构化摘要再传给下一个Agent。比如退款结果是一个5000字的内部日志经过摘要模型输出为{refund_id: R100023, status: success, amount: 199.00}所有下游Agent看到的都是这个精简结果而不是原始日志。这样不仅省token更重要的是大幅减少信息噪声对模型推理的干扰。4.3 重试风暴Agent-Reach节点故障时客户端无限重试有一回我们某个Agent节点因为内存泄漏挂掉了结果几十个客户端同时在那里无限重试把注册中心压垮了。重试风暴在多Agent系统里特别容易爆发因为每个调用方都觉得自己“再试一次就行了”。我们加入了三道防护第一道SDK内置指数退避且退避策略的初始值由注册中心下发默认base100msfactor2max_delay5s。第二道路由器端做熔断Circuit Breaker对每个目标Agent记录最近10次请求的成功率失败率超过60%时熔断5秒期间新请求直接返回“target_unavailable”。第三道一旦注册中心检测到节点不健康立刻把路由信息里的地址标记为“draining”新消息不再路由到这个节点只有正在处理的消息可以继续。这三道做完重试风暴再也没发生过。4.4 日志业务不分排查问题要拆三层Agent-Reach刚上线时我们天真地把所有日志放在一起打。业务消息日志、路由日志、Agent请求日志、心跳日志全混在一起排障时发现一个大问题一个订单查询失败你很难把“这个消息走了哪个路由”、“Agent内部做了什么”、“工具网关返回了什么”串联起来。后来我们把日志体系拆成三层Track层记录消息的message_id、source、target、路由决策和耗时。Adapter层记录工具网关对每个REST API的调用参数、原始响应和错误。Agent层记录Agent本身接收到的payload、调用了哪些工具以及自己的最终输出。三个层通过correlation_id关联在Log Viewer里可以根据一个message_id拉出整条链路。这个改造耗时不长但对调试效率和团队协作的提升非常明显。5. 性能优化、指标与后续规划5.1 压测数据我们实际测到的数字我们在测试环境里用了一个比较典型的硬件两台8核16G的机器一台跑注册中心和路由器一台跑三个业务Agent。压测工具模拟100个并发请求每个请求会触发一次“客服Agent-订单Agent-物流Agent-客服Agent”的完整链路。结果如下场景平均耗时P99耗时成功率单跳同步调用320ms480ms99.8%多跳链路3 Agent1.24s1.9s99.4%带工具网关调用780ms1.2s99.2%这个结果对我们来说够用了。但需要注意压测数据里的耗时大头不是Agent-Reach本身而是模型推理和外部API的延迟。Agent-Reach自身的路由开销大约只有1~3ms即使加上权限校验和链路追踪整体开销也控制在10ms以内。5.2 优化手段连接复用、批量消息与缓存路由表压测时我们发现了两个明显的优化空间。第一个是连接复用Agent-Reach节点之间如果没有连接池每次消息都要建HTTP连接瓶颈立刻暴露。我们让节点之间保持长连接池连接空闲超过180秒才断开。第二个是批量消息心跳、心跳响应这类低频但高频次的消息合并成一个批量消息体发送减少网络往返。还有一个收益很大的优化是路由表缓存。以前每条消息都要实时去注册中心查询即使注册中心是内存的查询开销也有小几毫秒。后来我们让每个节点本地维护一个路由表缓存的只读副本注册中心推送变更事件本地缓存更新。这样一来消息路由时完全不需要走网络代价是注册中心变更后会有最多几百毫秒的缓存延迟这个延迟在我们场景可接受。5.3 后续规划联邦模式、可观测性增强与插件化Agent-Reach目前的版本还是单注册中心的结构适合一个团队内部使用。我们下一步准备做联邦模式多个独立部署的Agent-Reach实例之间通过Broker桥接让不同部门或者不同数据中心的Agent可以跨域安全触达。联邦模式会在注册信息上增加域标识跨域消息默认要通过策略文件审核。可观测性方向我们在考虑把OpenTelemetry的Trace语义嵌入到ReachMessage链路里这样以后可以直接在Grafana里看Agent调用的瀑布图。现在只有log问题定位还是要靠人去拉日志体验还不够爽。插件化是呼声比较高的功能。目前ToolEndpoint的适配器只支持REST和Python函数但社区里有人问能不能支持GraphQL、数据库操作甚至gRPC。我们计划把适配器做成可插拔的用户可以写一个自定义Adapter加载进去。5.4 几点真实体会项目走到现在如果让我总结最想说的一句话多Agent系统难的地方不在于Agent本身而在于Agent之间的“触达”。模型能力提升带来的收益很容易被混乱的调用关系、脆弱的超时机制和权限漏洞抵消掉。Agent-Reach从一开始就把“触达”抬到核心位置这个决定让我们后续的扩展省了大量精力。另外自研一个中间件并不可怕前提是你要把范围守死。我们不过度设计不追求做一个“包罗万象的Agent框架”。市场上有太多平台想做所有事情结果用户一上手反而不知道该用哪个功能。Agent-Reach只做触达层这个“限定”反而是它最大的优势。最后再分享一个小技巧如果你也想自己写一个类似的轻量级Agent通信运行时第一个PoC不要引入太多依赖。我们第一版只用了FastAPI加一个Dict存储就能跑通所有核心流程。那些高可用、持久化、联邦能力是等到业务真的需要时才加的。过早引入分布式中间件只会让你的调试体验从崩溃变成分裂。
RELATED

相关推荐

KonopkaControls 8.0 在 Delphi 12.3 中的安装与验证指南

KonopkaControls 8.0 在 Delphi 12.3 中的安装与验证指南

简介:这是一份面向Delphi开发者的专业控件库资源,由KonopkaControls 8.0版本打包而成,专为Delphi 12.3(RAD Studio 13)环境设计,旨在通过现成的可视化组件减少UI编程工作量,提升应用界面质量与开…

📅 2026/10/6 10:35:35
Delphi 12.3 安装 KonopkaControls:VCL 圆角控件包实战指南

Delphi 12.3 安装 KonopkaControls:VCL 圆角控件包实战指南

简介:Delphi 13控件库KonopkaControls 8.0版压缩包,专为RAD Studio 12.3设计,面向需要快速构建专业用户界面的Delphi开发者。该控件库由Marcin Konopka创建,提供按钮、编辑框、树形视图、进度条、数据网格等丰富组件,能…

📅 2026/10/6 10:35:35
DDR4 SI/PI仿真全流程:从Allegro Layout到Sigrity收敛的避坑指南

DDR4 SI/PI仿真全流程:从Allegro Layout到Sigrity收敛的避坑指南

一块DDR4板子从Layout落地到仿真收敛,中间的路远比很多人想象的要长。Cadence Allegro做PCB设计是老本行,Sigrity做SI/PI分析也是经典组合,但真正把它们串成一条顺畅的流程,需要踩过不少坑才知道关键点在哪里。这篇内容我把从Layo…

📅 2026/10/6 10:35:35
MORE NEWS

更多资讯

📰

考毕兹、克拉泼、西勒:电容三端LC振荡器原理与选型指南

做射频电路的人大概都绕不过一个坎:明明考毕兹、克拉泼、西勒摆在一起,元件就那么五六个,可一旦要上频率、要稳定、要可调,三兄弟的表现立刻拉开差距。电容三端LC振荡器这个家族,入门容易精通难,难就难在很…

📰

本地部署AI模型的四大硬性门槛解析

1. 为什么这句话一出,无数人默默关掉了刚下载的模型压缩包 “不是所有AI模型,都能本地部署”——这短短十几个字,最近在技术社区、硬件发烧友群、甚至小红书和B站评论区反复刷屏。它不像一句技术公告,倒像一句深夜调试失败后的叹…

📰

DeepSeek图像与文本分类API调用实战与避坑指南

简介:一份面向Python开发者的DeepSeek接口实战指南,聚焦图像分类与文本分类两大场景,帮助具备编程经验的技术团队快速集成云端智能分类能力。文档以代码实例贯穿始终,详细演示了注册获取接口密钥、安装requests与Pillow库、构造带…

📰

基于MCP协议打造生产级AI自动化中台:架构、权限与可观测性

先说个我自己的状态:这两年我经手过不下十个 AI 自动化项目,从写脚本调接口的“个人玩具”,到真正给团队用的自动化中台,最大的感受就是—— Demo 和生产的差距从来不是代码行数,而是协议、边界和工程思想。 而 MCP&…

📰

VCS tmerge与Verdi TraceX协同定位X态根源

1. 为什么X态调试是数字前端验证工程师每天都在啃的硬骨头在VCS仿真中看到波形里突然冒出一串“X”,不是惊喜,是警报。它不像0或1那样明确,而是一种未定义状态——可能源于未初始化的寄存器、三态总线竞争、异步复位释放时序违例、或者跨时钟…

📰

网络103规约联调避坑:故障录波与FUN/INF映射实战

简介:南瑞继保网络103规约文档是电力系统远动通信领域的专业参考资料,面向调度自动化、变电站集控及二次设备调试维护人员,解决标准103规约在实际工程中如何适配南瑞继保设备与网络环境的问题。内容涵盖基于IEC 60870-5-103的扩展功能&#x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬