Dify 自定义插件实战:FastAPI + localtunnel 30 分钟搭一个天气查询工具

为什么写这篇 / 适合谁看

我在 Dify 里想让大模型「查天气」时,卡了两个问题:一是插件到底和 Function Calling 是什么关系,二是本地写好的接口,云端 Dify 根本访问不到 localhost

如果你也想给 Dify 挂一个自己的工具(查天气只是例子,换成查库存、查订单、调任意内部接口都一样),但不想先租服务器,这篇就是给你的。技术栈:FastAPI + localtunnel + Dify,30 分钟能跑通一个能用的自定义插件。

一、先理清概念:插件和 Function Calling 是什么关系

一句话:在 Dify 里,工具(插件)就是 Function Calling 的实现形式。

大模型本身只会「说话」,要让它能查天气、查数据库、调外部接口,就得给它挂上工具,由模型自己决定何时调用、传什么参数。

整体路径很简单:

复制代码
写一个 HTTP 接口 → 用 OpenAPI Schema 描述给 Dify → 模型按需调用

自定义接口开发的 6 个步骤:

步骤 操作 说明
Step 1 脚本开发 本地用 Python 实现接口逻辑
Step 2 运行脚本 后台跑起 API 服务
Step 3 创建工具 在 Dify 工具中创建自定义工具
Step 4 Schema 配置 配置 OpenAPI Schema
Step 5 测试 输入参数测试功能
Step 6 保存 在 Agent 中应用插件

二、FastAPI 服务搭建

2.1 安装依赖

bash 复制代码
# 创建虚拟环境(推荐)
python -m venv weather-env
source weather-env/bin/activate    # Linux/Mac
# weather-env\Scripts\activate     # Windows

pip install fastapi uvicorn requests

2.2 完整代码(main.py

城市编码直接硬编码,不依赖外部文件,方便一份文件跑起来:

python 复制代码
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
import requests

app = FastAPI()

# 身份验证令牌(个人使用,随便设一个自己知道的)
VALID_TOKEN = "my-secret-token"

# 城市编码(按需补充)
CITY_CODES = {
    "北京": "101010100",
    "上海": "101020100",
    "广州": "101280101",
    "深圳": "101280601",
    "杭州": "101210101",
    "成都": "101270101",
    # ......可继续补充
}

class WeatherRequest(BaseModel):
    location: str

@app.post("/weather")
def get_current_weather(request: Request, body: WeatherRequest):
    # 1. 验证身份
    auth_header = request.headers.get("Authorization")
    if auth_header != f"Bearer {VALID_TOKEN}":
        raise HTTPException(status_code=403, detail="Invalid Authorization header")

    location = body.location

    # 2. 查找城市编码
    city_code = CITY_CODES.get(location)
    if not city_code:
        return {
            "status": "error",
            "message": f"暂不支持 {location},目前支持:{','.join(CITY_CODES.keys())}"
        }

    # 3. 调用天气 API
    url = f"http://t.weather.itboy.net/api/weather/city/{city_code}"
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        data = response.json()
    except Exception as e:
        return {"status": "error", "message": f"天气服务请求失败: {str(e)}"}

    # 4. 解析并返回自然语言
    try:
        forecast = data["data"]["forecast"][0]
        weather_type = forecast["type"]
        high = forecast["high"].replace("高温 ", "")
        low = forecast["low"].replace("低温 ", "")
        return f"{location}今天是{weather_type},温度{high}/{low}"
    except (KeyError, IndexError) as e:
        return {"status": "error", "message": f"天气数据解析失败: {str(e)}"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8081)

2.3 启动服务

bash 复制代码
python main.py

跑起来应看到:Uvicorn running on http://0.0.0.0:8081

三、公网穿透:让云端 Dify 能访问你的本机

⚠️ 这是最容易卡住的一步。 Dify(尤其云端版)访问不到你本机的 localhost,必须给它一个公网可达的 URL。localtunnel 就是把本地端口临时映射到公网的小工具。

bash 复制代码
# 全局安装
npm install -g localtunnel

# 新开一个终端窗口
lt --port 8081

输出形如:your url is: https://random-name-123.loca.lt,把这个 URL 复制下来。

别关这个终端:窗口一关,链接立刻失效。

四、发到 Dify 前先自测

用 curl(或 Postman)先确认接口通,别等配到 Dify 再排错:

bash 复制代码
curl -X POST https://your-url.loca.lt/weather \
  -H "Authorization: Bearer my-secret-token" \
  -H "Content-Type: application/json" \
  -d '{"location": "杭州"}'

五、Dify 插件配置

5.1 OpenAPI 3.1.0 Schema

json 复制代码
{
  "openapi": "3.1.0",
  "info": {
    "title": "天气查询API",
    "description": "查询中国城市当前天气信息",
    "version": "v1.0.0"
  },
  "servers": [
    { "url": "https://your-url.loca.lt" }
  ],
  "paths": {
    "/weather": {
      "post": {
        "summary": "查询城市天气",
        "security": [ { "BearerAuth": [] } ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "location": {
                    "type": "string",
                    "description": "城市名称",
                    "example": "北京"
                  }
                },
                "required": ["location"]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "成功获取天气信息" }
        }
      }
    }
  },
  "components": {
    "schemas": {},
    "securitySchemes": {
      "BearerAuth": { "type": "http", "scheme": "bearer" }
    }
  }
}

