返回列表

Dify 调用外部数据的 5 种方式:怎么选、怎么用

2026年10月04日 9 次阅读

用 Dify 搭一个 AI 应用很快:拖几个节点、配一个模型,半天就能跑起来。但真正要用到业务里,几乎所有人都会在同一个地方停下来——

应用本身很聪明,但它不了解你的业务数据。 订单状态在数据库里,商品信息在内部 API 里,文档散落在对象存储和知识库中。怎么把这些数据安全、可控地接进 Dify?

这篇文章把 Dify 接入外部数据的 5 种方式一次讲清:每种方式是什么、适合什么场景、有什么坑,最后给一张选型决策表和一个完整示例。

本文是《Dify 源码与实战》系列第 1 篇。后续几篇会从源码角度拆解 Dify 的整体架构、RAG 链路和 Workflow 引擎,感兴趣可以先关注系列。

一、先看全景:5 种接入方式

# 方式 一句话定位
1 HTTP 请求节点 工作流里直接调你的 API,人来决定调用时机
2 代码执行节点 沙箱里跑 Python/JS,做数据加工和轻逻辑
3 自定义工具(OpenAPI) 把 API 注册成工具,模型决定何时调用
4 MCP 标准化工具协议,接入现成的 MCP Server
5 插件市场 装现成的插件(搜索、数据库连接器等)

这 5 种方式不是互斥的,一个稍复杂的应用往往会组合使用。下面逐个展开。

二、方式 1:HTTP 请求节点(最常用)

在工作流编排界面添加 HTTP 请求节点,填 URL、Method、Headers、Body,就能在工作流执行到该节点时向任意 REST API 发起调用。在 Dify 源码中,对应实现位于 api/core/workflow/nodes/http_request/。

适合场景:你的业务系统已经有现成的 REST API,想在工作流的固定位置拉取或推送数据。

一个典型配置——在工作流里查询订单状态:

