前言
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