ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Agent-Reach:多智能体协作的通信底座与消息路由实践

Agent-Reach:多智能体协作的通信底座与消息路由实践 多智能体这块最近两年被炒得很热但真正在项目里把多个Agent拉到同一张桌子上协作的时候你会发现一个很尴尬的问题各个Agent之间根本够不着对方。它们各自封装在自己的框架里跑在自己的进程里用的是各自的工具调用协议消息格式各说各话。我这个项目Agent-Reach就是冲着这个痛点去的——它解决的核心问题是Agent触达让一个Agent能够发现另一个Agent的存在、了解它能干什么、然后把任务和结果可靠地递过去。这不是一个华而不实的框架而是一套轻量的、可以直接嵌进现有系统的Agent间通信与发现基础设施。下面我把整个项目的设计思路、核心实现、踩坑过程都展开聊聊尤其是那些只在真实流量下才会暴露的问题。1. 多智能体协作的圈地困境Agent成了孤岛协作成了妄想先回顾一下我为什么要做这件事。在做Agent-Reach之前我参与过一个电商客服场景的项目里面有三个Agent一个负责售前咨询的、一个负责订单状态查询的、一个负责售后处理建议的。三个Agent分别基于不同的框架搭出来的售前的用了LangChain订单查询的是自己写的一套意图识别加工具调用售后那个直接调外部RPA接口。最开始的设计是三个Agent串行跑用户问一句售前Agent判断该不该转给订单Agent然后由售前Agent的代码里写死一个调用订单Agent的函数。这个方案看似没毛病跑起来之后全是麻烦。比如订单Agent换了个部署地址售前Agent的代码就得跟着改比如想新增一个物流咨询Agent售前Agent的代码要重新发布一版。更头疼的是三个Agent之间传消息完全没有统一格式售前传过去的是帮我查一下订单订单Agent那边需要的是结构化JSON中间还得套一层转换逻辑。这些小问题叠加在一起就是典型的硬编码集成之痛。我当时理想中的形态是这样的Agent之间不直接握手而是通过一个公共的通讯录互相发现消息格式统一某个Agent挂掉或者升级的时候不影响其他Agent的整体运行。这就是Agent-Reach立项的最初动机。所以Agent-Reach的第一层价值是解耦第二层价值是标准化。它做的事情不是一个Agent框架而是一个中间层。打个比方Agent-Reach不负责教你怎么做Agent它负责的是给Agent们发名片和信箱。从技术选型上看当时有几个现成方案可以选比如消息队列Kafka、RabbitMQ加一个服务注册中心Consul、Etcd。但我试过之后发现太重了——Kafka解决的是大数据量消息的吞吐问题而我这个场景的消息量一天也就几万条而且大部分是短小的一问一答为这个上Kafka有点杀鸡用牛刀。Consul那套偏向微服务治理健康检查、KV存储确实都有但Agent之间的消息路由语义——这个任务该发给哪个Agent、怎么回传结果——它是不懂的我还是得自己在上层写一套路由逻辑。思来想去与其拼装两个轮子不如自己做一个正好合适的轮子。Agent-Reach的本质就是一个带语义能力的Agent注册与消息路由服务底层用轻量的消息通道承载通信上层实现了Agent能力描述、发现、定向投递和结果回传。接下来我会把每一块设计拿出来细讲。2. Agent-Reach的定位与总体架构不造Agent只疏通Agent之间的路先说清楚架构边界这是后面所有细节讨论的前提。Agent-Reach包含四个核心模块Agent注册表Agent Registry、能力目录Capability Catalog、消息路由层Message Router、以及客户端SDKAgent-Reach Client。2.1 注册表设计每个Agent都要有一张实名名片注册表是整个系统的地基。每个Agent接入时向注册表登记一份元数据内容包括Agent的全局唯一ID、名称、描述、能力标签、通信地址比如WebSocket的URL或者消息队列的主题名、当前状态、负载指标。这张名片会带一个版本号Agent每次变更能力或者地址名片版本递增。这里有个很关键的设计决策注册表不要做成AP可用性优先模型而要倾向CP一致性优先模型。为什么不学Eureka那套因为Agent发现一旦读到过期数据消息就会发到一个已经不存在的实例上直接导致任务失败。相比之下宁可短暂地发现不到某个Agent也不要发现到一个幽灵Agent所以Agent-Reach的注册表内部用了Raft协议做多节点同步保证三个副本之间的数据强一致。实测下来几个节点之间的同步延迟在毫秒级别对Agent发现这种低频操作完全够用。名片信息的具体结构是这样定义的{ agent_id: order-agent-01, name: 订单状态查询Agent, version: 3, capabilities: [ { name: query_order, description: 根据订单号查询订单当前状态, input_schema: { type: object, properties: { order_id: {type: string} }, required: [order_id] }, output_schema: { type: object, properties: { status: {type: string}, eta: {type: string} } } } ], transport: { type: ws, endpoint: ws://10.0.1.12:9201/agent }, status: online, heartbeat_interval_sec: 15, max_concurrent_tasks: 10 }这份JSON就是Agent的名片。消费者也就是其他Agent拿到名片后不需要提前知道调用细节只靠名片里的capabilities就能判断这个Agent能不能帮我干活、该怎么传参数。接口契约的问题在这里就解决了以前是代码里硬写函数调用现在是数据驱动的动态路由。2.2 能力目录与匹配逻辑让谁该处理这件事变成可计算的注册表只是存储能力目录才是智能的地方。能力目录负责维护能力名到Agent集合的映射并对外提供匹配服务。比如调用方传一个自然语言描述或者结构化的任务标签目录服务返回能处理这个任务的Agent列表按匹配度排序。最开始我直接用关键词匹配能力名后来发现根本不够用。同样是查订单这件事A Agent管的是B2C订单B Agent管的是B2B订单光看能力名query_order分不出来。所以我把匹配升级成了两层第一层是硬匹配基于能力标签的精确匹配速度快适合已知明确标签的调用第二层是软匹配用Embedding做语义相似度计算调用方发来的任务描述和已有Agent能力描述算余弦相似度大于一个阈值才进入候选集。软匹配的引入让Agent-Reach对上层应用非常友好——调用方不需要记住每个Agent的能力名只要用一句人话描述任务系统帮他找Agent。这一步当时落地的时候花了不少功夫踩过Embedding模型选型的坑后面会详细讲。2.3 消息路由层设计请求/响应和异步任务两条腿走路消息路由是第三个模块也是通信语义的核心。Agent之间协作的模式其实就两种一种是同步的请求/响应比如帮我查一下订单12345的状态立刻要结果另一种是异步任务投递比如帮我把这批1000个订单做异常回访不用立刻出结果完成后通知我。Agent-Reach对两种模式分别做了通道设计。同步通道直接走WebSocket长连接实现上类似一个轻量RPC异步通道则持久化到内嵌的消息存储里Agent上线后拉取积压任务。之所以不用外部队列是因为异步任务数量和Agent会话状态有强关联存到注册表同一套存储里反而简单——Agent断线期间的任务会在它恢复心跳后自动补发。路由决策本身也不复杂按照这个优先级处理调用方显式指定了agent_id直接定向投递调用方传了能力名路由层查能力目录取匹配度最高的在线Agent调用方什么都没传只有一段任务描述路由层走语义匹配如果候选Agent都在忙达到max_concurrent_tasks上限任务进入等待队列而不是直接失败。前三条都好理解第四条值得多说一句。Agent-Reach默认不丢任务有背压机制。调用方发来一个任务如果目标Agent繁忙这个任务会在路由层排队由调用方决定等待超时时间。实际项目里我会建议调用方设置一个合理的超时默认30秒超过就返回繁忙请稍后再试由上层Agent决定是换个Agent还是告诉用户稍等。2.4 客户端SDK的边界只做三件事绝不多做客户端SDK的设计初衷是接进去简单拿到别的Agent的能力简单发消息简单。所以SDK只封装了三类能力注册与心跳、发现与订阅拿到名片、监听Agent上下线事件、消息发送与接收同步和异步两种模式。有个很刻意的设计SDK不做Agent能力编排不做多步流程控制也不内置提示词模板。这些交给上层Agent框架或者业务流程去处理。Agent-Reach是一个路由器而不是大脑大脑应该属于每个Agent自己这也是我坚持的原则。如果SDK越界做了编排Agent的自主性就被架空了那就跟传统ESB服务总线没有本质区别了。3. 核心模块逐行拆解注册表、心跳与路由的实现细节这一节直接上实现。Agent-Reach服务端我用Go写的原因很简单单机并发吞吐高、部署就是一个二进制文件、内存占用小。客户端SDK先做了Python版本因为接Agent的团队主力语言就是Python后来补了TypeScript版本给前端低代码平台用。3.1 注册表存储结构一张表搞定所有元数据注册表底层用SQLite单机模式或TiKV集群模式存储但对外暴露的是内存视图。Agent的元数据维护在内存里的一个并发安全Map里key是agent_idvalue是完整的元数据对象。每次写入或者更新时同时写持久化存储并广播变更事件。Go语言里这个结构大致是这样type Registry struct { mu sync.RWMutex agents map[string]*AgentMeta byCaps map[string]map[string]struct{} // capability - set of agent_id watchers map[string][]chan AgentEvent } type AgentMeta struct { AgentID string json:agent_id Name string json:name Version int json:version Capabilities []Capability json:capabilities Transport TransportInfo json:transport Status AgentStatus json:status LastHeartbeat time.Time json:last_heartbeat } type AgentEvent struct { Type string // registered, updated, offline, online AgentID string Meta *AgentMeta }byCaps这个反向索引是匹配性能的关键。能力匹配的请求一来先按能力名取Agent集合再逐个看状态和负载避免了全表扫描。注册、更新、心跳都通过mu.Lock()保护这个锁在低并发下毫无压力但到了Agent数量上百、心跳频率高的场景单把大锁会成为瓶颈。我后来做了分片锁优化按Agent ID哈希分成32个分片各自独立加锁吞吐量提升明显。3.2 心跳机制与僵尸Agent清理心跳的设计要回答两个问题多久算超时超时了谁负责清理我的做法是Agent默认每15秒发一次心跳注册表在3个心跳周期45秒没收到就标记为offline再过2个周期75秒还没恢复就把Agent从活跃列表里移除并广播下线事件。这个时间窗口不是拍脑袋定的跟Agent的业务类型有关系——客服Agent 45秒没心跳基本就是进程挂了但如果是有长耗时任务的Agent比如批量处理回访进程活着但主线程被阻塞心跳发不出去也是常事。所以我在心跳API之外还加了一个独立的/ping探活接口路由层的健康检查用这个接口注册表的离线判定用心跳两者分离。僵尸Agent的清理逻辑我建议做成软删除。不直接从agents里抹掉元数据而是只在byCaps活跃索引里摘除元数据保留24小时方便排查问题。线上问题排查时你会感激这个设计——Agent崩溃后你想查它崩溃前的元数据版本如果被物理删了就得从头查日志。3.3 消息路由的投递语义At-Least-Once与去重Agent之间消息投递的语义我直接定成了At-Least-Once至少一次。这不是偷懒是成本权衡下的理性选择。Exactly-Once在高吞吐消息系统里要靠事务消息或幂等消费来逼近对Agent协作这个场景来说成本太高。At-Least-Once配合消息里的全局唯一ID让接收方做幂等去重效果足够。每个消息的骨架长这样{ message_id: uuid-v7-xxxx, trace_id: trace-abc-123, task: { type: sync, capability: query_order, input: { order_id: 20250101001 }, timeout_ms: 30000 }, source: { agent_id: pre-sale-agent-01, session_ref: chat-session-7788 }, target: { agent_id: order-agent-01 } }message_id是全局去重的依据UUID v7自带时间排序写入存储的时候对索引友好。trace_id用来串起一次跨Agent协作的完整链路——用户的一个问题可能触发三个Agent先后处理靠trace_id能把整个链路的行为日志捞出来。这个字段特别值得重视没有它排障就是大海捞针。路由层接收到消息后按如下流程处理校验target.agent_id是否在线在线则通过WebSocket把消息推送过去等待接收方ACKACK不代表任务完成只代表消息被Agent进程收到了如果30秒内没有ACK标记为投递失败重试最多3次重试仍失败消息进入死信表同时给调用方返回一个投递超时响应。这里有个小坑WebSocket连接本身可能假死。TCP连接还在但Agent进程已经卡死消息发过去没有响应。所以ACK超时机制必须存在不能只靠TCP层面的连通性判断。3.4 Python SDK的接入代码三行登记一行发消息Python SDK的目标是让接入成本降到最低。Agent上线时的注册代码from agent_reach import AgentReachClient, SyncCall client AgentReachClient(registry_urlws://reach-server:8800/registry) # 声明能力完成注册 client.register( agent_idorder-agent-01, name订单状态查询Agent, capabilities[ { name: query_order, description: 根据订单号查询订单当前状态, input_schema: {...}, output_schema: {...} } ] ) # 处理入站请求 client.on_capability(query_order) def handle_query_order(input_data: dict) - dict: order_id input_data[order_id] status query_order_db(order_id) return {status: status, eta: 2025-02-01 14:00} # 启动监听开始接收消息 client.start()再看出站调用一个Agent想调用另一个Agent的能力时result client.call_sync( capabilityquery_order, input{order_id: 20250101001}, timeout_ms30000 ) # Business 语义错误 if result.get(error_code): fallback_to_another_agent(capabilityquery_order_v2) print(result[data][status])call_sync内部封装了能力发现、路由请求、等待响应、超时重试这几件事。对上层调用方来说就是一行函数调用完全不用感知对方Agent到底在哪台机器上、用的是什么框架、内部怎么实现的。这种动态发现统一契约的体验比自己在代码里写死HTTP调用要舒服得多改一个Agent的部署位置系统里的其他Agent什么都不用动。4. 接入真实业务三类Agent跨框架协作的完整通路设计讲完了来看实际接入效果。当时我们在测试环境搭了三类AgentA跑在LangChain上B是CrewAI里定义的角色型AgentC是一套完全自研的规则加LLM混合Agent。三个框架各走各的唯一共性就是都装了Agent-Reach的Python SDK。4.1 LangChain Agent接入用Tool封装打通最省事LangChain Agent本身有一套Tool机制它把外部功能抽象成Tool来调用。我做的事很简单把Agent-Reach的call_sync封装成一个LangChain的BaseTool。from langchain.tools import BaseTool from agent_reach import AgentReachClient class ReachTool(BaseTool): name: str agent_reach_query description: str ( 当用户需要查询订单状态时使用。 输入为订单号字符串输出为订单状态与预计送达时间。 ) def _run(self, order_id: str) - str: client AgentReachClient(...) result client.call_sync( capabilityquery_order, input{order_id: order_id}, timeout_ms20000 ) return json.dumps(result, ensure_asciiFalse)这样LangChain的Agent在推理时如果判断需要查询订单就会自动调用这个ToolTool内部走Agent-Reach把任务路由到订单Agent。整个过程对LangChain是无感知的——它只觉得自己调用了一个普通Tool实际上背后的目标Agent跑在另一个框架里。4.2 CrewAI角色Agent接入同步转异步避免阻塞CrewAI的多Agent是角色扮演式协作Agent之间通过Task传递工作。这里遇到一个实际问题CrewAI的Agent执行任务时如果卡在一个同步调用上很久整个流程会变慢。所以我给CrewAI的Agent封装的是Agent-Reach的异步调用模式。具体做法是CrewAI的Agent启动时注册进Agent-Reach并声明自己的角色能力当CrewAI内的Agent遇到需要外部协作的任务通过call_async发出消息不等结果立刻返回任务已提交。CrewAI流程继续推进外部Agent完成后再通过回调通知结果把结果喂回对应的session。这条路跑通之后效果很好CrewAI的内部流程没有被跨框架通信阻塞住整个协作节奏更接近真实的团队工作方式。4.3 自研Agent接入最大的阻力是对话轮次的传递自研Agent接入时遇到一个有意思的问题那套规则加LLM混合Agent里每次对话都要携带上下文轮次。一开始我把整个对话历史塞进消息input里结果消息体积动不动就几十KB路由和存储的压力都上来了。后来我调整了消息契约input里只传必要字段和会话指针真正完整的对话历史存在Agent自己的持久层里。Agent-Reach的消息体里只带一个session_ref字段接收方拿到引用后自己去共享存储里捞上下文。这么一改消息体积降到几KB几乎不影响路由性能业务侧也更清爽。这个经验很重要消息通道不是数据仓库别把该存库的东西塞进消息里。跨Agent的消息应该是任务指令必要的参数引用而不是整包的数据搬运。4.4 前端低代码平台的TypeScript SDK后来低代码平台也要接Agent所以补了TypeScript SDK。浏览器的WebSocket客户端和服务端交互能力发现API、消息发送API都支持。前端脚本里可以这样写import { AgentReachClient } from agent-reach/sdk; const client new AgentReachClient({ registryUrl: wss://reach-server/registry, }); await client.connect(); const availableAgents await client.listOnlineAgentsByCapability(query_order); const res await client.callSync({ capability: query_order, input: { order_id: 20250101001 }, timeoutMs: 30000, });低代码平台做一个拖拽流程编排每个节点绑定一个能力调用几十种业务流程都能拖着拖着就配完不用再为每种流程写专门的集成代码。5. 上线前必须面对的五个坑从超时风暴到消息乱序上面听起来一切顺利但生产环境跑起来之后问题一个接一个。我按踩坑的时间顺序梳理了五个最典型的问题每个都有实际的思考过程和解决路径。5.1 坑一注册表读写锁引发的超时风暴系统上线第一周就出事。某个中午流量高峰突然大量调用方报超时。查日志发现注册表的API响应时间从正常2毫秒飙升到800毫秒再一看注册表的锁等待严重。根因是这样的我当时byCaps反向索引和agents主Map共用一把大锁mu。Agent心跳每15秒一次几十个Agent的心跳本来没压力但有个Agent在频繁更新元数据——它的调用方每次调完就更新一次最近调用统计这个统计写在元数据里。高峰期每秒钟几十次更新跟心跳的写锁、调用的读锁互相排队锁竞争直接拖垮了API。解决方式分两步。第一步把最近调用统计从Agent元数据里拆出去单独放到Redis里跟注册表完全解耦第二步把大锁拆成32个分片锁按Agent ID哈希分片不同分片的读写互不阻塞。改完后单机压测从原先的每秒约2000次注册表操作提升到约1.6万次后续再也没在这个位置出过问题。这个坑给了一个教训别把高频率的统计信息跟低频的元数据放在同一个存储结构里读多写多互相搅和迟早出事。5.2 坑二语义匹配的Embedding模型选型失误能力目录的软匹配最初用的是本地部署的一个通用中文Embedding模型当时贪它体积小、部署简单。上线后发现匹配效果很差售后退款流程和查询订单状态明明在业务上是强相关的模型算出来的相似度才0.35低于我设的0.65阈值导致Agent匹配失败率高调用方经常收到找不到可用Agent的错误。后来做了个对照实验同一批测试样本换成当前主流的商用Embedding接口相似度直接跳到0.7以上效果好了不止一个档次。差距主要在于模型的语料覆盖和训练规模通用小模型对行业术语和业务流程的理解深度远不够。最后我采用了双模型策略离线场景批量任务、异步分析用本地小模型因为对实时性要求不高、又不依赖外部服务在线场景同步Agent调用用小模型先快速粗筛再用大模型精排。粗筛阈值放低到0.5精排阈值0.7这样既保实时性又保准确率。5.3 坑三Agent重启后的状态错乱这个坑很隐蔽。某次订单Agent发布新版本重启后进程起来了SDK自动重新注册状态很快变成online。但老版本进程还没完全退出它还持有一个旧的WebSocket连接路由层手上的Agent地址是新的老连接也没断干净。于是老进程在半死状态下偶尔还能收到新连接建立之前就在途的消息处理完往回发结果时结果发到了已失效的旧连接上调用方就丢了响应。后来我在SDK里加了一个优雅退出流程Agent进程收到SIGTERM信号后先发一个deregistering事件给注册表注册表把该Agent标记为draining路由层不再给它发新任务只等已有任务跑完最后SDK再关闭连接。整个过程强制要求在10秒内完成超时就强杀。加上这个机制后发布期间的丢消息问题基本绝迹。5.4 坑四消息乱序引发的脏数据异步任务场景下调用方给目标Agent连发了多条消息——比如批量更新多个订单状态。到了目标Agent那边处理线程是并发跑的两条消息的处理完成顺序和发送顺序不一致导致先发起的更新反而后落地最终数据库里的状态成了旧的。这个问题在单机单线程的Agent内部不会出现但Agent内部一旦用线程池并发处理就必然出现。解决方式在消息语义上做了两件事一是支持给消息加sequence序号接收方按序处理同一session内的消息二是提供一个同步屏障选项——发送方可以要求只有前一条消息处理完成才允许投递下一条。这两种方式实际上把并发还是顺序的选择权交还给业务方对顺序敏感的消息走同步屏障对顺序不敏感的批量任务继续保持并发提升吞吐。5.5 坑五WebSocket连接的半开问题WebSocket连接假死是分布式系统的老熟人。某一端进程还活着但事件循环卡死了TCP层面看起来连接还在实际上消息已经发不过去。排查这类问题特别费劲因为拨测连通性没问题但业务消息就是石沉大海。我在Agent-Reach里加了心跳Ping/Pong机制路由层每隔30秒给每个Agent连接发PingAgent收到后必须回Pong。如果连续3次Pong没回来路由层就主动断开连接并标记离线。同时Agent侧SDK也加了空闲连接探活——如果Agent觉得自己空闲超过60秒主动发一个轻量探活消息确保连接不只是看起来活着。这套双端探活机制上线后假死连接不用再靠人工重启解决。6. 结合真实负载的调优经验从配置参数到架构演进项目跑到第二个月系统渐渐稳定了。这时候我回头看有些参数和架构选择如果在一开始就能明确能少走不少弯路。6.1 关键参数清单与建议值很多同学拿到手第一句话就是参数该怎么配我总结了一张常用表都是实测下来比较稳的值参数建议值说明心跳间隔15秒太短会放大无效请求太长导致离线感知过慢离线判定45秒3个周期保证在漏判和误判之间取平衡同步调用超时30秒低于这个值慢Agent容易被误杀高于这个值调用方体验差投递重试次数3次一次投递失败大概率是目标Agent抖动3次足够覆盖能力匹配TopN3匹配度排名前3的Agent里挑一个可用性最高的消息体大小上限1MB大于1MB的应该走共享存储而不是塞消息体这些参数不是死的每个业务场景要根据Agent的响应耗时和可用性预期调整。核心原则是超时时间不要低于Agent的P99响应时间否则你会经常杀掉那些只是慢但没坏的Agent。6.2 单机部署到集群部署的平滑过渡Agent-Reach服务端支持从单机到三节点的平滑演进。单机模式下所有模块跑在一个进程里配置一个node_rolestandalone集群模式下节点分为registry-leader和registry-followerRaft协议负责选主和数据同步消息路由层则完全无状态可以水平扩展。路由层是无状态的这点很重要——它不保存任何会话数据所有状态都在注册表里所以水平扩容就是在前面加负载均衡器后面起新节点不需要迁移任何数据。如果未来想进一步扩展可以把消息的可靠存储拆到独立的消息队列里路由层进一步瘦身成纯粹的转发逻辑。但就目前这个项目的负载来看日均消息量几万条峰值几十条/秒单机加一个从节点做故障切换完全够用没有必要为了高级感上重型基础设施。6.3 可观测性trace_id是排障的生命线最后强调一下可观测性。Agent-Reach给Agent-Reach自己加了一整套链路追踪每次跨Agent调用都生成一个trace_id从调用方发出、路由层接收、目标Agent处理、结果回传全链路日志都打上这个trace_id。排查问题时一条trace_id就能把整条链路的时间线拉出来。我强烈建议任何Agent协作系统都要把链路追踪作为第一优先级能力而不是可有可无的锦上添花。Agent协作的排障难度和单体应用完全不同单体应用一行堆栈就能定位Agent协作要跨三四个进程如果没有贯穿全链路的trace_id一个用户说响应慢的问题你可能要花半天时间才能定位到是哪个环节慢。加上trace_id后三分钟就能定位。7. 关于Agent-Reach后续演进的一些个人思考项目到现在已经跑了几个月Agent-Reach的价值已经验证过了三个不同框架的Agent通过它实现了互相调用、动态发现、统一契约团队不再为Agent之间的接口变更和部署位置变更做无休止的联调。我自己在维护过程中总结了几件下一步值得做的事。第一是把动态编排能力加进来。现在系统只解决发现和路由但Agent之间的协作流程还是写死的。如果引入简单的编排描述语言比如一份JSON定义先调用A Agent再根据A的结果决定调B还是C那么业务流程的变化就不需要改代码只改编排配置。这个方向我看好但对正确性的要求会高很多得处理好编排流程和业务状态的一致性问题。第二是多租户隔离。现在所有Agent在同一个注册表里。如果业务线多了不同团队的Agent天然应该隔离——A团队的Agent不能用B团队的能力。方向是引入租户概念注册表按租户分域能力目录和消息路由按租户权限做校验。这块做起来不复杂但涉及权限模型设计得想清楚跨租户协作这种边界情况怎么处理。第三是Agent质量度量。系统跑着跑着注册表里会有大量历史数据——哪些Agent被调用得多、哪些Agent经常超时、哪些能力匹配总是失败。这些数据可以加工成Agent可用性报告、质量评分。如果能做出来对上层做Agent调度决策会是很好的数据支撑。最后说说对Agent生态的一点个人体会。Agent-Reach这类基础设施的价值不在于让单个Agent变聪明而在于让多个Agent能够像同一个团队一样协作——各自有专长、知道队友能干什么、消息能可靠送达。单Agent的能力天花板终究有限真正的质变发生在协作层。这个项目让我比较欣慰的地方是它没有跟任何具体Agent框架绑定是一个独立的中间层。等以后Agent框架之间的边界越来越模糊类似Agent-Reach这样的通信底座可能会成为Agent架构里不可或缺的一块。如果你手上也有几套割裂的Agent在跑试试把发现、路由、契约这三件事抽出来做成一个独立服务你会明显感受到集成成本和变更成本同时降下来。这就是Agent-Reach最核心的一句话总结。
返回列表