Method:  GET
URL:     https://api.yourcompany.com/v1/orders/{{#start.order_id#}}
Headers: Authorization: Bearer {{#env.order_api_key#}}
Timeout: 10s(可配置超时与重试次数)
输出:    status_code / body / headers,下游节点可直接引用 body 字段

三个值得注意的细节:

  1. URL 和 Header 里可以直接引用变量,比如上游节点的输出({{#节点ID.变量#}})和环境变量({{#env.xxx#}})。API 密钥不要硬编码在节点里,放进应用的环境变量更安全。
  2. 出网默认经过 SSRF 防护代理。Dify 的 Docker 部署自带 ssrf_proxy,HTTP 节点的请求默认走这层代理,防止工作流被诱导访问内网地址。如果你的 API 就部署在同一台机器的内网,可能需要调整代理放行规则。
  3. 它适合"固定流程",不适合"模型自主决策"。什么时候调、调哪个接口,都是你在编排时定死的。如果想让模型自己决定调用时机,该用方式 3 的自定义工具。

三、方式 2:代码执行节点(工作流里的瑞士军刀)

在工作流里直接写 Python 3 或 JavaScript 代码,处理上游节点传来的数据。源码实现位于 api/core/workflow/nodes/code/,代码实际运行在独立的沙箱容器(dify-sandbox)中。

适合场景:数据清洗、格式转换、拼接 Prompt、计算签名、把 JSON 拍平成 LLM 好读的文本——一切"轻逻辑"。

一个常见用法——把 API 返回的 JSON 整理成给 LLM 看的文本:

def main(order: dict) -> dict:
    # 把接口返回的订单 JSON 压缩成一段紧凑文本,供下游 LLM 节点阅读
    lines = [
        f"订单号:{order.get('id')}",
        f"状态:{order.get('status')}",
        f"下单时间:{order.get('created_at')}",
        f"物流:{order.get('logistics', {}).get('carrier')} / {order.get('logistics', {}).get('tracking_no')}",
    ]
    return { "text": "\n".join(lines) }

两个关键限制,很多人在这里踩坑:

  1. 沙箱默认禁止访问外网。代码节点不是用来当 HTTP 客户端或数据库客户端的,Dify 把网络能力刻意收走了——想调接口请用 HTTP 节点或自定义工具,想查数据库请看第八节的反面案例。
  2. 执行有超时和资源限制。重计算、长任务不适合放在代码节点里,那属于你后端服务该干的活。

四、方式 3:自定义工具(OpenAPI Schema)——Agent 应用的首选

进入 Dify 控制台「工具 → 自定义 → 创建工具」,导入一份符合 OpenAPI 3.0 规范的 YAML/JSON 文件,Dify 会把文件里的每个 endpoint 解析成一个可调用的工具。源码实现在 api/core/tools/custom_tool/。

它与 HTTP 节点的本质区别:HTTP 节点是你在编排时决定"第几步调用什么接口";自定义工具是把接口交给模型,Agent 在运行时根据用户意图自己决定调不调、调哪个、传什么参数。

给订单查询接口写一份最小 OpenAPI 描述:

openapi: 3.0.3
info:
  title: Order Query API
  version: 1.0.0
servers:
  - url: https://api.yourcompany.com/v1
paths:
  /orders/{order_id}:
    get:
      operationId: getOrderById
      summary: 根据订单号查询订单状态和物流信息
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: string
          description: 订单唯一编号,例如 ORD-20260101-0001
      responses:
        '200':
          description: 订单详情

几点经验:

  • summary 和参数的 description 就是给模型看的"说明书",写得越具体,模型选对工具、填对参数的概率越高。
  • 鉴权方式(Bearer / API Key / 自定义 Header)在工具的授权配置里统一填写,不需要写进 schema 的每个接口里。
  • 一份 schema 可以同时描述多个 endpoint,Dify 会拆成多个工具。

适合场景:Agent 应用;一个流程里"要不要查接口"本身依赖用户问题语义的场景。

五、方式 4:MCP(Model Context Protocol)

MCP 是 Anthropic 提出的开放协议,把"工具"抽象成标准化的 MCP Server:任何支持 MCP 的客户端(Claude Desktop、Cursor、Dify 等)都能即插即用地使用同一批工具。Dify 自 1.6 版本起支持以客户端身份接入 MCP Server,源码实现在 api/core/mcp/。

适合场景:

  • 团队内部已经沉淀了一批 MCP Server,希望 Dify 直接复用;
  • 同一套工具需要在多个平台(IDE、桌面客户端、Dify)之间共享,不想为每个平台重复实现一遍。

一句话理解它的价值:方式 3 的自定义工具是"Dify 私有的工具格式",MCP 是"行业通用的工具格式"。如果工具只给 Dify 用,自定义工具更简单;如果工具要跨平台复用,MCP 是更划算的投入。

六、方式 5:插件市场(开箱即用)

Dify 1.x 引入了插件(Plugin)体系,插件市场里有大量现成能力:Google/Bing 搜索、数据库连接器、飞书/钉钉集成、各类模型供应商接入等,点一下安装就能用。源码实现在 api/core/plugin/。

适合场景:通用需求,且市面上已有成熟插件——能不写代码就不写代码。

注意:插件质量参差不齐,涉及企业内部敏感数据源时,建议优先自研(HTTP 节点 / 自定义工具),可控性更好。

七、选型决策表

你的需求 推荐方式 原因
工作流固定步骤调你的 REST API HTTP 请求节点 可视化配置,最简单直接
Agent 自主决定调用哪个 API 自定义工具(OpenAPI) 模型能理解工具描述,自主选择
工作流里做数据清洗/格式转换 代码执行节点 沙箱内跑轻逻辑,专治数据加工
在工作流里直接连数据库执行 SQL ❌ 不要这么做 沙箱默认禁网,且直连数据库不安全
让 Dify 查数据库里的数据 后端暴露 API → HTTP 节点或自定义工具 数据库操作留在你的业务系统里
接入已有的 MCP Server MCP 标准协议,跨平台复用
用现成的搜索/数据库等能力 插件市场 开箱即用,不写代码

这张表里最值得强调的是倒数第三行:"直连数据库"是一个高频错误需求。Dify 的代码沙箱默认禁网,就算打开限制强行连库,也会把数据库连接串暴露给平台侧、失去对查询行为的审计能力。正确姿势永远是:你的后端包一层 API,Dify 只跟 API 对话。

八、综合示例:一个能查订单的问答机器人

把上面几种方式串起来。需求:用户在聊天窗口用自然语言提问,"我的订单 ORD-12345 发货了吗?",机器人实时回答。

选型:Agent 应用(而非固定工作流)——因为"是否需要查订单"取决于用户问的是什么;闲聊类问题不该触发接口调用。

搭建步骤:

  1. 写一份订单查询 API 的 OpenAPI schema(参考第四节),在「工具」里导入;
  2. 创建 Agent 应用,挂载该工具,System Prompt 中约定回答格式;
  3. 鉴权信息填在工具授权配置里,密钥放在环境变量。

运行时的完整链路:

用户提问
  → LLM 判断意图:需要查订单 → 提取订单号 ORD-12345
  → 调用工具 getOrderById(order_id="ORD-12345")
  → 你的业务 API 返回订单 JSON
  → LLM 把 JSON 组织成自然语言回答
  → 用户看到:"订单 ORD-12345 已于今日上午发货,承运商为顺丰…"

如果换成用户问"你能做什么",LLM 不会调用任何工具,直接回答——这正是"工具交给模型调度"和"工作流固定调接口"的分野。

九、写在最后

5 种方式没有高下之分,只有场景之别:固定流程用 HTTP 节点,数据加工用代码节点,模型自主调度用自定义工具,跨平台复用走 MCP,通用能力先逛插件市场。而无论哪种方式,数据库永远躲在你们的 API 后面。

下一篇是系列的架构篇:《Dify 架构总览与分层设计》——聊聊 Flask + Celery + PostgreSQL 这套组合是怎么协同工作的,controllers / services / core / models 四层各管什么,为什么说"Dify 后端最重要的设计原则"是薄控制器、厚领域层。


本系列基于 Dify 1.x(源码仓库 langgenius/dify)撰写,文中涉及的源码路径以该版本为准。