Dify工具插件開發和智能體開發全流程實戰
前言
Dify是一款開源的大語言模型應用開發平臺,旨在降低AI應用的開發門檻,幫助開發者和企業快速構建、部署及管理生成式AI應用。
Dify自1.0.0引入全新插件化架構,模型(Models)與工具(Tools)遷移為插件(Plugins),引入 Agent 策略(Agent Strategies)、擴展(Extensions)類型插件和插件集(Bundles)。通過全新的插件機制,能夠增強 AI 應用的感知和執行能力,拓寬AI在軟件操作領域的應用能力。
本文將介紹如下內容:
- 搭建基于Docker的MySQL數據庫環境
- 開發Dify工具插件實現MySQL數據庫操作
- 基于Dify搭建智能體通過插件操作MySQL實現理財助手智能體
文末可獲取完整插件代碼下載地址
搭建基于Docker的MySQL數據庫環境
1) 啟動Docker容器
- 建立docker_compose.yaml,內容如下:
services:
mysql:
image: mysql:5.7
container_name: mysql5.7
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- TZ=Asia/Shanghai
volumes:
- ./volumes:/var/lib/mysql
command: --character-set-server=utf8mb4
- 執行
docker compose up -d
啟動數據庫
2) 創建數據庫和表
- 下載MySQL客戶端軟件,例如dbeaver (https://dbeaver.io/download)
- 連接數據庫,創建數據庫和表
create database testdb;
use testdb;
CREATE TABLE `finance` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` varchar(36) NOT NULL DEFAULT '' COMMENT '用戶ID',
`date` datetime NOT NULL COMMENT '金額發生日期',
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '收入支出金額(收入記為正數,支出記為負數)',
`category` varchar(32) NOT NULL DEFAULT '' COMMENT '收支類別',
`remark` varchar(100) NOT NULL DEFAULT '' COMMENT '收支具體類目',
PRIMARY KEY (`id`),
KEY `idx_user_date` (`user_id`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日常收支';
開發Dify工具插件實現MySQL數據庫操作
以windows開發環境為例:
1) 下載插件開發腳手架工具
從https://github.com/langgenius/dify-plugin-daemon/releases
下載適用于windows的dify-plugin-windows-amd64.exe
。把程序所在目錄加到系統PATH路徑下,方便執行命令。運行命令查看版本信息,有輸出版本信息則說明安裝成功。
2) 創建項目
執行命令dify-plugin-windows-amd64.exe plugin init
創建項目,輸入插件名(mysql),作者和描述
按Enter確認后,選擇python做為開發語言
按Enter確認后,選擇插件類型,這里我們選tool
按Enter確認后,選擇插件權限。mysql插件不需要勾選任何權限,一直按down鍵移到最后一行,然后按回車即可完成項目創建,系統將自動生成插件項目代碼,目錄為mysql。
生成的目錄結構如下:
3) 創建python虛擬環境
進入生成的目錄mysql,修改生成的requirements.txt,添加用到的python包: mysql-connector-python。
創建python虛擬環境并安裝依賴包:
python -m venv .venv
# 激活環境
.\.venv\Scripts\activate
# 安裝依賴包
pip install -r requirements.txt
4) 封裝數據庫功能
在tools目錄下增加db.py,通過類DbManagerSingleton
封裝數據庫操作。DbManagerSingleton實現為單例模式,以便插件內不同的代碼共用類的實例對象。
import json
import mysql.connector
from contextlib import contextmanager
from threading import Lock
# 單例模式
class DbManagerSingleton:
_instance = None
_lock = Lock() # 線程鎖,確保線程安全
def __new__(cls, *args, **kwargs):
if not cls._instance:
with cls._lock:
if not cls._instance:
cls._instance = super().__new__(cls)
cls._instance.__init__(*args, **kwargs)
return cls._instance
def __init__(self, host, port, user, password, database):
self.connection_pool = mysql.connector.pooling.MySQLConnectionPool(
pool_name="db_pool",
pool_size=5,
pool_reset_sessinotallow=True,
host=host, # 數據庫服務器Host
port=port, # 數據庫服務器端口
user=user, # 數據庫用戶名
password=password, # 數據庫密碼
database=database, # 數據庫名
)
@contextmanager
def get_cursor(self):
with self.connection_pool.get_connection() as connection:
cursor = None
try:
cursor = connection.cursor()
yield cursor
connection.commit()
except Exception as e:
connection.rollback()
raise e
finally:
if cursor:
cursor.close()
def execute_sql(self, sql: str) -> str:
with self.get_cursor() as cursor:
cursor.execute(sql)
if cursor.description is not None:
rows = cursor.fetchall()
result = {
"columns": [desc[0] for desc in cursor.description],
"rows": rows,
}
return json.dumps(result, default=str)
else:
return f"row affected:{cursor.rowcount}"
5) 實現授權配置
修改provider/mysql.yaml。其中,credentials_for_provider的信息用于配置插件授權(配置數據庫連接相關信息)。內容如下:
identity:
author: testuser
name: mysql
label:
en_US: mysql
zh_Hans: mysql
description:
en_US: mysql tools
zh_Hans: mysql tools
icon: icon.svg
tools: # 插件包含的工具列表
- tools/get_table_definition.yaml
- tools/execute_sql.yaml
extra:
python:
source: provider/mysql.py
credentials_for_provider:
host: # 數據庫HOST
type: text-input # 輸入類型為普通文本
required: true # 此憑證是必需的
label: # 在 Dify UI 中顯示的標簽 (支持多語言)
en_US: MySQL Server Host
zh_Hans: MySQL Server主機
port: # 數據庫端口
type: text-input # 輸入類型為普通文本
required: true # 此憑證是必需的
label: # 在 Dify UI 中顯示的標簽 (支持多語言)
en_US: MySQL Server Port
zh_Hans: MySQL Server端口
user: # 數據庫用戶名
type: text-input # 輸入類型為普通文本
required: true # 此憑證是必需的
label: # 在 Dify UI 中顯示的標簽 (支持多語言)
en_US: user name
zh_Hans: 用戶名
password: # 數據庫密碼
type: secret-input # 輸入類型為密碼框
required: true # 此憑證是必需的
label: # 在 Dify UI 中顯示的標簽 (支持多語言)
en_US: password
zh_Hans: 密碼
database: # 數據庫名
type: text-input # 輸入類型為普通文本
required: true # 此憑證是必需的
label: # 在 Dify UI 中顯示的標簽 (支持多語言)
en_US: database name
zh_Hans: 數據庫名
修改provider/mysql.py,實現配置校驗,通過建立連接執行show tables
驗證參數是否正確。代碼如下:
from typing import Any
from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
class MysqlProvider(ToolProvider):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
"""
IMPLEMENT YOUR VALIDATION HERE
"""
from tools.db import DbManagerSingleton
dbManager = DbManagerSingleton(
host=credentials["host"],
port=credentials["port"],
user=credentials["user"],
password=credentials["password"],
database=credentials["database"],
)
dbManager.execute_sql("show tables")
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
6) 實現工具get_table_definition獲取表結構定義
provider/mysql.yaml的tools字段定義了插件包含的工具列表。
tools: # 插件包含的工具列表
- tools/get_table_definition.yaml
- tools/execute_sql.yaml
每個工具需要一個yaml文件進行描述,包含工具的名稱、描述、參數列表等。
將自動生成的tools目錄下的mysql.yaml和mysql.py分別重命名為get_table_definition.yaml和get_table_definition.py
get_table_definition.yaml修改為如下內容:
identity:
name: get_table_definition
author: testuser
label: # 在 Dify UI 中顯示的工具名稱 (多語言)
en_US: get database table definition
zh_Hans: 獲取數據庫表定義
description:
human: # 給人類用戶看的工具描述 (多語言)
en_US: get database table definition
zh_Hans: 獲取數據庫表定義
llm: get database table definition # 給 LLM 看的工具描述 (用于 Agent 模式)
parameters: # 定義工具的輸入參數列表
- name: table
type: string
required: true
label: # 在 Dify UI 中顯示的參數標簽 (多語言)
en_US: database table name
zh_Hans: 數據庫表名
human_description: # 給人類用戶看的參數描述 (多語言)
en_US: database table name
zh_Hans: 數據庫表名
llm_description: database table name # 給 LLM 看的參數描述 (指導 Agent 如何填充)
form: llm # 參數表單類型 ('llm' 或 'form')
extra:
python:
source: tools/get_table_definition.py
get_table_definition.py修改為如下內容:
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from tools.db import DbManagerSingleton
class GetTableDefinitionTool(Tool):
def __init__(self, runtime, session):
super().__init__(runtime, session)
self.dbManager = DbManagerSingleton(
host=runtime.credentials["host"],
port=runtime.credentials["port"],
user=runtime.credentials["user"],
password=runtime.credentials["password"],
database=runtime.credentials["database"],
)
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
table = tool_parameters["table"]
sql = f"show create table {table}"
yield self.create_text_message(self.dbManager.execute_sql(sql))
7) 實現工具execute_sql執行SQL語句
在tools目錄新建execute_sql.yaml和execute_sql.py
execute_sql.yaml修改為如下內容:
identity:
name: execute_sql
author: testuser
label: # 在 Dify UI 中顯示的工具名稱 (多語言)
en_US: execute sql
zh_Hans: 執行sql語句
description:
human: # 給人類用戶看的工具描述 (多語言)
en_US: execute sql
zh_Hans: 執行sql語句
llm: execute sql # 給 LLM 看的工具描述 (用于 Agent 模式)
parameters: # 定義工具的輸入參數列表
- name: sql
type: string
required: true
label: # 在 Dify UI 中顯示的參數標簽 (多語言)
en_US: sql
zh_Hans: sql語句
human_description: # 給人類用戶看的參數描述 (多語言)
en_US: the sql to execute
zh_Hans: 要執行的sql語句
llm_description: sql # 給 LLM 看的參數描述 (指導 Agent 如何填充)
form: llm # 參數表單類型 ('llm' 或 'form')
extra:
python:
source: tools/execute_sql.py
execute_sql.py修改為如下內容:
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from tools.db import DbManagerSingleton
class ExecuteSqlTool(Tool):
def __init__(self, runtime, session):
super().__init__(runtime, session)
self.dbManager = DbManagerSingleton(
host=runtime.credentials["host"],
port=runtime.credentials["port"],
user=runtime.credentials["user"],
password=runtime.credentials["password"],
database=runtime.credentials["database"],
)
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
sql = tool_parameters["sql"]
yield self.create_text_message(self.dbManager.execute_sql(sql))
8) 修改manifest.yaml
manifest.yaml定義了插件最基礎的信息,包括插件名稱、作者、包含的工具、模型等信息。
本插件雖然沒用到storage持久化存儲的權限,但需要將storage里的size字段從0改為大于等于1024,否則啟動插件時會報錯。
storage:
enabled: false
size: 1024
9) 調試
在Dify的插件管理頁面,點擊圖中紅框部分,彈出調試的URL和Key。
復制.env.example到.env,修改REMOTE_INSTALL_HOST和REMOTE_INSTALL_KEY。
執行命令python main.py
啟動插件,等待至顯示"dify_plugin.plugin:Installed tool",工具安裝成功。
此時,在Dify的插件管理頁面可以看到mysql插件。選擇mysql插件,在右側點擊“去授權”
填上相關參數并保存。
新建測試Agent,添加mysql插件的兩個工具,模型選擇doubao-1.5-pro-32k,模型會根據用戶提問自動調用數據庫工具,并根據工具的響應生成回復。效果如下圖:
10) 打包
確認插件能夠正常運行后,可以通過以下命令行工具打包插件,生成mysql.difypkg。
dify-plugin-windows-amd64.exe plugin package mysql
11) 發布
- 發布到Dify Marketplace參考: https://docs.dify.ai/zh-hans/plugins/publish-plugins/publish-to-dify-marketplace
- 發布到個人GitHub倉庫參考: https://docs.dify.ai/zh-hans/plugins/publish-plugins/publish-plugin-on-personal-github-repo
- 本地發布與分享
在Dify的插件管理頁面,點擊“安裝插件”=>“本地插件”。
選擇打包生成的mysql.difypkg,會提示簽名錯誤。
需要修改docker/.env,將FORCE_VERIFYING_SIGNATURE改為false,然后重建docker。修改該字段后,Dify平臺將允許安裝所有未在Dify Marketplace上架(審核)的插件,可能存在安全隱患。
docker compose down
docker compose up -d
docker重建后,重新安裝本地插件。
基于Dify搭建智能體通過插件操作MySQL實現理財助手智能體
1) 安裝Agent策略插件
點擊右上角“插件”按鈕,進入插件頁面,選擇“探索Marketplace“。
選擇插件Dify Agent 策略
進行安裝。
2) 創建應用
- 創建一個空白應用,類型為Chatflow。
- 調整工作流,把默認的LLM節點替換為Agent節點。
- 設置Agent節點的Agent策略,并添加MySQL工具策略選擇Function Calling。
- Agent節點的模型選擇doubao-1.5-pro-32k
- 設置Agent節點的指令(系統提示詞)
# 角色
你是記賬助手,可以通過調用數據庫工具完成記錄日常收入和支出并作分析。
為了完成記賬操作,需要先獲取數據庫表finance的定義。
記賬的用戶ID取值為{{#sys.user_id#}}
# 收支類別
收入:工資薪金,勞務報酬,投資收益,分紅收入,租金收入,其它收入
支出:住房,交通,通訊,保險,餐飲,電子產品,日用品,服飾,旅行,娛樂,醫療,學習,其它支出
# 技能
## 技能1:記錄日常開支
將開支信息記錄到數據庫表finance
## 技能2:統計日常開支
根據用戶輸入信息分析統計日常開支
# 限制
僅處理記賬相關問題,不回復其它問題
- 設置Agent節點的查詢和最大迭代次數Agent完成一項任務可能需要迭代多次調用工具,最大迭代次數設置過小可能導致無法正常完成任務。
- 預覽調試輸入“昨天吃飯用了50元,還花了35元買了拖鞋。今天買手機花了2999元,吃飯花了60元”,驗證輸出為成功記錄支出。
另外,通過數據庫表驗證數據正常插入。
輸入“匯總各個類別的金額”,驗證數據查詢。
確認無誤后,點擊右上角的“發布”按鈕發布應用。
總結
本文以實現MySQL數據庫操作插件詳細介紹開發Dify工具插件的全流程,并使用該插件搭建理財智能體,展示了Agent從語義理解到工具調用的完整決策鏈路。
跟MCP工具插件開發比較,Dify工具插件開發步驟相對復雜,且僅能使用Python開發,僅能用于Dify生態。但Dify插件的整體鏈路開銷較MCP插件低,如果你對系統時延和成本更敏感,且無需使用MCP的動態發現工具能力,Dify工具插件也許是個更好的選擇。
完整代碼地址:https://github.com/copilot-coder/dify-plugin-mysql