业务同事突然甩来一句:“查一下上个月华东区销量最高的3个产品”……你是不是也遇到过这种情况?以前总得手动写SQL,还得反复确认字段名对不对、表名有没有写错。但现在,用Dify拖拽编排加上Qwen本地模型,5分钟内就能让自然语言直接变成可执行的MySQL查询语句。说实话,整个过程不需要联网,不上传任何业务数据,所有推理和查询都在你本机完成──这才是真正意义上的“离线智能问数”。

这篇文章就是想告诉你,这事儿其实没那么复杂。
准备本地运行环境
先确保Docker装好并且跑得起来。Ubuntu 22.04或者WSL2环境最省心,如果你用的是Windows,别忘了确认WSL2已启用,并且Docker Desktop配置为WSL2后端。
接下来,把Dify的代码克隆下来再进到Docker部署目录:
git clone https://github.com/langgenius/dify.git → cd dify/docker
这一步不能偷懒:【cp .env.example .env】。如果跳过,后面服务启动时就会因为缺失环境变量直接报错。接着执行一键启动命令:【docker compose up -d】。首次运行需要拉取镜像,大概2到3分钟吧。完成后访问https://localhost:3000,就能看到Dify的登录页了。
配置Qwen模型接入Dify
方法一:通过Ollama本地托管(对新手特别友好)
在终端运行:【ollama run qwen2.5-coder:7b】。等模型下载完成后会自动加载,Ollama默认监听https://localhost:11434。
然后进入Dify后台,右上角头像 → 设置 → 模型供应商 → 添加模型。模型名称填 qwen2.5-coder,基础URL填 https://host.docker.internal:11434,保存即可。
这里有个坑必须提醒:Docker容器里不能用localhost访问宿主机服务,一定要用host.docker.internal这个别名,否则连接会超时。
方法二:用vLLM部署Qwen3-1.5B(适合手上有GPU的朋友)
执行:【vllm serve Qwen/Qwen2.5-1.5B-Instruct --port 9999 --dtype float16】。启动后基础URL改为https://host.docker.internal:9999/v1。
创建NL2SQL工作流
第一步:新建应用,选择“聊天编排”,命名它为“MySQL智能问数”。
第二步:删掉默认的LLM节点,然后在开始节点后面添加工具节点。选择rookie_text2data插件(这个需要提前从GitHub安装:https://github.com/jaguarliuu/rookie_text2data)。
第三步:配置rookie_text2data的参数:
- 数据库类型选 MySQL;
- 主机地址写你的MySQL容器名,或者宿主机IP。比如Docker网络中MySQL容器名叫
mysql-db,那就填mysql-db; - 端口、用户名、密码、数据库名按实际情况填写;
- “Schema Description”栏里粘贴表结构描述。这一步至关重要,字段描述越准确,生成的SQL就越靠谱。必须把主键、外键、字段类型和注释都写上。
第四步:在rookie_text2data节点后面接一个LLM节点。System提示词设置为:
你是一位MySQL专家,只输出标准SQL语句。检查输入SQL是否符合MySQL 8.0语法,修正表名/字段名大小写、反引号缺失、函数误用等问题。输出必须是单条可执行语句,不含解释、不含```标记。
第五步:再接一个rookie_excute_sql工具节点,输入变量选择上一步LLM输出的text字段。执行结果会自动以JSON格式返回。
调试与验证
点击右上角的“测试”按钮,输入一句自然语言问题:“查出销售额大于10万的商品名称和销量”。观察每个节点的输出:
rookie_text2data节点应该输出类似SELECT name, sales FROM products WHERE sales > 100000这样的原始SQL;- LLM节点输出的是修正后的标准SQL,比如字段加了反引号,WHERE条件加上了括号;
rookie_excute_sql节点输出的是一个JSON数组,包含字段名和数据行。
万一哪个节点报错了,先检查rookie_text2data的数据库连接参数,再看看Schema描述是不是和真实表结构完全一致。这个问题最容易被忽略:【字段名拼写错误或类型描述不符,都会导致SQL生成失败】。
最后,在rookie_excute_sql节点后面加一个模板转换节点,把JSON转成易读的文本。变量填{{#节点ID.output#}},格式设成{{item.name}}: {{item.sales}}。这样,自然语言到SQL的整个自动化闭环就算跑通了。