5.2 创建步骤

  1. 登录 Dify 控制台,进入「工具」→「自定义」
  2. 把上面的 JSON 完整粘进 Schema 编辑器
  3. ⚠️ servers.url 换成你自己的 localtunnel URL(这一步最容易忘,忘了就一直调不通)
  4. 鉴权方式选「请求头」→ 类型「Bearer」→ Value 填你的 VALID_TOKEN
  5. 在测试区输入 {"location": "深圳"},能返回天气就成了
  6. 点「发布」,选择要启用该插件的应用

六、在 Agent 里用起来

在应用的提示词里加一句引导,模型才知道什么时候调:

你可以使用天气查询工具帮用户获取天气。当用户询问天气时,调用天气插件。

然后在「工具」里启用这个插件。对话示例:

用户:北京今天天气怎么样? Agent:北京今天是多云,温度 32℃/22℃

七、我踩过的 5 个坑(最值得记的排查表)

现象 原因 解决
❌ Reached maximum retries Dify 访问不到 localhost 必须用公网 URL,不能填 localhost
{"detail":"Not Found"} 路由路径不对 确认代码里是 @app.post("/weather")
❌ 403 Forbidden token 不匹配 Dify 里填的 Bearer 值要和 VALID_TOKEN 一致
❌ 连接超时 localtunnel 断了 重启 lt --port 8081,并更新 Dify 里的 URL
❌ 城市未找到 城市不在 CITY_CODES 在字典里补上该城市编码

八、从临时到永久

localtunnel 适合验证阶段,缺点是链接可能 24 小时失效、终端得一直开着。验证跑通后,把 FastAPI 服务部署到免费云(如 Render)拿一个永久 URL,再把 Dify Schema 里的 servers.url 换成它即可,代码一行不用改。

一句话总结

Dify 自定义插件本质就是:把一个 HTTP 接口用 OpenAPI Schema 描述给大模型,模型按需 Function Calling 调用。 本地 FastAPI 写逻辑、localtunnel 暴露公网、Dify 配 Schema + Bearer 鉴权,三步跑通;验证可行后再迁云端做永久部署。

参考:Dify 官方文档

相关推荐
用户69371750013841 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端
dear_bi_MyOnly1 小时前
【SpringBoot配置文件】
java·spring boot·后端·学习·spring·java-ee·学习方法
吐了啊取名字太难1 小时前
美颜系统AI修图本地跑并支持Mac、win、安卓、iOS不卡顿
android·人工智能·windows·数码相机·mac·ai编程
一缕清烟在人间2 小时前
HarmonyOS应用开发实战:萌宠日记 - 健康提醒列表与动态数据绑定
后端
HONG````2 小时前
HarmonyOS 6.1 实战:Stack 层叠布局与 Overlay 浮层详解
后端
程序员cxuan3 小时前
离谱,GPT-5.6 竟然在后台执行了 rm -rf
人工智能·后端·程序员
Csvn3 小时前
📊 SQL 入门 Day 9:CTE(公用表表达式)— 让 SQL 像写代码一样优雅
后端·sql
运维行者_3 小时前
如何查看每个IP的带宽使用情况?NetFlow 技术实战指南
开发语言·网络·分布式·后端·架构·带宽
腻害兔3 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:ERP 企业资源模块,一个轻量级进销存的完整实现?
前端·javascript·vue.js·人工智能·前端框架·产品经理·ai编程