AI Agent落地卡在系统触达?Agent-Reach连接层架构实践复盘

发布时间:2026/10/28 19:18:15
AI Agent落地卡在系统触达?Agent-Reach连接层架构实践复盘 最近大半年我几乎所有业余时间都耗在了 AI Agent 相关的事情上。聊到 Agent 落地圈子里有个很普遍的现象Demo 都跑得飞起一到接真实业务系统就集体卡壳——不是模型不够聪明而是 Agent 根本“触达”不了内部那堆基于各种年代技术栈构建的服务和数据库。我自己做语言大模型应用越深入就越确认这点大模型像一颗聪明的大脑可大脑再强没有一套可靠的神经系统把手脚和器官连接起来它也只能干瞪眼。这套神经系统就是我一直想解决的核心问题。断断续续折腾了几个月我从零搭起了一套叫 Agent-Reach 的连接与编排基础设施。它不是一个新框架不负责“怎么让 Agent 思考”它只负责一件事让 Agent 能够安全、可控、可观测地触达任何外部系统。这篇就当是我的工程复盘把架构决策、真实接入链路、我在生产环境踩过的几个大坑以及多 Agent 协作时的编排思路全部摊开来讲希望能给同样卡在“Agent 接不上业务”这一步的朋友一些踩得实的参考。1. Agent 项目普遍卡在“触达”这一步问题剖析与我的切入点1.1 一堆框架用下来真正缺的不是模型能力而是系统接入最初我做 Agent 的方式和大家差不多LangChain 串一串工具函数写一堆ReAct 循环一跑看着模型自己决定调用哪个工具确实挺兴奋。但等兴奋劲过了把这套东西从示例代码迁移到真实环境麻烦立刻就出来了。真实系统的认证方式五花八门有走 OAuth2 的有走内部 JWT 的还有不少老系统直接要求 IP 白名单加专用 Header接口返回格式更是没法看有的包装在{code, data, message}里有的直接就是一份巨大的 XML再加上字段命名混乱、超时设置随意、错误详情全靠肉眼观察Agent 只要多调几个接口上下文里就会塞满“看似成功但实际失败”的脏数据。那时的我并没有去质疑大模型的能力而是发现一个残酷的事实几乎所有 Agent 框架都默认“tools 已可用”然后就跳到“模型如何选择工具”这个环节。但现实里真正的成本恰恰被前置了——在让模型做选择之前你得先让几十上百个工具接口能被稳定、可信地调用。没有这层打通CoT 写得再细、Prompt 优化得再好也没用。这个问题属于典型的“集成工程”我决定自己做一层专门解决这个问题的东西。1.2 触达的三个层次数据、工具、状态把“触达”这个词拆开看它其实包含三个层次每一层都会让 Agent 从“能用”变成“难用”。数据触达解决的是“模型能不能拿到需要的原始信息”。数据库在哪儿、表结构是什么、有没有数仓入口、文件系统里的资料怎么索引。这一层最容易被低估尤其当你需要连的是一堆日志文件或者第三方 SaaS 的历史数据时“取数”本身就成了瓶颈。工具触达解决的是“模型能不能安全地产生副作用”。查订单是读操作改库存、发邮件、创建工单是写操作一个成熟的系统不可能把所有函数直接暴露给模型。就像你不可能把一个没上保险丝的电钻直接递给用户触达层必须在“开放能力”和“真实副作用”之间做控制。状态触达解决的是“多轮交互里状态怎么延续”。一次自然语言请求拆成多个工具调用中间失败了怎么恢复用户中途改了条件怎么重新路由对话状态的保存、分支、回滚这些如果都交给模型自己管理逻辑很快就失控了。想清楚这三点之后我的目标就明确了做一套基础设施把这三个层面的触达通通标准化让上面的应用层不用再操心连接和管控让下面的实际系统不用感知 Agent 的存在。1.3 Agent-Reach 的定位连接层不做新轮子定位这件事最开始我有点贪心想顺手把 Agent 的规划Planning也一起做了。做得越多越觉得不对规划是模型和 Prompt 的强项轮不到中间件插手。Agent-Reach 应该只做连接层把自己限定在两个问题里第一如何屏蔽外部系统的异构性第二如何保证每一次触达都是安全、可观测、可审计的。一旦想清楚这个边界后面所有功能选型都有了依据。我给它画了一层非常直观的定位图最上面是各种 Agent 应用最下面是各种数据库、HTTP 服务、消息队列和文件系统。Agent-Reach 横在中间左边是北向 API把标准化的触达能力暴露给 Agent右边是南向连接器把异构协议翻译成统一 Schema。中间那层是策略引擎和链路追踪。这个结构决定了后续的所有演进也让我在和朋友讨论时能一句话讲明白Agent-Reach 就是给 Agent 装上一套标准化的“手和脚”。2. Agent-Reach 架构拆解连接器、触达协议与策略层2.1 连接器层把异构系统规范成同一种 Schema既然核心是屏蔽异构连接器就得设计得像充电器一样不同设备的充电协议不同但只要都插到同一个 USB 口上就能统一供电。Agent-Reach 里的连接器就是那个 USB 口。每一个连接器对外暴露的是一组标准化的动作声明Action Schema动作的输入输出都遵循同一套字段约定后端是 MySQL 也好、Salesforce 也好、一个只有远古 SOAP 接口的财务系统也好对外呈现都是清一色的 JSON 结构。我第一次写的连接器是一个只读 MySQL 数据源配置大概是这样的name: order_db type: mysql version: 1.0 actions: - action: query_orders description: 按时间范围和订单状态查询订单列表 input: - name: start_time type: string required: true description: 起始时间格式 yyyy-MM-dd HH:mm:ss - name: end_time type: string required: true - name: status type: string enum: [pending, paid, shipped, cancelled] optional: true output: type: object fields: order_id: string customer_id: string status: string amount: number关键在于“Action Schema”这套东西描述用的是纯 JSON Schema模型可以直接读“query_orders”这个名字本身语义清晰模型不需要额外理解 SQL 结构就知道该调什么。真正在连接器底层我封了一层查询构造逻辑把 start_time 这类参数映射到 SQL 条件上。字段说明写得越细模型选错参数的概率就越低。HTTP 类连接器我一般直接用 OpenAPI 导入生成。把现有系统的 API 文档喂给 Agent-Reach它会自动把每个 endpoint 转成一个 action连参数枚举都由拉取验证。这一步省了我大量人工定义的时间也降低了维护负担。表格是很适合呈现多样性的场景我用下面的对照表来说明三类连接器面对的核心挑战连接器类型典型系统难点解决方式数据库类MySQL / PostgreSQL / ClickHouse表结构多、SQL 注入风险白名单 SQL 模板 参数化绑定HTTP 类内部微服务、SaaS API鉴权方式杂、限流策略不同OAuth2/JWT/签名统一注入文件类对象存储、日志目录文件遍历慢、格式不可控索引预扫描 按会话导出2.2 触达协议给 Agent 和工具之间定一套“通用语言”连接器解决了“系统之间怎么对接”接下来要解决的问题是“Agent 和连接器之间怎么对接”。HTTP 之所以成功是因为它有统一的方法、状态码和头部约定而 Agent 生态里最缺的正是这种约定。我不想每个团队都自己发明一套“用自然语言描述工具调用需求”的格式所以在 Agent-Reach 里做了一层轻量的触达协议我内部管它叫 RAPReach Action Protocol处理的是“一次触达动作”的统一请求与响应包装。一次请求的核心字段大概是这样的{ reach_version: 1.0, action: order_db.query_orders, action_id: a3f1c2b1-9d4e-4a8f-b7c0-2e5a1d0f3c9b, args: { start_time: 2025-04-01 00:00:00, end_time: 2025-04-30 23:59:59, status: paid }, context: { user_id: u_1024, session_id: s_77881 }, timeout_ms: 3000 }action_id是全链路唯一的我用它来做超时追踪和幂等context会贯穿到审计日志里方便回答“谁在什么会话里让 Agent 做了这件事”。响应侧也做了标准化统一分为success、user_error参数错误模型可自行修正后重试、system_error系统故障应该直接放弃或升级和timeout响应超时需要触发幂等逻辑。这四类区分很有必要否则模型收到一段报错不知道是自己传参传错了还是对方的服务崩了就会无限重试。RAP 的价值不在于协议本身多复杂而在于给了模型一个极低成本的解析模型字段名固定、错误分类固定、状态字段永远是第一优先级。我实测下来模型理解 RAP 响应格式的准确率远高于解析“每个接口自己定义的错误体”。2.3 策略层权限、限流、审计一个都不能少连接器让触达“可能”策略层让触达“被允许”。这是我认为 Agent-Reach 和普通工具调用 SDK 之间最大的区别。接口一旦变成 Agent 可以调用的动作就不再有“前端按钮”这种人为防线策略必须前置到框架层。权限模型上我做了最小粒度的控制每一个 action 都可以绑定角色和白名单。比如order_db.query_orders只允许被销售场景的 Agent 调用crm.create_ticket则必须要求请求上下文里的 user_id 具备对应权限否则直接拒绝。策略决策发生在每次触达之前有一票否决权模型就算规划得再离谱也绕不过去。限流和熔断也放在这层。单个 Agent 的 QPS 上限、单连接器的并发数、连续失败后的熔断开关这些都能通过一段简单的配置表达policies: - action: order_db.query_orders limit: qps: 5 burst: 10 circuit_breaker: fail_threshold: 20 reset_after_sec: 60 audit: true每个动作默认开启审计记录动作名、参数、调用者、会话、耗时和结果。这套审计日志后来救了我很多次——有用户质疑“Agent 是不是擅自改了数据”一查链路发现所有写操作都被策略层拦截了日志里清清楚楚。3. 一项真实接入实验让 Agent 查询 MySQL 并调用订单 API3.1 环境准备与注册数据源说了这么多架构来点实操。这一节我演示一个最典型的场景搭一个本地 Agent-Reach 服务注册一个 MySQL 订单库和一个订单查询 API然后用自然语言让 Agent 完成“查一下这个月付费订单超过 500 元的客户有哪些”。部署 Agent-Reach 本身很简单它是个无状态的中间层可以单独跑一个进程也可以集群部署。我本地用 Docker 起了一个 reachd 服务配置里指定了监听端口和后端存储用来存策略配置和审计日志开发环境用 SQLite 就够了生产环境切 PostgreSQL。启动之后就是注册数据源。我用 Agent-Reach 自带的命令行工具把刚才那份order_db连接器配置提交上去再执行一条命令导入订单 API 的 OpenAPI 定义。整个注册过程会立刻做一次连通性校验连接器会真的去试连一次数据库失败会反馈具体的网络或认证错误不会把坏配置假装注册成功。3.2 Agent 自然语言请求的触达链路全流程环境就绪后我通过 Agent-Reach 的北向 API 发了一条自然语言请求“帮我统计一下 4 月份已付款订单总共多少钱顺便告诉我是哪些客户贡献的。”这条请求的触达过程可以被拆成五步。第一步Agent 先把自然语言映射成动作意图它选择了order_db.query_orders第二步Agent-Reach 的策略引擎检查权限、限流和审计规则通过第三步连接器把参数翻译成 SQL 并执行查询第四步结果按 RAP 格式返回触达链路追踪信息也一并回传第五步Agent 基于返回的 JSON 生成最终的自然语言回答。从技术角度Agent-Reach 本身不负责第一步的意图理解和最后一步的语言生成这两步由大模型完成。它管的是中间三步保证任何触达都经过同一个受控管道。下面的 Python 示例展示了如何使用 Agent-Reach 的 SDK 在本地模拟这个过程from reach_sdk import ReachClient, ActionRequest client ReachClient(http://localhost:8600, api_keydev_xxx) # 一次标准触达 req ActionRequest( actionorder_db.query_orders, args{ start_time: 2025-04-01 00:00:00, end_time: 2025-04-30 23:59:59, status: paid }, context{user_id: u_1024, session_id: s_77881}, timeout_ms5000, ) resp client.execute(req) if resp.is_success(): total_amount sum(row[amount] for row in resp.data[items]) print(f4月已付款订单总额: {total_amount}) else: print(f触达失败: {resp.error.category} / {resp.error.message})这段代码看着简单但在没有 Agent-Reach 之前我得给每个接口分别写调用客户端、处理各自的鉴权和错误格式还要手工加审计日志。这套封装省掉的不是那几十行代码而是“每一个新接口接入都要重新走一遍既有流程”的隐性成本。3.3 用 Reach Trace 排查一次慢查询任何系统接入真实环境一定会遇到性能问题Agent-Reach 的链路追踪在这里发挥了很大作用。我在实验时执行了一次请求明显感觉到返回慢模型都开始胡言乱语了。换做以前我只能在数据库侧慢慢看慢日志。这次我直接打开 Reach Trace看到触达链路里每一步的时间分布RAP 解析耗时 2 毫秒策略检查 5 毫秒然后连接器执行 SQL 阶段耗时 4.8 秒连接器内部又细分为“建立连接 4.5 秒”和“执行查询 0.3 秒”。问题一目了然连接池没生效每次查询都重新建立数据库连接。我修复了连接器的连接复用配置整体触达耗时立刻降到 260 毫秒。Reach Trace 的价值就在这里——它把“Agent 答不好”的问题拆解为“具体哪一段链路慢了”让优化不再靠猜。我还做了一个不算复杂但很实用的小功能把每次触达的参数摘要和返回条数也记录下来方便在追踪页面上看到类似“这个 Agent 每次请求都会带一个超大参数实际只有前 3 个字段是必须的”这种浪费模型 token 的坏习惯。4. 触达链路的高危雷区权限失真、重复副作用与上下文爆炸4.1 身份失真Agent 调用系统时到底该用谁的凭证前几节演示的都是顺利路径真正让我长记性的是在生产准备阶段踩的坑。第一个坑来自身份失真。最初我把所有 Agent 都配置成一个服务账号逻辑上“Agent 是系统的一个内部服务只要这个服务账号授权的数据源它都能访问”。听起来没什么问题但真实业务里立刻暴露了风险服务账号回答了“以系统的名义查”却回答不了“是哪个员工让系统去查的”。于是订单 Agent 可能因为某个客服人员的一句话去调用了那个客服本不该看到的高级别数据。权限本质上没被绕过但被“服务账号”这个抽象层给放大了。解决思路是“身份传递”触达时要带着末端用户的身份走完整条链路。Agent-Reach 在每个请求的 context 里强制要求带上 user_id 和权限范围策略层把这个身份和具体的 action 白名单做二次匹配。这意味着触达动作的授权判定是“Agent 角色 用户的权限范围”双重叠加不是其中任何一个。改完之后我再也没敢回到单纯服务账号的模式。4.2 超时重试引发的订单重复幂等键救场第二个坑特别典型。某个调用下单 API 的 Agent 在线上跑了一阵之后有客户投诉说订单被重复创建了。排查了一圈问题出在 Agent 侧的自动重试外部 API 响应太慢SDK 触发了超时Agent 觉得“这次动作可能没执行成功”于是重新调了一次下单接口。实际上第一次调用在服务端已经成功了只是响应报文超时没回来。Agent 出于好心重试一次结果就多了一个订单。Agent-Reach 的 RAP 协议里加了幂等机制每个携带副作用的动作都会要求调用方传一个idempotency_key服务端按这个键缓存执行结果。同一个键只执行一次后续所有重复触达直接返回第一次的结果。这个设计其实借鉴了支付系统很早的幂等方案但多数 Agent 框架都忽略了它。重试本身没问题重试之前必须想清楚“如果上一次其实成功了怎么办”。因为加了幂等校验Agent-Reach 的action_id还多了一层用途既能做链路追踪又能天然充当幂等键。同一会话内重试同一个 action_id旧结果直接复用不给外部系统造成二次副作用。4.3 返回结果撑爆上下文输出裁剪与摘要路由第三个坑藏得最深也最让人头疼。有一次我给数据分析类 Agent 接入一个数据仓库连接器测试阶段一切正常放到真实环境后模型开始频繁输出无关内容甚至复述数据库字段名。追到触达日志里才发现一次查询返回了 8000 多行数据JSON 序列化后大约有 1MB。这 1MB 全部塞进了模型的上下文窗口不仅让模型“选择性失明”还让整轮对话的 token 成本暴涨。原因很直白连接器返回的原始数据是给程序看的不是给大模型看的。Agent-Reach 的处理方式是在连接器层增加“输出回流”能力对于大数据量结果先做聚合、摘要、截断而不是原样返回。现在每次触达返回的默认上限是 200 行结构化摘要如果模型确实需要原始明细它可以再发起一个分页触达动作去取下一页。这个改动带来的效果非常明显。模型不再被无关字段干扰输出的相关性和稳定性显著提升。也是这次改造让我坚信触达层不能只做“传输”它还得做“翻译”把程序世界的原生数据翻译成模型世界能消化的小份信息。5. 多 Agent 编排的触达网络从单兵作战到协同联动5.1 为什么要做多 Agent 触达两类真实需求在 Agent-Reach 稳定支撑单 Agent 场景之后很自然的下一步是多 Agent。这个需求的驱动力来自两类真实场景。一类是“纵向分工”一个复杂工单大概率不是某个 Agent 能独立搞定的需要客服 Agent 判断意图、工单 Agent 创建记录、库存 Agent 核验可用量、通知 Agent 发消息每个 Agent 有相对单一的能力边界。另一类是“横向扩展”某个高频 Agent 的负载已经很高与其打破单 Agent 的能力边界不如保持一套能力语义部署多个实例用路由把请求分散到不同实例上。这两种需求都指向同一个底层能力Agent 之间需要像“服务调用”一样实现互触达。Agent-Reach 的多 Agent 编排本质上就是把这个互触达变成可路由、可追踪、可管控的内部调用关系。5.2 能力路由表驱动的请求流转我不想把多 Agent 编排放到 Agent 自身内部逻辑里去实现那样会把编排和被编排的对象混在一起复杂度很快就失控了。Agent-Reach 的做法是维护一张能力路由表每个 Agent 在接入时提供一份能力描述文件声明“我能做什么、需要什么输入、产出什么输出”。路由服务收到上级 Agent 的请求时根据能力描述匹配目标 Agent再把请求转发过去。能力描述其实也是一种“触达声明”和连接器的 action 声明很像。我把每个 Agent 当作一个特殊的“连接器”这带来一个很自然的统一抽象无论下游是数据库、外部 API还是另一个 Agent从调用方的视角看都是一次 RAP 请求。这大概是我在整套架构里最满意的一个简洁决策——编排不需要发明新协议复用同一套动作协议即可。5.3 扇出与聚合多 Agent 结果如何合流一旦请求可以被转发马上就出现第二个问题一次上级请求需要同时触达多个 Agent结果如何聚合后再回给模型。我做了两种模式。第一种是“扇出-聚合”适合多个 Agent 的工作相互独立。上级 Agent 一次性向三个子 Agent 发触达动作三个结果全部收集成功后统一汇总成一份合并结果任何一个子 Agent 失败都可以决定整体失败或部分成功。第二种是“串行流水线”适合有依赖关系的动作比如必须先创建工单、再核验库存、最后发通知。串行流水线的调度状态全部记录在链路追踪里任何一步失败都能明确看到卡在哪。这两种模式我都写成了配置化的编排模板而不是硬编码在 Agent 逻辑里。业务方想改流程顺序改一下模板配置就可以重新生效完全不用动代码。从工程运维角度这种“流程与逻辑分离”的架构把 Agent 应用本身的复杂度降了一个量级。6. 上线后的运维视角可观测性、告警与成本控制6.1 核心监控指标成功率、P95、错误分布Agent-Reach 上了生产之后我把它当成一个正经的基础设施来运维首先建立的就是监控体系。核心指标我不看太多就看三组。第一组是触达成功率按 action 维度和连接器维度分别统计。成功率下跌往往直接对应外部系统或者权限配置变更。第二组是延迟分布尤其 P95 和 P99。Agent 对慢系统特别敏感模型在等待期间可能在“自由发挥”所以延迟指标直接关联回答质量。第三组是错误分布按 RAP 协议里的错误类别参数错误、系统错误、超时、限流分层统计。限流触发率过高可能意味着某些 action 的并发配置不对参数错误率过高往往指向连接器的 schema 描述不够清晰——后者模型根本不会自己修复需要人来优化声明。告警规则我配置得很克制只对成功率低于 99%、P99 超过 3 秒、限流拦截率超过 2% 这三种情况发告警。宁可少收到一些噪音告警也不能让团队对告警产生疲劳。6.2 触达链路追踪与日志关联监控指标负责“告诉我出问题了”链路追踪负责“告诉我哪里出问题了”。Agent-Reach 每次触达的 action_id 会贯穿整条链路包括 Agent 内部生成的 token 消耗、模型的推理记录以及框架侧的触达记录。这三个来源的数据以 action_id 为关联键可以拼出一条完整的“用户请求模型决定触达外部响应生成回答”的全链条日志。排查问题时最大的幸福感来自于不用看三方日志对时间线。我只需要输入 session_id就能拉出这次会话里所有触达动作的完整时间线、参数摘要、返回码、耗时和 token 消耗。Agent 应用在运维侧能像传统的分布式调用链一样被追踪这在早期的 Agent 项目里是几乎做不到的而现在被我视为 Agent-Reach 上线的必备组件。6.3 成本优化三板斧缓存、批处理、模型分级最后聊成本。大模型和传统后端的成本逻辑很不一样一次低质量触达不仅浪费了外部系统资源还浪费了大量 token 和用户耐心。所以 Agent-Reach 在成本控制上做了一些机制核心是把无关信息挡在触达之前。缓存是第一板斧。相同参数的热点查询直接走缓存不进入连接器也不消耗模型 token。这个改动让热门查询的平均响应时间缩短了 70%同时大幅降低了 token 消耗。批处理是第二板斧。某些场景一次会话里会有多个结构一致的查询需求我用连接器层面的批量动作把多次请求合并为一次调用再拆分返回。对数据库这类连接型系统一次大查询远优于二十次叠加查询。模型分级是第三板斧也是最有 Agent 特色的简单可控的触达动作比如查明细、算总和、转换格式我直接用一个小参数模型做参数提取需要复杂推理的多步规划才调用满血大模型。因为两次触达对模型档位的需求不同Agent-Reach 的 action 定义里允许标注 recommended_model_levelAgent SDK 会优先按这个级别选择模型。做完这三板斧整体成本下降了大约一半而生成质量没有可感知的下降。成本优化的核心思路始终是不要为用不上的信息付费不要让大模型做小模型就能做的事。最后再分享一个我在整个过程里最深的体会。Agent 能不能落地往往不看模型层而看触达层的可靠程度。Agent-Reach 做完之后我拿同样的数据源分别跑过普通函数调用方案和带连接器方案同样是查询订单前者的错误率明显更高而且很难回溯——因为错误都散落在模型层和外部接口的各种角落里。真正把 Agent 从“玩具”推向“工具”的是背后的连接、策略、追踪这些基础设施。如果你也在搭 Agent我建议你尽早把“触达”提到和“规划”同等重要的位置来设计哪怕一开始只有一层简单的封装也比事后补要省太多时间。