apt update # 更新软件源 apt install -y python3 python3-pip python3-venv # 安装 Python3 + pip + 虚拟环境模块 python3 -m venv /workspace/venv source /workspace/venv/bin/activate pip install -r requirements.txt
python3 -m venv /workspace/venv
source /workspace/venv/bin/activate
bash scripts/download_qwen3_models.sh
使用 Function Call 技术让生成的 SQL 在真实数据库上执行并返回结果。
parse_intent → generate_sql → execute_sql → echo → ENDgraphs/state.py: 新增 execution_result 和 executed_at 字段graphs/nodes/execute_sql.py: SQL 执行节点实现tools/db.py: 数据库客户端封装,支持 SQLite 查询scripts/setup_db.py: 自动下载 Chinook 示例数据库data/chinook.db: Chinook 示例数据库 (音乐商店数据)tests/test_m2_acceptance.py: M2 验收测试 (8 个测试用例)git checkout 02-func-call-db
# 复制环境变量模板
cp .env.example .env
# 编辑 .env 文件,填入你的 API Key
# 推荐使用 DeepSeek (国内访问快,价格低)
.env 配置示例:
LLM_PROVIDER=deepseek
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 数据库配置 (默认值,通常不需要修改)
DB_TYPE=sqlite
DB_PATH=data/chinook.db
python scripts/setup_db.py
这会自动下载 Chinook 数据库 (约 1MB),包含 11 个表:
测试数据库工具:
python tools/db.py
测试 SQL 执行节点:
python graphs/nodes/execute_sql.py
运行完整图:
python graphs/base_graph.py
运行 M2 验收测试:
python tests/test_m2_acceptance.py
所有测试用例必须成功执行并返回正确结果 (100% 通过率)
==================================================
Starting NL2SQL Graph (M2 - Function Call DB)
==================================================
=== Parse Intent Node ===
Question: Show all albums
Intent: {...}
=== Generate SQL Node ===
Question: Show all albums
LLM Response:
SELECT * FROM Album;
Extracted SQL:
SELECT * FROM Album;
=== Execute SQL Node ===
SQL: SELECT * FROM Album;
✓ Query successful
Rows returned: 100
Columns: AlbumId, Title, ArtistId
First row:
AlbumId: 1
Title: For Those About To Rock We Salute You
ArtistId: 1
=== Echo Node ===
Session ID: xxx
Question: Show all albums
Generated SQL:
SELECT * FROM Album;
Execution Result:
✓ Success
Rows: 100
Columns: AlbumId, Title, ArtistId
First row: {'AlbumId': 1, 'Title': 'For Those About To Rock We Salute You', 'ArtistId': 1}
==================================================
SQL Generated: ✓
SQL Executed: ✓
from tools.db import db_client
# 执行 SQL 查询
result = db_client.query("SELECT * FROM Album LIMIT 5")
if result["ok"]:
print(f"Rows: {result['row_count']}")
print(f"Columns: {result['columns']}")
for row in result['rows']:
print(row)
else:
print(f"Error: {result['error']}")
特点:
返回格式:
{
"ok": True, # 是否成功
"rows": [ # 查询结果
{"col1": "value1", ...},
...
],
"columns": ["col1", "col2"], # 列名
"row_count": 10, # 行数
"error": None # 错误信息
}
def execute_sql_node(state: NL2SQLState) -> NL2SQLState:
# 1. 获取生成的 SQL
# 2. 调用数据库客户端执行
# 3. 处理结果和错误
# 4. 返回更新后的 State
特点:
标准的示例数据库,模拟音乐商店业务:
主要表关系:
Artist (艺术家)
↓ (1:N)
Album (专辑)
↓ (1:N)
Track (歌曲) → Genre (风格)
↓ (N:M)
PlaylistTrack → Playlist (播放列表)
↓
InvoiceLine → Invoice → Customer (客户)
使用 Function Call 模式实现数据库查询:
这是典型的 Agent Tool Use 模式。
查询结果以字典列表形式返回,便于:
from tools.db import db_client
# 1. 查询数据
result = db_client.query("SELECT * FROM Album LIMIT 10")
# 2. 获取表名
tables = db_client.get_table_names()
# ['Album', 'Artist', 'Customer', ...]
# 3. 获取表结构
schema = db_client.get_table_schema("Album")
# {'table_name': 'Album', 'columns': [...]}
# 4. 获取所有表结构
all_schemas = db_client.get_all_schemas()
# 5. 测试连接
if db_client.test_connection():
print("Connected!")
result = db_client.query("SELECT * FROM NonExistent")
if not result["ok"]:
print(f"Error: {result['error']}")
# Error: Database error: no such table: NonExistent
常见错误:
Q: 数据库文件没下载成功? A:
Chinook_Sqlite.sqlite 重命名为 chinook.db 并放入 data/ 目录Q: 查询失败 "no such table"? A:
python tools/db.py 查看可用表名Q: 如何使用自己的数据库?
A: 修改 .env 文件:
DB_TYPE=sqlite
DB_PATH=path/to/your/database.db
Q: 如何添加 MySQL/PostgreSQL 支持?
A: 在 tools/db.py 的 DatabaseClient 中添加相应的驱动和连接逻辑:
if self.db_type == "mysql":
import mysql.connector
conn = mysql.connector.connect(...)
elif self.db_type == "postgresql":
import psycopg2
conn = psycopg2.connect(...)
Q: 执行时间太长怎么办?
A: M5 模块会添加超时控制和查询优化。当前可以通过 fetch_limit 参数限制返回行数:
result = db_client.query(sql, fetch_limit=10)
模拟一个在线音乐商店,类似 iTunes Store。
-- 1. 查询所有艺术家
SELECT * FROM Artist;
-- 2. 查询 AC/DC 的所有专辑
SELECT a.Title
FROM Album a
JOIN Artist ar ON a.ArtistId = ar.ArtistId
WHERE ar.Name = 'AC/DC';
-- 3. 统计每个风格的歌曲数量
SELECT g.Name, COUNT(*) as TrackCount
FROM Track t
JOIN Genre g ON t.GenreId = g.GenreId
GROUP BY g.Name
ORDER BY TrackCount DESC;
-- 4. 查询消费最多的前 10 个客户
SELECT c.FirstName, c.LastName, SUM(i.Total) as TotalSpent
FROM Customer c
JOIN Invoice i ON c.CustomerId = i.CustomerId
GROUP BY c.CustomerId
ORDER BY TotalSpent DESC
LIMIT 10;
MIT License