接手智能体平台这半年,我渐渐明白一个道理:模型再聪明,工具调不通,一切都白搭。团队内部拆了很多业务场景,最后出现频率最高的故障不是模型答错,而是“连不上”“配置漂移”和“环境对不上号”。后来我们沉淀出一个内部组件 Agent-Reach,专门解决智能体与外部服务之间的连接、注册和可达性治理问题。这个组件不负责调度,不写业务逻辑,只干一件事:让每个 Agent 都能准确、稳定、可控地触达到它需要的资源。
如果你也在做类似的事情,比如给公司内部系统接入 AI 助手、让 Agent 调用一堆乱七八糟的内部 API、或者想搞清楚为什么同一个 Agent 换个环境就失灵,那这篇文章值得看完。我会从设计思路、核心模块、实操流程到踩坑记录,把 Agent-Reach 的来龙去脉完整拆一遍,你照着做也能拼出一个适合自己的版本。
1. 从“编排优先”到“触达优先”:Agent-Reach 到底想解决什么
先说一个背景。我们最开始构建 Agent 项目时,几乎所有讨论都集中在“决策如何编排”上,比如任务拆解、工具选择、上下文的记忆窗口。大家默认底层的连接是可靠的,毕竟服务发现、网关这些东西在微服务场景里已经玩得很熟了。真实情况完全不是这样。
Agent 和微服务的“连接”有一个本质区别:微服务的消费者是确定性的代码,它知道自己在找哪一个具体实例;Agent 则面对的是一个动态意图。举个例子,用户说“帮我查一下上个月所有订单的退款状态”,Agent 要自己决定去哪几个系统查数据,再组合结果。这个过程里,它要触达的可能是订单服务、支付服务、内部BI系统、甚至是某个老旧的报表SQL接口。每一个目标都可能有独立的环境、鉴权方式、限流策略和数据格式。
传统注册中心在这个场景下帮不上大忙,因为注册中心只解决“服务在哪里”,不解决“这个能力适不适合当前意图”。于是我们干脆把这一层单独拎出来,做了 Agent-Reach。它本质上是一个面向智能体场景的触达治理层,核心目标有三个:让 Agent 知道有什么可用,让 Agent 能选到对的入口,让 Agent 调用的过程可观测、可管控。
1.1 传统服务发现解决不了的三个盲区
仔细复盘之后,我们发现传统方案至少有三个盲区是 Agent 场景特有的。
第一个盲区是能力语义的缺失。服务发现里注册的是服务名、IP、端口,而 Agent 需要知道的是“这个服务能不能干我要的事”。比如同样一个 HTTP 接口,它可能是查询实时库存,也可能只是返回静态配置。单独看服务定义你根本分辨不了。Agent-Reach 在设计时把注册对象从“服务”提升到了“能力”,每个能力都带有一段语义描述,甚至可以直接映射到函数级别的工具定义。
第二个盲区是环境的动态性。开发联调时 Agent 连的是测试环境,上线后连的是生产环境,但你不可能把环境切换逻辑硬编码在 Agent 的 prompt 里。传统服务的环境隔离靠 DNS 和配置中心,但 Agent 的工具调用路径往往更曲折,很可能要经过三层转发、两套鉴权。Agent-Reach 把环境上下文作为一级概念管理,请求进来时通过路由规则自动匹配当前运行环境,而不是让 Agent 自己去猜。
第三个盲区是健康状态的含义不同。微服务健康检查只需要知道进程活着、端口通着,而 Agent 调用的“健康”必须是端到端的:不仅仅是连接成功,还要凭证有效、权限足够、返回格式符合预期。这个区别直接决定了探活逻辑的设计,后面我会专门展开讲。
1.2 为什么不是又一个 Agent 编排框架
这里有一个关键的设计取舍。市面上有不少 Agent 编排框架,都内置了工具注册和调用能力,那你为什么还要单独做一层?我的看法是:编排框架更关注“怎么把任务串起来”,而可达层更关注“底层通道怎么管好”。两者职责不同,耦合在一起初期省事,后期非常痛苦。
比如你有十个业务 Agent,分别负责订单、客服、财务、库存,它们可能两两之间需要触达同一个底层系统。如果用编排框架自带的能力注册,每个 Agent 都得各自维护一份目标地址和凭证,换个系统密码就要改十个地方。Agent-Reach 的出现就是把这些公共责任收拢到一层,提供统一的注册、路由、探活、审计能力。Agent 只负责说“我要什么能力”,至于怎么连、连哪个环境、凭证从哪里取,通通交给 Reach 层。
在实际落地中,这个解耦给团队带来的最大改变是:业务 Agent 的配置体积大幅缩减,而基础设施团队终于有了一个统一的地方去做权限控制和连接治理。后续接入新系统时,也不再需要动 Agent 的业务代码,只注册能力就行。
2. 核心模块拆解:注册、路由、探活
如果把 Agent-Reach 看成一个连接“Agent 意图”和“业务能力”的中间层,那它主要由三个核心模块构成:能力注册中心、意图路由器和端到端探活器。每个模块都不复杂,但组合起来能解决很多实际问题。这一节我把每个模块的设计思路和关键细节讲清楚。
2.1 能力注册中心:不只是登记一个地址
很多团队做 Agent 工具时,工具体现形式就是一个函数描述加一个 URL,用 OpenAI function calling 之类的机制直接塞进模型上下文。Agent-Reach 的能力注册中心做的比这个更重,但重得值。它把每个能力抽象成一张结构化的元数据卡片。
capability: order_refund_query display_name: 订单退款状态查询 description: 根据订单号查询退款进度,返回当前状态和预计到账时间。 entrypoints: - id: http_v1 protocol: http url: ${ORDER_SERVICE_URL}/api/v1/refund/query method: POST transport: json auth: type: oauth2 scope: ["order:read"] env: alias: prod available: [dev, staging, prod] semantics: category: readonly latency_sla_ms: 800 timeout_ms: 3000 idempotent: true这张卡片里的字段每一条都有用。description是给模型看的,做意图匹配时用来判断“这个能力是否对应当前用户需求”;entrypoints是给路由器看的,定义了实际调用方式;auth是给凭证托管模块看的,Agent 请求进来时由 Reach 统一附加鉴权信息;semantics是给调度和容错逻辑看的,比如idempotent字段决定了失败后能不能安全重试。
注册方式有两种。一种是静态注册,由系统管理员编写 YAML 文件后加载进中心;另一种是动态注册,服务提供方通过 SDK 在启动时主动上报能力元数据。动态注册适合那些自研的内部系统,静态注册适合老旧的第三方系统,两边都支持会灵活很多。
注册中心内部还维护了一个“版本”的概念。每个能力变更后都会生成新版本,Agent 可以根据自己的兼容性偏好来选择版本。比如新接入的 Agent 强制使用 v2,存量 Agent 继续走 v1,直到验证稳定再整体切换。这一步看似多余,实际上在真实环境里能救命,因为工具接口变更是 Agent 场景里最隐蔽的破坏性变更之一。
2.2 意图路由器:让语义目标找到正确的入口
拿到用户的自然语言请求后,Agent 的规划模块会产出“需要调用哪些能力”的结论,但这个结论往往是语义级的,而不是协议级的。比如规划模块说“我需要查询退款状态”,但具体该访问 dev 还是 prod、走 HTTP 还是走内部消息队列,规划模块不应该关心。这就是意图路由器该登场的位置。
路由器的工作流程大体上有四步:解析能力 ID、匹配意图描述、应用路由策略、生成调用上下文。前两步是找“选哪个能力”,后两步是决定“怎么发起调用”。匹配意图描述时,我们做了一个轻量级的语义评分,不依赖大模型,而是用 embedding 相似度加关键词权重。为什么不直接用大模型来做这个判断?因为路由是高频操作,每次调用都让大模型思考一次既慢又费钱,规则加向量匹配的方式稳定在毫秒级,完全够用。
路由策略里比较重要的是环境选择。我们有一套基于上下文的规则:如果当前请求来自沙箱对话,则路由到 staging 环境;如果请求带着生产工单标识,则路由到 prod 环境;如果什么都没带,按默认链路处理。这条规则单独讲起来很简单,但它帮我们消灭了一整类“Agent 在测试环境调了半天,最后发现线上数据根本没变”的经典事故。
生成调用上下文时,路由器会把能力卡片里的 URL 模板、鉴权 token、超时配置都组装好,再传给执行器。执行器只做一件事:发起请求,等待响应,返回结果。在这个环节里,Agent 的业务代码拿到的已经是可以直接执行的“就绪上下文”,不需要再关心任何连接细节。
2.3 端到端探活器:Agent 版的“拨测”
微服务架构里也有健康检查,那套做法通常是周期性地请求一个/healthz端点,看返回 200 就认为服务正常。Agent-Reach 的探活器把这个逻辑做得更贴近 Agent 的使用方式,我们内部叫它“能力拨测”。
为什么不能只检查端口连通性?因为 Agent 真正在意的是“我用这个能力能不能得到正确结果”。端口是通的,但可能数据库密码已经过期,可能上游依赖拿不到,可能返回结构变了。这些问题只有在完整走一遍调用链时才能暴露。所以探活器会按一定周期,对每个注册能力和入口组合执行一次真实的测试调用,用预埋的哑数据或者只读操作验证端到端链路。
探活结果会回写到注册中心,把能力标记为 healthy、degraded 或 unreachable。路由器在决策时会优先跳过 unreachable 的能力,避免 Agent 拿到一个“看起来存在但实际用不了”的工具。degraded 状态则会触发告警,但不一定立即下线,因为有些接口虽然偶发超时,但整体可用,直接下线会让 Agent 丢掉一个重要能力。
探活间隔需要按能力类型差异化设置。只读查询类能力可以 30 秒拨测一次,涉及真金白银的写入类能力最好只在维护窗口内做测试调用,否则拨测本身就可能产生脏数据。这里有一个教训:我们早期对写入接口也做了高频真实拨测,结果在测试库生成了一堆垃圾订单,后来全部改成“校验参数+Mock 响应”的方式才消停。
3. 把 Agent-Reach 接入现有 Agent:一份可抄的实操记录
讲完设计,接下来是接地气的内容:实际操作中 Agent-Reach 怎么部署、怎么配置、怎么从一个普通的 Agent 项目里把调用链路替换成 Reach 管理的版本。我以我们实际用下来最顺手的路径为例,环境用的是 Python 3.11 + FastAPI,Reach 本身用 Go 写的,但对外暴露的都是标准 REST 接口,所以语言不重要,你换成 Node、Java 也完全跑得通。
3.1 部署与依赖准备
Agent-Reach 由两部分组成:一个中心服务,提供注册、路由、探活和配置查询能力;一个轻量 SDK,嵌入到 Agent 进程中,负责和中心服务通信。中心服务依赖一个 Postgres 数据库来存能力元数据和探活历史,另外需要 Redis 做路由器的高频缓存,其他就没什么外部依赖了,整体部署成本控制在一天以内。
# 拉取构建产物并启动中心服务 docker run -d --name agent-reach-core \ -p 8080:8080 \ -e DATABASE_URL=postgres://reach:reach_pwd@10.0.0.5:5432/agent_reach \ -e REDIS_URL=redis://10.0.0.6:6379/0 \ -e MANAGEMENT_TOKEN=change_me_before_use \ reach-registry:1.4.0 # 验证健康状态 curl -H "Authorization: Bearer change_me_before_use" http://localhost:8080/healthz启动完成后,第一步一定是用管理员 token 创建团队空间。Agent-Reach 里有 workspace 的概念方便做权限隔离,不同事业部的 Agent 可以注册在各自的空间里,互不干扰。我见过有人省掉这一步直接把所有能力塞到一个空间,短期没问题,等 Agent 多了之后会变成一个无法收拾的大杂烩。
接着在 Agent 进程里安装 SDK,并配置中心服务地址。
pip install agent-reach-python-sdkfrom agent_reach_sdk import ReachClient reach = ReachClient( endpoint="http://localhost:8080", api_key="sk_reach_test_key", workspace="ecommerce", timeout_seconds=5, )SDK 本身不重,就是一个 HTTP 客户端加本地缓存。它不会阻塞 Agent 的主流程,所有和中心服务的交互都是异步的,所以即使中心服务暂时故障,Agent 也能通过本地缓存继续运行一段时间。
3.2 注册你的第一个业务能力
部署完成后,下一步就是注册能力。我们拿“订单退款状态查询”这个功能做例子。这个过程有两种方式,我推荐先通过管理接口做静态注册,确认配置符合预期后再转成动态注册。
curl -X POST http://localhost:8080/api/v1/capabilities \ -H "Authorization: Bearer change_me_before_use" \ -H "Content-Type: application/json" \ -d '{ "workspace": "ecommerce", "capability": "order_refund_query", "display_name": "查询订单退款状态", "description": "根据订单号查询退款进度,返回退款状态和预计到账时间", "entrypoints": [{ "id": "http_v1", "protocol": "http", "url": "http://order-svc.internal:8080/api/v1/refund/query", "method": "POST", "transport": "json" }], "auth": {"type": "fixed_token", "token_env": "ORDER_SVC_TOKEN"}, "env": {"alias": "prod"}, "semantics": {"timeout_ms": 3000, "idempotent": true} }'注册成功的标志是响应里带了capability_id和version字段。如果你之后改了 URL 或者超时参数,注册接口会生成一个新版本,不会覆盖旧版本。这里有一个细节要注意:auth.token_env指的是中心服务所在环境里的环境变量名,不是 Agent 进程的。换句话说,凭证只需要中心服务持有,Agent 不应该保存任何下游系统的密钥。这是 Agent-Reach 能做好权限收敛的关键。
注册完之后,建议立刻跑一次手动拨测,确认这条链路真的通。手动拨测可以直接调用探活器的内部接口,传入测试参数,看返回是否正常。这一步千万别省,我们通常要求每注册一个能力,必须附带一条测试通过记录才能合并到主线配置。
3.3 改造 Agent:从直连到 Reach 路由
现在把 Agent 的工具调用逻辑从“直接向订单系统发请求”改成“通过 Reach 获取上下文并执行”。改造前你要先想清楚一个问题:Agent 的主流程不变,它还是要规划、还是要有函数定义,只是拿到函数参数之后,真正发起调用的那一层换掉。
from agent_reach_sdk import CapabilityRuntime cap = CapabilityRuntime(reach) async def refund_query_tool(order_id: str) -> dict: result = await cap.invoke( capability="order_refund_query", payload={"order_id": order_id}, env="prod", # 由上层预判后传入,Agent 不感知 ) return result.json()这段代码看着简单,但背后发生了不少事:SDK 先检查本地缓存里的能力元数据,确认这个 capability 存在且健康;然后向中心服务申请一个调用上下文,带上鉴权 token 和路由目标;拿到上下文后执行 HTTP 请求;最后把响应和耗时长尾数据一并上报给中心服务。对 Agent 来说,它只需要知道“我调用了这个函数,拿到了结果”,剩下的一切都被封装掉了。
我们在改造初期遇到了一个适配问题:很多 Agent 框架已经有自己的 tool 定义体系,SDK 返回的调用上下文需要再转换成框架要求的格式。我的建议是不要强行改框架,而是在你已有 tool 定义的内部包一层转换逻辑。你完全可以保留下发给模型的 function schema,只是在执行阶段调用cap.invoke。这样模型侧感知不到 Reach 的存在,下层改动对上层透明。
还有一个实操技巧:给每个 tool 都加上一个统一的reach_meta参数,内容是能力 ID 和环境别名。这样即使某个 agent 逻辑出现分支,也能在调用前通过 SDK 做一次“预检”,确认能力可用再真正执行。预检可以减少无效调用,尤其当 Agent 需要连续操作多个工具时,避免在第三个工具环节才发现第二个工具连不上。
3.4 从 LLM 到函数的完整链路示意
最后用一个极简示例串起整条链路。假设用户问:“订单 ORD-2024-001 的退款到哪一步了?”
Agent 的规划模块把它解析成一个工具调用请求refund_query_tool(order_id="ORD-2024-001"),然后进入 Agent-Reach 的调用流程:
- SDK 根据能力 ID
order_refund_query检查本地缓存,命中健康状态为 healthy。 - SDK 向中心服务请求生成调用上下文,中心服务检测环境上下文是 prod,并取出该能力 prod 环境入口
http://order-svc.internal:8080/api/v1/refund/query。 - 中心服务从凭证管理模块取到
ORDER_SVC_TOKEN,附加到调用上下文,这个过程 Agent 永远看不到 token。 - 执行器向订单系统发起 POST 请求,body 结构按照能力卡片里定义的 JSON Schema 生成。
- 订单系统返回退款状态,执行器把结果传回 SDK,SDK 上报调用日志和耗时。
整个链路下来,Agent 端拿到的只是一个 JSON 对象,中间任何环节失败,中心服务都会返回结构化错误码和可读信息。Agent 可以依据错误码决定是换入口、降级还是直接向用户解释失败原因。这一层“可解释性”对调试 Agent 非常重要,你总不希望在排障时面对一堆莫名其妙的 HTTP 500。
4. 我踩过的坑和排查技巧实录
Agent-Reach 上线以来,我们积累了不少问题处理经验。这些经验很多都不是官方文档会写的,属于实际被坑过才知道的细节。我把高价值的内容整理成几个主题。
4.1 不要让 Agent 的“触达”变成攻击入口
这是安全性上最重要的一个教训。Agent 的工具调用本质上是拿用户输入驱动后端请求,如果外部用户可以在对话里注入一段恶意文本,让它变成某个工具的参数,那就等于让 Agent 成为攻击者的跳跃点,典型的风险就是 SSRF。
你可能会觉得“我们的工具都是白名单内部接口,没有那么危险”。实际上真正危险的是能力卡片里 URL 模板被拼接。我们早期允许 URL 里带占位符,比如{target_server},结果测试时发现把target_server设成内网数据库地址也能打通,这实际上就成了一个开放的内部代理。后来我们做了两层加固:第一,URL 模板里的变量只能从预定义的枚举里取值,绝不接受自由字符串;第二,在entrypoints解析完成后,执行前增加一个目标地址校验,必须落在允许的网段内。
ALLOWED_CIDRS = ["10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12"] def validate_target(url: str) -> bool: host = parse_host(url) return any(ip_in_cidr(host, cidr) for cidr in ALLOWED_CIDRS)这层校验看起来和 Agent 业务无关,但它是整个系统的底线。你永远不能假设所有下游系统的开发者都做了完善的入参校验,Agent 这层的触达管控必须把最坏情况考虑进去。
4.2 超时和重试:一个长期调优的参数博弈
Agent 场景的超时设置比传统接口调用复杂,因为 Agent 往往在同一个任务里串行调用多个工具,单个工具的超时直接决定用户体验。太短,接口偶发抖动就被判定失败;太长,Agent 会卡在一个慢接口上,后面的任务全部排队。
我们最终的解决方案是三层超时配置。第一层是能力卡片里的timeout_ms,这是单次调用的硬超时,默认按接口 P95 耗时 * 2 来定;第二层是重试策略里单轮退避上限,比如重试最多 2 次,每次退避时间在 200ms 到 800ms 之间;第三层是 Agent 任务级的总超时,防止一个工具重试耗尽整个任务的时间预算。
retry_policy: max_attempts: 2 max_total_ms: 1500 backoff_algorithm: exponential initial_delay_ms: 200 max_delay_ms: 800 retryable_status: [408, 429, 502, 503, 504]重试也不是所有状态码都能重试。408、429、502、503、504 基本属于“服务临时过载或链路抖动”,重试是有意义的;4xx 的其他错误基本是参数或权限问题,重试一百次也没用。我们一度把 401 也加进了重试列表,结果每次凭证过期后系统都会疯狂重试,直到把所有凭证配额刷完。那天的教训就是:重试前先区分错误类别,否则你只是把一个错误延迟了几秒再失败。
4.3 环境漂移:Agent 觉得自己在连生产,其实在连测试
这是另一个高频事故。Agent 的调用链路经过多层转发,任何一个环节的环境配置错了,都会导致 Agent 连到错误的环境。最麻烦的是,这种错误有时候不会直接报错,因为接口结构完全一样,只是数据不同,Agent 可能还“很高兴”地给你返回了一堆测试数据。
Agent-Reach 解决这个问题靠的是环境追踪。我们在每个调用上下文里强制注入一个x-reach-env请求头,下游系统可以读取这个头来做环境校验。同时审计日志会记录每次调用的目标环境别名。一旦发现线上 Agent 在调 staging 环境,直接从审计系统里一眼就能看到,不用靠猜。
如果你已经在跑 Agent 项目,建议你做一个简单的检查:在 Agent 的日志里搜一下最近一小时的调用记录,看看目标地址里有没有出现staging、test、local之类的关键词。如果有,那说明你的环境路由可能已经漂移了,和开发就“能运行”是两码事。我们整治之后,把环境别名做成强制字段,注册能力时如果没有声明env.alias,直接拒绝注册,从源头杜绝了这类问题。
4.4 凭证最小化和安全的存放方式
Agent 要触达的业务系统越来越多,每个系统都有自己的账号密码或者 token。如果这些凭证被散落在 Agent 的配置文件、环境变量或者代码库里,那几乎等于给攻击者送人头。Agent-Reach 的中心服务天然适合做凭证的集中托管,但我们遇到过团队不习惯、嫌麻烦的问题。
我的建议是一步一步来:先不要追求完全的云端托管,至少在卡片的auth字段里引用环境变量,而不是把明文 token 写进去;然后把中心服务所在的主机纳入公司的密钥管理系统,让它能动态读取密钥;最后再开放动态签发短时凭证的能力。整个过程不需要一次性做完,但方向必须从一开始就定好:凭证永远只流向中心服务,Agent 拿到的只能是“调用上下文”,而不是“身份凭证”。
这里有一个很明显的判断标准:打开 Agent 的进程环境变量,如果能看到任何下游系统的密钥,那就是不合格的。不管你的 Agent 是跑在服务器上、用户的笔记本上还是某个云函数里,这个标准都适用。
4.5 探活误报:拨测数据和真实数据打架
探活器虽然好用,但它的误报率一度达到每天十几条,搞得运维团队对告警麻木了。原因很简单:拨测用的测试数据和生产环境的真实数据在特征上差异很大,有些下游系统会根据数据特征走不同分支,导致拨测链路没问题,真实调用却出问题。
对策是双轨探活:一是显式拨测,用测试数据主动验证链路;二是隐式采集,从真实调用日志里提取成功率、P95 延迟、错误码分布。显式拨测保证“基本链路通”,隐式采集反映“真实链路稳”。给能力打健康分时,两个信号加权在前者占 40%、后者占 60%,这样既能发现链路断了的问题,也能发现真实流量下才暴露的性能劣化。上线这个模式之后,误报率降到了每天两三条以内,而且剩下的告警基本都是真实问题。
4.6 把可观测性做进调用上下文里
最后一条经验,也是我认为整个项目里最容易低估价值的一部分:可观测性。Agent 调用链路又是异步又是多跳,如果没有一个贯穿全链的追踪 ID,排查问题的难度会成倍上升。
Agent-Reach 在生成调用上下文时,会分配一个全局唯一的trace_id,这个 ID 会随着 HTTP 请求头发送给下游,也会出现在 SDK 日志、中心服务的审计日志和 Agent 日志里。不要小看这个设计,实际上后期几乎所有问题排查都是靠它把一段对话、一次工具调用、一条下游日志串联起来的。如果没有这个 ID,你面对的是一个黑匣子:只知道 Agent 给出了一个错误结果,完全不知道是哪一步造成的。
我们还在审计日志里记录了每次调用的 prompt 摘要、工具参数、返回结果的截断,以及流量方向。这样做一方面方便回溯,另一方面也为后续做 Agent 质量评估积累了数据。只要存储成本允许,建议尽量保留完整的调用轨迹,因为这些数据在优化 prompt、调整路由策略、定位环境漂移时都能派上大用场。
5. 现场实录:一次系统性可达性整治
理论讲了不少,我用一次我们内部的整治过程来做收尾。你可能也有类似经历:某天开始,Agent 系统的成功率从 98% 突然掉到 70%,查了半天发现是连接治理长期没人管,各种问题累积在一起爆发了。
5.1 排查前的状态
那段时间业务侧反馈很集中:Agent 经常说“查询失败”“系统繁忙”,但问具体哪个环节失败,用户描述不出来。我们拉了一下 Agent-Reach 的仪表盘,整体触达成功率确实在下滑。进一步拆解发现,问题不是单点故障,而是多点并发:部分能力已经连续 15 分钟处于 unreachable 状态,但路由器没有把它们剔除,Agent 还在傻傻地调用;一部分调用在网关层耗时超过 5 秒,触发了 Agent 端的兜底超时;还有一个老系统的证书马上过期,探活器已经报警三天,但值班同学没在处理。
这就是典型的“可达性失控”:不是某个接口坏了,而是整个触达链路缺乏治理,小问题互不关联、互相叠加,最终表现成用户感知的系统性崩溃。Agent-Reach 在这种场景里的价值不是避免小故障发生,而是让你在故障发生时能看见全局图谱,而不是拿一堆零散日志挨个猜。
5.2 整治过程
我们分了三天来做系统性处理。第一天,先把所有 unreachable 能力从路由器里剔除,让 Agent 不再调用它们,同时通知各业务方确认是否已经迁移或下线;第二天,把所有高延迟能力按业务优先级排序,逐个和下游团队核对容量和超时配置;第三天,针对证书过期、凭证失效这类“隐形故障”做了资产盘点,把每个能力的凭证到期时间列入日历,提前一个月告警。
整治过程中一个比较大的动作是给每个 Agent 配置“降级路径”。比如订单查询不可用时,如果还有缓存服务可以兜底,就自动切换;如果连兜底也没有,那 Agent 必须返回明确错误,而不是一遍遍重试或者编造一个结果。这个原则在 Agent 场景里特别重要:让模型承认“我暂时查不到”,往往比让它胡说八道更有价值。
另外一个细节是给探活器和路由器之间加了一个“冷却窗口”。某个能力连续三次探活失败后,并不会立即下线,而是进入 degraded 状态,再连续失败三次才标记 unreachable。因为探活器受网络抖动影响可能会出现瞬时误报,立即下线会让 Agent 频繁切换能力,反而增加不确定性。
5.3 整治后的效果
三天整治完成后,Agent 的触达成功率回升到 98.5%,重试率下降了一半,用户侧从“经常等很久然后失败”变成了“偶发慢但基本成功”。更重要的是,团队建立了一个每周可达性巡检的固定节奏:每周关注探活器报表、能力健康分、凭证到期日历,用十分钟完成走访,把很多小事故消灭在用户感知之前。
从那次以后我形成了一个习惯:每次看一个 Agent 项目,第一件事不是看 prompt 写了什么,也不是看模型选得对不对,而是先看它的触达层是不是清醒的。模型再聪明,发言要有底气,而底气的来源就是底层通道真实可用。Agent-Reach 对我个人最大的启发是:让 Agent 学会“连接”和让 Agent 学会“思考”一样重要。思考负责上限,连接负责下限,下限不稳,上限再高也落不了地。