先说说我做 Agent-Reach 这个项目的动机。过去半年我一直在折腾大模型应用,团队里的智能体 Demo 一个接一个,可真到上线阶段,麻烦全挤在“触达”这两个字上。你让模型写文案、写代码,它很能打;但你要它查一下真实订单状态、调一下内部审批接口、读一下今天的实时报表,它就傻了——大模型自己不会“伸手”,它只能给你一段看起来合理、实际上是编出来的回答。
Agent-Reach 就是为解决这类问题设计的一个连接层:让大模型智能体通过一套统一协议,去调用真实世界里的工具、内部系统、SaaS 服务和既有 API。可以把它理解成“智能体与外部系统之间的网关 + 适配器全家桶”。这篇文章我会从为什么需要它、协议怎么设计、适配器怎么写、上线怎么排查,完整地过一遍。如果你正在做 Agent 应用,头疼怎么把 LLM 和业务系统真正打通,下面这些内容可以直接拿去用。
1. 项目整体设计与思路拆解
1.1 大模型只会“说”,不会“做”,连接层就是那双“手”
先想清楚一个问题:大模型本身是一个文本生成模型,它的知识截止到训练数据那一天,它没有任何实时数据的访问能力,也碰不到你公司内网里的任何一个系统。模型“知道”订单通常有已下单、已发货、运输中、已签收这些状态,但它并不知道你这条订单当前到底是什么状态。所以在 Agent 架构里,模型负责的是“理解意图 + 生成调用计划”,真正去查数据、去改状态、去发消息的,必须是一套外部执行机制。
早期我做智能体的时候,走了不少弯路。一开始是给模型写一堆 Python 函数,然后在代码里判断模型说了什么再手动去调。后来用 Function Calling,模型能输出结构化的参数了,但参数输出了又怎样?你还是得在代码里写一个个if function_name == "query_order":的分支去接业务系统。每接入一个系统,就要写一遍鉴权、写一遍错误处理、写一遍超时重试,工具一多,这团胶水代码就完全不可维护了。
Agent-Reach 的思路是把这层胶水抽出来,变成标准化的中间层:智能体只认识一套统一协议,连接层负责解析、鉴权、路由、执行、返回。用个生活化的类比,这就像 USB-C 接口——你不需要给每个设备定制一根专用线,设备内部再怎么千奇百怪,对外都是同一个标准,电脑那边也只需要一个协议去适配。Agent-Reach 就是给外部系统“开了个统一口”,让模型轻松插上去。
1.2 核心架构拆解:统一协议 + 适配器(Reacher)
整个项目分三层来看,非常清爽。
第一层是智能体侧,也就是各种大模型,GPT、Claude、通义、文心,或者开源部署的 Qwen、Llama 都行。模型不关心你后端是怎么实现的,它只负责根据用户问题,决定“要调用哪个能力、传什么参数”。
第二层是 Agent-Reach 连接层本身,我把它叫做 Reach Server。它是一个独立部署的服务,对外暴露一个 HTTP 接口,接收智能体发来的调用请求,做参数校验、权限校验、超时控制,然后把请求路由到对应的适配器上去。
第三层是各种适配器,我管它叫 Reacher。每个外部系统对应一个适配器,适配器负责把统一参数翻译成目标系统的原始 API 调用。比如订单系统的参数是订单号,报表系统的参数是日期区间,模型不需要关心这些差异,它只需要知道“这个能力叫什么、需要提供什么字段”。
这个三层结构最核心的好处是解耦。外部系统改了接口,只需要改对应的 Reacher,智能体侧完全无感知;新增一个业务系统,也只需要写一个新的 Reacher 并注册,不需要改模型的那套调用逻辑。
1.3 为什么不直接复用 LangChain Tools 或 Function Calling
我在设计初期其实想过直接套 LangChain 或者纯用 Function Calling,但实际一摆,各有各的别扭。
| 方案 | 优点 | 主要问题 |
|---|---|---|
| 原生 Function Calling | 模型支持好,参数解析稳定 | 只解决“模型如何输出参数”,不解决“参数如何调到业务系统”,接入代码还得自己写 |
| LangChain Tools | 工具封装友好,社区生态多 | 绑定 Python 和 LangChain 框架,换技术栈就废,企业内部治理能力偏弱 |
| MCP(Model Context Protocol) | 标准推进快,方向正确 | 对内部系统接入偏重,协议相对厚重,小型团队上手成本高 |
| Agent-Reach | 轻量、语言无关、偏企业内部集成 | 需要自己维护协议和适配器生态 |
我个人的取舍标准很简单:Agent-Reach 定位是“企业内部系统与智能体之间的专用通道”,它不试图替代模型层的东西,而是专注把“调用外部系统”这件事做得扎实——有鉴权、有审计、有错误码规范、有超时熔断。这些恰恰是把 Agent 推到生产环境时最缺的部分。
1.4 技术选型背后的几个关键决定
实现语言我选了 Python。原因很直白:AI 生态里 Python 最顺手,团队里随便找一个算法工程师就能看懂,而且对接各种 SDK 的成本最低。连接层本身并不复杂,不像是要压到极致性能的高并发网关,Python 完全扛得住,真要性能不够,后面把鉴权和路由模块单独拆成 Go 服务也容易。
协议上我选了 JSON-RPC 风格的轻量协议,没有用 GraphQL,也没有用 gRPC。原因有两个:第一,JSON 结构对模型来说是天然友好的,模型生成 JSON 最稳定,解析也最不容易出错;第二,HTTP + JSON 这种组合排障时太方便了,直接curl一下就能模拟调用,不用装额外的客户端工具。传输层用 HTTP/HTTPS,后续要支持流式输出再加 SSE,演进空间留有即可。
注册中心这块,单机部署的时候直接用内存字典就够了;上了多节点的生产环境,可以换成 etcd 或者 Redis。Reacher 启动时把自己的能力元数据注册到中心,并周期性发心跳,连接层通过注册中心做服务发现和路由。整体方向就是无状态设计,Reach Server 自身不保存任何业务状态,只做转发,这样水平扩展就是加节点的事。
2. 核心细节解析与实操要点
2.1 调用协议:每一个字段都是有意为之的
整个项目最值钱的部分就是这套协议。先看请求结构:
{ "request_id": "req_20250201_ab12cd34", "trace_id": "trace_7f3e9d21", "capability": "order.query", "params": { "order_id": "ORD20240001" }, "timeout_ms": 5000, "caller": "assistant" }再看响应结构:
{ "request_id": "req_20250201_ab12cd34", "status": "success", "data": { "order_id": "ORD20240001", "status": "运输中", "estimated_delivery": "2025-02-03 18:00" }, "error": null, "usage_ms": 132 }这里每个字段都有讲究。request_id是整个链路唯一标识,做幂等和审计都靠它;重复调用同一个 request_id,Reach Server 会直接返回缓存结果,避免模型或者业务方因为网络超时重复下单、重复扣款。trace_id是贯穿智能体、连接层、外部系统的链路 ID,出了问题一路查日志,能精确到是哪一跳慢。capability是能力名,命名我推荐用“领域.动词”的格式,比如order.query、invoice.create,这样层次清晰,模型也容易理解。timeout_ms是本次调用的超时上限,每个能力还有一个默认值,两者取更小值生效。
错误码的规范同样重要。我定了几个基础码:AUTH_FAILED鉴权失败、NOT_FOUND能力不存在、VALIDATION_ERROR参数校验不过、UPSTREAM_TIMEOUT上游超时、UPSTREAM_ERROR上游业务异常、RATE_LIMITED触发限流。为什么错误码要分这么细?因为模型拿到错误信息之后,是要根据它做自我纠正或者向用户解释的。你给它一个笼统的“failed”,它大概率只能回一句“出错了”;你给它VALIDATION_ERROR: order_id 格式不正确,正确格式为 ORD 后接 8 位数字,它就能礼貌地让用户重新提供订单号。
2.2 能力注册与发现:让大模型“看见”你的系统
Reacher 注册时会上报一份元数据,这份元数据既是给连接层做路由用的,也是给模型做决策用的,字段如下:
{ "name": "order.query", "description": "按订单号查询订单实时状态和预计送达时间。适用于用户询问订单进度、物流状态时。", "params_schema": { "...": "JSON Schema" }, "permission": "order:read", "timeout_ms": 5000, "idempotent": true, "version": "1.0.0" }这里面坑最多的是description。我反复强调一句话:description 是写给模型看的,不是写给程序员看的。它不应该写“本接口用于调用订单中心”,而应该写清楚“什么场景下用、需要哪些参数、参数什么格式、返回里有什么信息”。一段描述写成“适用于用户询问订单进度、物流状态”和写成“查询订单”的效果天差地别。前者模型在用户问“我买的电脑什么时候到”时就会主动命中,后者模型可能根本没想到去调用。
params_schema建议直接用 JSON Schema 标准。它有两个作用:一是连接层校验参数合法性,传错了直接拒绝,不用去打扰上游系统;二是会被我后面提到的工具构建逻辑直接转成模型的 function parameters,所以 schema 里每个字段的description也要写清楚,例如订单号的正则格式。这块写好了,模型的参数填充准确率能提升一大截。
分布式环境下,注册中心我建议用 Redis 或者 etcd 存储能力元数据和 Reacher 实例地址,Reacher 每 10 秒发一次心跳,连续三次没收到心跳就摘除节点。单机环境就简单了,Reach Server 启动时扫描配置目录,动态加载所有 Reacher 模块即可。
2.3 权限、审计与安全:Agent 调用不等于放权
这是最容易在 Demo 阶段被忽略、上线时被安全团队找上门的部分。模型本身没有安全意识,它可能被用户的提示词诱导,去调用它本不该碰的能力。所以权限控制的唯一原则就是:以最小权限为默认,每个能力绑定一个权限标识,由调用方声明身份,连接层负责鉴权。
我这里的做法是给每个能力配置permission属性,比如订单查询需要order:read,创建订单需要order:write。智能体侧请求时携带访问令牌,令牌里标明调用者的最终用户身份,适配器拿到context后,如果需要调用内部系统,就用这个用户令牌往下游传递,而不是用连接层自己的高权限服务账号。这样审计日志里每一行都能回答“谁在什么时候通过哪个模型调了什么能力”。
另一个必须防的是提示注入(Prompt Injection)和 SSRF。简单说,模型传进来的任何内容都不可信,不能因为参数里有一个 URL 就让适配器直接去请求。适配器里应该做白名单校验,只允许访问配置好的域名,禁止访问内网网段,外部 API 返回的重定向也要拦截。有一次我们压测时发现,模型根据用户输入构造了一个 URL,指向了内部管理后台,幸好适配器有白名单拦截,不然就出大事故了。
审计日志这块,建议记录五个要素:request_id、trace_id、capability、caller、result_status,关键的写操作再加一条params的脱敏副本。不需要记录所有原始参数,敏感字段在入日志之前要做脱敏处理。
2.4 超时、重试与熔断:别让慢接口拖垮整段对话
Agent 和传统的后端 API 调用有个很不一样的地方:它经常是多轮串联的。模型先调 A 能力,拿到结果后再决定调 B 能力,最后汇总输出。如果 A 接口慢了三秒,B 再慢三秒,用户等一段完整回答可能就要十几秒,体验直接崩掉。
所以我在连接层做的第一道控制就是默认超时 5 秒,写操作能力可以放宽到 10 秒,但上限就是这么多了。第二道控制是重试策略,这里的前提是能力元数据里的idempotent标志:只有幂等能力(比如查询、只读操作)才允许自动重试,重试次数最多 2 次,每次间隔 500 毫秒;非幂等能力(比如创建订单、扣款)绝不自动重试,宁可返回错误让模型向用户说明,也不能产生重复单据。第三道控制是熔断,每个能力维护一个连续失败率,20 秒窗口内失败率超过 50%,直接快速失败,不请求上游,等 30 秒后再半开试探。
降级策略也值得提前设计好。比如地址解析服务挂了,可以让适配器返回一个“服务暂时不可用”的结构,并且把标记写到返回数据里,让模型直接、诚实地说“这个功能暂时查不到,请稍后再试”。这比模型瞎编一个结果强太多了。
3. 实操过程与核心环节实现
3.1 搭一个能跑的最小环境
项目结构我建议这样组织,把连接层、适配器、示例客户端分得清清楚楚:
agent-reach/ ├── reach_server/ │ ├── server.py │ ├── registry.py │ └── permission.py ├── reachers/ │ ├── order_query.py │ └── weather.py ├── examples/ │ └── openai_agent.py └── config.yaml环境方面,Python 3.10 以上即可,核心依赖只需要fastapi和uvicorn,再加一个jsonschema用来做参数校验。我这里写的示例是基于我实际项目简化后的版本,不是官方的固定代码,但思路是通用的。配置放在config.yaml里:
server: host: "127.0.0.1" port: 8890 registry: type: "memory" # 生产可用 redis reacher_dirs: - "./reachers"启动起来之后,Reach Server 会自动扫描reachers目录,把每个带装饰器的函数注册成能力,并暴露两个端点:GET /capabilities返回全部能力元数据,POST /invoke执行能力调用。
3.2 从零编写一个 Reacher 适配器
最核心的能力实现长这样。以下订单查询为例,我故意保留了一些注释,方便对照理解:
# reachers/order_query.py import requests from reach_sdk import ReachServer server = ReachServer() @server.reacher( name="order.query", description="按订单号查询订单实时状态和预计送达时间。适用于用户询问订单进度、物流状态时。", params_schema={ "type": "object", "properties": { "order_id": { "type": "string", "pattern": "^ORD[0-9]{8}$", "description": "订单号,例如 ORD20240001" } }, "required": ["order_id"] }, permission="order:read", timeout_ms=5000, idempotent=True, ) def query_order(params, context): order_id = params["order_id"] resp = requests.get( f"https://internal-api.example.com/orders/{order_id}", headers={"Authorization": f"Bearer {context.access_token}"}, timeout=3, ) resp.raise_for_status() data = resp.json() return { "order_id": data["order_id"], "status": translate_status(data["status_code"]), "estimated_delivery": data["eta"], } def translate_status(code): mapping = {0: "已下单", 1: "已发货", 2: "运输中", 3: "已签收"} return mapping.get(code, "状态未知")有三个细节值得展开。第一,返回值不要直接照抄上游的原始结构。上游可能是这个世纪的老系统,字段叫st,值还是0/1/2/3这种状态码,模型根本不知道1代表什么。适配器存在的意义之一就是“翻译成面向模型的语言”,所以我把它转成了“已发货”“运输中”这样的枚举描述。第二,不要在适配器里写复杂业务逻辑,它应该保持薄,逻辑放到外部系统本身,否则连接层会越来越重。第三,适配器内部请求上游时的超时,要设置得比能力声明的timeout_ms略短,宁可自己报错也不要卡到最后一刻才超时。
3.3 把 Agent-Reach 接入大模型智能体
适配器写完,连接层跑起来,下一步就是让模型真正会用这些能力。下面是接入 OpenAI 系模型的示意代码,核心是“把能力元数据动态构造成 tools,把调用结果回填给模型”:
# examples/openai_agent.py import json import requests from openai import OpenAI client = OpenAI() def build_functions(server_url): caps = requests.get(f"{server_url}/capabilities", timeout=3).json() # 只挑选与当前任务最相关的能力注入,控制上下文长度 functions = [] for cap in caps[:20]: functions.append({ "type": "function", "function": { "name": cap["name"], "description": cap["description"], "parameters": cap["params_schema"], } }) return functions def call_reach(server_url, capability, params): resp = requests.post( f"{server_url}/invoke", json={ "request_id": new_id(), "capability": capability, "params": params, "timeout_ms": 5000, }, timeout=6, ) return resp.json() def run(): server_url = "http://127.0.0.1:8890" functions = build_functions(server_url) messages = [{"role": "user", "content": "帮我查一下订单 ORD20240001 现在到哪了"}] while True: resp = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=functions, tool_choice="auto", ) msg = resp.choices[0].message messages.append(msg) if not msg.tool_calls: print(msg.content) break for tc in msg.tool_calls: args = json.loads(tc.function.arguments) result = call_reach(server_url, tc.function.name, args) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": json.dumps(result, ensure_ascii=False), }) if __name__ == "__main__": run()这里最容易踩的坑是把全部能力无条件注入。能力一多,不仅吃上下文,还会让模型产生选择困难。我的做法是在构建functions之前,根据用户问题的关键词做一个粗粒度筛选,只注入命中阈值的能力。比如用户问“快递”,只注入order.query和logistics.track,不会把invoice.create也塞进去。
3.4 端到端联调:一条查询怎么跑通
搭好之后,完整链路是这样的:用户问“我的订单 ORD20240001 现在到哪了”,智能体调用模型,模型输出一个 tool call,内容是order.query加{"order_id": "ORD20240001"}。客户端把这个请求 POST 给 Reach Server,Reach Server 校验参数和权限后路由到订单适配器,适配器带凭证请求内部订单服务,拿到状态码后翻译成“运输中”,连同预计送达时间一起返回。客户端把这段 JSON 作为 tool 执行结果回传给模型,模型组织语言:“您的订单 ORD20240001 目前在运输中,预计明天 18:00 前送达。”
联调阶段我强烈建议开一个中间日志控制台,逐行看每一步的耗时和内容。我项目里每个环节都会打 Log:
[trace_7f3e9d21] agent -> reach invoke order.query params={"order_id":"ORD20240001"} 2ms [trace_7f3e9d21] reach -> reacher route to order_query.py 1ms [trace_7f3e9d21] reacher -> upstream GET /orders/ORD20240001 88ms [trace_7f3e9d21] upstream -> reacher 200 status_code=2 eta=2025-02-03 [trace_7f3e9d21] reacher -> reach status=success usage_ms=132看到这样的日志,整条链路就非常明确:到底是模型参数填错了,还是适配器翻译错了,还是上游慢了,一眼就能定位。
4. 常见问题与排查技巧实录
4.1 高频问题速查表
我把项目上线以来被问得最多的问题整理成了表格,几乎每个新接入的团队都会命中其中一两条:
| 现象 | 可能原因 | 排查思路与修复 |
|---|---|---|
| 能力注册成功但调用返回 404 | 能力名称拼写不一致,或注册到了不同节点 | 先curl /capabilities确认实际名称,再看注册中心的节点列表 |
| 模型总是传错参数 | params_schema 里的 description 写得含糊 | 给每个字段补上格式、示例值,比如订单号必须是 ORD 开头后接 8 位数字 |
| 模型反复调用同一个接口 | 返回结果不满足模型生成回答所需的信息 | 检查返回值是否缺少关键字段,很多情况是模型拿不到需要的信息所以反复查 |
| 调用经常超时 | 上游接口慢,或者适配器超时设置比声明值还长 | 把适配器内请求超时调成能力声明超时的 80%,再对上游做性能优化 |
| 权限拒绝频繁出现 | 令牌作用域太小,或用户身份没有正确传递 | 查看审计日志里的 caller,确认下传的是最终用户身份而非服务账号 |
| 上下文被塞爆 | 能力列表全部注入,tools 太大 | 做关键词粗筛,只注入与当前问题相关的能力 |
| 模型答非所问但调用是成功的 | 返回值结构是“给程序看的”而不是“给模型看的” | 在适配器里把状态码、内部标记翻译成自然语义的枚举和描述 |
| 外部系统接口变更后模型行为异常 | 适配器还是旧契约,返回结构和描述不一致 | 建立能力版本管理,变更走契约评审,用回归集跑调用成功率 |
4.2 一次真实故障复盘:模型为什么答非所问
有一次我们上线了一个物流查询能力,智能体用得很频繁,但运营反馈说“用户问物流,智能体回答了一大段但都是废话”。我去翻 trace 日志,发现调用其实全部成功,order.query返回了上游的完整 JSON,状态字段是数字2,物流节点是一个内部编码ST_WH_01。模型拿到这些东西后根本不知道2表示什么,也不认识ST_WH_01,于是它只能基于猜测进行解释,结果自然就是“答非所问”。
这个故障的根子不在模型,在适配器——它把给程序看的原始数据直接透传给了模型,没有做“面向模型”的翻译。修复方案就是我在适配器里加状态映射和节点名称翻译,返回结构改成:
{ "order_id": "ORD20240001", "status": "运输中", "current_location": "华东转运中心", "next_milestone": "预计明天到达配送站", "estimated_delivery": "2025-02-03 18:00" }修复之后,同样的问题再问一遍,模型的回答准确率立刻上来了。这个案例给我的教训非常深刻:Agent 项目的适配器,本质上是“外部系统与模型之间的翻译官”,翻译质量直接决定用户体验。
4.3 我踩过的几个坑,列成避坑清单
第一,能力描述里别写“如果不确定就不要调用”之类的话,模型对这类反向指令的理解非常混乱,容易导致该调的时候不调。描述只讲“什么场景用、怎么用”,不写否定句式。
第二,参数名用完整可读的名字,别用缩写。order_id比oid好,start_date比sd好。模型看到字段名就能猜出含义,缩写会让它填错字段。
第三,返回 JSON 不要嵌套三层以上。模型对深嵌套结构的解析能力有限,我踩过坑,深层字段它经常忽略。优先扁平结构,最多两层。
第四,从第一天就要打trace_id日志,不要等出了问题再补。很多团队跑 Demo 时不打日志,上线后一次链路排查能花整天,而一个贯穿式 trace 能把定位时间缩到几分钟。
第五,外部系统的任何接口变更都要走契约评审。我在 4.2 里已经吃过亏了,现在凡是适配器对应的上游接口要改字段,必须提前同步给我,我会跑一遍能力回归集,确认返回结构变化会不会影响模型表现。
第六,不要通过能力元数据泄露敏感信息。description和params_schema对模型可见,本质上用户也可能间接看到,所以不要在描述里写“此接口能绕过审批”之类的内容,更不要把内部地址和密钥写进元数据。
第七,上线后要做在线巡检。外部 API 变更不一定会通知你,更常见的是静默失败:接口还通着,但返回的字段含义变了。我每隔几天会对所有能力跑一遍模拟调用,比对返回结构与期望 schema,及时发现不兼容。
5. 从单机 Demo 到生产级 Agent 基础设施
5.1 多 Agent 复用与资源隔离
项目跑顺之后,团队里会有好几个智能体同时接入:客服机器人、运营助手、风控助手。它们要调的能力有重叠,但权限边界不一样。客服能查订单,但绝不能改价;运营能拉报表,但绝不能看用户实名信息。这时候连接层的作用就体现出来了——所有智能体都走同一个 Reach Server,由权限模块按调用方身份去控制能力可达性,能力做到一次注册、多处复用,权限做到按调用方隔离。
资源隔离也要跟上。高频的查询类能力不要拖垮低频的关键写能力,所以在连接层按能力配置并发上限和令牌桶限流。写操作的桶小一些,查询可以宽松些。这里没有特别复杂的策略,但一定要有,否则一个智能体被刷屏,全公司的 Agent 都跟着抖。
5.2 长任务与流式响应的扩展
有些能力不是秒回的,比如批量生成报表、图片渲染、跑数据分析,可能要十几秒甚至几分钟。这个时候同步 HTTP 请求就不合适了。我的扩展方案是引入异步任务模式:Reach Server 收到请求后立刻返回accepted状态和task_id,后台线程去执行长任务,执行完了通过回调把结果推送到结果中心,智能体侧轮询或者订阅获取。对模型侧的表现形式就是“这个任务正在处理中”,然后把 task_id 暴露给用户,后续用户问任务进度时再查一次。
流式响应方面,SSE 也值得预留。等模型需要逐步输出分析过程或流式日志时,Reach Server 可以通过 SSE 把一个能力内部的中间状态推给智能体。这个不强求在第一版就上,但协议层最好不要设计成只能返回一次性大 JSON,把“执行事件”的概念预留好,后面接起来会省很多事。
5.3 能力生命周期与插件生态治理
能力多了之后,最怕的是“僵尸能力”:写着写着没人在用,但又一直占着上下文输出和注册中心的空间。我会给每个能力加enabled和deprecated状态,下线一个能力时要提前声明废弃,并且观察一周的调用日志,确认没有智能体还在依赖它才真正下线。
版本管理也很关键。能力元数据里有version,当 params_schema 或者返回结构发生不兼容变化时,我会把它作为新版本注册,老版本保留一段时间。这样单个智能体可以灰度切换,不用逼着所有消费方同一时间升级。等团队积累到几十个能力时,你会发现这已经不只是一个网关项目了,而是在建设一套 Agent 时代的内部集成规范。
我个人在实际运维里最大的体会是,Agent-Reach 这类连接层真正难的不是写适配器,而是契约管理。外部系统的接口一变,模型的输出质量立刻跟着崩,所以现在我把能力描述和返回结构的所有变更都纳入评审流程,并且维护一个回归集,每次改版都跑一遍全部能力的调用成功率和回答准确率,比上线前临时抱佛脚管用得多。
最后再分享一个小技巧:第一个接入的适配器,千万别选那种复杂业务接口,挑一个返回结构简单、失败率低的只读查询接口先跑通,比如查天气、查汇率、查字典。把“模型出参 → 连接层路由 → 适配器执行 → 结果回填 → 模型生成回答”这整条链路看一遍日志,确认每一步都符合预期,之后再横向扩展其他能力。这个稳扎稳打的路线,我试过,后面基本没出过大乱子。