1. 项目起点:为什么需要 Agent-Reach
这两年做大模型应用,身边所有人都在聊 Agent。朋友圈里晒得最多的就是"我用 AutoGPT 做了什么"、"我的 Agent 能自己订机票了"。但真正把 Agent 推到生产环境的人,心里都清楚一件事:模型本身从来不是瓶颈,卡脖子的是"触达"。
所谓触达,就是 Agent 能调多少工具、能连多少系统、能把一次真实业务操作跑通到哪一步。你让 Agent 写一首诗、总结一份文档,这很容易,因为只涉及纯文本生成。但你要是让 Agent 去查一下这个月的订单数据、帮用户提交一个退款申请、再同步一下客服工单状态,问题马上就来了——它能不能稳定地接通你们内部系统?调用失败了它怎么处理?它在什么范围内有权限做这些操作?这些事,模型一概不知道,得靠工程架构来解决。
Agent-Reach 这个名字,字面意思是"智能体的触达能力"。我最初把它定位成一个内部工具,用来解决 Agent 项目里反复出现的那些"够不着"、"不稳定"、"没记录"的问题。后来发现它实际上可以沉淀为一套通用的 Agent 触达与编排框架:负责把外部工具、内部 API、数据库、消息通道统一接入,让 Agent 只通过一套标准接口就能完成各种真实操作,同时把权限、超时、重试、审计这些底层脏活扛下来。
这篇文章把整个项目的设计思路、核心实现、踩坑记录和排查经验完整写出来。适合正在做 Agent 落地、或者准备把 AI 接入到生产线上的开发者参考。如果你只是调用一个现成的 API 做做 Demo,那 Agent-Reach 对你帮助不大;但如果你要把 Agent 接到真实业务系统里,这篇文章里的很多问题你早晚会遇到。
2. 项目整体设计与思路拆解
2.1 Agent 落地的真实瓶颈:模型不缺能力,缺"手脚"
回顾我自己做 Agent 项目的经历,最痛苦的不是 prompt 调不好,也不是模型选型,而是"让 Agent 真正动手干活"这一环。
早期我做过一个客服工单分类助手,模型部分用 GPT-4,效果非常好,分类准确率超过人工。但一到生产环境就崩了——不是因为模型分类分错了,而是因为 Agent 需要去查工单系统、需要知道这个用户是不是 VIP、需要把处理结果写回系统。这些操作每一个都要对接一套不同的 API 协议,有的走 REST,有的是老项目的 WebService,还有的得直接连数据库查。所有对接代码堆在一起,项目里充满了各种"胶水代码",改一个接口,牵一发动全身。
很多 Agent 项目死在这一步,不是模型不行,是 Agent 的手脚不够长、不够稳。
Agent-Reach 要解决的核心问题,就是把这些"手脚"统一管理起来。让 Agent 不用关心目标系统用什么协议、走什么认证、返回什么格式,只需要向 Agent-Reach 描述自己的需求,由 Agent-Reach 负责路由到正确的工具、执行调用、处理异常、返回结果。
整个设计围绕一个关键词:Reach,触达。具体拆成四个问题:
- 触达什么:Agent 能调哪些工具、哪些数据源,这是资源侧的接入问题。
- 怎么触达:Agent 的请求如何被路由到正确的工具,这是调度问题。
- 允不允许触达:谁能调这个工具,一次调用最多能消耗多少资源,这是权限与配额问题。
- 触达得怎么样:调用成不成功、耗了多久、返回了什么,这是可观测性问题。
想明白这四个问题之后,Agent-Reach 的架构其实已经出来了。
2.2 Agent-Reach 的核心架构:连接、路由、执行、观测四层
我见过很多 Agent 框架,上来就给你画一个非常复杂的调度图,各种消息中间件、状态机、向量数据库。但对于大多数团队来说,真正需要的不是复杂度,是清晰度。所以 Agent-Reach 从一开始就坚持一个原则:分层少、边界清楚、每层只做一件事。
整个架构分四层:
连接层(Connect):负责把各种不同的外部系统接入进来。每个接入目标被封装成一个 Connector,屏蔽掉底层协议差异。比如同样一个"查询订单"操作,电商系统走 HTTP API,老 ERP 走 FTP 文件交换,新数据平台走 GraphQL,但只要封装成 Connector,对上暴露的接口就是一致的。
路由层(Route):接收自然语言请求,把它匹配到具体的 Connector 和参数。这是 Agent-Reach 和"一个普通的 API 网关"最大的区别。普通网关是"调用方明确知道自己要调哪个接口",而 Agent 场景下,模型只知道自己要"查订单",并不知道应该调哪个 Connector,路由层需要解决这种语义不确定性。
执行层(Reach):真正发起调用,处理超时、重试、限流、幂等等问题。这一层相当于给 Agent 的每次触达上了一道保险,防止 Agent 因为一次网络抖动就处于失控状态。
观测层(Observe):记录每一次触达的完整链路:谁调的、调了什么、带了什么参数、返回了什么、花了多久、有没有报错。这层在生产环境中尤其重要。因为 Agent 的行为天然具有不确定性,如果没有完整的调用日志,出问题时你只能对着模型输出干瞪眼。
这四个层的设计不是一开始就定好的,是做了几个项目、烂摊子收多了之后才总结出来的。接下来我会把每一层的具体实现细节拆开讲。
3. 连接层设计:怎么让 Agent "够得着"所有系统
3.1 Connector 抽象:一个统一接口吃遍所有协议
连接层是 Agent-Reach 的第一道关。它的职责可以用一句大白话讲清楚:不管目标系统是什么技术栈,对上层统统表现为"给我输入参数,我给你返回结果"。
我定义了一个最小的 Connector 接口,核心只有三个方法:
class BaseConnector: def describe(self) -> dict: """返回这个连接器的能力描述,包括功能说明、参数 Schema、返回结果 Schema""" pass def validate(self, params: dict) -> dict: """校验参数合法性,转换成目标系统需要的格式""" pass def execute(self, params: dict, context: dict) -> dict: """实际发起调用,返回统一格式的结果""" pass这里最关键的是 describe 方法。它返回的是一份结构化能力描述,包含这个 Connector 能做什么、需要哪些参数、参数的类型和约束、返回结果的格式。为什么重要?因为 Agent 路由层做语义匹配时,全靠这份描述来判断"当前这个请求应该交给谁处理"。
你可能会问,为什么不让模型直接看函数签名然后决定调用哪个 API?我试过,最早期就是这么干的,把十几个 API 的文档直接塞给模型。结果模型经常误解参数含义,比如把用户的"手机号"字段传到"订单号"参数里,还振振有词地觉得自己没错。后来把所有工具统一描述成标准 Schema,再配合严格的参数校验,这个问题才基本解决。
在具体实现上,我封装了三种最常用的 Connector 类型:
- HttpConnector:适用于绝大多数 REST/GraphQL API,支持 GET/POST/PUT/DELETE,支持 JSON/Form 数据格式,认证方式支持 API Key、OAuth2、Basic Auth。
- SqlConnector:适用于需要直查数据库的场景。注意,这个连接器做了 SQL 白名单限制,只允许执行 SELECT 查询,写操作一律走专门的业务 API,避免 Agent 手滑执行了 DELETE。
- ScriptConnector:适用于执行本地脚本、调用命令行工具,比如让 Agent 跑一段 Python 脚本处理数据,或者执行一个运维脚本检查服务器状态。
这三种类型基本覆盖了绝大多数业务场景。每种 Connector 里,我最关注两个问题:参数映射和错误归一化。参数映射解决的是目标系统字段名不一致的问题,错误归一化则是把所有底层异常统一转成标准的错误码,后文会展开讲。
3.2 能力描述规范:让 Agent 真正"看懂"每个工具
Connector 的 describe 方法里返回的能力描述,实际是一份 JSON Schema,只不过我对它做了几个 Agent 场景特有的约定。
一个典型的能力描述长这样:
{ "name": "order.query", "description": "根据用户手机号或订单号查询订单详情,包含商品清单、金额、物流状态", "params": { "type": "object", "properties": { "phone": {"type": "string", "pattern": "^1\\d{10}$", "description": "用户手机号"}, "order_id": {"type": "string", "description": "订单号,格式为 20 位数字"} }, "oneOf": [{"required": ["phone"]}, {"required": ["order_id"]}] }, "returns": { "type": "object", "properties": { "order_status": {"type": "string", "enum": ["pending", "paid", "shipped", "completed", "cancelled"]}, "total_amount": {"type": "number"}, "items": {"type": "array"} } } }这里有三个跟普通 API 文档不一样的设计点。
第一个是参数约束必须可校验。上面 Schema 里的 pattern、oneOf 不是摆设,Connector 的 validate 方法会严格校验这些约束。模型传参不合法时,Agent-Reach 会返回一个结构化的错误信息,并自动引导模型修改参数后重试,而不是把错误抛给用户。
第二个是能力描述必须"面向任务"而不是"面向接口"。我见过很多团队做工具接入时,直接把后端接口的文档原样照搬当作工具描述,结果模型看到"createOrderV2"这种接口名根本不知道怎么用。Agent-Reach 的要求是:description 必须描述"这个工具解决什么问题",比如"创建一个新订单,需要提供商品 ID 和数量,会自动计算金额",而不是"POST /api/order 接口"。
第三个是返回结果必须标准化。我把所有结果统一包装成 {success, data, error} 三层结构。这样模型处理结果时逻辑极其简单:先看 success 字段,为真就提取 data,为假就读取 error 信息调整策略。
3.3 配置化接入流程:不写代码也能接新系统
Connector 的封装写起来不难,难得是每个新系统都要写一遍代码,费时费力。Agent-Reach 做了配置化改造,大部分系统接入已经不需要写代码了。
现在接入一个新 API 的完整流程是:在配置文件里声明这个 API 的 base_url、认证方式、接口路径、参数映射规则,然后写一份能力描述 JSON,重启服务即可。真正需要写 Python 代码的,只有那些本就无法通过标准 HTTP 访问的遗留系统。
配置文件大概长这样:
connectors: - name: erp.inventory.query type: http base_url: https://erp.internal.example.com endpoint: /api/v1/inventory/{sku_id} method: GET auth: type: api_key header_name: X-ERP-TOKEN secret_ref: secrets/erp_token params_mapping: sku_id: sku_id response_mapping: stock: data.stock_count warehouse: data.warehouse_name配置化的好处显而易见:接入新工具的时间从一两天缩短到两小时,而且配置本身可以纳入 Git 版本管理,每次改动都有记录,出了问题能追溯。
4. 路由与执行层:让 Agent 每次都"触达"正确
4.1 三级路由策略:从精确匹配到语义兜底
路由层的任务,是把 Agent 的自然语言请求映射到具体的 Connector。我踩过很多坑之后,总结了一套三级路由策略,从最硬到最软,逐级兜底。
第一级是精确匹配。如果请求里直接提到了工具名或明确的动作词,比如用户说"帮我查一下 order.query 这个功能",或者 prompt 里明确要求调用某个工具,直接命中对应 Connector。这级基本不会出错,但覆盖的场景有限。
第二级是语义匹配。这是最常用的路径,借助向量相似度把请求映射到 Connector。实现上,我把每个 Connector 的 describe 信息里的 name、description、params 字段拼成一段文本,用 embedding 模型转成向量存起来。运行时,把用户的请求也转成向量,计算余弦相似度,取 Top-K 个候选连接器。
这里我踩过一个大坑:只比较 description 的相似度远远不够。比如"帮我查快递到哪了"和"查询订单物流状态"语义上是同一件事,但字面上差异很大,单纯文本向量匹配的分数不高。后来我把返回结果的字段名、参数的取值范围说明也拼进向量文本里,命中率明显上升。核心逻辑是:能力描述向量 = 工具名 + 功能描述 + 参数说明 + 返回值说明,信息越全面,匹配越准。
第三级是通用兜底。当语义匹配的分数低于阈值时,说明请求大概率不在当前工具集的可覆盖范围内。这时候 Agent-Reach 不会硬编一个匹配结果,而是返回一个"未找到合适工具"的响应,并列出当前已接入的工具清单,让模型自己判断是改写请求重新匹配,还是如实告知用户能力边界。这个兜底行为极其重要,宁可让 Agent 承认自己做不到,也不能让它调错工具。
三级路由的完整判断流程可以这样理解:先用精确规则拦下明确的请求,再用向量匹配处理大部分自然语言需求,最后用阈值判断拦住匹配不上或置信度太低的请求,形成一个从高置信到低置信的漏斗。
4.2 权限与配额控制:不能让 Agent "为所欲为"
Agent 一旦能真正触达业务系统,权限控制就成了整个项目里最不能马虎的环节。我的原则是:给 Agent 的最小权限,应该小于等于给一个人工客服的最小权限。
Agent-Reach 的权限模型包含三个维度:
第一个维度是用户维度。系统要记录"当前这个 Agent 是在为谁服务"。A 用户登录后请求查询订单,Agent-Reach 会校验这个用户是否有查询订单的权限,以及他能否查询目标订单。这一层直接对接公司现有的 SSO 和权限系统,防止用户通过 Agent 越权访问别人的数据。
第二个维度是工具维度。每个 Connector 可以单独开关,也可以配置允许调用的用户组/角色。比如"订单退款"这个 Connector,只对客服主管角色开放,"查询库存"则对所有运营人员开放。这个维度的配置极其简单,一个 YAML 文件搞定,但它能避免很多灾难性事故。
第三个维度是资源配额。Agent 调用是有成本的,无论是 API 费用还是对内部系统的压力。Agent-Reach 支持每个 Connector 配置单次调用配额(比如最大返回行数、最大请求体大小)、每分钟调用频率、每日调用次数上限。超限就熔断,熔断后自动通知管理员。
我在生产环境里最常遇到的一个事故,就是 Agent 在循环重试一个失败的接口。模型发现调用失败后,会认为"我多试几次可能就成功了",然后以极高频率反复调用同一个接口,直接把下游系统打挂。后来我做了两个防护:一是单次会话里同一个 Connector 最多自动重试 3 次,且间隔递增;二是全局限流,同一个 Agent 实例对同一个 Connector 的调用频率超过阈值后,后续请求直接排队而不是立即执行。有了这两道闸,才彻底治好了"Agent 手滑连击"的问题。
4.3 执行保障:超时、重试、幂等一个都不能少
执行层处理的是真实调用过程中的各种不确定因素。三个词总结:超时、重试、幂等。
超时控制是我最早做的,也是最容易做错的。一开始我给所有 Connector 统一设置了 10 秒超时,结果有些秒级返回的接口被无谓地挂起 10 秒,有些本来需要 30 秒生成长的报表接口却一直被超时中断。后来改成每个 Connector 在能力描述里可以声明自己的超时时间,比如 order.query 设 5 秒,report.generate 设 60 秒。执行层按声明值动态调整超时设置。
重试策略要讲究"稳",但不能"傻"。Agent-Reach 的重试判断标准是:连接超时和 5xx 错误可以重试,4xx 错误(参数错误、认证失败)绝不重试。因为 4xx 错误重试一万次结果都一样,只会浪费资源、延时暴露问题。重试次数默认 3 次,采用递增间隔:0.5 秒、2 秒、5 秒。还有一个细节,重试前要重新走一遍参数校验,防止模型第一次传参不完整,第二次补全后才成功。
幂等是执行层里容易被忽略但极其重要的设计。Agent 和普通程序不一样,它自己不知道上次操作到底成没成功。比如 Agent 发起了一个"给用户退款"的调用,网络超时了,Agent 会重试。如果退款接口没有幂等保护,用户就被退了两次款。Agent-Reach 的做法是:每次调用生成一个全局唯一的 request_id,透传给下游系统,下游系统记录这个 request_id 与处理结果的对应关系。当重试请求带着同一个 request_id 到达时,下游直接返回上次的处理结果,不再重复执行。这个机制跟支付系统里的幂等键是一个思路。
5. 实战演练:让 Agent 自动处理一条客服工单
5.1 场景设定与工具准备
理论讲多了容易飘,我用一个完整的实战场景把前面这些设计串起来。
假设我们要做一个"智能客服工单处理 Agent"。用户通过客服页面提交了一个问题:"我上周买的手机订单还没发货,帮我查一下怎么回事。"
这个 Agent 需要触达的系统有三个:
- 用户中心服务:提供用户身份验证和基本信息查询
- 订单服务:提供订单查询、状态更新
- 工单服务:创建工单、更新工单状态
在 Agent-Reach 里接入这三个系统,每个系统只需要一个 HttpConnector 配置。以订单服务为例,配置文件里声明好 base_url、接口路径、参数映射。接入完成后的目标很明确:Agent 收到用户的咨询后,能够自动完成"验证用户身份 -> 查询订单 -> 获取物流状态 -> 把处理结果更新到工单"这一串真实业务操作。
5.2 Prompt 与工具描述配合:让 Agent 知道"何时用哪个工具"
工具接入好了,还有一个关键点:怎么让 Agent 在正确的场景里主动使用工具。
我在系统 prompt 里写明了工具使用规则,核心就三条:
第一条,先查再答。所有涉及具体数据的问题,必须先调用工具查询,不能凭记忆回答。防止模型"一本正经地胡说八道",比如虚构出一个根本不存在的订单号。
第二条,一次只做一件事。如果用户的诉求很复杂,拆成多个步骤,每一步先调用工具、拿到结果确认无误后,再进入下一步。不要让模型一次性写出"调用用户中心 + 调用订单服务 + 返回结果"的猜测性答案,因为后续步骤依赖于前一步的真实返回结果。
第三条,工具调用失败时不要瞎编替代方案。遇到明确报错(用户不存在、订单号格式错误),必须向用户索要正确信息或如实反馈,而不是伪造一个结果继续对话。
prompt 里有了这三条规则,配合 Connector 的能力描述,Agent 的行为就会稳定很多。这里我特别想强调"先查再答"这条,它几乎消除了 Agent 在业务场景里一半以上的幻觉问题。
5.3 完整执行链路拆解:一次真实对话背后的流程
现在用户发来消息:"我上周买的手机订单还没发货,帮我查一下怎么回事。"
这条消息进入 Agent-Reach 后的完整处理链路如下:
第一步,路由层接收请求文本"我上周买的手机订单还没发货,帮我查一下怎么回事",经过语义匹配,最高分的是 order.query 这个 Connector,置信度 0.91。
第二步,执行层调用 order.query,但参数校验发现缺少必填参数 order_id。Agent-Reach 不会直接报错,而是返回一个参数缺失提示,带上"需要 20 位订单号或 11 位手机号"的指引。
第三步,Agent 看到参数缺失提示,向用户追问:"查到您的账号了,请问方便提供一下订单号或者下单手机号吗?"用户回复了手机号。
第四步,Agent 带着手机号参数重新发起对 order.query 的调用。此时 Agent-Reach 先做权限校验——确认这个用户是否有查询该订单的权限。校验通过后,实际向订单服务发起 HTTP 请求。
第五步,订单服务返回该手机号最近的订单,状态为"paid",物流信息为空,说明还未发货。Agent-Reach 把返回结果包装成标准结构,即 success=true、data 包含订单号和状态字段。
第六步,Agent 拿到订单信息后,判断这是一个"未发货但已付款"的异常场景。它继续调用另一个工具,即工单服务里的 create_ticket,创建一个催发货工单,并把订单号、用户问题描述都填进去。
第七步,工单创建成功后,Agent 向用户回复:查到了,订单已付款,但目前还在备货中,已经帮您提交了催发货申请,工单号是 T20240613001,后续会有人跟进。
整个链路走完,用户只看到了两轮对话,但背后 Agent 已经完成了用户身份验证、订单查询、异常判断、工单创建四步真实操作。我的测试结果是,这个场景在工具配置完善的情况下,成功率能稳定达到 90% 以上,剩余 10% 基本是用户提供的手机号格式异常或订单确实不存在,属于需要人工介入的边界情况。
5.4 人工复核与回退机制:Agent 不是全自动的
我在做过一个版本之后,意识到"全自动"在真实业务环境下是不现实的。Agent 执行关键操作之前,需要插入人工复核节点。
我的做法是,给 Connector 增加一个叫 review_policy 的配置。对于"查询订单"这类只读操作,走全自动;对于"创建工单"这类写操作,默认走人工复核;对于"退款""删除数据"这类高风险操作,则强制双人复核,即主管和该业务负责人系统各审一道,全部通过后才会真的执行给下游系统。
具体实现是在执行层埋了一个 Hooks 机制。当某个 Connector 的调用被标记为"需要复核"时,请求不会立刻发送到目标系统,而是进入一个審批队列,由人工审核通过后再自动执行。Agent 在进入复核状态时会收到一个返回:操作已提交,等待人工确认。然后它会告诉用户,这个操作需要稍等片刻。
这个机制在项目上线初期帮了大忙,有几单高危操作都被人工审核拦了下来,原因是 Agent 在语境理解上还是会有偏差,比如它把"客户本人退款"和"帮客户发起退款"搞混了。人工复核是 Agent 落地时最实际的一道保险,宁可慢一点,不能错一点。
6. 常见问题与排查技巧实录
6.1 典型问题速查表
做 Agent-Reach 的这几个月,我在内部和几个合作团队里收集了不少典型问题,整理了一张速查表,遇到问题可以先对着这张表排查。
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent 明确提到某工具但路由匹配不到 | 精确匹配规则里没有包含该工具的别名 | 检查路由日志,查看用户原始请求和匹配分数 | 在 exact_match 配置里补充常见说法,如订单和"查单" |
| 所有请求都走兜底,匹配分数低 | 工具能力描述过于简略,或者向量索引未更新 | 检查 embedding 是否落后于工具描述变更 | 修改 describe 后重新生成本向量 |
| 工具调用频繁超时 | 下游系统本身慢,或超时设置不合理 | 查看观测层耗时分布,区分 P50/P99 | 调整该连接器单独的 timeout 配置 |
| 重试后仍然失败 | 下游系统持续性故障 | 查看错误码是否为 5xx 或连接超时 | 开启熔断,暂停该工具一段时间避免无效重试 |
| Agent 循环调用同一失败工具 | 模型认为多试几次能成功 | 查看调用链是否出现同一工具反复调用 | 启用单会话重试次数限制 |
| 查询到了别人的数据 | 权限校验未生效 | 检查当前用户的角色和授权范围 | 补齐用户维度到工具维度的授权映射 |
6.2 排查思路:从观测日志里找线索
Agent-Reach 的观测层是整个项目里投入产出比最高的模块。每次调用都会记录一条完整的 trace,包括 request_id、用户、Connector 名称、输入参数、输出结果、耗时、错误信息、路由命中方式等。
排查问题的第一原则:不要去看模型输出,先看 trace。因为模型输出只是表象,真正的问题往往发生在调用链路的某个环节。我举个例子,有次 Agent 总在"查询订单"后告诉用户"订单不存在",但用户明明能看到自己的订单。查 trace 发现,Agent 把用户提供的"手机号"传给了"order_id"参数,参数校验时手机号无法通过 order_id 的数字格式校验,于是返回了"订单不存在"。定位到这一步,解决方案就很简单:在能力描述中加强参数说明,同时在 promt 里明确要求确认参数含义后再调用。
第二个经验是给每次关键决策点打日志。我在路由层、参数校验、重试判断、权限校验这四个位置都打了结构化日志。出问题时,可以快速判断是匹配错了、参数错了、权限不够还是环境故障,不用从海量日志里大海捞针。
第三个经验是建立 trace 与对话的关联。用户的一次对话可能触发多次工具调用,我把这些调用都用同一个 session_id 串起来。这样如果用户反馈"机器人答非所问",可以通过 session_id 把整个对话期间的每一次工具调用都拉出来,一目了然。
6.3 经验教训:三个让我印象最深的坑
第一个坑:向量匹配太乐观。一开始我的兜底逻辑是"只要 Top-1 匹配分数大于 0.6 就用它",结果发现有时两个工具描述相似度很高,但实际用途截然不同。比如"订单列表查询"和"已删除订单查询",描述几乎一样,处理逻辑却完全不同。后来我加了一道"关键约束校验",在路由阶段预先检查请求里是否包含某个工具的必要信息(比如用户请求里没有订单号且没有手机号,就不可能路由到订单查询工具),把明显不满足前置条件的候选工具过滤掉,匹配准确率才上来。
第二个坑:生产环境中的配置漂移。有一次下游订单服务升级了接口,把订单状态的枚举值从 pending/paid 改成了 pending_payment/paid/unfulfilled,但 Agent-Reach 的返回 Schema 里还写的是旧枚举。结果 Agent 拿到新枚举后无法理解,返回结果全是"未知状态",用户体验瞬间崩塌。这次事故让我意识到,工具描述与下游系统的契约必须建立自动化检查机制,检测到下游接口变化时,主动标记该 Connector 为"不健康",暂停使用并通知维护人员更新描述。
第三个坑:把 Agent 当普通 API 调用方设计。普通 API 网关只关心"路由对不对、参数全不全、响应快不快",但 Agent 场景多了一个"语义理解偏差"的维度。用户说"查一下订单",Agent 可能理解为"查一下最近订单列表",也可能理解为"查一下某笔订单详情"。这两种理解调用的是不同的工具。Agent-Reach 之所以要保留三级路由而不是直接用全语义匹配,就是为了兼容"模型可能理解错"这件事。系统设计的默认假设越贴合模型的实际行为模式,就越稳。
7. 从 Agent-Reach 到通用 Agent 基建的一些反思
Agent-Reach 做到现在,我最大的感受是:Agent 落地的工程难度,不在模型侧,而在"触达"侧。模型的能力今天已经非常强了,真正决定一个 Agent 项目能否上线的是那些看似不起眼的基础设施——工具怎么接入、路由怎么设计、权限怎么控制、问题怎么追踪。
这个项目从一开始的"内部工具",逐渐演变成了一套相对通用的 Agent 基础设施,核心经验可以总结为几条:工具接入必须配置化,别让开发人力成为接入瓶颈;路由必须有多级容错,精确匹配兜不住所有自然语言;权限必须最小化并且能复核,Agent 不值得你无条件信任;观测必须全程留痕,否则出了问题就只能猜。
如果你也在做类似的 Agent 落地项目,我建议不要急着写业务代码,先把"触达"这件事想清楚。问自己几个问题:Agent 要连几个系统?每个系统的认证和参数规范是什么?谁来控制它的权限边界?调用失败时它该怎么做?有没有完整的调用日志能回溯?这几个问题有答案了,再开始动手写代码,你会发现整个开发过程顺畅得多。
我自己在实际使用中最受益的一个设计,是"所有工具都走统一 Schema 描述"这件小事。一开始觉得多写了很多冗余配置,甚至有点不耐烦。但后来排查问题的时候,几乎每一次都是靠着这份统一描述快速定位到"模型误解了参数含义"或者"工具描述还不够精确"这两类核心原因。现在回头看,这个当初觉得麻烦的设计,反而是整个系统里性价比最高的一笔投入。
最后再分享一个小技巧:Agent-Reach 的控制面板里,我把每个工具最近 24 小时的调用成功率、平均耗时、Top 失败原因都放在一页里。每天早上先扫一眼这块面板,哪些工具有异常、哪些外部系统不稳,几分钟就能掌握全局。做 Agent 基础设施,稳定性永远比花哨的功能重要。把"触达"这条链路管住了,Agent 才能真正从 Demo 走向生产环境。