之前有小伙伴咨询,Dify不支持直连数据库,如何做到通过自然语义给大模型然后返回对应的查询数据?
收到这个留言的时候,脑海里就已经有了一个大致的方案,流程如下图:
首先利用DeepSeek帮我准备数据库表结构和需要插入的数据
# 创建班级表CREATE TABLE classes ( class_id INT AUTO_INCREMENT PRIMARY KEY, class_name VARCHAR(50) NOT NULL);
# 创建用户表CREATE TABLE users ( user_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, class_id INT, FOREIGN KEY (class_id) REFERENCES classes(class_id));
# 插入班级数据INSERT INTO classes (class_name) VALUES ('Class A'),('Class B'),('Class C'),('Class D'); # 插入用户数据INSERT INTO users (username, email, class_id) VALUES ('Alice', 'alice@example.com', 1),('Bob', 'bob@example.com', 2),('Charlie', 'charlie@example.com', 3),('David', 'david@example.com', 4),('Eve', 'eve@example.com', 1),('Frank', 'frank@example.com', 2),('Grace', 'grace@example.com', 3),('Hank', 'hank@example.com', 4),('Ivy', 'ivy@example.com', 1),('Jack', 'jack@example.com', 2);
然后通用利用DeepSeek帮我生成一个暴露接口用于查询数据库的服务,用python写
from flask import Flask, request, jsonifyimport pymysqlimport re
app = Flask(__name__)
DB_CONFIG = { 'host': 'localhost', 'user': 'test', 'password': '123456', 'database': 'test', 'cursorclass': pymysql.cursors.DictCursor}
@app.route('/query', methods=['POST'])def execute_query(): data = request.json if not data or 'sql' not in data: return jsonify({'error': 'Missing SQL statement in request body'}), 400 sql = data['sql'] if not re.match(r'^SELECTs', sql, re.IGNORECASE): return jsonify({'error': 'Only SELECT queries are allowed'}), 400 try: connection = pymysql.connect(**DB_CONFIG) with connection.cursor() as cursor: cursor.execute(sql) result = cursor.fetchall() return jsonify(result) except pymysql.MySQLError as e: return jsonify({'error': str(e)}), 500 finally: if connection: connection.close() if __name__ == '__main__': app.run(debug=True)
创建一个单独的知识库,里面存放的就是我们的创建表结构的语句,可以是text格式也可以是markdown格式,这个都可以,我这边就很简单粗暴的直接扔建表语句进去,最好是带注释的,我演示用就简洁一点:
【知识检索】节点的输出作为上下文传入,并在提示词内设置【开始】节点输入的"content"字段,以供LLM去做分析:
我设置的提示词也贴在下面,“{}”花括号内的是你需要在节点中设置的参数,参考上图显示的最终结果:
你是一个数据分析师,根据用户输入的{content}需求以及检索到{上下文},生成对应的查询SQL,SQL必须经过严格的校验。## 你可以使用的其他方法用户输入类似于“求和”或“总和”时,则在sql语句中使用SUM()。用户输入类似于“平均数”或“平均”时,在在sql语句中使用AVG()。## 要求1.如果用户输入的内容无法生成为sql语句,请直接说“抱歉,该命令无法形成数据库查询操作”。2.当可以生成sql语句时,请确保输出的内容为完整正确的sql语句,不要输出此外的其他任何字符,确保你生成的内容用户可以直接执行查询操作。3.对于字符串内容的查询请使用LIKE操作而不是等于操作。4.请不要在回复中包括除sql语句之外的任何内容。5.禁止中间过程输出。6.输出的sql为一整行。
在【自然语言推导SQL语句LLM】节点后,添加【HTTP请求】节点,并在节点上添加API和查询参数(JSON格式),查询参数就是【自然语言推导SQL语句LLM】节点输出的SQL语句,参数名称"sql"可以根据上面的python代码中处理的字段名称来调整:
需要注意的是,Dify是我本地Docker部署的,然后这个查询接口也是我本地启动的,所以这个接口地址不能用127.0.0.1,需要改成host.docker.internal。
在【查询SQL结果请求】节点之后,就可以添加【结束】节点了,输出内容就是我们【查询SQL结果请求】的输出内容即可。因为输出的是JSON格式的数据,也可以让返回结果为表格等,但是需要额外的节点去做处理,在这儿就不在演示,感兴趣的小伙伴们可以在这个基础上再做优化调整。
感谢小伙伴们提供的留言问题,我这只是一种思路方案,还有一种方案就是,通过python代码连接Dify然后结合知识库解析自然语言并推导出对应的SQL语句提供给python去执行,这种方案和我提供的方案大同小异,都是可以实现效果。
上述只是一个简单的demo,只是为了跑通这个逻辑,实际使用的话,要做很多层的限制,对SQL的推导也需要做更多的条件分支和二次分析等节点,以提高准确性和可用性。