ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从MCP到Agent-Reach:智能体触达外部异构系统的工程实践

从MCP到Agent-Reach:智能体触达外部异构系统的工程实践 1. 为什么在 MCP 声浪正盛时我仍要另起炉灶做 Agent-Reach2024 年年底到 2025 年我身边几乎所有做 AI 应用的朋友都在聊同一个词MCP。模型上下文协议确实解决了一个大问题——让大模型能以标准化方式调用外部工具那段时间我也在内部项目里引进了不少 MCP Server跑通了 GitHub 操作、本地文件读写、SQL 查询这些能力。体验很惊艳模型终于不再是聊天机器人了它可以真正把手伸向外部系统。但用得越深我越觉得不对劲。我手头有一堆历史包袱型系统一套老旧的订单查询接口、一个只能通过命令行调用的内部数据清洗脚本、一套基于 RPA 编写的浏览器自动化流程、还有一个同事离职前留下的 PostgreSQL 分析库。这些系统里面没有一个是天然带 MCP 适配器的有些连 HTTP 接口都不完整只有 CLI 或数据库连接串。MCP 是标准世界的通用语但真实世界的绝大多数系统还停留在方言时代。你要让它们接进智能体要么给每个系统都套一层 MCP Server 壳要么找到一个更粗粒度、更实际的接入方式。这个接入方式就是我启动 Agent-Reach 的初衷。Agent-Reach 不是一个模型也不是一个 Agent 框架。它是一层触达层基础设施——专门解决智能体如何真实触达外部系统并执行动作这个问题。你可以把它理解成一套替 Agent 安装的手和脚模型负责思考Agent-Reach 负责拿到思考结果之后把动作落到真实世界里去执行。和 MCP 最大的差异在于Agent-Reach 并不要求被接入方做出任何改造。它通过适配器模式把 HTTP、CLI、数据库、RPA、消息队列等异构系统统一封装成能力点然后用一套基于语义寻址的路由机制让模型端的 function calling 可以直接命中这些能力点。这篇文章我就从动机、架构、核心实现、端到端实测、踩坑记录五个维度展开把我从 0 到 1 做完这一层基础设施的经验完整写出来。如果你也在做 Agent 应用正在被模型很聪明但够不着系统卡住这篇文章应该能给你一套可以直接复制的思路。2. Agent-Reach 的骨架大脑、神经与手脚的分工在动手写第一行代码之前我先想清楚了一个核心问题Agent 系统里规划、触达、执行这三件事到底该怎么分工2.1 触达层为什么不能和规划层混在一起很多 Agent 框架的问题是把工具调用逻辑直接焊死在 Agent 的循环里面。模型每轮推理之后框架直接去调用一个函数返回结果再塞回上下文。听起来很顺但一旦你接入超过五个工具问题就来了工具描述词塞满上下文模型推理质量肉眼可见地下降不同工具的鉴权方式混在一起安全策略根本没法细粒度管控工具返回结果过大直接把上下文窗口撑爆排查问题的时候根本分不清是模型选错了工具还是工具执行本身出了问题我经历过的项目里这些都是实际发生过的不是理论担忧。Agent-Reach 的做法是把三件事彻底拆开大脑规划层只负责决定下一步做什么不关心动作如何落到具体系统神经触达层Agent-Reach 本体负责把规划结果翻译成可执行的调用请求手脚执行层真实的外部系统提供实际能力这种分层最大的好处是你可以单独升级规划模型也可以单独替换底层系统互不干扰。2.2 核心模块一览Agent-Reach 的代码结构分成五个核心模块我在下面的表里把它们列出来模块职责关键技术点Capability Registry能力点注册与元数据管理静态 schema 动态心跳支持存量系统与临时脚本Reach Gateway语义寻址与请求路由能力点匹配、参数映射、超时熔断Protocol Adapters异构协议转换HTTP、CLI、SQL、RPA、WebSocket 五种适配器优先落地Execution Sandbox隔离执行环境Docker 容器 权限降级 审计日志Feedback Channel执行结果回流截断、摘要、结构化错误码三类回调这里需要重点展开的是 Capability Registry。它解决的是智能体怎么知道有哪些能力可用的问题。传统 API 网关给开发者看文档Agent-Reach 的 Capability Registry 是给模型看 schema——每个能力点必须提供机器可读的调用契约。举个例子一个订单查询接口注册成能力点之后它的 schema 大概是这样的{ capability: order.query, description: 查询订单状态按订单号或客户手机号查询, input: { order_id: string, 可选订单号, mobile: string, 可选客户手机号, time_range: string, 可选最近N天默认7天 }, output: { order_status: string, logistics_track: array }, adapter_type: http, endpoint: http://legacy-order-svc/api/query }这个 schema 的设计原则是给模型看的不是给开发者看的。描述词必须用自然语言写清楚业务语义因为模型不像人一样会去看接口文档它只靠这些描述词来理解。2.3 为什么不沿用传统的工具列表模式如果你用过 function calling会发现我刚才说的和把函数列表传给模型本质上似乎差不多。确实出发点相似但 Agent-Reach 在三个点上做了关键增强第一工具列表是硬编码的Agent-Reach 的能力点是动态注册的。系统新上线一个数据接口不用改 Agent 代码只要向 Registry 注册一个能力点Agent 下次就能触达它。第二工具列表是模型可见的全局空间能力点是分域隔离的。你可以把财务域的能力点只开放给财务 Agent把运维域的能力点只开放给运维 Agent模型根本不会看到它不该用的工具。第三工具列表假设工具是函数Agent-Reach 承认能力是服务。函数是一次性调用服务包含生命周期、鉴权、限流、幂等等语义。这个区别在后面做工程实现和排查问题的时候体现得非常明显。3. 工程落地核心细节注册、寻址、协议转换与沙箱隔离架构层面想清楚之后剩下的问题全是工程问题。这里我挑四个最关键的说透。3.1 能力注册的心跳机制与 schema 设计Capability Registry 如果只有静态注册在真实生产环境里会出大问题——服务挂了 Agent 还在傻乎乎地调用它。所以我给 Registry 加了动态心跳每个能力点注册时强制要求一个健康检查地址Agent-Reach 会以 30 秒为周期探测一次。心跳失败的能力点会被自动标记为不可用Reach Gateway 在路由时直接跳过它。这个机制让我免掉了无数个模型调了接口结果服务早挂了的脏活。关于 schema 设计我最想分享的一个经验是描述词里一定要包含触发条件和禁止条件。很多人写能力描述词只写这个工具能做什么完全不写什么时候不应该用。实际跑下来效果差异巨大。我给能力点描述词总结了一个模板功能描述这个能力做什么适用场景在什么业务场景下应该被调用不适用场景什么情况下绝对不要调用它输入约束哪些参数是必填哪些之间互斥输出预期调用之后大概会返回什么示例如下{ capability: rpa.invoice_download, description: 登录财务系统并下载指定供应商的电子发票PDF, when_to_use: 用户申请报销、财务核对进项发票时需要抓取发票文件, when_not_to_use: 已经本地存在发票文件时不要重复下载供应商编码缺失时禁止调用, input: { supplier_code: string, 必填供应商编码, month: string, 必填格式YYYY-MM } }不夸张地说写清楚 when_not_to_use 之后模型乱调工具的次数直接降了一个量级。原因很简单模型学会一个工具的使用边界比学会一个工具的触发场景难得多。3.2 路由不是选个工具而是三段式匹配传统工具路由的逻辑很简单——模型返回一个函数名框架调用它。Agent-Reach 因为要接异构系统路由逻辑必须做得更重。我把路由拆成三段第一段是意图匹配模型不直接返回我要调用 order.query而是返回一段自然语言意图例如我想查一下订单 20250315 的物流状态。Reach Gateway 把这句意图和 Registry 里的能力点描述做语义匹配得到候选能力点列表。第二段是参数映射模型输出的不是结构化的 order_id而是散布在自然语言里的信息。参数映射器负责把实体抽取结果填到能力点的 input schema 里。这里有一步校验很关键——校验不通过的默认走缺参追问流程而不是强行调用。第三段是路由决策根据候选能力的健康状态、权限域、资源配额做最终决策。如果两个能力点都能完成同一个目标优先选成本低的轻量 API 优先于 RPA 脚本。我特意把意图匹配挪到模型输出之后来做而不是让模型直接输出工具名是因为实测发现直接让模型输出工具名时模型经常因为过度自信而选错但让它输出意图再让路由层做匹配准确率会高很多。这本质上是把部分决策负担从模型转移到确定性代码上。3.3 协议适配器老系统接入的核心枢纽协议适配器是 Agent-Reach 里工作量最大的模块。一个全新的外部系统接入进来80% 的工作量在适配器上。HTTP 适配器相对简单本质上封装了请求构造、签名鉴权、响应解析。但有两个细节值得说存量系统的鉴权方式五花八门有的是 Token 放在 Header有的是放在 Query 参数里有的是 cookie 鉴权。适配器必须把鉴权方式做成可配置项而不是硬编码存量接口的响应结构基本都不规范可能顶层是 JSON 字符串嵌套可能错误码和 HTTP 状态码对不上。适配器要做一层响应标准化把业务错误和系统错误严格区分CLI 适配器是我个人最偏爱的一个。内部很多工具没有 API只有命令行可执行文件。CLI 适配器的思路是把命令行封装成能力点把python clean_data.py --input xxx.csv --output yyy.csv转成一个带参数校验的调用。超时控制在这里极其重要很多 CLI 脚本没有超时机制会一直挂住我统一在适配器层加了硬超时。数据库适配器则直接接受 SQL 模板。注意这里我不允许模型自由生成 SQL——那太危险了。数据库适配器只接受预注册的参数化 SQL 模板模型只能填充参数不能修改 SQL 结构。这是我在数据库接入这件事上最大的安全底线。至于 RPA 适配器主要用于控制浏览器流程。这一块的抽象思路是把 RPA 步骤拆成动作单元比如登录、填写表单、点击按钮、下载文件。每个动作单元暴露成能力点。Agent 编排动作单元而不是直接控制浏览器。3.4 沙箱隔离Agent 触达能力的代价是必须承担安全责任让 Agent 能触达系统就相当于给模型发了一把能开门钥匙。能力越大出事风险越大。Agent-Reach 的执行沙箱参考了云函数和 CI 系统的实现思路做了一套三层隔离第一层是网络隔离每次执行默认在一个独立网络命名空间中运行出网需要通过显式配置的规则。老系统如果只需要访问内网数据库就只开放内网网段不给公网访问能力。第二层是权限降级所有执行动作使用最小权限账号。CLI 脚本跑在低权限用户下RPA 操作使用仅限目标系统的只读账号数据库连接使用只读用户。第三层是审计追踪每一次执行记录完整的请求、参数、响应、耗时、调用链 ID。后面排查模型为什么会执行这个动作时这套日志是唯一的依据。我在生产环境里遇到过不止一次因为没做沙箱而出事故的案例。有一个朋友的项目Agent 拿到了一个删除接口的调用权模型在一次错误推断中调用它清空了测试数据。虽然数据有备份但这件事直接把整个项目的上线时间推迟了三周。所以这里我宁可多说几句任何让 Agent 触达外部系统的设计都必须先想清楚安全边界否则后面一定会出事。4. 端到端实测让 Agent 从只会聊天到跑完一个数据分析闭环架构和核心模块都实现之后我找了一个真实场景做端到端验证。场景是这样的市场部每周末要一份上周的订单分析简报包含订单量趋势、Top 10 商品、异常订单预警。以前这个活靠一个运营同事手工操作流程是把订单数据从数据库导出到 Excel写 SQL 做聚合再套一个固定的周报模板。Agent-Reach 的目标是让 Agent 自动完成这个流程。4.1 接入前的建模把手工流程翻译成能力点手工流程里有三个核心动作我把它翻译成三个能力点注册到 Registrydb.order_daily_aggregate参数化 SQL 模板能力点输入起止日期输出每日订单量和 GMVanalysis.top_skuPython 脚本能力点输入日期范围输出 Top 10 SKU 列表report.weekly_markdown报告生成能力点输入分析结果 JSON输出 Markdown 格式周报这里我非常刻意地做了一件事把第一个能力点做成参数化 SQL 模板而非自由 SQL。参数只有start_date和end_dateSQL 模板是写死在适配器里的。模型唯一能决定的是填哪两个日期。4.2 实际调用链路Agent 是怎么一步步完成任务的我给 Agent 下达的指令很简单请生成本周周一到周日的订单分析周报包括每日订单趋势、Top 10 商品和异常订单预警。然后 Agent 的完整决策链路是这样的第一步Agent 把指令解析为三个子目标聚合订单数据、分析商品排名、生成报告。第二步Agent 先调用db.order_daily_aggregate意图是查一下这周每天的订单情况。参数映射器把它翻译成start_date2025-03-10, end_date2025-03-16路由命中数据库适配器。第三步拿到聚合结果后Agent 发现需要把数据进一步加工成 Top 10 商品列表于是调用analysis.top_sku把上一步的结果 JSON 作为输入。第四步Agent 看到 Top 10 列表里有一个商品销量异常高是平时均值的 8 倍它主动额外查询了该商品最近 7 天的日销量变化。这里它复用了第一个能力点用 SKU 过滤字段又跑了一次参数化查询。第五步Agent 调用report.weekly_markdown把前面的所有结果整合成结构化 Markdown 周报。整个流程耗时 2 分 17 秒期间 Agent 总共发起了 7 次能力调用没有一次人工干预。我之前用纯 function calling 的框架跑过类似任务通常到第三步就开始出问题了——模型要么把参数填错要么直接编造一个不存在的工具名。4.3 实测结果与调优两个意外的发现第一次跑通之后我做了两轮调优有两个发现值得分享。发现一给能力点增加执行成本标签之后整个任务耗时降低了 40%。我在 Registry 里给每个能力点加了一个cost_score字段RPA 类操作 10 分数据库查询 3 分脚本分析 2 分。路由决策时会优先选低成本方案。最初 Agent 倾向于把每一步都交给 RPA 去模拟人工操作因为 RPA 能力描述看起来最全能加上成本标签之后它改成了优先走 API / 数据库直查只有 API 不可用时才走 RPA。这个优化立竿见影。发现二返回给模型的执行结果不能是完整原文必须摘要化。第一次跑的时候数据库查询返回了 2000 多行结果直接把上下文的可用长度打掉了一截。后来我在 Feedback Channel 里加了一个结构化摘要器对大结果自动聚合数值型字段算总和/均值/最大值文本型字段保留 Top N 条。模型拿到的仍然是完整信息但上下文消耗只有原来的十分之一。这个改动之后长链路任务的成功率明显提升了。5. 踩坑记录触达层最容易翻车的五个工程问题做完端到端实测之后我又把 Agent-Reach 接到了四个真实业务场景里跑了一周这期间踩了不少坑。我把最值得说的五个问题整理出来每一个都是我在日志里追了很久才定位到根因的。5.1 工具描述词描述得越全能模型越容易乱用我最早给一个支付接口写描述时写了支持查询支付结果、退款结果、对账单三个功能。结果实际代码只实现了查询支付结果模型却在用户要求退款时也调用了它导致返回空结果。这个问题的教训是能力描述必须和真实实现严格对齐宁可少写也别多写。多写一个未经测试的功能等于给模型埋了一颗雷。后来我定了一条规矩新能力点上线前必须有开发者手动确认描述词与实际行为一致性。5.2 超时参数的三层玄学框架超时 ≠ HTTP 超时 ≠ 业务超时跑一个内部报表接口时Agent 经常报错工具调用失败。排查了很久发现是超时设置的问题Agent 侧的请求超时是 5 秒HTTP 适配器内部又设了 3 秒而下游接口本身在高峰期要 6 秒才能返回。三层超时叠加正常请求也会被误杀。我的解决方案是给 Agent-Reach 引入了超时预算概念下游接口的超时时间在注册时按 P95 耗时设定适配器层超时 下游超时的 1.5 倍顶层调度超时 适配器超时的 2 倍。把超时参数分成模型可见和模型不可见两类——模型只感知最终结果不感知链路内部时间分配。5.3 上下文膨胀一次调用返回 500 万字符的迷惑行为这个坑是我印象最深的。某次联调时我让 Agent 查一个客户的订单历史结果客户下了 8000 多单查询接口把所有订单明细一次性返回了。那一次直接让模型的上下文窗口溢出整轮对话崩了。后来我在 Feedback Channel 里加了响应门禁任何单一能力点的回调结果超过 5000 字符就必须经过摘要器压缩。摘要器会针对数据类结果自动做聚合把明细变成统计。如果聚合之后还是太大比如 8000 条明细确实都需要就把结果缓存到外部存储只给模型返回一个结果已生成可用 req_idxxx 查询的占位引用。5.4 工具调用的循环暴走模型陷入某个工具一直调有一次 Agent 在做一个数据清洗任务时陷入了死循环——同一个清洗脚本被调了 19 次每次都报参数校验失败但它不更换策略反而换个参数值继续试。这个问题表面上看是模型推理能力不足实际上是触达层缺少重试护栏。我加了三重保护同一能力点在单次任务里调用超过 5 次触发预警连续 3 次相同参数的错误自动中断错误信息必须携带可执行的修正建议而不是单纯报错第三点最重要。之前脚本报错就一句话 processing failed模型根本不知道该改哪里。我在适配器层统一加了错误码映射把参数缺失类型不匹配权限不足服务不可用区分开并在错误码后面附上修正提示例如PARAM_MISSING: required field order_id is not provided, please check input schema。这个改动之后模型的乱试行为少了很多。5.5 多工具并行调用的潜在竞争资源配额被无辜打满我最初的设计里Agent 可以并行调用多个能力点来提高效率。某次压测时Agent 同一时间向数据库适配器发起了 6 个并发查询直接把开发库的连接池打满了导致其他正常业务跟着受影响。我的解决方案是给每个能力点增加了并发配额配置默认 3。超过配额的新请求进入等待队列而不是立刻打过去。同时在网关层做了能力点维度的限流——同一个任务里同一能力点的 QPS 上限是 5。这类问题在单个工具接入时根本注意不到但多工具并行编排的场景几乎是必然遇到的。5.6 关于模型幻觉调用不存在工具名的补救方案开始用的时候也遇到过模型突然输出一个根本没有注册过的工具名 get_user_balance_2025。最开始我没处理直接返回工具不存在然后整个对话就卡住了。后来我在 Reach Gateway 里加了一个纠偏机制模型输出的非法工具名会先做一次字符串归一化匹配看是否与已注册的能力点高度相近。比如 get_user_balance_2025 归一化后和 user.balance.get 匹配度很高网关会自动路由到正确能力点同时把修正信息反馈给模型下次它就不会再编名字了。兜底方案是返回未找到该能力时附带候选能力点列表引导模型重新选择。6. Agent-Reach 后续演进从触达层到能力生态跑完这几个业务场景我对触达层这件事的认知比刚启动时清楚了很多。Agent-Reach 本质上解决的是一个问题让 Agent 的能力边界不再受限于协议标准而是取决于你是否愿意为存量系统写一个适配器。MCP 解决的是标准世界的语言统一Agent-Reach 解决的是方言世界的触达自由。两者可以共存甚至配合使用——MCP Server 可以作为一个能力点被接入 Agent-Reach。下一步我计划把适配器生态往两个方向铺一是把内部所有有 HTTP 接口但没 MCP 适配器的系统快速接入二是把适配器开发模板开源出去。我目前已经把 HTTP 和 CLI 两类适配器的模板抽出来了新系统接入一个能力点只需要填一份 schema、写一个 handler大约半小时搞定。如果让我给正在做 Agent 应用的朋友一句忠告我会说别把精力全花在提示词和模型选型上多想一想你的 Agent 到底能不能触达它该触达的系统。模型负责聪明触达层负责可靠——这两件事缺一个都跑不远。
返回列表