实战:代码助手与 SQL 问答
让 LLM 理解表结构生成可执行 SQL,并安全地执行与解释结果 —— 自然语言查询数据库的完整方案。
Text-to-SQL 的核心:把表结构喂给模型
from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///sales.db")
table_info = db.get_table_info() # 建表语句 + 示例行
sql_prompt = ChatPromptTemplate.from_messages([
("system", "你是 SQL 专家。根据表结构把问题转成 SQLite 查询。"
"只输出一条 SQL,不要解释。\n\n表结构:\n{schema}"),
("human", "{question}"),
])
sql_chain = sql_prompt | llm | StrOutputParser()
sql = sql_chain.invoke({"schema": table_info, "question": "上个月销售额最高的三个商品"})
安全执行:只读 + 白名单
LLM 生成的 SQL 绝不能直接执行写操作:
- 数据库账号只授予 SELECT 权限,或使用只读副本;
- 执行前校验:SQL 必须以 SELECT 开头,禁止分号后跟第二条语句;
- 限制返回行数(默认 LIMIT 100),防止全表扫描拖垮库。
sql = sql.strip().rstrip(";")
if not sql.upper().startswith("SELECT"):
raise ValueError("仅允许查询语句")
result = db.run(sql + " LIMIT 100")
让模型解释结果
answer_chain = ChatPromptTemplate.from_messages([
("system", "用中文解释查询结果,给出关键数字和一句话结论。"),
("human", "问题:{question}\n结果:{result}"),
]) | llm | StrOutputParser()
进阶方向
- 把表结构与业务术语说明("GMV=成交总额")一起入向量库,先检索相关表再生成 —— 多表大库必备;
- 记录每次"问题→SQL"对,用户纠正后作为 few-shot 示例,越用越准。
📝 课后练习
quiz-1 执行 LLM 生成的 SQL 前,最关键的安全措施是?