Dify 企业级实验(11):企业 API 工具化——如何把客户系统封装成 Dify 工具?
1. 业务场景
先讲一个我们实际遇到的场景。
接企业项目时,客户最常见的开场白是:「我们的 ERP 有 API,你们接一下。」
我们第一次接这类需求时,第一反应也是「有 API 就好办,写个脚本调一下」。真正动手才发现——客户系统的 API 是既定的,要改的是我们这边:把 HTTP API 封装成 Dify 可调用的工具,工作流里像用内置工具一样调用它。每次手搓 HTTP 的代价,会在环境切换和错误处理上成倍还回来。这是接企业单的必备能力。
这不是个例。任何系统集成交付都是这个模式:ERP/CRM/OA 的 API 要进工作流,封装成工具是标准姿势,而不是每次手搓 HTTP。
2. 场景痛点
这个流程的痛点,在做集成的开发身上体现得最直接:
- 客户不改代码:让客户为你的工作流改 API 不现实,接口长什么样,就得按什么样接。
- 裸 HTTP 调用难维护:URL、鉴权、参数散在工作流里,换一个环境改一堆节点。
- 错误处理粗糙:5xx/超时直接抛 ToolInvokeError,工作流整体 failed,没法优雅降级。
- 工具描述含糊:LLM 不知道什么时候该调、参数什么意思,要么不调、要么乱调。
本质上,客户系统的能力要变成 Dify 的「零件」,而不是每次手搓 HTTP——封装的关键是描述清楚、错误可控。
3. 方案:为什么是OpenAPI 自定义工具
Dify 的 Console API 支持创建 OpenAPI 自定义工具,正好把客户 API 包装成标准零件。选它的理由:
- 平台原生:Console API 创建工具,schema 声明参数与鉴权,工作流里像内置工具一样调用;
- 描述驱动:工具描述写清楚「什么场景用 + 参数业务含义」,LLM 才能正确决定何时调用(102-12/13 实测);
- 业务失败码:错误路径放在「业务失败码」层处理,工作流可以优雅分支降级。
这篇文章我们就用它把 ERP 的订单查询 API 封装成 Dify 工具。
4. 整体架构
链路很清晰:调自定义工具 → 解析响应 → 成功输出订单详情 / 业务失败走降级提示。错误路径放在「业务失败码」层、而不是依赖节点 failed,是这条链的关键设计。
5. 模块设计
5.1 自定义工具定义(OpenAPI schema)
工具用 Console API 创建,schema 的 servers 指向演示端点(KV /echo 回显参数):
schema_type: openapi
credentials:
auth_type: none # 演示无鉴权;生产用 api_key/oauth,Key 存工具配置不进 prompt
servers:
- url: http://172.19.0.50:8123/echo
paths:
/echo:
get:
operationId: getOrder
summary: 查询订单详情
description: 根据订单号查询 ERP 订单状态、金额与物流信息。订单号以 ERR 开头时返回业务失败。
parameters:
- name: order_id
in: query
required: true
schema: {type: string}创建命令(Dify Console API):
POST /tool-provider/api/add
{"schema": "<上述 OpenAPI JSON>", "schema_type": "openapi", "credentials": {"auth_type": "none"}}工具描述要写清楚「什么场景用 + 参数业务含义」——LLM 靠描述决定何时调用(102-12/13 实测)。
5.2 工具节点与响应解析
tool_erp:
provider_name: dify104_erp_api
tool_name: getOrder
tool_parameters:
order_id: "{{#start.order_id#}}"# cd_parse:解析工具 text 输出,业务失败用「业务失败码」判断
def main(text: str) -> dict:
import json
try:
data = json.loads(text or "{}")
args = data.get("args", {})
order_id = args.get("order_id", "")
except Exception:
order_id = ""
if not order_id:
return {"success": "false", "detail": "ERP 接口未返回订单数据(响应异常)"}
if str(order_id).upper().startswith("ERR"):
return {"success": "false", "detail": "ERP 返回业务失败:订单 " + str(order_id) + " 查询被拒绝"}
return {"success": "true", "detail": "订单 " + str(order_id) + " 查询成功:状态=已发货,金额=¥1,299.00,物流=顺丰(SF1234567890)"}5.3 错误路径设计
自定义 API 工具对 5xx/超时会直接抛 ToolInvokeError(节点 failed),工作流无法优雅分支——所以错误处理放在「业务失败码」层:工具返回 ERR 前缀的业务失败 → cd_parse 输出 success=false → if_ok 走 cd_fallback 降级提示。超时与重试在工具/HTTP 层配置。
6. 运行验证
| 输入(order_id) | 预期 | 实测 |
|---|---|---|
| O20240801001 | 工具返回订单详情,走 end_ok | 与预期一致,输出状态=已发货/金额/物流信息 |
| ERR-O20240801002 | 业务失败,cd_parse 识别 → 降级提示 | 与预期一致,返回「ERP 系统暂时不可用或查询失败,请稍后重试」 |
| 空订单号 | 工具无有效数据 → 结构化错误 → 降级 | 与预期一致(cd_parse 响应异常分支) |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| 演示服务不可达 | 工具 servers 指向 httpbin.org,本机连不通,调用即失败 | 指向本机 KV /echo(172.19.0.50:8123),生产换真实 ERP 域名(实测) |
| 5xx 直接抛 ToolInvokeError | http 状态异常时工具节点 failed,没有错误输出可判断 | 错误路径改用「业务失败码」(ERR 前缀)在 cd_parse 判断降级;simulateError 操作保留演示真实 4xx(实测) |
| 工具描述含糊 | LLM 不调用/乱调工具 | 描述写「什么场景用 + 参数业务含义」(102-12/13 实测) |
| 参数 schema 缺必填校验 | 调用方传错参数,接口报错 | OpenAPI parameters 的 required 严格声明(实测) |
| 工具重建后 provider_id 变化 | 重新导入生成新 app_id,旧工具失效,调用方报 provider 不存在 | 删旧工具重建,调用方 DSL 同步 provider_id(交付说明实测) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤,含工具 schema 全文):DIFY-104-11:企业API工具化——把客户系统封装成Dify工具.md
- 源码(可直接导入):dify104_11_01_API工具测试.yml
- 源码目录:dify-104/dsl
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 企业级实验(01):多应用编排——如何让多个 Dify 应用协同完成一条业务链?
- Dify 企业级实验(02):跨应用状态传递——多轮对话的状态如何跨应用不丢?
- Dify 企业级实验(03):事件驱动流水线——Webhook 与定时触发如何组成异步处理链?
- Dify 企业级实验(04):性能优化实战——长流程从 60 秒到秒回有哪些手段?
- Dify 企业级实验(05):Token 成本控制——AI 应用省钱改造怎么做?
- Dify 企业级实验(06):可观测性体系——日志埋点与监控告警如何落地?
- Dify 企业级实验(07):安全与合规——全链路脱敏与权限分级怎么做?
- Dify 企业级实验(08):人机协同审批流——机器预审与人工确认如何配合?
- Dify 企业级实验(09):复杂业务状态机——订单状态流转与非法跳转防护?
- Dify 企业级实验(10):知识库持续更新闭环——数据飞轮怎么转起来?
- Dify 企业级实验(11):企业 API 工具化——如何把客户系统封装成 Dify 工具?
- Dify 企业级实验(12):外部系统集成——第三方系统如何通过 Dify API 双向编排?