最近半年我把主要精力都压在一个内部项目上项目代号叫Agent-Reach。用一句话概括它要解决的问题只有一个让 AI 智能体稳定、安全、可控地“触达”执行任务所需的一切资源——内部 API、数据库里的工单、审批流、邮件、文件签名服务甚至同事的在线状态。这句话听起来很像传统集成平台的宣传语但真正从零搭一遍坑比想象中多得多。我见过太多团队把智能体项目做成“调 Prompt 竞赛”模型换得越来越贵对话效果却还是时好时坏。原因倒不复杂——大多数任务卡点根本不在模型会不会推理而在它“够不到”数据。数据库在防火墙后面API 返回格式不统一两个同名接口指向不同系统下游服务超时一次整个任务跟着失败。Agent-Reach 给我的最大启发是与其让模型瞎猜怎么连资源不如把“触达”本身当成一个基础设施来设计。如果你正在做智能体类应用或者正在为模型接入各种内部系统而头疼这篇内容应该能帮上忙。我会把这几个月的设计决策、踩坑过程和可直接照搬的配置方式完整拆开重点是为什么要有能力注册表、路由怎么做才不会乱、熔断兜底怎么接以及哪些东西是我后悔没早点做的。1. 为什么做 Agent-Reach智能体“够不到”资源才是真卡点1.1 表象是模型推理弱本质是链路触达断先看一个标准任务链路用户发起请求 → 模型理解意图 → 抽取参数 → 调用某个动作 → 拿回结果 → 组织回答。多数智能体项目在演示阶段都能跑通前两步因为模型确实能理解“帮我把订单退掉”这种自然语言。可一旦进入真实业务环境七八成的失败发生在第四步和第五步连接超时、权限不足、参数校验失败、上游改了字段、返回内容缺失。我习惯把这种问题统称为“触达问题”。触达不是单纯指网络通不通它是一个组合概念权限是否够用 连接是否可靠 能力是否可发现 路由是否准确 执行是否有兜底。五个条件缺一个模型再聪明也拿不到结果。Agent-Reach 这个名字就是这么来的Agent 是智能体Reach 是触达半径。它不打算替模型做推理只负责把“从模型到资源”这条路径变成一条有护栏、能观测、可拆分的路。1.2 三个最典型的“接不到”现场项目启动前我们收集了组内三个反复出现的问题后来它们成了 Agent-Reach 的核心需求来源。第一个是网络边界。工单系统只在内网部署模型服务跑在云端容器里直接调用私有接口会被网关挡回来。最后只能在边界处放一个代理网关把内网 API 安全地暴露给智能体同时把访问凭证换成短期令牌。第二个是格式不统一。订单系统返回payTime支付系统返回Paid_At文件名服务返回的是 Base64 文件流。如果智能体直接把这两段数据拼接给用户就会生成“订单支付成功但时间无法确认”的矛盾回复。模型不知道字段差异它只会诚实地说字段对不上。第三个是路由摇摆。我们有两个系统都提供“查询订单”能力一个查电商订单一个查线下门店订单。模型对着同义词反复犹豫一会儿调这个一会儿调那个用户看到的结果完全随缘。1.3 从“加几个工具”到“做一个平台”一开始我们只是想给模型加几个 function call但很快发现工具越多控制成本越高。每个工具的鉴权方式不同、超时配置不同、可重试性不同如果让模型自行管理这些细节相当于让司机同时兼任修理工和交警。于是我们把思路转向了中间层Agent-Reach 不是又一个工具库而是一层面向智能体的触达网关。它对外连接多个业务系统对内只暴露一个统一接口给智能体智能体不需要知道目标系统是 HTTP 还是 gRPC也不需要知道凭证藏在哪里。这样做的好处非常直接模型侧的接入成本大幅降低安全边界从各系统收拢到一个平台后续增加新资源也不用改模型代码。2. Agent-Reach 的分层架构把触达能力拆成可运维模块2.1 连接层协议适配与通道归一任何集成平台的核心都是适配器。Agent-Reach 的连接层负责把所有外部资源转换成统一调用模型目前支持 HTTP/REST、gRPC、数据库只读连接、消息队列和 GraphQL。每种协议用一个独立适配器实现适配器只做三件事建立连接、参数映射、返回结果归一化。这里有个设计决策值得展开数据库接入只允许只读而且只允许参数化查询模板。原因是让智能体直接拼 SQL 太危险了模型一旦把用户输入里的字段带进 WHERE 子句就可能出现非预期结果。Agent-Reach 的做法是把常用查询场景固化成语义化的能力模型只传业务参数实际执行的是我们预审过的模板语句。连接层还要统一处理心跳、重连和 DNS 缓存。我们在内网环境遇到过一个问题下游服务重启后 IP 变了但客户端的连接池还持有旧地址导致连续失败几分钟。后来在适配器里增加了地址解析和连接池自动刷新机制才算稳定下来。2.2 能力层可发现的能力目录能力层的作用是把“能做什么”变成机器可读的目录。每个能力都有唯一的 name、输入输出 Schema、鉴权标签、超时等级和幂等性标识。这些信息被写进能力注册表智能体在运行时只和注册表对话而不是逐个探测下游系统。为什么一定要 Schema因为大模型生成参数时没有 Schema 就只能靠猜。一个订单号字段可能同时存在字符串和数字两种格式如果不在注册表里明确定义类型和最大长度模型生成的参数就经常过不了系统校验。另外能力层还承担了dry-run 校验的功能。环境准备阶段Agent-Reach 会先发送一次模拟请求验证下游接口真实可用。这个动作避免了“注册了一堆能力实际一个都跑不通”的尴尬。上线前我们把所有能力注册项跑了一遍 dry-run当场发现两个系统已经无法访问比等到用户反馈再排查省了不少事。2.3 编排层从 Reach Graph 到分配策略编排层是 Agent-Reach 最灵活的部分它维护一张Reach Graph。这张图不是网络拓扑而是“谁可以触达什么”租户、角色、资源能力三者之间的绑定关系。简单理解它就是一张权限拓扑图每个智能体实例只能触达图上连到自己身上的节点未连通的节点在路由阶段就会被直接屏蔽。这张图带来一个明显变化智能体的行动范围从“模型自己觉得能用什么”变成了“平台允许它用什么”。比如同一个前端页面背后挂着三个 Agent客服 Agent 可以触达订单和退款接口但对内部报表库不能触达运营 Agent 可以触达报表却不能发起退款。权限控制的粒度从整个系统细化到了单个能力。在分配策略上Agent-Reach 支持两种模式固定路由和语义路由。固定路由适合低频且目标明确的动作比如“查物流”永远调到物流系统语义路由适合高频且容易混淆的动作比如“订单查询”需要根据上下文判断走电商还是线下渠道。这部分的实现细节放在下一节展开。3. 落地核心能力注册、Reach 路由与熔断兜底3.1 一份能力注册表让 Agent 与系统自动建联先给一个最简化的能力注册 YAML 示例方便说明结构capabilities: - name: ticket.read type: http endpoint: https://gateway.internal/tickets/{ticket_id} method: GET auth: service-account/ticket-reader timeout: 3s idempotent: true schema: params: ticket_id: { type: string, required: true, max_len: 32 } response: fields: - field: status type: string - field: owner type: string这份配置读起来很直白能力名称是ticket.read走 HTTP GET地址带一个ticket_id参数鉴权使用ticket-reader这个服务账号超时 3 秒幂等可以安全重试。字段idempotent是重试策略的关键依据后面熔断部分会提到。注册表的第二个用处是生成模型提示词。我们在给模型的工具说明里只放能力名称、参数描述和返回值摘要不会暴露完整 endpoint 和鉴权方式。这是安全需要也是 token 成本控制需要。模型要做的只是描述“我要调用哪个能力、传什么参数”剩下的事交给 Agent-Reach 完成的协调。注册过程建议做两步校验Schema 格式校验和生产环境 dry-run。第一道防止配置写错第二道防止接口已经下线。曾经有一个团队把接口路径写错一位看起来完全没问题直到真实调用才发现 404dry-run 机制能提前抓住这种错误。3.2 路由决策上下文打分而不是让模型自由选择让模型从二十个能力里直接选一个失败率一定不会低。Agent-Reach 采用两段式召回加评分排序先用语义检索找出候选能力再用规则过滤做可达性约束最后按得分排序返回给模型。打分公式可以简化为Score SemanticScore × 0.7 PriorityBonus × 0.2 HistoryScore × 0.1其中 Semanticscore 来自能力描述和当前用户请求的相似度PriorityBonus 是固定配置的优先级加成HistoryScore 是过去一段时间内该能力被相同类型请求调用的准确率。需要特别强调如果可达性约束不通过得分直接归零。也就是说一个权限上不允许触达的能力无论如何都不能进入候选集。我在实测中发现一个反常识现象单纯提高语义相似度权重并没有改善路由准确率反而让模型在两个高度相似的能力之间反复横跳。后来加入 historyScore也就是让“最近正确的调用记录”参与排序准确率才明显提升。这个逻辑跟搜索引擎有点像经常被用户点击的结果下一次排序应该靠前。3.3 执行保护超时、重试与熔断链路下游系统不是每次都可靠所以执行阶段必须有保护机制。Agent-Reach 默认给每个能力设置独立超时多数读接口设 3 秒写接口可以放宽到 5 到 10 秒避免模型长时间等待。重试策略严格依赖幂等标识。幂等接口最多重试两次且间隔退避非幂等接口默认不重试直接报错交给上层决策。举一个例子支付退款接口如果因为网络超时而被重试用户可能收到两次扣款确认这种事故责任很难解释。所以在注册表里把idempotent: false标清楚比事后写任何防御代码都管用。熔断器我选的是滑动窗口实现连续 10 次调用中如果失败率超过 50%就临时摘除该能力在接下来 30 秒内不再路由给它。熔断期间 Agent-Reach 会返回一个“能力暂不可用”的降级响应模型收到后可以尝试其他路径或者直接告知用户稍后再试。这个设计避免了单个慢接口拖垮整条调用链路。4. 一个完整用例让 Agent 跨系统完成“客户退款前材料核验”4.1 业务场景与角色定义用一个客服场景来串联 Agent-Reach 的完整流程。业务方需求是用户在 App 上申请退款但需要客服确认三类信息订单状态是否为已完成、支付流水是否真实存在、退款须知文件是否已勾选“已读”。这三类信息分别存在于三个系统订单系统、支付系统、内容服务系统。如果让客服人工查询平均每单要花三分钟让模型直接访问三个系统的数据库既不安全也容易产生误判。Agent-Reach 就成了中间协调者。参与角色包括客服智能体、退单核验流程、订单能力、支付能力、文件状态能力。每个能力都按前面介绍的注册表方式接入凭证各不相同有的是服务账号有的是只读账号其中支付能力的凭证被标成“敏感不回传”。4.2 注册能力与编排调用代码现在在 Agent-Reach 里注册三个能力capabilities: - name: order.check_completed auth: read-order - name: payment.verify_transaction auth: read-payment - name: consent.file_ack_status auth: read-consent接下来是调用代码。Agent-Reach 提供了一个 Python SDK智能体只需要调用统一入口from agent_reach import ReachClient client ReachClient(tenantcustomer_service) plan client.create_plan([ order.check_completed, payment.verify_transaction, consent.file_ack_status, ]) plan.set_context(order_idSO-2024-001, user_idU-10086) result plan.execute(timeout12.0) print(result.to_dict())这段代码看起来很简单但内部做了不少事情先检查这个客服 Agent 是否对所有三个能力都有触达权限再检查对话上下文中抽取的order_id是否符合 Schema接着逐个调用并收集结果最后把三个系统的响应统一成同一份 JSON。如果其中一个调用失败plan.execute会返回局部成功和失败原因而不是直接崩掉整个流程。4.3 实测中的两个问题和修正第一个问题出现在路由阶段。客服输入“麻烦看看这个客户的订单能不能退”语义检索同时命中了order.check_completed和payment.verify_transaction因为“退款”这个词在支付系统描述里频繁出现。结果是模型先调了支付核验再去查订单状态顺序反了。修法很直接在能力描述里增加明确的业务标签并把order.check_completed的 PriorityBonus 调高。同时对支付能力补充描述“仅用于核验交易流水不承接订单状态判断”。这个改动让路由准确率从 82% 升到 96%。第二个问题更隐蔽。文件状态能力返回的数据里有read_status: true字段名看着没问题但实际映射错了用户 ID。原因是内容系统允许同时传入多个用户 ID我们只传了一个默认返回了第一个用户的签署状态。后来在 Schema 里强制要求用户 ID 字段必须显式传入且一次只允许传一个问题才算根除。5. 从线上事故里总结的运维与安全经验5.1 权限错配是最大事故源必须做审计Agent-Reach 上线第三周出现过一次严重误操作一个测试 Agent 使用了生产环境的服务账号把一条测试订单误标成了完成状态。排查后发现是测试环境注册表里复制了生产配置且没有做环境隔离。教训是能力注册表必须按环境强制划分不同环境不能共享凭证配置发布要过一套统一的审核流程。我建议在每个能力执行后记录完整审计日志至少包含执行时间、操作者身份、调用的能力、传入参数摘要、返回结果摘要、耗时和状态码。参数里如果有手机号、身份证这类敏感信息只记录脱敏版本。这一步在发生争议时是唯一的追溯依据。5.2 网络拓扑变化比代码 bug 更频繁智能体服务到下游系统的链路中最大的不稳定因素不是程序逻辑而是网络和基础设施变动。我们遇到过三次因为下游服务更新导致接口响应格式变化两次因为负载均衡策略调整导致部分请求超时。Agent-Reach 的每个能力都带一个健康检查定期打一次最小探测请求一旦连续失败就告警。对于需要跨网络访问的资源我建议在边界处统一走安全网关而不是让每个智能体直接暴露地址。这里不展开网络架构细节但记住一点即可控制面和数据面分离配置变更尽量不影响在线流量能力编排尽量放在独立服务里。5.3 配额、并发与成本控制要提前设计智能体调用下游接口的频率远高于人工操作一台客服机器人可能每秒钟发起几十个查询。如果不去控制并发下游数据库先扛不住紧接着就是整条链路雪崩。Agent-Reach 在能力层增加了每秒请求上限和每日配额超过上限的请求进入排队或直接拒绝。成本控制上也需要依赖配额机制。我们在接入一个需要外部付费的短信能力时就给该 Agent 配置了日限额防止模型因为用户一句“给我发验证码”而批量触发短信造成超额费用。设置配额不是在限制业务而是在让系统有明确的预算边界。5.4 可观测性让每一次 Reach 都有迹可循没有可观测性的智能体项目出了问题只能靠猜。Agent-Reach 给每个请求生成一个全局 trace_id从用户输入、模型调用、路由决策到最终执行能力整条链路都可以在同一份日志里串起来。实践中最有价值的三类指标能力调用成功率、平均响应时长、路由命中占比。按 Agent 维度看这些指标能快速揪出“某个 Agent 总是乱调接口”的情况按能力维度看能发现某个下游接口已经处于亚健康状态。建议把这些指标接入现有的监控大盘并用 Alerting 规则配置异常阈值能省掉很多手工排查时间。6. 如果你想自建一个最小可用版这几个建议能省两周6.1 一开始别做的事很多人上来就想做一个可视化配置后台、支持多租户、对接几十种协议结果周期拖得很长。我的建议是首版只做三件事一个能力注册表、一个固定路由、一个统一执行器。可视化后台可以后置多租户后置协议适配器只支持 HTTP 和 PostgreSQL 就够了。早期应该把精力放在接口稳定性和权限正确性上把两三个真实业务场景跑透比支持一堆假想场景有用得多。如果连两三个核心能力都不能稳定执行后台做得再漂亮也没有意义。6.2 推荐的技术组合如果用最小可用版来设计我会选以下组合一个 FastAPI 服务作为执行中枢一个 PostgreSQL 存能力注册表和绑定关系一个 Redis Streams 做异步任务队列。智能体端接一个简单的 Python SDK核心代码不超过 500 行。执行中枢不推荐放太多业务逻辑它只负责把请求翻译成下游调用。能力描述可以用 YAML 维护Schema 校验用 JSON Schema 格式。通信协议全部走 HTTPS鉴权用服务账号加短期令牌。这套组合的好处是每个组件都常见招人接手也不会有学习成本。6.3 合理的迭代顺序第一步先把一个下游系统的只读接口接进来让智能体能查到某张表的数据。这一步验证的是基本链路不追求复杂调度。第二步加入能力注册表和固定路由让模型不能自己选接口只能按平台指定路径调用。第三步再加入语义路由和上下文评分。第四步做重试和熔断最后再做可观测性和审计日志。按这个顺序走每一步都有明确验证点前一步不通过就不进入下一步。我们当时跳过了一步直接上了语义路由结果一上线就出现路由混乱又把固定路由加回来当兜底。后来才明白基础的确定性路径永远是智能体的安全网。踩过几次坑之后我最大的体会是Agent-Reach 这类中间层表面上是技术架构问题本质上是在用工程手段重新划分业务系统的触达边界。它把“谁能碰什么数据、模型能调什么动作、失败了该怎么兜底”这些原本散落在代码里的规则收敛成了一层可以审阅、可以测试、可以演进的基础设施。如果你的智能体项目也到了“模型够聪明但你总担心它够不到”的阶段不妨按这个思路先搭一个最小版把触达这件事做扎实了再回去优化 Prompt 也不迟。