← 返回文章列表

Dify 企业级实验(11):企业 API 工具化——如何把客户系统封装成 Dify 工具?

1. 业务场景

先讲一个我们实际遇到的场景。

接企业项目时,客户最常见的开场白是:「我们的 ERP 有 API,你们接一下。」

我们第一次接这类需求时,第一反应也是「有 API 就好办,写个脚本调一下」。真正动手才发现——客户系统的 API 是既定的,要改的是我们这边:把 HTTP API 封装成 Dify 可调用的工具,工作流里像用内置工具一样调用它。每次手搓 HTTP 的代价,会在环境切换和错误处理上成倍还回来。这是接企业单的必备能力。

这不是个例。任何系统集成交付都是这个模式:ERP/CRM/OA 的 API 要进工作流,封装成工具是标准姿势,而不是每次手搓 HTTP。

2. 场景痛点

这个流程的痛点,在做集成的开发身上体现得最直接:

本质上,客户系统的能力要变成 Dify 的「零件」,而不是每次手搓 HTTP——封装的关键是描述清楚、错误可控

3. 方案:为什么是OpenAPI 自定义工具

Dify 的 Console API 支持创建 OpenAPI 自定义工具,正好把客户 API 包装成标准零件。选它的理由:

这篇文章我们就用它把 ERP 的订单查询 API 封装成 Dify 工具。

4. 整体架构

graph TD start["开始(order_id)"] tool_erp["tool_erp:ERP 订单查询(自定义工具 dify104_erp_api/getOrder)"] cd_parse["cd_parse:解析工具响应(成功/业务失败)"] if_ok{"if_ok:业务是否成功"} end_ok["end_ok:输出订单详情"] cd_fallback["cd_fallback:降级提示"] end_fail["end_fail"] start --> tool_erp --> cd_parse --> if_ok if_ok -- "ok" --> end_ok if_ok -- "false" --> cd_fallback --> end_fail

链路很清晰:调自定义工具 → 解析响应 → 成功输出订单详情 / 业务失败走降级提示。错误路径放在「业务失败码」层、而不是依赖节点 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. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过