【第 7 篇:API 调用 Agent——外部系统打通与结果解析】
第 7 篇:API 调用 Agent——外部系统打通与结果解析
系列记录:《从零搭建企业级 LLM 应用》,这是第 7 篇
上一篇:数据分析 Agent——代码沙箱与 150 倍性能提升
下一篇:规则引擎——在 LLM 的输出上加一道保险
一、RAG 和数据分析之后,还缺什么
前面几篇搭好的两个 Agent
- RAG Agent:检索的是 Dify 知识库里的文档——制度条款、操作规范、应急预案等内容型资料。它的核心能力是"从文档里找答案"。
- Data Agent:读取的是本地上传的数据文件——Excel 考勤表、CSV 统计导出等。它的核心能力是"对已落盘的离线数据做计算"。
Dify 知识库的文档是管理员提前上传的,本地的 Excel 是用户手工导出的,相当于都是静态管理的数据,有一定的维护成本。
但在实际场景中,可能需要接入大量业务接口内的数据:
- 实时数据和历史统计
- 设备管理系统的传感器监测值
- 平台的排查记录和整改状态
- 系统业务的完成情况
这些数据的特点是:体量大、持续更新、只能通过 HTTP 接口获取。
所以典型的问题场景就出现了:
“今天数据指标有没有超标?”
“这个月违规有多少起?”
“有多少台设备处于故障状态?”
所以需要一个专门的组件,让 Agent 能调用外部 HTTP 接口获取实时结构化数据。这就是 api_agent 的定位。
二、设计方案
2.1 为什么不硬编码接口调用?
最直接的做法是在 Agent 的工具函数里硬编码 API 调用——比如写死一个 get_people_count() 函数,里面拼好 URL 和参数,调完返回结果。
但这个做法有一个根本问题:每接入一个新接口,就要改代码、重部署。而企业环境里,接口的数量和参数是持续变化的。
所以选择了配置驱动:
用户通过前端页面配置接口 → 保存为 JSON → Agent 工具读取 JSON → 动态发起 HTTP 调用
整条链路里没有一行硬编码的接口调用逻辑。新增一个接口,就是在 interface_configs.json 里加一条记录,Agent 自动就能用。
2.2 为什么没有接入 Supervisor 路由?
api_agent 和其他三个 Agent 有一个关键区别:它没有被纳入 Supervisor 的自动路由。
Supervisor agents = [rag_agent, data_agent] # 没有 api_agent
原因很简单:实时数据查询的触发条件太模糊了。"这个月违规多不多"这句话,既可以理解为问知识库里的违规管理规定(走 rag_agent),也可以理解为查实时违规系统的统计数据(走 api_agent)。让 Supervisor 去猜用户的真实意图,准确率注定不高。
所以 api_agent 走的是显式模式切换——用户在对话页面选择"API 查询"模式,前端直接调 chat_direct("api_agent")。这和"数据统计"模式直接调 data_agent 是同一个路子。用户自己知道他想查的是实时数据还是知识库文档,不需要让 LLM 去猜。
三、两个工具的设计逻辑
api_agent 只配了两个工具,这反映了它的设计原则:能做少就做少,把复杂性压到配置层。
工具一:list_interfaces —— 让 Agent 知道能调什么
list_interfaces → 读取 interface_configs.json → 返回所有已启用接口的 ID/方法/URL/参数
这个工具的设计有几个考虑:
实时读取文件,不是缓存。每次调用 list_interfaces 都重新读 JSON 文件,因为接口配置通过前端页面修改后直接写文件,热加载才能让 Agent 感知到新增或修改的接口。
分组信息带进输出。接口按"违规管理"“设备管理”“安全隐患"等分组,Agent 看到分组名就能做语义匹配——用户问"设备状态”,它自然会优先看"设备管理"分组下的接口。
工具二:call_interface —— 把配置翻译成 HTTP 调用
call_interface(interface_id, extra_params) → 合并默认参数和 extra_params → 发起 HTTP → 返回 JSON
这个工具的核心思路是参数合并策略:
# 1. 默认参数(配置中已启用的)
default_params = {p["key"]: p["value"] for p in params if p["enabled"]}
# 2. extra_params 覆盖同名默认值
default_params.update(json.loads(extra_params))
用户问"最近一周的低级违规有多少"➡Agent 通过 list_interfaces 知道违规统计接口➡用自带参数覆盖默认参数➡查询接口数据
四、真实接口的复杂性:一个配置模型要应对多少种变化
interface_configs.json 定义了每条接口记录的结构
请求方式:不只是 GET
GET → query params 拼在 URL 后
POST → body 里放 JSON,body_type 决定数据在 body 还是 params
PUT → 同 POST
DELETE → query params
认证方式:不是只有无认证
# Bearer Token
auth_type = "bearer" → headers["Authorization"] = f"Bearer {auth_value}"
# API Key
auth_type = "apikey" → headers[auth_key_name] = auth_value
# Basic Auth(预留)
auth_type = "basic" → 拼接 username:password
参数状态:默认 vs 可选
enabled: true → 每次调用自动携带,值来自配置(如 start_date=2025-01-01)
enabled: false → Agent 可根据用户意图通过 extra_params 指定,不指定则不传
自定义请求头和 Body
接口可能要求特定的 Content-Type、自定义 Header,POST 接口的 body 可能是复杂的 JSON 结构。这些都支持在配置中声明,Agent 调用时自动拼接。
五、三个发现
配置热加载
最初考虑过启动时加载配置并缓存到内存。但使用场景中,接口配置通过前端页面随时修改——新增接口、调整参数、变更 URL。缓存会让修改延迟生效,用户困惑"明明加了接口为什么 Agent 说没有"。
所以 list_interfaces 和 call_interface 都是每次调用时实时读文件。性能上这不是问题——读一个几 KB 的 JSON 文件是微秒级的。
处理超时和错误
调外部接口和调 Dify 知识库不同。Dify 是可控的服务,可靠性相对高。但外部接口可能是用户自己部署的系统,随时可能挂了、慢了、改了端口。
所以 call_interface 里有明确的超时和错误处理:
timeout=15 # 15 秒超时,避免长时间挂起
ConnectionError → "无法连接到 {url},请确认服务已启动"
Timeout → "请求超时:{url}"
HTTP >= 400 → "接口返回错误 HTTP {status_code}:{body[:300]}"
根据不同的错误类型设置具体的错误提示词,方便排查问题。
六、与主流方案的对标
让 LLM 调用外部 API,业界有三条路线:
| 方案 | 代表 | 思路 | 适合的场景 |
|---|---|---|---|
| Function Calling | OpenAI, Anthropic 原生 | 定义 function schema,LLM 选择函数 + 填参数 | 固定 API 集合,接口数量少且稳定 |
| MCP (Model Context Protocol) | Anthropic 提出 | 标准化协议,Agent 通过 MCP Server 发现和调用工具 | 有现成 MCP Server 的场景 |
| 配置驱动的 HTTP 直调 | 本项目 | JSON 配置声明接口,Agent 动态发现和调用 | 企业内部系统,接口多变,无 MCP Server |
Function Calling 的问题在于:每个新接口要写一个函数定义 + 修改 Agent prompt + 重部署。对于频繁变动的企业接口场景,维护成本太高。
MCP 的问题在于:它要求接口提供方实现 MCP Server。企业内部系统(违规平台、设备管理、工单系统)不可能为了一个 Agent 去改造自己的协议。
所以选择了中间路线——比 Function Calling 灵活(配置即接口,无需改代码),比 MCP 轻量(不需要服务端改造)。本质上是把接口描述标准化(JSON 配置),把调用逻辑统一化(HTTP 客户端),把接口选择交给 LLM(自然语言匹配)。
七、各Agent怎么配合
现在四个 Agent 各司其职:
| 用户问什么 | 走哪个 Agent | 数据来源 |
|---|---|---|
| “指数多少算超标” | RAG Agent | 知识库文档 |
| “上月各部门出勤率排名” | Data Agent | Excel 考勤表 |
| “现在XX指标为多少” | API Agent | 实时监控接口 |
| “写一份月度报告” | Doc Agent | 综合以上三者 |
最终的用户体验是:正常聊天走知识库问答模式(Supervisor 自动路由),需要查实时数据时切换到 API 查询模式。选择权在用户,而非 LLM 猜测。
下一篇预告:规则引擎——为什么要在 LLM 的输出上加一道保险,以及怎么设计一套分层拦截的规则体系。
更多推荐



所有评论(0)