Skip to content

About

零依赖、可离线运行的 Text2SQL 全链路演示:Schema Linking 三阶段 + SQL 安全校验 + 执行级评估(Execution Accuracy)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

text2sql-mini

一个零依赖、可离线运行的精简 Text2SQL 全链路演示:问题 → Schema Linking → Prompt 构造 → SQL 生成 → 安全校验 → 执行 → 执行级评估。

为什么会有这个仓库

我在企业环境里做过一个智能经营分析平台(制造业绩效/利润分析场景),但那是涉密项目, 代码和数据都不能外传。所以我把其中最能体现工程判断的几个环节抽出来,用公开可复现的 方式重新实现了一遍,放进这个仓库。

这个仓库不追求 SOTA 准确率,它想证明的是三件事:

  1. 我知道 Table 多了以后不能把全量 Schema 塞进 Prompt,并且能实现"召回 → 匹配 → 推理"的替代方案;
  2. 我知道生成 SQL 之前必须过安全闸门,并且能说清闸门该拦哪些东西;
  3. 我知道效果不能靠感觉验证,并且能用执行结果级比对把准确率算出来。

快速开始

# 1. 初始化演示数据库(生成 data/university.db)
python -m app.main --init

# 2. 提一个问题,看完整链路(每步耗时、召回了什么、命中了哪条业务规则)
python -m app.main --ask "计算机学院学生的平均年龄"

# 3. 跑评估集,输出 Execution Accuracy
python -m app.main --eval

# 4. 看全量 Schema 与精简 Schema 的 Token 差多少
python -m app.main --compare --ask "计算机学院学生的平均年龄"

# 5. 单元测试
python -m unittest discover -s tests -v

要求:Python 3.10+,无第三方依赖(只用 sqlite3 / re / json / urllib / unittest)。

可选:接入真实模型生成 SQL

# 任意 OpenAI 兼容端点即可
set LLM_API_KEY=sk-xxx
set LLM_BASE_URL=https://api.openai.com/v1
set LLM_MODEL=gpt-4o-mini
python -m app.main --ask "各院系学生的平均成绩" --driver llm

目录结构

text2sql-mini/
├── app/
│   ├── schema_index.py   # Schema 元数据 + TF-IDF 字符 n-gram 检索(可替换为真实 Embedding)
│   ├── linker.py         # Schema Linking 三阶段:表召回 → 字段语义匹配 → Join 路径推理
│   ├── prompt.py         # 动态精简 Schema 组装 + Token 估算
│   ├── generator.py      # 两种 SQL 生成 driver:rule(离线)/ llm(OpenAI 兼容)
│   ├── guard.py          # SQL 安全校验:单语句 / 只读 / 表白名单 / 禁止关键字 / LIMIT 收敛
│   ├── executor.py       # SQLite 执行 + 值链接(value linking)
│   ├── runner.py         # Pipeline 编排 + Execution Accuracy 评估
│   └── main.py           # CLI
├── data/
│   ├── university.sql    # 教务场景演示库(6 张表,含外键关系)
│   └── eval_questions.json  # 14 条评估用例(simple / medium / complex 三层)
└── tests/test_pipeline.py   # 15 个单元测试

三个核心设计

1. Schema Linking:不是"知道得更多",而是"给模型的上下文更准"

全量 Schema 注入在表少时能用,表一多就会同时撞上三个问题:Token 成本高、无关字段成为噪声、相似字段互相干扰。 所以拆成三步:

阶段 做法 解决的问题
表级召回 表描述(表名+中文别名+字段说明)建索引,按问题取 Top-K 从 N 张表缩到 3 张
字段语义匹配 向量相似度 + 业务规则黑白名单,规则可直接压过向量分 "成绩"必须取 enrollments.score,不能被名字更像的字段抢走
Join 路径推理 从锚表出发做 BFS,求最短 Join 路径与 Join 条件 3 表关联时不必猜 Join 怎么写

实测(--compare):同一个问题,全量 Schema 约 428 tokens,精简后约 185 tokens,省下约 57%。

关键取舍写在这里:召回阶段宁多勿漏,生成阶段宁少勿错。Linking 召回 3 张表,但生成时只 JOIN 真正需要的表——否则 COUNT(*) 会因为多余的 JOIN 被行数放大,直接把答案算错。

2. SQL 安全校验:闸门放在执行之前

guard.py 的顺序是:单语句 → 必须是 SELECT/WITH → 危险关键字扫描 → FROM/JOIN 表白名单 → LIMIT 收敛(上限 200)。

为什么"表白名单"比"关键字黑名单"更重要:黑名单永远有漏网之鱼,而白名单是默认拒绝。

3. 执行级评估:不比 SQL 字符串,比结果集

同一语义的 SQL 写法可以完全不同(别名、括号、JOIN 顺序),字符串比对会大面积误判。 所以 execution_match() 的口径是:列数相同 → 行数相同 → 结果按行排序后逐值比对(数值统一四舍五入到 4 位,规避浮点误差)。

实测结果

python -m app.main --eval(rule driver,离线确定性生成):

  simple   4/4
  medium   4/4
  complex  4/6
Execution Accuracy = 12/14 = 85.7%

故意保留了两条失败用例,因为它们正好说明规则生成器的能力边界:

用例 问题 失败原因
C05 列出学生姓名和他所在的院系名称 需要跨表多列投影,规则生成器被刻意限制为"只投影锚表字段",于是漏掉了 departments.dept_name
C02 成绩最高的前 5 条记录对应的学生姓名和成绩 问的是"取最大值对应的整行",不是 MAX() 聚合值;规则生成器只会生成 MAX(score)

这两类问题靠规则硬凑会越来越脆,正是真实系统里需要 LLM 的地方——所以这个仓库同时提供了 --driver llm,两者对比着看比只给一个数字更能说明问题。

与真实项目的对应关系

真实项目里的做法 本仓库 简化点
Embedding 向量检索 + 混合评分 TF-IDF 字符 n-gram 为了零依赖;接口一致,可替换
指标知识库 RAG(指标定义/公式/依赖) RULE_CONDITIONS + RULES 规则表 规则是知识库的退化形态
BFS Join 路径推理 同样的 BFS 图规模小
行列级权限 + 字段脱敏 仅保留表白名单 + LIMIT 收敛 单用户演示库,无角色概念
Plan-and-Execute 多步 Agent 未实现 单问题单 SQL,复杂分析不在本仓库范围
FastAPI + SSE 服务化 CLI only 用命令行让链路更透明

已知边界

  • 数据集不是 Spider/BIRD 原始数据。data/university.sql 的 schema 参照公开 Text2SQL 基准里 典型的教务多表 Join 场景设计,数据为演示构造,不含任何真实业务信息。如果要接真实基准, 替换 data/ 与 schema_index.py 里的元数据即可(检索与生成逻辑不用改)。
  • 评估集只有 14 条。小样本上的百分比只能当趋势看,不能当结论。
  • rule driver 是确定性生成器,不是模型。它存在的意义是让链路可以在完全离线的情况下被验证。
  • 没有做多轮对话上下文。每条问题独立处理。

License

MIT

About

零依赖、可离线运行的 Text2SQL 全链路演示:Schema Linking 三阶段 + SQL 安全校验 + 执行级评估(Execution Accuracy)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages