Dify + FastAPI 创建自定义工具
Dify 的流程编排能力和内置工具确实丰富,但真到项目落地时,总有些个性化需求需要自己动手。最常见的一个场景,就是和我们私有的服务做集成。今天咱们就从头走一遍,看看怎么用 FastAPI 开发一个自定义工具,挂到 Dify 上用。

这篇文章会覆盖以下几个关键环节:
- • 源码运行 Dify
- • 容器挂载插件代码
- • FastAPI 服务的搭建
- • 自定义工具的创建与配置
- • JSON 解析节点的使用
- • 条件判断与汇总节点的组合
先直接看下最终效果,心里有个底。
容器运行中间件
其实用中间件和源码分离的方式来跑 Dify,并不是非做不可的一步。你也可以直接参考官方文档,用源码启动本地服务。
先克隆项目:
git clone https://github.com/langgenius/dify.git cd dify
复制环境变量文件:
cp middleware.env.example middleware.env
如果本机已经部署了 Postgresql 之类的组件,记得改下端口,避免冲突。比如这样调一下:
EXPOSE_POSTGRES_PORT=5542 EXPOSE_REDIS_PORT=6389 EXPOSE_SANDBOX_PORT=8594 EXPOSE_SSRF_PROXY_PORT=3528 EXPOSE_WEA VIATE_PORT=8580
然后用 Compose 启动容器:
docker compose -f docker-compose.middleware.yaml up -d
检查了一下 compose 文件,里面确实引用了 middleware.env,但在我的环境中没生效。如果有同样踩坑的,可以试试下面这条命令,手动指定下 env 文件:
docker-compose --env-file middleware.env -f docker-compose.middleware.yaml up -d
正常运行的话,输出大致是这样:
> docker-compose --env-file middleware.env -f docker-compose.middleware.yaml up [+] Building 0.0s (0/0) [+] Running 6/6 ✔ Network docker_default Created 0.1s ✔ Network docker_ssrf_proxy_network Created 0.0s ✔ Container docker-ssrf_proxy-1 Created 0.3s ✔ Container docker-sandbox-1 Created 0.2s ✔ Container docker-db-1 Created 0.2s ✔ Container docker-redis-1 Created 0.2s
源码启动应用
Dify 的应用代码在 api 目录下,进去启动就行。
cd api
先修改 .env 文件,设置 SECRET_KEY。
MacOS 系统的操作:
openssl rand -base64 42 sed -i '' 's/SECRET_KEY=.*/SECRET_KEY=/g' .env
Linux 系统的操作:
openssl rand -base64 42 sed -i 's/SECRET_KEY=.*/SECRET_KEY=/' .env
不习惯命令的话,直接打开文件手动编辑也一样。
初始化Python环境
用 Conda 创建一个干净的新环境并激活:
conda env create -n dify python=3.10 conda activate dify
Dify 用 Poetry 管理依赖,安装它并拉取依赖包:
pip install poetry poetry install
数据迁移和初始化:
# poetry shell flask db upgrade
然后启动应用:
flask run --host 0.0.0.0 --port=5001 --debug
运行前端
前端的启动相对简单:
cd web pnpm install pnpm start
启动成功的输出类似:
> pnpm start > dify-web@0.6.16 start dify/web > cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public && cross-env PORT=$npm_config_port HOSTNAME=$npm_config_host node .next/standalone/server.js ▲ Next.js 14.2.4 - Local: http://localhost:3000 - Network: http://0.0.0.0:3000 ✓ Starting... ✓ Ready in 144ms
浏览器访问 http://localhost:3000 就能看到界面了。如果是新数据库,需要先设置用户名和密码。
容器运行 Dify
自定义开发 Dify 组件通常只需要新增文件,所以不一定要用源码运行。通过文件挂载的方式添加代码,会更省事。
比如在 Compose 文件里加一个 volume,效果一样:
volumes:
# Mount the storage directory to the container, for storing user files.
- ./volumes/app/storage:/app/api/storage
- ./external_data_tool/weather_search:/app/api/core/external_data_tool/weather_search
IDE 运行
对 Dify 接口不熟悉的同学,肯定希望在开发过程中能调试。在喜欢的 IDE 里,用 Flask 程序的标准方式启动调试即可。
比如用 PyCharm,调试配置参考如下:
自定义工具
好,进入正题,看看怎么自定义工具。
FastAPI 服务
先实现一个 FastAPI 服务,让 Dify 来调用。
逻辑很简单:
- • 实现一个简单的认证,密码设为
123456 - • 接收并打印收到的消息
- • 返回一个简单的 JSON
代码直接贴出来,不啰嗦了:
from fastapi import FastAPI, Body, HTTPException, Header
from fastapi.responses import FileResponse
from pydantic import BaseModel
app = FastAPI(servers=[
{"url": "http://localhost:8000"}
])
class InputData(BaseModel):
prompt: str
params: dict
@app.post("/api/dify/receive")
async def dify_receive(data: InputData = Body(...), authorization: str = Header(None)):
"""
Receive API query data from Dify.
"""
expected_api_key = "123456"
auth_scheme, _, api_key = authorization.partition(' ')
if auth_scheme.lower() != "bearer" or api_key != expected_api_key:
raise HTTPException(status_code=401, detail="Unauthorized")
return {
"result": "ok"
}
启动应用:
uvicorn main:app --reload --host 0.0.0.0
访问 http://127.0.0.1:8000/docs#/,就能看到 Swagger 文档。
再访问 http://127.0.0.1:8000/openapi.json,可以看到 OpenAPI 格式的 JSON 文件。把它整个拷贝下来,后面要用。
添加自定义工具
回到 Dify 界面,点击 工具 -> 自定义 -> 创建自定义工具。
在弹出的窗口中填写工具名称,然后把刚才拷贝的 JSON 粘贴进去。
鉴权方式选择 API Key -> Bearer,然后保存。
接下来创建一个空白应用,选择 聊天助手 和 工作流编排 类型。
右键点击画布,添加节点,选择我们刚刚创建的自定义节点。
节点配置如下:
当然,认证信息也可以在工具创建的时候直接配置好。
连接好节点,发个消息测试一下。可以看到,数据已经按照预期返回了。
返回信息默认按文本显示,而且支持 Markdown 格式。这意味着我们可以用它来展示更丰富的数据,比如超链接、文件下载、图片等。
细心看上面的截图,会发现回复信息不是那么干净,有引号之类的字符。
JSON解析节点
刚才提到,虽然返回内容支持 Markdown 展示,但内容要么是 JSON,要么字符串带着引号。如何干净地展示文件内容呢?这就轮到 JSON 解析节点出场了。
先把后端返回改成这样:
return {
'success': True,
'text': data.prompt,
'image': ''
}
然后添加一个 JSON 解析节点。
流程和节点的具体配置如下:
再测试一下,聊天窗口就能干净地返回图片了。
条件判断
接下来看看怎么做分支逻辑,实现这样一个场景:
- • 当请求
success时,显示图片 - • 否则,显示
text的内容
增加一个条件分支节点。
把之前配置好的 JSON 解析 节点放在 IF 分支后面,再添加另一个 JSON 解析 节点放在 ELSE 分支后面。
然后增加一个 变量聚合器 节点,不需要额外配置,直接连线即可。
调整后端返回代码:
return {
'success': data.prompt=='hello',
'prompt': data.prompt,
'xlsx': '[Prompts.xlsx](http://localhost:3001/api/v1/openai/chat/completions)',
'image': ''
'text': """## 生成式 AI 应用创新引擎..."""
调试运行,在聊天窗口输入 world,就会得到文本回复;如果输入 hello,则会展示图片。
结语
通过自定义工具,再配合流程编排、JSON 解析这些节点,常见的业务逻辑处理基本都能覆盖了。
唯一美中不足的是,Dify 对消息的入参和出参定义还是比较克制,需要我们用 AI 时代的程序编排思维去使用它。另外,目前还不支持上传文件(只能上传图片),所以一些文件处理功能——比如之前做过的 Excel 问答填写——暂时还不太好集成。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名