自然语言生成 SQL,就像恋人读懂你没说出口的需要。
而今天,我们要从一行行真实的 Python 代码出发,看看这份"默契"是如何被构建出来的。
🧠 背后的主角:一个会写 SQL 的 AI 助手
在你的笔记本中,核心逻辑其实非常清晰------
用大模型(如 DeepSeek)将用户的自然语言问题,转化为可执行的 SQL 语句。
整个流程分为四步:
- 连接数据库,建表
- 提取表结构(Schema)作为上下文
- 构造 Prompt,调用 LLM
- 执行生成的 SQL,返回结果
这看似简单,却蕴含了现代 AI 应用的关键思想:上下文驱动 + 精准约束 + 安全执行。
🔌 第一步:轻量级数据库,快速验证想法
你使用了 Python 内置的 sqlite3 模块,创建了一张员工表:
python
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees(
id INTEGER PRIMARY KEY,
name TEXT,
department TEXT,
salary INTEGER
)
""")
接着插入几条示例数据(虽然因主键冲突报错,但无伤大雅):
scss
sample_data = [ (6, "黄佳", "销售", 50000), (7, "宁宁", "工程", 75000), (8, "谦谦", "销售", 60000), (9, "悦悦", "工程", 80000), (10, "黄仁勋", "市场", 55000), (11, "杜经理", "工程", 80000)]
cursor.executemany("INSERT INTO employees VALUES(?,?,?,?)", sample_data)
💡 这体现了快速原型开发的精髓:用最轻量的方式验证核心逻辑。
🗺️ 第二步:把"地图"交给 AI ------ Schema 是关键!
真正让 LLM 不瞎猜的,是你主动提供的 表结构信息:
ini
schema = cursor.execute("PRAGMA table_info(employees)").fetchall()
schema_str = "CREATE TABLE EMPLOYEES (\n" + "\n".join([f"{col[1]} {col[2]}" for col in schema]) + "\n)"
输出结果为:
sql
CREATE TABLE EMPLOYEES (
id INTEGER
name TEXT
department TEXT
salary INTEGER
)
这一步至关重要!
没有 Schema,LLM 只能靠猜字段名(比如会不会叫 dept 而不是 department?),极易出错。
而有了它,AI 就像拿到了一张精准的数据地图,知道哪里是"姓名",哪里是"部门"。
✅ 经验总结:Text-to-SQL 成功率 80% 取决于 Schema 提供是否完整清晰。
💌 第三步:构造 Prompt ------ 教 AI "好好说话"
你设计的提示词非常克制而有效:
ini
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个schema,请输出一个SQL查询,来回答以下问题:
只输出SQL查询语句本身,不要使用任何markdown格式,不要包含反引号、代码块标记或额外说明。
问题:{query}
"""
并设置:
temperature=0→ 确保输出确定、稳定max_tokens=2048→ 防止截断- 明确禁止多余内容 → 避免返回解释、注释或 Markdown
为什么这么严格?
因为后续你要直接 cursor.execute(sql) ------
如果 AI 返回的是:
"好的!以下是查询语句:\n
sql\nSELECT ...\n"
那程序就会崩溃!
✅ 最佳实践 :对 LLM 的输出做格式强约束,是生产落地的前提。
🎯 实战演示:从"人话"到"机器语"
场景一:查询
用户问:"工程部门员工的姓名和工资是多少?"
AI 输出:
ini
SELECT name, salary FROM EMPLOYEES WHERE department = '工程';
执行结果:
css
[('宁宁', 75000), ('悦悦', 80000), ('杜经理', 80000)]
✅ 完美匹配中文语义,且字段、表名大小写一致(注意你用了 EMPLOYEES 全大写,SQLite 不敏感,但好习惯值得保持)。
场景二:插入
用户说:"在销售部门增加一个新员工,姓名为张三,工资为45000"
AI 输出:
sql
INSERT INTO EMPLOYEES (name, department, salary) VALUES ('张三', '销售', 45000);
注意:它自动省略了 id 字段 !
因为你在建表时设了 id INTEGER PRIMARY KEY,SQLite 会自动递增。
这说明 LLM 理解主键自增语义,非常聪明。
场景三:删除
用户指令:"删除市场部门的黄仁勋"
AI 输出:
ini
DELETE FROM EMPLOYEES WHERE department = '市场' AND name = '黄仁勋';
✅ 条件完整,避免误删。
但如果用户说"删掉黄仁勋",没提部门,就可能误删重名者------
这提醒我们:业务语义需足够明确,或系统应主动澄清。
⚠️ 安全反思:不能盲目信任生成结果
尽管 Demo 很美好,但真实场景必须考虑:
| 风险 | 你的代码现状 | 建议改进 |
|---|---|---|
| 执行任意 SQL | 直接 cursor.execute(sql_query) |
加入 SQL 白名单(如只允许 SELECT) |
| 表名/字段注入 | 依赖 LLM 输出 | 用正则校验是否只含字母、数字、下划线 |
| 无 WHERE 删除 | 当前示例有 WHERE | 强制校验 DELETE/UPDATE 必须带条件 |
🔒 记住:Demo 可以浪漫,生产必须严谨。
🌱 延伸思考:如何融入 MVC 架构?
正如理念所倡导的 AI First + Mobile First + MVC,你可以这样扩展:
- Model :封装数据库操作,提供
query_by_natural_language(text)方法 - View:移动端聊天界面,输入框 + 结果卡片
- Controller:接收用户消息 → 调用 AI 生成 SQL → 执行 → 返回结构化数据
未来甚至可加入:
- 多轮对话("刚才那个工程部的人,再查下他的入职时间")
- 权限控制(销售只能查销售部数据)
- 自然语言转可视化图表("画个柱状图")
✨ 结语:技术的温度,在于"理解"而非"执行"
回到开头那句话:
你说"我想知道上个月谁最常买我们的花。"
TA 默默写下 SQL,为你打捞答案。
而在这份代码中,我们看到的不仅是 OpenAI 客户端、sqlite3 游标、executemany,
更是一种以人为本的设计哲学:
- 给 AI 足够上下文(Schema)
- 约束输出格式(纯 SQL)
- 安全执行结果(commit / fetchall)
真正的智能,不是炫技,而是让非技术人员也能平等地使用数据。
而这,或许就是下一代应用的起点。
📬 现在,轮到你了 :
试着改写 ask_deepseek 函数,让它支持多表关联,或者自动处理日期范围(如"上个月" → BETWEEN '2025-11-01' AND '2025-11-30')。
你会发现,AI 是笔,你是诗人。