
我们内部把 Agent-Reach 这套东西做出来之前多 Agent 协作链路一直处于“能跑但非常脆”的状态。单个 Agent 的能力再强只要一涉及两个 Agent 互相配合问题就接踵而来A 调 B 超时了C 也在调 BB 还没来得及告诉 A 自己已经降级A 又发起一轮重试最后整条流水线被重试消息淹没。Agent-Reach 就是冲着这个场景来的——它把 Agent 之间“硬编码调用”改成“意图触达”由基础设施统一负责路由、投递、确认、重试与降级。如果你正在做多 Agent 编排、流程自动化或者只是想把两个 LLM 驱动的子任务安全地串起来这篇文章值得你读完。我会把 Agent-Reach 的设计思路、协议细节、路由策略、可靠性机制以及我们踩过的真实坑一次讲清楚。1. 从单 Agent 自嗨到多 Agent 协作Agent-Reach 到底在解决什么问题1.1 多 Agent 协作的第一个塌方点通信单个 Agent 好做因为它只需要跟用户和工具打交道一旦进入多 Agent 场景最先崩掉的一定是通信。大多数团队的第一版都是硬编码调度编排器里写死“先调 analyse_agent再调 execute_agent”每个 Agent 暴露一个 HTTP 接口参数格式靠文档约定。这套方案在 Agent 数量少于五个、调用频率不高的时候是能用的但一旦拓扑变大它有三个明显的结构性毛病。第一拓扑是静态的。你想新增一个能力更强的 Agent 替换旧的得改动调用方代码重新发布更麻烦的是一旦某个 Agent 所在服务迁移了地址所有引用它的地方都得跟着改。第二重试策略是各管各的。A 超时重试三次B 也超时重试三次C 一看失败了又补一刀这种“重试叠加”在低流量下没事流量一上来就是雪崩式的自我加压。第三可用性完全靠运气。没有人知道某个 Agent 此刻是忙是闲、是活着还是半死也没有一个统一机制来摘除异常节点。我当时做技术方案评审的时候把这几个问题摆到桌面上负责业务的同事说得很直白我不关心你用 HTTP 还是消息队列我关心的是——我发出一个任务能不能有一个可靠的东西帮我找到能处理的 Agent并且保证任务不被弄丢、不被重复执行。这个诉求其实就是 Agent-Reach 的雏形。1.2 Agent-Reach 是什么从“调用接口”到“触达能力”Agent-Reach 的名字本身就点题了。Reach 是触达、可达的意思。我们想传递的核心语义不是“A 可以去调 B 的接口”而是“A 有一个任务意图需要被某个具备相应能力的 Agent 触达并处理”。所以 Agent-Reach 定位为一个多 Agent 场景下的触达编排层它维护所有在线 Agent 的能力注册信息接收上游 Agent 发出的“意图消息”通过路由决策找到最合适的接收方再把消息可靠地投递过去最后跟踪这个消息从发出到 ACK 的全过程。它不参与具体业务逻辑不替 Agent 做决策只负责把“触达”这件事做扎实。具体来说Agent-Reach 提供四类能力能力注册Agent 启动时上报自己的能力清单包括能处理的意图、输入输出格式、限流信息、可用时段。语义路由根据消息中携带的意图匹配到最合适的 Agent而不是靠写死的目标地址。可靠投递消息一旦被接受就进入投递状态机直到 Agent 显式确认处理成功。全链路观测每一条触达消息都有一个全局唯一 ID从发出到 ACK 的每一步都能追踪和统计。Agent-Reach 适合谁适合那些 Agent 数量不少、但编排方式还停留在“点对点硬调”的团队。它不要求你推翻现有 Agent 的实现只要求你改变一种思考方式别再问“我要调用谁”改成问“我要达成什么意图”。1.3 哪些问题不是 Agent-Reach 该管的做架构设计最容易犯的错就是想把所有问题都揽进来。Agent-Reach 从一开始就划清了边界。它不管 Agent 内部怎么做规划、怎么调用工具、怎么生成回复那是 Agent 框架自己的事它也不管两个 Agent 之间需要传多复杂的对象——传 JSON 序列化之后的结构化参数就行它更不会去替代你的人工审批流、权限系统。Agent-Reach 只管触达链路路由对不对、投递成不成、确认有没有、重试会不会叠加、慢 Agent 会不会拖垮全局。这个边界非常关键。因为一旦触达层开始掺和业务逻辑它的通用性就会下降维护成本会急剧上升。把触达做成通用基础设施把业务留在 Agent 内部是我在不只一个项目里验证过的分工方式。2. “触达”而不是“调用”Agent-Reach 的核心抽象与协议设计2.1 “调用”这个词在 Agent 场景里的局限传统微服务里“调用”是清晰的你知道要调谁知道它的接口签名依赖关系在代码里是显式的。但 LLM 驱动的 Agent 有一个天然特性——它的输出不是结构化指令而是“意图 参数”的混合体。你让 Agent 生成一段订单处理结果它可能说“我建议把拆分订单交给仓配 Agent 处理”也可能直接输出一个 JSON。指望它每次都能精确拼出目标服务的 URL 和参数既不现实也不健壮。这种情况下“调用”语义太硬了。硬的目标地址、硬的参数模式、硬的失败处理会让 Agent 的灵活性大打折扣。Agent-Reach 改成“触达”语义你只需要把意图和结构化参数丢进来剩下的事——找谁、怎么送到、没送到怎么办——全部由基础设施兜住。这个抽象对 LLM 场景尤其有意义。LLM 输出的内容天然有歧义同一个意图可能有几十种表达方式。如果采用严格的接口匹配这些表达方式都得在代码里穷举如果走意图匹配只要 Agent 的能力描述写得足够好语义相似度就能把表达差异抹平。2.2 一束触达消息的完整结构Agent-Reach 里的消息我们叫 ReachMessage。设计字段的时候我们参考了消息队列和 RPC 两边的经验最终定下这样一组核心字段{ reach_id: reach_a1b2c3d4e5, message_id: msg_20250101_000123, intent: generate_warehouse_order, target_agent_id: optional_agent_id, payload: { order_id: SO-20250101-001, sku_items: [SKU-001, SKU-002], ship_address: CN-SH-01 }, priority: 2, ttl_ms: 30000, retry_policy: { max_attempts: 5, base_delay_ms: 200, jitter_ms: 50 } }这里每个字段都有它的用途。reach_id是触达链路的全局追踪 ID贯穿消息的每次路由、每次重试、每次日志记录message_id是消息本身的唯一标识用来做幂等控制防止重复消费intent是投递的核心Agent-Reach 会根据它做语义路由target_agent_id是可选字段——如果发消息的一方明确知道要发给谁可以显式指定相当于“定向触达”如果不指定就走全量语义路由。payload是结构化业务参数所有内容必须能 JSON 序列化。这样设计是为了让协议本身保持中性不绑定具体编程语言也不绑定 Agent 框架。priority支持 0-2 三个级别0 是最高优先级通常给核心交易链路2 是最低给后台批处理类任务。ttl_ms是整条消息的存活时间超过就直接判失败进死信不让消息在队列里无限滞留。关于retry_policy我想多说一句。很多团队把重试策略放在调用方代码里导致同一个 Agent 被五个调用方用五种不同的重试节奏轰炸。Agent-Reach 把重试策略收敛到消息里由基础设施统一执行这样至少能做到一条消息最多重试 N 次且每次重试的间隔由全局调度器来控节奏。这个改动看似简单但对抑制重试风暴非常有效后面事故部分会详细展开。2.3 能力注册Agent 怎么告诉 Agent-Reach “我能干什么”路由的前提是目录。Agent-Reach 规定每个 Agent 必须在启动时向注册中心上报一份能力注册清单也就是 manifest。{ agent_id: agent-warehouse, agent_version: 2.4.0, capabilities: [ { intent: generate_warehouse_order, description: 根据订单上下文生成仓配履约单支持拆单与合单, input_schema: schema://warehouse/order_input_v3.json, output_schema: schema://warehouse/order_output_v3.json, rate_limit: 50, cost_per_call: 0.03, available_hours: [00:00-23:59], region_allowed: [cn-*] } ], contact: agent-warehouseinternal, health_check: agent-warehouse/healthz }最关键的是intent和description。intent是计算机便于索引的短标识比如generate_warehouse_orderdescription是人类可读的自然语言描述用于语义匹配时计算向量相似度。我强烈建议在description里写清楚“这个 Agent 擅长什么、不擅长什么”因为语义路由的效果很大程度上取决于这里的描述质量。rate limit 和 cost per call 是给路由打分用的动态约束。比如两个 Agent 都能处理同一意图一个便宜但慢一个贵但快路由层可以根据预算和时间要求来做取舍。available_hours和region_allowed是静态过滤条件避免把消息路由给一个当前不该工作的 Agent。这里有一条实操教训manifest 的 schema 要尽早稳定因为它会被所有 Agent 共享。我们第一版把input_schema定义成内嵌 JSON后来加字段导致所有 Agent 都要升级第二版改成外部 schema 引用Agent 只存 URL需要校验的时候再去拉取这样兼容性就好多了。3. 路由层设计Agent-Reach 怎么知道消息该发给谁3.1 路由的第一层静态约束过滤路由的核心问题只有一个一条意图消息该发给哪个 Agent。Agent-Reach 把路由拆成两层先过滤再打分。过滤阶段处理各种硬约束设计目标是宁可让消息路由不出去也不能把消息发给一个不该接收它的 Agent。静态约束包括四类可用时段、地域限制、权限标签、目标定向。可用时段直接从 manifest 的available_hours读取比如某个 Agent 只在白天运行夜间消息就不能投给它地域限制处理多机房场景region_allowed不匹配的直接排除权限标签是为了防越权消息里如果没有携带某个 Agent 要求的敏感标签就不允许进它的队列定向目标则是最简单的情况——target_agent_id非空时直接跳过语义匹配只检查目标 Agent 是否存活。过滤这层花不了多少计算量主要是把明显不合法的选项去掉给后面的打分阶段减负。很多团队一开始跳过了这一步结果语义匹配把不该路由的消息路由过去被 Agent 拒绝后重试白白消耗资源。我建议这层一定要放在最前面它是稳定性的一道廉价保险。3.2 路由的第二层语义召回与动态打分过滤完之后剩下的候选 Agent 进入打分阶段。Agent-Reach 的打分模型是“语义相似度 动态运行状态”的加权组合。我们用一个简化的打分函数来说明def score_capability(cap: Capability, msg: Message, state: AgentState) - float: # 1. 语义相似度意图描述向量的余弦相似度 semantic cosine_similarity(msg.intent_embedding, cap.description_embedding) if semantic 0.55: return 0.0 # 2. 动态状态队列越浅、成功率越高、延迟越低分数越高 load_score 1.0 - min(state.queue_depth / cap.max_queue_depth, 1.0) * 0.5 health_score state.success_rate_1m # 0 ~ 1 latency_score clamp(1.5 - state.p50_latency_s / msg.ttl_ms * 1000, 0.0, 1.0) # 3. 加权求和语义权重最大 return semantic * 0.5 load_score * 0.15 health_score * 0.2 latency_score * 0.15先说语义相似度。Agent-Reach 会在注册时把每条description预计算成 embedding 向量消息进来时算一次意图 embedding然后跟候选集的向量做余弦相似度。这个做法不需要每次实时调用大模型接口性能足够扛住高并发。相似度阈值我们一开始设 0.7结果发现漏掉了很多表达不太标准但意图明确的请求调低到 0.55 之后漏投少了但错投略微上升。具体阈值没有标准答案跟你们的 embedding 模型和意图描述风格有关建议用历史数据做一次小规模回放观察不同阈值下的准确率和漏投率再定。再看动态状态。两个 Agent 都能处理同一意图时队列深度浅的、近一分钟成功率高的、P50 延迟低的当然应该获得更高分数。这三个状态指标每 10 秒刷新一次写入路由层的本地缓存避免每次路由都去查实时数据否则路由本身会成为瓶颈。最后一个是静态偏好参数。如果某条消息的调用方明确只接受某个 Agent 处理可以在消息里加一个preferred_agents列表路由打分时给这些 Agent 加一个固定加成。这个机制在灰度上线新 Agent 时特别好用——先把少量流量导向新 Agent等确认稳定后再逐步放开。3.3 漏投和错投哪个更不能接受路由设计的核心权衡是漏投与错投。漏投是一个意图没有任何 Agent 匹配上消息进死信错投是把消息投给了一个能力不匹配的 Agent导致对方处理失败或者产出垃圾结果。我们内部的观点很明确漏投比错投更不能接受尤其在 LLM 场景下。原因是漏投会导致上游超时而上游 Agent 通常会用自然语言生成一段“处理失败”的分析结果这个结果有可能被继续当作输入传给下游产生连锁错误。错投则不同——正确的 Agent 能力描述加上幂等机制至少能让接收方快速识别“这个活不是我的”返回一个明确拒绝触发重试去匹配其他 Agent。所以 Agent-Reach 的策略是语义阈值宁低勿高靠动态打分做二次筛选并用规则兜底。也就是说即使语义相似度不高只要静态过滤通过了我们也会保留 1-2 个候选进入最终打分避免因为 embedding 模型的一次误差导致意图彻底无处可达。这是我们在实际调参中摸索出来的经验跟教科书里的“高精度匹配”思路正好相反。4. 可靠性不是附加项ACK 机制、退避重试与幂等消费4.1 投递状态机每条消息都有迹可循可靠投递的关键不是把消息发出去而是知道消息“现在在哪个阶段”。Agent-Reach 为每条消息维护一个状态机received - routing - dispatched - accepted - acked - done任何一步失败消息进入对应分支路由失败进route_failed投递超时进retry_pendingAgent 明确拒绝进rejected超过 TTL 或者重试次数上限进dead_letter。每个状态转换都会写入 trace 日志并更新消息的可观测指标。这里有一个容易被忽视的细节accepted和acked是两种完全不同的状态。accepted只代表 Agent 收到了消息不代表处理成功。LLM Agent 经常出现“收到请求后生成了一段无意义的回复”的情况所以 Agent-Reach 规定只有接收方 Agent 在处理完成后显式返回一个ack信号消息才会置为acked。如果只是accepted就置为成功等于把 Agent 的“哑火”也当成正常结果这是不可接受的。Agent 端 SDK 会把这个机制封装成一句话处理成功就调用reach.ack(message_id, result)处理失败就调用reach.nack(message_id, reason)。尽量不要让 Agent 业务代码直接操作状态机否则状态一定会被改乱。4.2 ACK、超时与自适应重试超时设计直接决定用户体验也直接决定重试会不会变成雪崩。Agent-Reach 把超时拆成三档连接超时、处理超时、总存活时间。连接超时指投递到 Agent 接口之前建立连接的超时一般设 1-2 秒处理超时指 Agent 从接收消息到返回 ACK 的期限这个值应该根据 Agent 的历史 P95 耗时来定而不是拍脑袋总存活时间就是消息里的ttl_ms超过即判失败不管重试了多少次。重试策略我们用的是“指数退避 抖动”的变体。标准公式是delay min(base_delay * (2 ** retry_count) random(0, jitter), max_delay)base_delay 默认 200msmax_delay 上限 5 秒重试次数最多 5 次。加上抖动是为了避免多条失败消息在同一个时间点集中重试造成波峰。真正让系统稳定下来的是“自适应退避”。固定退避的问题在于如果接收 Agent 本身已经很慢了固定间隔的重试只会继续压榨它的资源。Agent-Reach 在调度重试时会参考目标 Agent 最近一分钟的 P50 响应时间动态拉长退避间隔。比如目标 Agent 的 P50 是 2 秒那么第一次重试至少等在 2 秒之后P50 是 12 秒那就等 12 秒再重试。这样重试的节奏和目标 Agent 的恢复节奏是匹配的不会出现目标 Agent 还没缓过来、重试又压上去的恶性循环。4.3 幂等消费重复投递是常态不是异常再好的基础设施也无法保证消息只投递一次最多只能保证“至少投递一次”。所以幂等消费是 Agent 端必须承担的责任。Agent-Reach 的做法是强制每个消息带上message_id并且约定同一message_id不允许被同一 Agent 重复处理两次。Agent 端要在自己的存储里维护一张去重表处理消息前先查一下message_id是否出现过。如果出现过直接返回上一次的结果快照或者跳过处理如果没出现过写入一条“处理中”的记录处理完成后再更新状态。我这里推荐一个更省事的方案如果你的 Agent 本身有外部存储直接用处理结果的业务主键做幂等键不要单独维护一张 message_id 表。比如订单处理场景如果某个order_id已经生成了履约单重复收到同样订单直接返回已有履约单即可。业务幂等永远比基础设施幂等更可靠因为你真正要防的是“同一业务被执行两次”而不是“同一消息被消费两次”。幂等键还有一个坑消息重试时会换一个新的message_id吗不会。重试必须沿用原始message_id否则幂等就失效了。这个约束要写进协议规范里测试用例里也得覆盖。4.4 死信队列与回放机制所有重试耗尽仍失败的消息统一进入死信队列。Agent-Reach 的死信队列不只是一个垃圾桶每条死信都保留了完整的路由历史和失败原因方便人工排查。我们会在死信上打一个标签比如no_matched_agent、agent_rejected、processing_timeout、ttl_expired这样值班同学可以按标签批量处理而不是一条条打开看。回放机制同样重要。修正问题之后运维可以把死信队列里的一部分消息重新导入正常队列并附带一个replayed_from字段。回放消息必须走独立优先级队列防止历史积压把新消息的队列挤爆。这个细节我们第一版没做结果一次回放把正常链路延迟拉高了 3 倍后来才补上。5. 一次线上事故逼出来的取舍重试风暴与优先级饥饿5.1 事故场景还原Agent-Reach 上线第二个月我们经历了一次印象极深的事故。起因是某个负责价格计算的 Agent 上游数据源突然变慢单次处理耗时从 P50 2 秒直接涨到 P50 12 秒。单个 Agent 慢本身并不致命致命的是它把所有相关链路都拖下水了。当时有超过十个上游 Agent 会调用价格 Agent。每个上游都有自己的超时重试逻辑有的超时 5 秒重试 2 次有的超时 8 秒重试 3 次。价格 Agent 变慢之后大量请求超时触发上游重试重试又带来新的请求价格 Agent 的队列越积越深响应更慢进而引发更大规模的重试。我盯着监控面板看到队列深度从几百涨到几万最诡异的是业务调用量并没有明显增长消息总量却翻了几倍。聚合之后发现队列里超过一半的消息source字段都是retry_scheduler也就是说系统正在自己给自己制造流量。5.2 排查链路先看指标再定位重试叠加这次排查我用了一个固定的思路从入口流量到链路状态逐步收敛。第一步是看入口流量确认业务方 QPS 没有突增排除单纯流量高峰的可能。第二步按消息的source字段聚合发现重试消息占比异常高初步判断是重试风暴。第三步看每个 Agent 的响应耗时分位和队列深度把目光锁到价格 Agent 上发现它的 P99 已经到了 15 秒而它的下游数据源根本没有超时保护。第四步看消息在不同 Agent 间的流转路径确认是多个上游的重试策略叠加导致价格 Agent 被压垮。整个定位过程大约用了 40 分钟。最关键的一步是第一张图——如果一开始纠结于“哪个 Agent 的代码有 bug”很容易走偏。先看指标再沿着消息流找重试源是排查这类问题最高效的路径。5.3 修复方案隔离、优先级与熔断三件事缺一不可修复不是改一行代码的事而是把稳定性机制补全面。我们落地了三项改动到现在依然觉得缺一不可。第一项是重试隔离。Agent-Reach 在路由层限制同一目标 Agent 的待重试消息数不能超过它队列容量的 30%超出部分直接转为快速失败返回一个明确的agent_busy信号由上游 Agent 自己决定是否降级。这样即使某个 Agent 再慢重试流量也没办法把它彻底淹没。第二项是优先级队列。价格 Agent 和电商履约强相关属于核心链路priority 设为 0后台批量计算的 Agent 设为 2。Agent-Reach 保证高优先级消息的调度权重是低优先级的五倍以上且低优先级消息最多只能占用目标队列 20% 的容量。这样一来就算后台任务积压再多也不会挤掉核心交易。第三项是熔断。Agent-Reach 每 10 秒计算一次每个 Agent 的成功率如果成功率跌到 30% 以下自动熔断 15 秒期间所有指向它的消息直接返回agent_unavailable不进入队列也不触发重试。熔断期结束后放少量探测消息成功率达到阈值再逐步放开流量。这三项结合的效果非常明显同样的故障再次发生时队列深度被压在原来的十分之一以内核心交易链路不再受到连锁影响。5.4 复盘得出的防御性设计原则这次事故让我想明白了一个原则Agent 基础设施必须假设任何一个 Agent 都会随时变慢、变烂、彻底不可用。这不是悲观而是防御性设计的出发点。基于这个假设Agent-Reach 的可靠性设计有一套明确的底线不允许重试流量超过真实流量这是硬约束必须在路由层强制执行任何 Agent 的慢或烂都不能阻塞其他意图的消息投递队列隔离是底线失败时要给上游一个清晰的信号agent_busy、agent_unavailable、agent_rejected让上游能做合理的降级决策而不是傻等超时所有保护机制要在故障发生时自动生效不能依赖人盯着面板再动手。这套原则后来成为 Agent-Reach 每次设计评审的检查项。凡是动了队列、路由、重试相关代码都要过一遍这四条防止有人偷偷绕过保护机制。6. 落地与演进Agent-Reach 从单节点到联邦路由的实践路径6.1 部署形态与规模匹配Agent-Reach 的部署形态不复杂但也不是一个无状态服务那么简单。它内部需要维护 Agent 目录、消息状态、路由统计信息因此是有状态的。好在这套状态的结构很清晰核心数据只有三类注册目录、消息元数据、路由指标缓存。我的建议是分阶段部署。第一阶段单实例 Agent-Reach 加一个 Redis Streams 作为消息队列注册目录放 etcd。这个组合在日触达量百万级以内完全够用运维成本也很低。第二阶段如果 Agent 数量超过 30 个、消息量继续涨再把 Agent-Reach 拆成路由节点和调度节点路由节点无状态、可以水平扩容调度节点持有消息状态按reach_id哈希分片。第三阶段多个机房独立部署 Agent-Reach 实例实例之间只同步能力摘要不同步全量目录各自路由本机房的消息这就是联邦形态。千万不要第一步就上 Kafka、上多集群。Agent-Reach 的价值不在队列本身而在路由和触达语义用一个你熟悉的队列把它支撑起来就够了。我们线上到现在还用着 Redis Streams只是加了持久化和主从备份没有引入额外的消息中间件。6.2 最小落地步骤把第一个 Agent 接进来从零接入 Agent-Reach我建议按照下面五步走半天之内就能跑通第一条触达链路部署 Agent-Reach 服务和注册中心。先不接任何 Agent验证服务健康检查和注册接口可用。用 SDK 注册第一个 Agent把它能处理的两个意图写进 manifest确认注册中心能看到能力列表。把一条原本写死的调用改成意图消息不指定target_agent_id靠intent走语义路由。这一步是验证路由效果的关键。观察一周触达成功率、路由匹配率、重试次数和 ACK 时延和原来的硬编码调用做对比。逐步迁移其他调用的同时补上观察监控和熔断参数。这里有一个很重要的建议第一个接的 Agent 不要选核心链路的选一个边缘的、允许出错的 Agent比如内部报表生成、日志分析、离线标注这类任务。等团队摸熟了 Agent-Reach 的脾气再把核心链路迁过来。6.3 观测指标触达链路该看什么日志看板我们做了很多但真正每天盯着的指标就五个路由匹配率正常应该在 99% 以上低于这个值说明意图描述和 manifest 描述有缺口。触达成功率acked消息数除以进入队列的消息数这是触达链路健康度的核心指标。重试贡献率重试消息占总消息的比例正常应该低于 15%超过这个值就要警惕雪崩趋势。队列积压深度按目标 Agent 聚合任何单个 Agent 的积压超过容量 50% 就要告警。ACK 时延分位P50、P95、P99用来决定处理超时阈值和自适应退避的参数。我特别想说一下重试贡献率。它比成功率更能反映系统稳定性。很多团队只盯成功率但成功率在大量重试的掩盖下可能依然好看实际上系统已经在空转。重试贡献率一旦异常升高说明某种自我保护机制可能失效了。6.4 往后的演进方向Agent-Reach 目前覆盖了触达链路的核心能力但还有几个方向是我们明确要做的。一个是自适应路由的自动化。现阶段打分权重还是静态配置下一步准备根据历史路由结果做在线学习——比如记录每次投递的成功与否、处理耗时、返回质量用这些反馈定期调整语义权重和动态参数。这样做的前提是先积累足够多的带标签的路由样本急于一步到位反而容易把路由调乱。另一个是跨域联邦路由。多机房场景下消息在本机房找不到可处理 Agent 时现在只能走死信。联邦路由的思路是每个 Agent-Reach 节点向其他节点广播自己的“能力摘要”路由时先查本地再查远端摘要必要时跨机房投递。摘要的更新频率和一致性模型还需要仔细设计但方向很明确。最后一个是意图 schema 校验。现在 payload 是否匹配 Agent 的输入 schema全靠 Agent 自己判断等于浪费了一次触达机会。Agent-Reach 计划在路由阶段做一次轻量校验对可疑 payload 直接返回格式错误引导上游 Agent 修正后再投递。这个功能对 LLM 场景尤其有价值因为大模型的输出格式问题实在是太常见了。以我个人实际落地的体会来说Agent-Reach 最难做的不是路由算法也不是 ACK 机制而是让团队接受一个心智模型的转变我的 Agent 不再是“被某个上游点名调用”的被动节点而是“通过能力描述被触达”的主动服务节点。这个转变一旦完成后续的稳定性建设、故障隔离、灰度替换都变得顺理成章。先把一个边缘 Agent 接进来跑通用真实数据证明这套触达逻辑比硬编码更稳再逐步铺开是让我放心的落地节奏。