GuidesAgents
用 LangChain 构建 SQL Agent
按官方 SQL agent 教程:下载 Chinook、封装只读 SQL tools,再用 create_agent 让模型查表、校验并执行查询。
基于 LangChain Docs 整理 · 官方资料 ↗
把自然语言变成 SQL,再解释结果——这就是 SQL Agent。官方教程用 Chinook 示例库,走通:列表面、读 schema、生成查询、检查错误、执行、回答。
模型生成的 SQL 有固有风险。官方强调:数据库权限尽量收窄,演示工具仅供学习,不能当生产安全方案。
SQL Agent 实际在做什么
高层流程是:取可用表与 schema → 判断相关表 → 生成查询 → 用 LLM 检查常见错误 → 执行 → 若引擎报错则修正 → 基于结果作答。
核心不是一次写出完美 SQL,而是把错误信息反馈给模型,让它重写,直到成功或达到上限。
准备 Chinook 与模型
官方安装
pip install langchain langgraph
pip install -U "langchain[openai]"选一个支持 tool-calling 的 chat model。官方示例常用 init_chat_model;下面以 OpenAI 为例。
初始化模型
import os
from langchain.chat_models import init_chat_model
os.environ["OPENAI_API_KEY"] = "sk-..."
model = init_chat_model("gpt-5.5")从官方托管的 GCS 下载 Chinook.db,并确认表存在:
下载并探查 Chinook
import pathlib
import requests
import sqlite3
url = "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db"
local_path = pathlib.Path("Chinook.db")
if not local_path.exists():
response = requests.get(url, timeout=60)
response.raise_for_status()
local_path.write_bytes(response.content)
con = sqlite3.connect("Chinook.db")
cursor = con.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = [row[0] for row in cursor.fetchall() if not row[0].startswith("sqlite_")]
print(tables)
con.close()把数据库操作封装成 Tools
官方用 @tool 做四件套:list_tables、schema、query、query_checker。它们是演示级最小封装,不是安全的生产包装。
官方 SQL tools(核心路径)
import sqlite3
from langchain.tools import tool
@tool
def sql_db_list_tables() -> str:
"""Input is an empty string, output is a comma-separated list of tables in the database."""
con = sqlite3.connect("Chinook.db")
try:
cursor = con.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = [row[0] for row in cursor.fetchall() if not row[0].startswith("sqlite_")]
return ", ".join(tables)
finally:
con.close()
@tool
def sql_db_schema(table_names: str) -> str:
"""Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables.
Be sure that the tables actually exist by calling sql_db_list_tables first!
Example Input: table1, table2, table3"""
con = sqlite3.connect("Chinook.db")
try:
cursor = con.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
valid_tables = {row[0] for row in cursor.fetchall() if not row[0].startswith("sqlite_")}
results = []
for table in table_names.split(","):
table = table.strip()
if table not in valid_tables:
results.append(f"Error: table_names {{{table!r}}} not found in database")
continue
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name=?;", (table,))
schema_row = cursor.fetchone()
if schema_row:
results.append(schema_row[0])
return "\n\n".join(results)
finally:
con.close()
@tool
def sql_db_query(query: str) -> str:
"""Input to this tool is a detailed and correct SQL query, output is a result from the database."""
con = sqlite3.connect("Chinook.db")
try:
cursor = con.cursor()
cursor.execute(query)
return str(cursor.fetchall())
except Exception as e:
return f"Error: {e}"
finally:
con.close()
@tool
def sql_db_query_checker(query: str) -> str:
"""Use this tool to double check if your query is correct before executing it.
Always use this tool before executing a query with sql_db_query!"""
trigger_prompt = f"""{query}
Double check the sqlite query above for common mistakes...
Output the final SQL query only.
SQL Query: """
response = model.invoke(trigger_prompt)
return response.text.strip()
tools = [sql_db_list_tables, sql_db_schema, sql_db_query, sql_db_query_checker]query_checker 在执行前让模型复查常见 SQL 错误;真正跑库的是 sql_db_query。
create_agent 并跑第一个问题
系统提示要强制:先看表、再读 schema、查询前必须检查、禁止 DML,并限制返回行数。
系统提示与 create_agent
from langchain.agents import create_agent
system_prompt = """
You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct {dialect} query to run,
then look at the results of the query and return the answer. Unless the user
specifies a specific number of examples they wish to obtain, always limit your
query to at most {top_k} results.
You MUST double check your query before executing it. If you get an error while
executing a query, rewrite the query and try again.
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the
database.
To start you should ALWAYS look at the tables in the database to see what you
can query. Do NOT skip this step.
Then you should query the schema of the most relevant tables.
""".format(dialect="sqlite", top_k=5)
agent = create_agent(
model,
tools,
system_prompt=system_prompt,
)官方示例问题
question = "Which genre on average has the longest tracks?"
stream = agent.stream_events(
{"messages": [{"role": "user", "content": question}]},
version="v3",
)
for kind, item in stream.interleave("messages", "tool_calls"):
if kind == "messages":
for token in item.text:
print(token, end="", flush=True)
elif kind == "tool_calls":
print(f"\nTool call: {item.tool_name}({item.input})")
print(f"\nTool result: {item.output}")正常轨迹会先 list_tables,再 schema(Track, Genre),再 checker,再 query,最后用聚合结果回答。
容易踩的坑
Agent 直接写 DELETE / UPDATE
核对 system_prompt 是否包含禁止 DML;生产环境必须用只读账号,不只靠提示词。
查错列名反复失败
确认先 list_tables / schema;query_checker 与错误回传要保留在对话里。
把演示 tools 直接上生产
官方写明仅供演示。上线前要权限隔离、查询白名单与审计。
官方资料
LangChain Docs
Build a SQL agent ↗