用 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 字段
三个值得注意的细节:
- URL 和 Header 里可以直接引用变量,比如上游节点的输出(
{{#节点ID.变量#}})和环境变量({{#env.xxx#}})。API 密钥不要硬编码在节点里,放进应用的环境变量更安全。 - 出网默认经过 SSRF 防护代理。Dify 的 Docker 部署自带
ssrf_proxy,HTTP 节点的请求默认走这层代理,防止工作流被诱导访问内网地址。如果你的 API 就部署在同一台机器的内网,可能需要调整代理放行规则。 - 它适合"固定流程",不适合"模型自主决策"。什么时候调、调哪个接口,都是你在编排时定死的。如果想让模型自己决定调用时机,该用方式 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) }
两个关键限制,很多人在这里踩坑:
- 沙箱默认禁止访问外网。代码节点不是用来当 HTTP 客户端或数据库客户端的,Dify 把网络能力刻意收走了——想调接口请用 HTTP 节点或自定义工具,想查数据库请看第八节的反面案例。
- 执行有超时和资源限制。重计算、长任务不适合放在代码节点里,那属于你后端服务该干的活。
四、方式 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 应用(而非固定工作流)——因为"是否需要查订单"取决于用户问的是什么;闲聊类问题不该触发接口调用。
搭建步骤:
- 写一份订单查询 API 的 OpenAPI schema(参考第四节),在「工具」里导入;
- 创建 Agent 应用,挂载该工具,System Prompt 中约定回答格式;
- 鉴权信息填在工具授权配置里,密钥放在环境变量。
运行时的完整链路:
用户提问
→ 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)撰写,文中涉及的源码路径以该版本为准。