AIGC 时代,用自然语言操作数据库:SQLite + LLM 的轻量级实践
一、为什么我们需要"自然语言查数据库"?
SQL 是结构化查询语言,精准但门槛不低。对于产品经理、运营、测试甚至初级开发者来说,写一条 JOIN 或 GROUP BY 可能比写周报还难。
而如今,大语言模型(如 DeepSeek、GPT、Claude 等)已经具备极强的语义理解与代码生成能力。只要给它清晰的数据库结构(Schema)和一句自然语言问题,它就能生成准确的 SQL。
这不仅是效率工具,更是人机协作范式的升级:
你负责"想问什么",AI 负责"怎么查"。
二、为什么选择 SQLite?
在众多数据库中,SQLite 是轻量级场景的王者:
- 零配置、单文件 :整个数据库就是一个
.db文件,无需安装服务。 - 嵌入式友好:微信、Chrome、Android 系统内部都用它做本地存储。
- 标准 SQL 支持:支持大部分 SQL 语法,足以应对日常 CRUD。
- Python 原生支持 :
import sqlite3即可开干,无额外依赖。
💡 对比 MySQL/PostgreSQL:它们适合高并发、分布式场景,但对本地工具、桌面应用、原型开发来说"杀鸡用牛刀"。
三、动手实践:5 分钟搭建"自然语言查库"系统
1. 创建数据库与表
python
# 轻量级数据库
import sqlite3
from openai import OpenAI
# ========== 1. 连接数据库(仅一次)==========
conn = sqlite3.connect("test1.db")
cursor = conn.cursor()
# ========== 2. 创建表 ==========
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
id INTEGER PRIMARY KEY,
name TEXT,
department TEXT,
salary INTEGER
)
""")
2. 插入示例数据
scss
# ========== 3. 插入示例数据 ==========
sample_data = [ (1, "陈", "开发部", 32000), (2, "张", "销售部", 20000), (3, "徐", "开发部", 33000), (4, "李", "销售部", 15000)]
cursor.executemany('INSERT OR IGNORE INTO employees VALUES (?, ?, ?, ?)', sample_data)
conn.commit()
✅ 使用
INSERT OR IGNORE避免重复插入导致主键冲突。
3. 获取表结构(Schema)
这是 LLM 理解数据库的关键上下文:
python
# ========== 4. 获取 Schema(统一用小写表名)==========
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);"
print("数据库Schema:")
print(schema_str)
输出:
sql
数据库Schema:
CREATE TABLE employees (
id INTEGER
name TEXT
department TEXT
salary INTEGER
);
✅ 表名统一为小写
employees,符合 SQLite 实际建表习惯,避免大小写混淆。
4. 接入 LLM(以 DeepSeek 为例)
ini
# ========== 5. 接入 LLM ==========
client = OpenAI(
api_key='你的 API Key',
base_url='https://api.deepseek.com/v1'
)
def ask_deepseek(query: str, schema: str) -> str:
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个Schema,你能输出一个SQL查询来回答以下问题吗?
只输出SQL查询,不要输出任何其他内容,也不要带任何格式。
问题:{query}
"""
response = client.chat.completions.create(
model="deepseek-reasoner",
max_tokens=256,
messages=[{"role": "user", "content": prompt}]
)
# ✅ 关键:返回纯 SQL 字符串
return response.choices[0].message.content.strip()
🔑 注意:函数最终返回的是
.content.strip(),确保是干净的 SQL 字符串,而非 ChatCompletionMessage 对象。
5. 自然语言提问 → 执行 SQL
python
# ========== 6. 自然语言提问 → 生成并执行 SQL ==========
question = "开发部部门员工的姓名和工资是多少?"
generated_sql = ask_deepseek(question, schema_str)
print("\n生成的SQL查询:")
print(generated_sql)
# ✅ 安全提示:此处仅为演示!生产环境需校验SQL
try:
result = cursor.execute(generated_sql).fetchall()
print("\n查询结果:")
for row in result:
print(row)
except Exception as e:
print(f"\n执行出错: {e}")
典型输出:
sql
生成的SQL查询:
SELECT name, salary FROM employees WHERE department = '开发部';
查询结果:
('陈', 32000)
('徐', 33000)
✅ 成功!你用中文问问题,AI 自动生成 SQL 并返回结果。
6. 关闭连接(良好习惯)
bash
# ========== 7. 关闭连接 ==========
conn.close()
四、Prompt 工程:让 LLM 更可靠
LLM 不是万能的,好的 Prompt 是稳定输出的关键。我们通过以下设计提升准确性:
| 技巧 | 说明 |
|---|---|
| 明确 Schema 上下文 | 必须提供表结构,否则 LLM 会"瞎猜字段" |
| 限制输出格式 | "只输出 SQL,不要其他内容" 避免冗余 |
| 指定大小写与引号 | 统一表名小写、字符串用单引号,减少语法错误 |
| 示例引导(Few-shot) | 可追加 1~2 个输入-输出样例,效果更佳 |
🔒 安全警告 :
上述代码中的
cursor.execute(generated_sql)直接执行 LLM 输出的 SQL,存在严重安全风险 (如 SQL 注入、DROP TABLE 等)。在真实项目中,务必:
- 使用 白名单机制(只允许 SELECT,禁止 DDL/DML)
- 对生成的 SQL 进行 AST 解析校验
- 或采用 参数化查询模板,仅让 LLM 填充 WHERE 条件部分