如果您已经熟悉Dify的基础使用,希望通过二次开发来扩展其功能,这份指南将为您梳理一条清晰的上手路径。

一、二次开发概述

Dify作为一个开源的大语言模型应用开发平台,其核心优势在于模块化设计和可扩展性。二次开发主要涉及两个方面:

  • 自定义插件开发:扩展Dify的功能,接入外部API、处理特定业务逻辑
  • 源码级定制:修改Dify核心代码,适配企业特定需求

对于新手而言,建议从插件开发入手,这门槛较低且风险可控。

二、开发环境搭建

2.1 基础环境准备

# 克隆Dify源码(指定稳定版本)
git clone https://github.com/langgenius/dify.git
cd dify
git checkout v1.11.1  # 使用最新稳定版本

# 本地运行Dify(便于调试)
cd docker
cp .env.example .env
docker compose up -d

2.2 开发工具配置

  • IDE推荐:VS Code + Python插件
  • 调试工具:安装Dify官方CLI工具plugin-cli
    npm install -g @dify/plugin-cli
    

三、第一个Dify插件:Hello World

3.1 插件核心结构

每个Dify自定义插件本质上是一个符合特定规范的函数模块,需暴露主处理函数并定义输入输出接口。

my-plugin/
├── manifest.json      # 插件元数据
├── main.py            # 核心逻辑
└── requirements.txt   # 依赖声明

3.2 编写第一个插件

manifest.json(插件元数据):

{
  "name": "hello-world",
  "version": "1.0.0",
  "author": "your-name",
  "description": "我的第一个Dify插件",
  "inputs": [
    {
      "name": "text",
      "type": "string",
      "required": true,
      "description": "输入文本"
    }
  ],
  "outputs": [
    {
      "name": "result",
      "type": "string"
    }
  ]
}

main.py(核心逻辑):

def main(input_data: dict) -> dict:
    """
    将输入文本转换为大写
    :param input_data: 包含 'text' 字段的字典
    :return: 包含 'result' 字段的字典
    """
    text = input_data.get("text", "")
    if not text:
        return {"error": "Missing required field: text"}
    
    # 核心处理逻辑
    return {"result": text.upper()}

# 示例输入/输出
# 输入: {"text": "hello world"}
# 输出: {"result": "HELLO WORLD"}

3.3 插件部署与测试

本地注册模式(适合开发调试):

# 进入插件目录
cd my-plugin

# 使用CLI工具注册
plugin-cli register --path . --local

验证方法:在Dify工作流中添加自定义节点,选择你的插件进行测试。

四、进阶开发:调用外部API

4.1 基础API调用示例

以下示例展示如何在插件中调用外部天气API:

import requests
import json

def main(input_data: dict) -> dict:
    """
    天气查询插件
    """
    city = input_data.get("city", "")
    if not city:
        return {"error": "请输入城市名称"}
    
    try:
        # 调用天气API
        api_key = input_data.get("api_key", "")
        url = f"https://api.weather.com/v1/location/{city}/current"
        
        response = requests.get(
            url,
            params={"apiKey": api_key},
            timeout=10
        )
        response.raise_for_status()
        
        data = response.json()
        
        # 格式化返回结果
        return {
            "city": city,
            "temperature": data["current"]["temp"],
            "conditions": data["current"]["weather"][0]["description"],
            "humidity": data["current"]["humidity"]
        }
        
    except requests.exceptions.RequestException as e:
        return {"error": f"API调用失败: {str(e)}"}

4.2 插件配置界面

为插件添加可视化配置界面,提升用户体验:

<!-- 简单配置界面模板 -->
<div class="plugin-config">
  <div class="form-group">
    <label>API密钥</label>
    <input type="password" v-model="config.apiKey" 
           placeholder="请输入API密钥">
  </div>
  
  <div class="form-group">
    <label>超时设置(秒)</label>
    <input type="number" v-model="config.timeout" 
           min="1" max="60" value="10">
  </div>
  
  <div class="form-group">
    <label>缓存策略</label>
    <select v-model="config.cacheStrategy">
      <option value="none">不缓存</option>
      <option value="short">短期(5分钟)</option>
      <option value="long">长期(1小时)</option>
    </select>
  </div>
