在数据分析、业务运营等场景中,非技术人员往往需要通过 SQL 查询数据库,但编写 SQL 对他们来说门槛极高。LangChain 提供了一套完整的解决方案,能将自然语言问题自动转为 SQL 查询、执行并返回自然语言答案。本文将手把手教你搭建两种 SQL 问答方案:SQL Chain(固定流程链) 和 SQL Agent(智能决策代理),满足不同复杂度的查询需求。
一、核心原理:SQL 问答的三步法
无论是 Chain 还是 Agent,LangChain 处理 SQL 问答的核心逻辑都遵循三步原则:
2. 数据准备
本文使用经典的 Chinook.db 示例数据库(包含音乐商店的艺术家、专辑、订单等表),可直接从 LangChain 示例库 下载,放在项目根目录即可。
3. API Key 配置
创建 .env 文件,填入 OpenAI API Key(需在官网申请):
env
OPENAI_API_KEY=你的OpenAI API密钥
三、第一步:连接 SQL 数据库
首先建立 LangChain 与数据库的连接,验证数据库可访问:
python
运行
from langchain_community.utilities import SQLDatabase
from dotenv import load_dotenv
import os
# 加载环境变量
load_dotenv()
openai_api_key = os.getenv("OPENAI_API_KEY")
# 连接 SQLite 数据库(替换为你的数据库 URI)
# MySQL 示例:mysql+pymysql://user:password@host:port/dbname
# PostgreSQL 示例:postgresql+psycopg2://user:password@host:port/dbname
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
# 验证连接
print("数据库方言:", db.dialect) # 输出:sqlite
print("可用表名:", db.get_usable_table_names()) # 输出数据库中的表列表
print("测试查询结果:")
print(db.run("SELECT * FROM Artist LIMIT 10;")) # 执行测试 SQL
关键说明:
四、方案一:搭建 SQL Chain(固定流程问答链)
SQL Chain 是 "固定流程" 的问答方案,适合简单、标准化的查询场景,步骤清晰且可控。
步骤 1:生成 SQL 查询语句
先用大模型将自然语言问题转为 SQL:
python
运行
from langchain.chains import create_sql_query_chain
from langchain_openai import ChatOpenAI
# 初始化大模型(GPT-3.5-turbo 性价比最高)
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0, # 温度设为0,保证 SQL 生成稳定
api_key=openai_api_key
)
# 创建 SQL 查询生成链
write_query = create_sql_query_chain(llm, db)
# 测试生成 SQL
response = write_query.invoke({"question": "How many employees are there"})
print("生成的 SQL:", response)
# 输出示例:SELECT COUNT(*) FROM Employee;
步骤 2:添加 SQL 执行能力
仅生成 SQL 还不够,需结合 QuerySQLDataBaseTool 执行 SQL:
python
运行
from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool
# 初始化 SQL 执行工具
execute_query = QuerySQLDataBaseTool(db=db)
# 串联"生成 SQL + 执行 SQL"的链
sql_chain = write_query | execute_query
# 测试执行结果
result = sql_chain.invoke({"question": "How many employees are there"})
print("SQL 执行结果:", result)
# 输出示例:[(8,)](表示有8名员工)
步骤 3:生成自然语言回答
最后将 "问题 + SQL + 执行结果" 传给大模型,生成易懂的自然语言答案:
python
运行
from operator import itemgetter
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
# 定义答案生成的提示词模板
answer_prompt = PromptTemplate.from_template(
"""Given the following user question, corresponding SQL query, and SQL result, answer the user question in natural language.
Question: {question}
SQL Query: {query}
SQL Result: {result}
Answer: """
)
# 构建答案生成链
answer_chain = answer_prompt | llm | StrOutputParser()
# 整合完整链:生成 SQL → 执行 SQL → 生成答案
full_chain = (
RunnablePassthrough.assign(query=write_query).assign(
result=itemgetter("query") | execute_query
)
| answer_chain
)
# 测试完整链
final_answer = full_chain.invoke({"question": "How many employees are there"})
print("最终回答:", final_answer)
# 输出示例:There are 8 employees in total.
关键说明:
五、方案二:搭建 SQL Agent(智能决策代理)
SQL Chain 适合固定流程的简单查询,但面对复杂问题(如多表关联、分步查询、需要解释表结构)时,Agent 更灵活 ------ 它能自主决策是否需要多轮查询、是否需要查看表结构,甚至修正错误的 SQL。
步骤 1:初始化 SQL 代理
python
运行
from langchain_community.agent_toolkits import create_sql_agent
# 创建 SQL 代理
agent_executor = create_sql_agent(
llm=llm,
db=db,
agent_type="openai-tools", # 适配 OpenAI 函数调用的代理类型
verbose=True # 开启详细日志,查看代理的思考过程
)
步骤 2:测试复杂查询
python
运行
# 测试1:多表关联查询(总销售额按国家统计,找出消费最多的国家)
result1 = agent_executor.invoke(
{
"input": "List the total sales per country. Which country's customers spent the most?"
}
)
print("代理回答1:", result1["output"])
# 测试2:查询表结构(无需手动写 SQL,代理自动判断需要执行 DESC 语句)
result2 = agent_executor.invoke({"input": "Describe the playlisttrack table"})
print("代理回答2:", result2["output"])
执行日志示例 (开启 verbose=True 后可见):
plaintext
> Entering new AgentExecutor chain...
Invoking: `sql_db_list_tables` with `{}`
Got tables: Artist, Album, Track, Playlist, PlaylistTrack, Customer, Invoice, InvoiceLine, Employee, Genre, MediaType
Invoking: `sql_db_schema` with `{'table_names': ['Invoice', 'Customer']}`
Got schema: ...(表结构)
Invoking: `sql_db_query` with `{'query': 'SELECT c.Country, SUM(i.Total) AS TotalSales FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.Country ORDER BY TotalSales DESC LIMIT 1;'}`
SQL 执行结果:[('USA', 523.06)]
> Finished chain.
代理回答1:The United States has the highest customer spending with a total of 523.06 in sales.
关键说明:
六、Chain vs Agent:该怎么选?
| 特性 | SQL Chain | SQL Agent |
|---|---|---|
| 适用场景 | 简单、标准化的单步查询 | 复杂、多步、需要决策的查询 |
| 灵活性 | 低(固定流程) | 高(自主决策) |
| 可解释性 | 高(流程透明) | 中(需查看日志) |
| 性能 | 快(少轮次) | 稍慢(多轮思考) |
| 学习成本 | 低(代码简单) | 中(需理解代理逻辑) |
选型建议:
七、常见问题排查
八、总结
-
自然语言转 SQL:大模型根据用户问题和数据库结构,生成符合语法的 SQL 查询语句;
-
执行 SQL 查询:调用数据库工具执行生成的 SQL,获取查询结果;
-
结果转自然语言 :大模型将 SQL 执行结果整理成通俗易懂的自然语言回答。
二、前置准备
1. 环境依赖安装
bash
运行
pip install langchain langchain-openai sqlalchemy python-dotenv -
langchain:核心框架,提供 SQL 链 / 代理能力; -
langchain-openai:对接 OpenAI 大模型(也可替换为开源模型); -
sqlalchemy:数据库连接工具,支持 SQLite/MySQL/PostgreSQL 等; -
python-dotenv:管理环境变量(如 OpenAI API Key)。 -
SQLDatabase.from_uri支持主流数据库,只需替换 URI 即可切换; -
get_usable_table_names()可查看数据库中可访问的表,方便大模型了解表结构; -
db.run()可直接执行 SQL,用于验证数据库连接是否正常。 -
RunnablePassthrough.assign:用于给链的上下文添加新变量(如query和result); -
itemgetter("query"):提取上下文里的query变量,传给执行工具; -
整个链的执行流程:
用户问题 → 生成 SQL → 执行 SQL → 生成自然语言答案,全程自动化。 -
Agent 会先查看数据库的表列表 → 再查看相关表的结构 → 生成并执行 SQL → 整理答案;
-
相比 Chain,Agent 具备 "自主思考" 能力,能处理更复杂的问题,比如:
- "先找出销量最高的专辑,再查看该专辑的所有曲目";
- "如果表中没有销售额字段,告诉我有哪些可用字段"。
-
日常简单查询(如 "统计某表的行数""查询某类数据的总数"):选 Chain,速度快、稳定;
-
复杂业务查询(如多表关联、分步分析、需要动态调整查询逻辑):选 Agent,更智能。
-
生成的 SQL 语法错误 :
- 原因:大模型不了解数据库方言(如 SQLite/MySQL 语法差异);
- 解决:确保
db.dialect正确,或在提示词中指定数据库类型(如 "生成 SQLite 语法的 SQL")。
-
代理无法找到相关表 :
- 原因:
get_usable_table_names()返回的表名不全; - 解决:初始化
SQLDatabase时,用include_tables指定可用表(如db = SQLDatabase.from_uri(..., include_tables=["Employee", "Customer"]))。
- 原因:
-
API Key 无效 :
- 检查
.env文件中的密钥是否正确,或是否有足够的余额。
- 检查
-
数据库连接失败 :
- 确认数据库 URI 正确,且数据库服务已启动(如 MySQL/PostgreSQL)。
-
LangChain 搭建 SQL 问答系统的核心是 "自然语言转 SQL → 执行 SQL → 生成答案" 三步;
-
SQL Chain 适合简单、固定流程的查询,代码简洁、性能快;
-
SQL Agent 适合复杂、需要自主决策的查询,具备更强的灵活性;
-
两种方案都无需手动编写 SQL,非技术人员可通过自然语言直接查询数据库。