🧠 AI First 时代:用自然语言操作数据库,让业务人员也能写 SQL
“未来,每个人都是开发者。” —— 在 LLM(大语言模型)加持下,这句话正加速成为现实。
最近我在一个实验性项目中尝试将 自然语言生成 SQL 的能力嵌入到日常的数据操作流程中。通过 DeepSeek 等开源大模型,配合 SQLite 数据库,我构建了一个极简但极具潜力的 “AI 驱动后台管理系统” 原型。今天就来和大家分享这个想法与实现过程。
📌 背景:从 “Mobile First” 到 “AI First”
在传统的 Web 开发范式中,我们经历了 PC First → Mobile First 的转变。而如今,随着大模型能力的普及,“AI First” 正在成为新的开发哲学。
“AI First vibe coding + gemini 等自然语言生成 SQL,和我们的应用结合起来……让小编、业务、任何人加入后台运营和维护。”
这意味着:不再需要懂 SQL 的 DBA 或后端工程师,普通用户只需用自然语言描述需求,系统就能自动生成并执行正确的数据库操作。
🔧 技术实现:Text-to-SQL 的轻量级实践
初始化一个与 DeepSeek 大模型 API 通信的客户端对象
from openai import OpenAI
client = OpenAI(
api_key="sk-8e9b4d6c8a0f4c3b9e1a2d5f7g6h5j4k",
base_url="https://api.deepseek.com/v1"
)
1. 搭建本地 SQLite 数据库并插入数据
# coding
import sqlite3
# 连接数据库
conn = sqlite3.connect("test.db")
# 创建一个游标对象
# 用于执行sql 获取查询结果
cursor = conn.cursor()
# 员工表
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees(
id INTEGER PRIMARY KEY,
name TEXT,
department TEXT,
salary INTEGER
)
""")
# list
sample_data = [
# 元祖类型 有顺序的 tuple
(6, "黄佳", "销售", 50000),
(7, "宁宁", "工程", 75000),
(8, "谦谦", "销售", 60000),
(9, "悦悦", "工程", 80000),
(10, "黄仁勋", "市场", 55000),
(11, "枫枫", "工程", 80000)
]
# 插入sql
cursor.executemany(
"INSERT INTO employees VALUES(?,?,?,?)",
sample_data
)
# 提交sql事务
conn.commit()
关键点分析:
- 游标是操作数据库的关键,在操作之前,先创建一个游标对象方便后续操作cursor =conn.cursor()
- 当执行的sql语句是增删改等操作时,需要进行事务提交-->conn.commit()
- executemany() 可以一次插入多条数据,后面接受的一个参数,可以是列表
2. 提取 Schema 作为上下文
为了让 LLM 理解数据库结构,我们将表结构转换为标准的 CREATE TABLE 语句:
# text2sql 给llm什么? context
# 表结构 作为上下文给prompt schema
schema = cursor.execute("PRAGMA table_info(employees)").fetchall()
print(schema)
# 列表推导式
schema_str = "CREATE TABLE EMPLOYEES (\n"+"\n".join([f"{col[1]} {col[2]}" for col in schema]) + "\n)"
print(schema_str)
这个 Schema 会被拼接到 Prompt 中,作为模型生成 SQL 的依据。
关键点解析:
-
PRAGMA table_info(employees):这是 SQLite 特有的元数据查询命令
PRAGMA是 SQLite 提供的一种用于获取或设置数据库内部状态/结构信息的机制。 -
table_info(表名) 会返回该表的完整列定义信息,包括:
- 列序号(cid)
- 列名(name)
- 数据类型(type)
- 是否允许为空(notnull)
- 默认值(dflt_value)
- 是否为主键(pk,1代表是主键,0代表不是)
-
.fetchall() 获取所有返回行,组成一个 Python 列表
-
列表推导式:从数据库元数据中提取每列的名称和类型,生成格式化的字段定义列表,用于构建 LLM 可理解的表结构描述。
schema_str = "CREATE TABLE EMPLOYEES (\n"+"\n".join([f"{col[1]} {col[2]}" for col in schema]) + "\n)"
3. 调用 DeepSeek 大模型生成 SQL
使用 OpenAI 兼容的 API(这里接入的是 DeepSeek 的 endpoint),构造如下 Prompt:
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个Schema,请输出一个SQL查询来回答以下问题。
只输出SQL查询语句本身,不要使用任何Markdown格式,
不要包含反引号、代码块标记或额外说明。
问题:{query}
"""
def ask_deepseek(query, schema):
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个Schema,请输出一个SQL查询来回答以下问题。
只输出SQL查询语句本身,不要使用任何Markdown格式,
不要包含反引号、代码块标记或额外说明。
问题:{query}
"""
print(prompt)
response = client.chat.completions.create(
model="deepseek-chat",
max_tokens=2048,
messages=[{
"role": "user",
"content": prompt
}],
temperature=0//回答随机度
)
return response.choices[0].message.content
例如,当用户问:
“工程部门员工的姓名和工资是多少?”
模型返回:
SELECT name, salary FROM EMPLOYEES WHERE department = '工程';
程序直接执行该语句,得到结果:
[('宁宁', 75000), ('悦悦', 80000), ('枫枫', 80000)]
关键点解析:
- 提供精确的上下文:将表结构作为固定上下文注入prompt。让 LLM 知道数据库有哪些表、字段和类型,避免“凭空猜测”
- 明确指令约束输出格式:明确输出格式,确保返回结果可直接被 cursor.execute() 安全执行,无需后处理。
- 采用标准 Chat Completion 接口:使用 OpenAI 兼容的
chat.completions.create调用 DeepSeek,便于切换不同 LLM(如换成 Qwen、Gemini 等),只需改 endpoint 和 key。 - 使用低 temperature=0:相同输入始终生成相同 SQL,提升系统可靠性
4. 支持增删改查全操作
更令人兴奋的是,这套机制不仅能查,还能 写!
- 新增:“在销售部门增加一个新员工,姓名为刘锦苗,工资为45000”
→ 生成INSERT INTO ... - 删除:“删除市场部门的黄仁勋”
→ 生成DELETE FROM ... WHERE ...
只要权限控制得当,这几乎就是一个 自然语言驱动的数据库管理界面。
🚀 应用场景:让非技术人员参与系统维护
想象一下这些场景:
- 运营同学:想临时加一个促销员工账号?直接说“在市场部加一个叫小美的员工,工资6000”,系统自动执行。
- 产品经理:想看某部门薪资分布?问一句即可,无需提工单等后端支持。
- 小程序后台:结合前端语音/文本输入,打造“对话式管理面板”。
这正是 “数据库平权” 的体现——把技术门槛降到最低,释放业务创造力。
⚠️ 注意事项与未来方向
当然,当前方案仍有局限:
- 安全性:必须严格限制可执行的 SQL 类型(如禁止
DROP),最好通过白名单或沙箱机制。 - 准确性:复杂查询(多表 JOIN、子查询)可能出错,需人工校验或引入 RAG 增强上下文。
- 权限控制:不同角色应有不同的操作范围(如销售只能看本部门)。
未来,我们可以:
- 将此能力封装为 AI Agent,集成到钉钉/企微/飞书机器人中;
- 结合 ModelScope(魔搭) 微调专属 Text-to-SQL 模型;
- 扩展至 MySQL / PostgreSQL 等生产级数据库;
- 加入 自然语言反馈:“共找到3人,是否导出为 Excel?”
💡 结语:编程的尽头是自然语言
过去,我们学 SQL 是为了和机器沟通;
现在,机器学会了我们的语言。
在这个 AI First 的时代,代码不再是壁垒,而是桥梁。
而我们要做的,就是搭建更多这样的桥,让更多人轻松跨越。
让技术隐形,让价值凸显。
附:完整代码参考
from openai import OpenAI
client = OpenAI(
api_key="your_api_key",
base_url="https://api.deepseek.com/v1"
)
import sqlite3
conn = sqlite3.connect("test.db")
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees(
id INTEGER PRIMARY KEY,
name TEXT,
department TEXT,
salary INTEGER
)
""")
sample_data = [
(6, "黄佳", "销售", 50000),
(7, "宁宁", "工程", 75000),
(8, "谦谦", "销售", 60000),
(9, "悦悦", "工程", 80000),
(10, "黄仁勋", "市场", 55000),
(11, "枫枫", "工程", 80000)
]
cursor.executemany("INSERT INTO employees VALUES(?,?,?,?)", sample_data)
conn.commit()
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)"
def ask_deepseek(query, schema):
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个Schema,请输出一个SQL查询来回答以下问题。
只输出SQL查询语句本身,不要使用任何Markdown格式,
不要包含反引号、代码块标记或额外说明。
问题:{query}
"""
print(prompt)
response = client.chat.completions.create(
model="deepseek-chat",
max_tokens=2048,
messages=[{
"role": "user",
"content": prompt
}],
temperature=0
)
return response.choices[0].message.content
question = "工程部门员工的姓名和工资是多少"
sql_query = ask_deepseek(question, schema_str)
results = cursor.execute(sql_query).fetchall()
print(results)
question = "在销售部门增加一个新员工,姓名为刘锦苗,工资为45000"
sql_question = ask_deepseek(question, schema_str)
cursor.execute(sql_question)
conn.commit()
question = "删除市场部门的黄仁勋"
sql_query = ask_deepseek(question, schema_str)
cursor.execute(sql_query)
conn.commit()
question = "查询所有员工信息"
sql_query = ask_deepseek(question, schema_str)
results = cursor.execute(sql_query).fetchall()
print(results)