</div>

五、实战项目:文档处理插件

5.1 需求分析

创建一个能够提取PDF文档内容并生成摘要的插件。

5.2 核心实现

import PyPDF2
from transformers import pipeline

def main(input_data: dict) -> dict:
    """
    PDF文档摘要生成插件
    """
    pdf_path = input_data.get("pdf_path", "")
    max_length = input_data.get("max_length", 200)
    
    # 提取PDF文本
    text = extract_text_from_pdf(pdf_path)
    
    # 使用预训练模型生成摘要
    summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
    summary = summarizer(text, max_length=max_length)[0]['summary_text']
    
    return {
        "original_length": len(text),
        "summary": summary,
        "word_count": len(summary.split())
    }

def extract_text_from_pdf(pdf_path):
    """PDF文本提取辅助函数"""
    text = ""
    with open(pdf_path, 'rb') as file:
        pdf_reader = PyPDF2.PdfReader(file)
        for page in pdf_reader.pages:
            text += page.extract_text()
    return text

5.3 集成到Dify工作流

  1. 在Dify工作流中添加自定义节点
  2. 选择刚开发的PDF处理插件
  3. 配置输入参数(PDF路径、摘要长度)
  4. 连接LLM节点进一步处理摘要结果

六、源码级开发:理解Dify架构

如果需要更深度的定制,需要理解Dify的源码架构:

dify/
├── api/                 # 后端核心代码
│   ├── controllers/     # 路由层
│   ├── services/        # 业务逻辑层
│   ├── models/          # 数据模型层
│   └── core/            # 核心功能模块
│       ├── embedding/   # 向量化模块
│       └── rag/         # RAG检索模块
├── web/                 # 前端代码 (Next.js + React)
├── docker/              # 部署配置
└── sdks/                # 多语言SDK

6.1 扩展RAG能力示例

如果要自定义知识库检索逻辑,可以修改core/rag/query.py

# 在现有检索逻辑基础上添加自定义重排序
def retrieve_context_with_rerank(query: str, top_k: int = 5):
    # 原始向量检索
    vector = embedding_model.encode(query)
    initial_results = vector_db.similarity_search(vector, top_k * 2)
    
    # 添加自定义重排序逻辑
    reranked_results = custom_reranker(initial_results, query)
    
    return [doc.text for doc in reranked_results[:top_k]]

七、开发资源与最佳实践

7.1 学习路径建议

阶段 学习内容 预期时间 产出目标
入门 环境搭建 + Hello World插件 3小时 跑通第一个插件
基础 API集成插件 + 配置界面 1-2天 完成天气查询插件
进阶 复杂业务插件 + 工作流集成 3-5天 完成文档处理插件
源码级 理解核心架构 + 提交PR 2-3周 定制化RAG逻辑

7.2 避坑指南

  1. 版本锁定:开发时务必锁定Dify版本,避免API变动
  2. 错误处理:插件必须有完善的错误捕获,避免影响主流程
  3. 性能考虑:耗时操作需异步处理,避免阻塞
  4. 本地优先:先用本地注册模式调试,再考虑远程部署

7.3 常用资源

  • Dify官方文档:https://docs.dify.ai
  • 插件示例库:https://github.com/langgenius/dify-plugins
  • 开发者社区:Dify Discord #plugin-development频道

八、从新手到贡献者

完成以上学习后,您可以:

  1. 分享插件:将开发的插件提交到Dify官方插件市场
  2. 参与社区:解答其他开发者的插件相关问题
  3. 贡献代码:向Dify主仓库提交Pull Request,改进核心功能

记住:二次开发的本质是理解契约——理解Dify的扩展接口规范,然后用自己的代码填补能力空白。从最简单的Hello World开始,逐步构建复杂的业务插件,您很快就能成为Dify生态的贡献者。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