系列目录(共 15 篇):
- 开篇:为什么这个时代我们需要 Text2SQL
- 技术原理深度剖析(本文)
- 企业语义治理:ChatBI 落地的核心难题
4-15(略)关键词:Text2SQL 技术原理、NL2SQL 链路、Schema Linking、SQL 生成、SQL 校验、意图识别、实体抽取、向量检索
目录
- 一、写在前面:为什么要懂技术原理
- [二、Text2SQL 的完整技术链路](#二、Text2SQL 的完整技术链路)
- [三、第 1 步:意图识别](#三、第 1 步:意图识别)
- [四、第 2 步:实体抽取与归一化](#四、第 2 步:实体抽取与归一化)
- [五、第 3 步:Schema Linking(最关键的一步)](#五、第 3 步:Schema Linking(最关键的一步))
- [六、第 4 步:SQL 生成](#六、第 4 步:SQL 生成)
- [七、第 5 步:SQL 校验与修正](#七、第 5 步:SQL 校验与修正)
- [八、第 6 步:执行与返回](#八、第 6 步:执行与返回)
- 九、关键技术挑战与解决方案
- [十、技术方案对比:Prompt Engineering vs Fine-tuning vs Agent](#十、技术方案对比:Prompt Engineering vs Fine-tuning vs Agent)
- 十一、一个完整的真实案例
- [十二、未来趋势:从单步到 Agent](#十二、未来趋势:从单步到 Agent)
- 十三、总结
一、写在前面:为什么要懂技术原理
你可能会问:作为 Text2SQL 的使用者,我为什么要懂技术原理?
答案是:只有理解技术原理,你才能:
- ✅ 准确判断一个 Text2SQL 项目的技术深度
- ✅ 在选型时不被营销话术忽悠
- ✅ 在落地时知道哪些坑可以避开
- ✅ 在出问题时知道排查方向
这篇博客,我会从最底层的技术原理讲起,把 Text2SQL 的完整链路拆解给你看。
二、Text2SQL 的完整技术链路
Text2SQL 的完整链路可以分为 6 大步骤:
┌─────────────────────────────────────────────────────────┐
│ │
│ 用户问题:"上个月华东地区新客的复购率是多少?" │
│ │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 1 步:意图识别 │
│ 判断用户想做什么:查询?对比?归因?预测? │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 2 步:实体抽取与归一化 │
│ 提取时间、维度、指标:上个月、华东、新客、复购率 │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 3 步:Schema Linking │
│ 把业务术语映射到数据库表和字段 │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 4 步:SQL 生成 │
│ 大模型根据 Schema 和问题生成 SQL │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 5 步:SQL 校验与修正 │
│ 语法、Schema、权限、时间四层校验 │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 第 6 步:执行与返回 │
│ 执行 SQL,返回数据 + 可视化 + 洞察 │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 最终结果:复购率 18.5%,样本用户数 12,485 │
│ │
└─────────────────────────────────────────────────────────┘
截图位置 1:6 大步骤的完整流程图,建议用流程图清晰展示
三、第 1 步:意图识别
3.1 什么是意图识别?
意图识别是判断用户想要做什么的第一步。
3.2 常见的用户意图类型
| 意图类型 | 示例问题 | 系统响应 |
|---|---|---|
| 查询 | "上个月 GMV 多少?" | 返回具体数字 |
| 对比 | "Q1 vs Q2 的 GMV 对比" | 返回对比图表 |
| 归因 | "为什么 Q2 GMV 下降了?" | 分析原因 |
| 预测 | "下个月 GMV 预计多少?" | 给出预测 |
| 排名 | "GMV Top 10 的商品" | 返回排名表 |
| 明细 | "列出所有退款订单" | 返回明细数据 |
| 趋势 | "GMV 过去 6 个月趋势" | 返回趋势图 |
3.3 意图识别的实现方式
方式 1:分类器(传统 ML)
python
# 用 BERT 等模型做意图分类
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('bert-base-chinese')
intent = model.predict(user_query) # 输出:query/comparison/...
方式 2:大模型(推荐)
python
prompt = """
判断用户问题的意图类型,从以下类别中选择:
- query(简单查询)
- comparison(对比分析)
- attribution(归因分析)
- prediction(预测)
- ranking(排名)
- detail(明细查询)
- trend(趋势分析)
用户问题:{user_query}
意图类型:
"""
3.4 真实案例
用户问:「为什么 Q2 GMV 下降了?」
- ❌ 错误理解:执行 SQL 查询 Q2 GMV
- ✅ 正确理解:识别为归因意图,调用归因分析 Agent
意图识别看似简单,但它决定了后续所有步骤的方向,错了就全错。
四、第 2 步:实体抽取与归一化
4.1 什么是实体抽取?
从用户问题中提取出结构化的查询条件。
4.2 实体类型
| 实体类型 | 示例 |
|---|---|
| 时间 | "上个月"、"最近 7 天"、"Q1 2026" |
| 地理 | "华东"、"北京"、"一线城市" |
| 人群 | "新用户"、"VIP 用户"、"女性用户" |
| 指标 | "GMV"、"复购率"、"转化率" |
| 维度 | "按渠道"、"按品类"、"按设备" |
| 数值条件 | "大于 1000"、"Top 10" |
| 排序方式 | "升序"、"降序"、"最高" |
4.3 实体抽取的真实挑战
挑战 1:歧义性
用户问:「最近一周的销售情况」
- "最近一周"是自然周(周一到周日)还是最近 7 天?
- "销售情况"是指 GMV、订单数还是用户数?
挑战 2:省略
用户问:「华东的复购率」
- 时间范围省略了(默认?近 30 天?)
- 用户分群省略了(默认全部用户?新用户?)
挑战 3:业务术语
用户问:「一路生花的播放量」
- "一路生花"是歌曲名还是维度值?
- 字段名是
song_name还是track_title?
4.4 实体归一化
提取出的实体要归一化为标准形式:
| 原始表述 | 归一化后 |
|---|---|
| "上个月" | time_range: 'last_month' |
| "最近 7 天" | time_range: 'last_7_days' |
| "华东" | region: 'east_china' |
| "新用户" | user_type: 'new' |
| "复购率" | metric: 'repurchase_rate' |
4.5 实现方式
python
prompt = """
从用户问题中提取关键实体,返回 JSON 格式:
{
"time_range": "...",
"region": "...",
"user_type": "...",
"metrics": ["..."],
"dimensions": ["..."]
}
用户问题:{user_query}
"""
五、第 3 步:Schema Linking(最关键的一步)
5.1 什么是 Schema Linking?
Schema Linking 是把业务术语 映射到数据库表和字段的过程。
5.2 为什么这是最关键的一步?
某研究统计了 Text2SQL 错误的原因:
Schema linking 错误:37%
Join 错误:21%
Group by 错误:23%
其他错误:19%
Schema linking 占了 37% 的错误! 它是 Text2SQL 最大的瓶颈。
5.3 Schema Linking 的挑战
挑战 1:表和字段数量巨大
一家中大型企业的数仓:
- 表数量:500-2000 张
- 字段总数:5000-20000 个
直接把这么多表和字段塞给大模型,会超出 token 限制,也会让模型抓不住重点。
挑战 2:命名不一致
业务说:"订单金额"
数据库里可能的字段:
order_amount(订单表)paid_amount(支付表)revenue(收入表)gmv(GMV 指标表)
AI 不知道哪个对。
挑战 3:跨表关联复杂
电商业务的核心数据模型:
- 用户表(dim_user)
- 订单表(dwd_order)
- 商品表(dim_product)
- 支付表(dwd_payment)
- 物流表(dwd_logistics)
- 售后表(dwd_refund)
"上个月华东新客的复购率"涉及:
- 时间维度(订单表 + 支付表)
- 地理维度(用户表)
- 用户类型(用户表)
- 复购计算(订单表 + 支付表 + 售后表)
5.4 Schema Linking 的解决方案
方案 1:全量 Schema + 大模型
python
# 把所有表结构给大模型
prompt = f"""
数据库 Schema:
{all_tables_schema}
用户问题:{user_query}
请找出需要的表和字段。
"""
问题:表太多,超过 token 限制。
方案 2:向量检索(主流方案)
1. 把每个表、每个字段的描述向量化
2. 把用户问题向量化
3. 用向量相似度检索最相关的 Top-K 个表/字段
4. 把 Top-K 个表/字段给大模型
方案 3:关键词匹配 + 编辑距离
1. 用关键词匹配("GMV" → "gmv" 字段)
2. 用编辑距离("GVM" → "GMV" 字段)
3. 结合业务词典(同义词映射)
方案 4:知识图谱(高级)
1. 构建业务术语 → 数据库字段的知识图谱
2. 通过图查询找到相关字段
3. 包含字段之间的关联关系
5.5 真实案例
用户问:"上个月华东新客的复购率"
Schema Linking 步骤:
1. 向量化用户问题
embedding = vectorize("上个月华东新客的复购率")
2. 检索相关表
Top-5 相关表:
- dwd_order(相似度 0.92)
- dim_user(相似度 0.88)
- dwd_payment(相似度 0.85)
- dwd_refund(相似度 0.78)
- dim_region(相似度 0.75)
3. 检索相关字段
从 Top-5 表中检索相关字段:
- dwd_order.user_id, order_id, created_at, amount
- dim_user.user_id, region, register_time, user_type
- dwd_payment.order_id, paid_at, paid_amount
- dwd_refund.order_id, refund_at, refund_amount
4. 组装 Schema 上下文
把相关表和字段组织成结构化文本
截图位置 2:Schema Linking 的流程示意图,建议展示从问题到表/字段的映射过程
六、第 4 步:SQL 生成
6.1 SQL 生成的本质
有了 Schema 上下文,大模型就可以生成 SQL 了。
6.2 SQL 生成的 Prompt 设计
python
prompt = f"""
你是 SQL 专家。根据以下信息生成 SQL:
数据库类型:MySQL
数据库 Schema(已筛选相关表):
{relevant_schema}
用户问题:{user_query}
提取的实体:
- 时间范围:{time_range}
- 维度:{dimensions}
- 指标:{metrics}
要求:
1. 只生成 SQL,不要解释
2. 使用标准 SQL 语法
3. 考虑性能优化
4. 添加必要的注释
SQL:
"""
6.3 关键技术点
关键技术点 1:Few-shot Learning
提供几个示例让大模型学习:
示例 1:
问题:上个月 GMV
SQL:SELECT SUM(order_amount) FROM dwd_order WHERE dt BETWEEN ... AND ...
示例 2:
问题:各渠道转化率
SQL:SELECT channel, SUM(paid_orders) / SUM(click_uv) AS cvr FROM ...
关键技术点 2:Chain-of-Thought(思维链)
python
prompt = """
让我们一步步思考:
1. 需要哪些表?dwd_order, dim_user
2. 需要哪些字段?user_id, region, order_id, created_at
3. 关联关系?dwd_order.user_id = dim_user.user_id
4. 过滤条件?region='华东' AND user_type='new' AND created_at 上个月
5. 聚合逻辑?每个用户订单数 ≥ 2 算复购,复购用户数 / 总用户数
6. 最终 SQL:SELECT ...
"""
关键技术点 3:Self-Correction(自校正)
让大模型先生成 SQL,再让大模型自己检查:
python
# 第一轮:生成 SQL
sql_1 = llm.generate(prompt_1)
# 第二轮:检查 SQL
prompt_2 = f"""
检查以下 SQL 是否正确:
SQL:{sql_1}
用户问题:{user_query}
如果有错误,请修正。
"""
sql_2 = llm.generate(prompt_2)
6.4 SQL 生成的主要错误
| 错误类型 | 占比 | 示例 |
|---|---|---|
| Schema 错误 | 37% | 表名/字段名错误 |
| Join 错误 | 21% | Join 条件错误,笛卡尔积 |
| Group by 错误 | 23% | Group by 字段遗漏 |
| 语法错误 | 10% | SQL 语法不规范 |
| 时间错误 | 5% | 时间范围理解错误 |
| 其他 | 4% | 业务逻辑错误 |
七、第 5 步:SQL 校验与修正
7.1 为什么需要校验?
即使是最强的大模型,SQL 生成准确率也只能做到 85-90% 。剩下的 10-15% 必须靠校验机制来兜底。
7.2 四层校验机制
┌─────────────────────────────────────┐
│ 校验层 1:语法校验 │
│ - SQL 语法是否合法 │
│ - 数据库是否能解析 │
└─────────────────┬───────────────────┘
↓
┌─────────────────────────────────────┐
│ 校验层 2:Schema 校验 │
│ - 表名、字段名是否在 Schema 中 │
│ - 数据类型是否匹配 │
└─────────────────┬───────────────────┘
↓
┌─────────────────────────────────────┐
│ 校验层 3:权限校验 │
│ - 用户是否有权限访问这些表 │
│ - 是否访问了敏感字段 │
└─────────────────┬───────────────────┘
↓
┌─────────────────────────────────────┐
│ 校验层 4:业务校验 │
│ - 时间范围是否合理 │
│ - 指标口径是否符合定义 │
│ - 是否违反业务规则 │
└─────────────────────────────────────┘
7.3 校验的具体实现
语法校验:
python
# 用 sqlparse 库
import sqlparse
parsed = sqlparse.parse(sql)
if not parsed:
raise SyntaxError("SQL 无法解析")
# 或者在 EXPLAIN 前用数据库 EXPLAIN
cursor.execute(f"EXPLAIN {sql}")
Schema 校验:
python
# 检查表名和字段名是否在允许的 Schema 中
allowed_tables = get_allowed_tables(user)
allowed_columns = get_allowed_columns(user, table)
if not table in allowed_tables:
raise SchemaError(f"无权访问表 {table}")
for column in columns:
if not column in allowed_columns[table]:
raise SchemaError(f"无权访问字段 {column}")
权限校验:
python
# 行级权限
if "user_id = 'self'" not in sql and not is_admin(user):
raise PermissionError("只能查询自己的数据")
# 列级权限
sensitive_columns = ["id_card", "phone", "password"]
for col in sensitive_columns:
if col in columns and not has_permission(user, col):
raise PermissionError(f"无权访问敏感字段 {col}")
业务校验:
python
# 时间范围校验
if "BETWEEN '1900-01-01'" in sql:
raise BusinessError("时间范围异常")
# 指标口径校验
if metric_def and not sql_matches_definition(sql, metric_def):
raise BusinessError("SQL 不符合指标定义")
7.4 修正机制
当校验失败时,系统需要自动修正:
python
def correct_sql(sql, error):
if isinstance(error, SyntaxError):
# 用大模型修正语法错误
return llm.correct_syntax(sql, error.message)
elif isinstance(error, SchemaError):
# 修正表名/字段名
return sql_corrector.correct_schema(sql, error)
elif isinstance(error, PermissionError):
# 移除敏感字段
return sql_corrector.remove_sensitive(sql, sensitive_columns)
elif isinstance(error, BusinessError):
# 用规则或大模型修正业务错误
return sql_corrector.correct_business(sql, error)
八、第 6 步:执行与返回
8.1 执行 SQL
python
# 通过数据库连接执行
result = database.execute(sql)
# 返回结构
{
"columns": ["repurchase_rate", "user_count"],
"rows": [[0.185, 12485]],
"execution_time": 1.2, # 秒
"row_count": 1
}
8.2 数据可视化
根据结果自动选择可视化方式:
| 数据特征 | 推荐可视化 |
|---|---|
| 单个数字 | 大字 + 同比环比 |
| 2 个数字对比 | 对比卡片 |
| 时间序列 | 折线图 |
| 分类对比 | 柱状图 |
| 占比 | 饼图 |
| 地理分布 | 地图 |
| 多维度 | 透视表 |
8.3 数据洞察
更进一步,系统可以给出数据洞察:
📊 数据结果
复购率:18.5%
对比上月:+2.3%
样本用户数:12,485
💡 数据洞察
- 复购率上升主要来自 25-30 岁年龄段
- 该群体贡献了 67% 的复购订单
- 相比之下,35+ 用户复购率反而下降 1.2%
🔍 指标口径
复购率 = 30 天内重复下单用户数 / 总下单用户数
数据更新时间:每日凌晨 3:00
截图位置 3:执行与返回的完整结果展示,建议截一张真实的效果图
九、关键技术挑战与解决方案
9.1 挑战 1:复杂查询的 SQL 生成
问题:多表关联、嵌套子查询、窗口函数等复杂 SQL
解决方案:
- 分步生成:先生成简单查询,再嵌套
- 提供参考示例:让大模型学习历史正确 SQL
- Text-to-SQL 微调:在特定领域微调大模型
9.2 挑战 2:大模型幻觉
问题:大模型会"编造"不存在的表或字段
解决方案:
- 严格限制 Schema:只提供相关表和字段
- 强制 Schema 校验:发现幻觉立即修正
- 多次生成取最优:生成 N 个 SQL,选最好的
9.3 挑战 3:性能问题
问题:大模型推理慢,每次查询需要 2-10 秒
解决方案:
- 查询缓存:相同问题直接返回缓存
- 预计算:常见指标预计算好
- 小模型 + 大模型混合:简单查询用小模型
9.4 挑战 4:成本问题
问题:每次查询都要调大模型,成本高
解决方案:
- 问题分类:简单问题用小模型
- 缓存复用:相同/相似问题用缓存
- 本地部署:用开源模型本地推理
十、技术方案对比:Prompt Engineering vs Fine-tuning vs Agent
10.1 三种技术方案
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Prompt Engineering | 通过 prompt 设计引导大模型 | 实施快、成本低 | 复杂场景效果差 |
| Fine-tuning | 在特定数据上微调大模型 | 效果好、可控性高 | 数据准备难、成本高 |
| Agent | 多个 Agent 协作完成 | 灵活、可扩展 | 复杂度高、调试难 |
10.2 选型建议
| 场景 | 推荐方案 |
|---|---|
| 简单查询、PoC 验证 | Prompt Engineering |
| 特定领域、高准确率要求 | Fine-tuning |
| 复杂任务、多步推理 | Agent |
10.3 主流项目采用的技术方案
| 项目 | 技术方案 |
|---|---|
| Vanna | Fine-tuning + RAG |
| SuperSonic | Agent + 语义层 |
| DB-GPT | Agent + AWEL 编排 |
| WrenAI | Prompt Engineering + GenBI |
| SQLBOT | Prompt Engineering + 语义层 |
十一、一个完整的真实案例
为了让你更直观地理解,我们走一遍真实案例。
11.1 用户问题
"上个月华东地区新客的复购率"
11.2 第 1 步:意图识别
json
{
"intent": "query",
"confidence": 0.95,
"description": "用户想查询复购率"
}
11.3 第 2 步:实体抽取
json
{
"time_range": {
"type": "relative",
"value": "last_month",
"standardize": "2025-06-01 to 2025-06-30"
},
"region": {
"type": "enum",
"value": "east_china",
"standardize": "华东"
},
"user_type": {
"type": "enum",
"value": "new",
"standardize": "新用户"
},
"metrics": [
{
"name": "repurchase_rate",
"definition": "30天内重复下单用户数 / 总下单用户数"
}
],
"dimensions": []
}
11.4 第 3 步:Schema Linking
json
{
"tables": [
{
"name": "dwd_order",
"alias": "o",
"reason": "订单主表,包含订单信息"
},
{
"name": "dim_user",
"alias": "u",
"reason": "用户维度表,包含地域和用户类型"
}
],
"columns": [
{"table": "dwd_order", "column": "user_id", "alias": "o.user_id"},
{"table": "dwd_order", "column": "order_id", "alias": "o.order_id"},
{"table": "dwd_order", "column": "created_at", "alias": "o.created_at"},
{"table": "dim_user", "column": "region", "alias": "u.region"},
{"table": "dim_user", "column": "user_type", "alias": "u.user_type"},
{"table": "dim_user", "column": "register_time", "alias": "u.register_time"}
],
"joins": [
{
"type": "INNER JOIN",
"condition": "o.user_id = u.user_id"
}
]
}
11.5 第 4 步:SQL 生成
sql
SELECT
COUNT(DISTINCT CASE WHEN order_cnt >= 2 THEN user_id END) * 1.0
/ COUNT(DISTINCT user_id) AS repurchase_rate
FROM (
SELECT
o.user_id,
COUNT(o.order_id) AS order_cnt
FROM dwd_order o
INNER JOIN dim_user u ON o.user_id = u.user_id
WHERE o.created_at BETWEEN '2025-06-01' AND '2025-06-30'
AND u.region = 'east_china'
AND u.user_type = 'new'
GROUP BY o.user_id
) t;
11.6 第 5 步:SQL 校验
✅ 语法校验:通过
✅ Schema 校验:表和字段都在允许范围
✅ 权限校验:用户有权限访问这些数据
✅ 业务校验:时间范围合理,指标口径正确
11.7 第 6 步:执行与返回
json
{
"data": {
"repurchase_rate": 0.185,
"user_count": 12485
},
"execution_time": 1.2,
"visualization": "number_with_comparison",
"insights": [
"复购率 18.5%,环比上升 2.3%",
"主要增长来自 25-30 岁年龄段",
"该群体贡献了 67% 的复购订单"
]
}
截图位置 4:完整真实案例的执行结果截图,建议展示从问题到结果的完整流程
十二、未来趋势:从单步到 Agent
12.1 当前主流:单步 Text2SQL
用户问题 → 单一流程 → SQL → 结果
12.2 未来趋势:Agent 化
用户问题 → Planner Agent
↓
拆解为多个子任务
↓
┌─────────┼─────────┐
↓ ↓ ↓
Text2SQL Data Agent Knowledge Agent
↓ ↓ ↓
└─────────┼─────────┘
↓
综合 Agent 汇总
↓
返回用户
Agent 化的优势:
- ✅ 支持复杂任务(多步推理、跨数据源)
- ✅ 自动调用工具(数据解读、报告生成)
- ✅ 可扩展性强
十三、总结
Text2SQL 的完整技术链路分为 6 大步骤:意图识别 → 实体抽取 → Schema Linking → SQL 生成 → SQL 校验 → 执行返回。
每一步都有关键技术挑战,其中:
- Schema Linking 是最大的瓶颈(37% 错误)
- SQL 生成依赖大模型能力(85-90% 准确率)
- SQL 校验是质量兜底(4 层校验)
- 执行返回需要可解释(数据 + 洞察)
理解了这个链路,你再看任何 Text2SQL 项目,都能快速判断它的技术深度和能力边界。
下一篇预告 :[企业语义治理:ChatBI 落地的核心难题](#企业语义治理:ChatBI 落地的核心难题)------我会从 4 大核心机制(Domain Scoping、Metric Registry、Entity Modeling、强制消歧)的角度,把 ChatBI 落地的核心难题讲透。
本系列博客基于 2026 年 6-7 月调研撰写,参考资料包括 Vanna、SuperSonic、DB-GPT 等开源项目 GitHub 仓库、BIRD 基准测试、Spider 基准测试等技术资料。
如果觉得有用,欢迎点赞、收藏、关注三连!你的支持是我更新这个系列的最好动力。