构建智能SQL生成引擎:n-best候选+反思纠错+轨迹检索增强实践
构建智能SQL生成引擎:n-best候选+反思纠错+轨迹检索增强实践
引言
在Text-to-SQL领域,如何让大模型生成准确、可执行的SQL查询一直是个挑战。传统的单轮生成方法往往面临以下问题:
- 语法错误:模型可能生成不符合目标数据库方言的SQL
- 语义偏差:即使语法正确,也可能无法准确回答用户问题
- 缺乏自我修正:一次生成失败后没有纠错机制
本文将介绍我们如何构建一个带反思纠错和轨迹检索增强的智能SQL生成系统,通过多轮迭代和案例学习显著提升SQL生成质量。
系统架构概览
我们的系统采用模块化设计,核心流程包含三个关键阶段:
plaintext
用户问题 → [首次生成] → [执行验证] → [裁判评估] → [反思修正] → 最终SQL
↑ ↓
轨迹检索增强 ← ← ← ← ← ← ← ← ← ← ← ← ← ←
核心技术栈
- LLM调用层:支持OpenAI兼容API(Qwen3等)
- 数据库执行层:SQL Server连接与执行
- 裁判模型:实时评估SQL质量并诊断错误
- 轨迹检索:FAISS向量库 + Few-shot样例增强
- 模块化架构:清晰的职责分离(sql_core/judge_engine/db_executor/trajectory)
关键技术实现
1. n-best候选生成策略
传统方法通常只生成一条SQL,一旦出错就全盘失败。我们采用n-best并行生成策略:
python
# 同时生成N个候选SQL,增加成功率
gen_n = 3 # 每次生成3个候选
gen_parallel = 2 # 并行度为2
优势:
- 提高命中率:多个候选中至少有一个正确的概率更高
- 多样性保证:不同温度采样产生多样化结果
- 容错能力强:单个候选失败不影响整体流程
2. 反思纠错机制(最多3轮迭代)
当SQL执行失败或结果不合理时,系统自动进入反思模式:
第1步:裁判模型诊断
裁判模型接收以下信息:
- 用户原始问题
- 生成的SQL语句
- 数据库执行结果(成功/失败、行数、预览数据)
- 表结构元数据
输出结构化诊断报告:
json
{
"is_correct": false,
"score": 65,
"error_tags": ["COLUMN_NAME_FIX"],
"repair_task": "sql_correct",
"comment": "列名拼写错误",
"advice": "将PART_NM改为PART_NAME_C"
}
第2步:构建反思Prompt
系统根据诊断结果构建反思Prompt,包含:
- 上一轮失败的SQL
- 执行反馈摘要(错误信息或空结果提示)
- 裁判给出的修复建议
- (可选)相似历史案例参考
第3步:重新生成SQL
小模型根据反思Prompt生成修正后的SQL,再次执行验证。
关键设计:
- 最多3轮迭代,避免无限循环
- 每轮保留完整的执行轨迹(用于后续学习)
- 支持断点续跑,中断后可从上次进度继续
3. 轨迹检索增强(RAG for SQL)
这是系统的核心创新点之一。我们从历史成功案例和失败案例中学习,形成双路检索增强:
成功案例检索(首次生成阶段)
python
# 检索相似的成功案例作为few-shot样例
retrieved = trajectory_index.search_success(
table="VIEW_PART_USER_HISTORY",
category="价格查询",
question="查询零件价格",
top_k=3,
threshold=0.6
)
检索策略:
- 编码维度:
table || category || question三元组 - 相似度计算:余弦相似度 × 质量分数
- 质量分数公式:
judge_score * (1 / reflect_rounds)- 分数越高越好
- 轮次越少说明越容易生成
反思案例检索(纠错阶段)
当SQL执行失败时,系统检索历史上犯过类似错误的案例及其修正方案:
python
reflection_cases = trajectory_index.search_reflection(
table="VIEW_PART_USER_HISTORY",
category="价格查询",
question="查询零件价格",
error_tags=["COLUMN_NAME_FIX"], # 关键:按错误类型匹配
top_k=2
)
检索策略:
- 编码维度:
table || category || question || error_tags - 标签加权:共同错误标签数越多,权重越高(
tag_boost = 1.0 + 0.3 * common_tags) - 返回内容:错误SQL + 修正SQL + 裁判评论
FAISS向量库 vs 内存模式
系统支持两种存储模式:
| 特性 | FAISS模式 | 内存模式 |
|---|---|---|
| 适用场景 | 大规模案例库(>1000条) | 小规模测试(<500条) |
| 检索速度 | 毫秒级(近似最近邻) | 线性扫描 |
| 持久化 | 索引文件 + meta.json | JSONL文件 |
| 内存占用 | 低(压缩索引) | 高(全量加载) |
初始化示例:
bash
# FAISS模式
python generate_big_model_sql.py \
--use-trajectory \
--trajectory-db ./trajectory_db
# 内存模式
python generate_big_model_sql.py \
--use-trajectory \
--trajectory-success sql_gen_success.jsonl \
--trajectory-fail sql_gen_failures.jsonl
4. SQL清洗与鲁棒性增强
大模型输出的SQL常包含噪声(Markdown代码块、推理过程、结束标记等)。我们设计了多层清洗管道:
python
def _strip_code_fences(text: str) -> str:
# 1. 去除Markdown代码块
# 2. 去除<think>...</think>推理块
# 3. 识别并剔除推理开场白("好的,我需要...")
# 4. 从第一个SQL关键字开始截取
# 5. 去除EOS标记(</s>, <|eot_id|>等)
# 6. 去除外层双引号
# 7. 清理末尾分号
# 8. 清除推理结束标记
return cleaned_sql
实际效果:
- 输入:
好的,首先我需要分析用户的问题...\n\nSELECT PART_NAME_C FROM ... - 输出:
SELECT PART_NAME_C FROM ...
5. 模块化架构设计
为了提升可维护性,我们将近4000行的单文件重构为模块化架构:
plaintext
modules/
├── sql_core.py # SQL生成核心(Prompt构建、Few-shot检索)
├── judge_engine.py # 裁判模型评估(错误诊断、修复建议)
├── db_executor.py # 数据库执行层(SQL Server连接)
└── trajectory.py # 轨迹检索增强(FAISS索引、案例管理)
functions/
└── generate_big_model_sql.py # 主流程编排
优势:
- 职责清晰:每个模块不超过800行
- 易于测试:可独立单元测试各模块
- 团队协作:多人并行开发不冲突
- 故障隔离:单个模块bug不影响全局
使用指南
快速开始
bash
# 基本用法
python generate_big_model_sql.py \
--questions-xlsx questions_filled_partial.xlsx \
--ref-xlsx sql3.xlsx \
--db-host localhost \
--db-user sa \
--db-password your_password \
--db-name your_database \
--base-url http://your-llm-api \
--api-key your_api_key \
--model qwen3 \
--use-trajectory \
--trajectory-db ./trajectory_db
关键参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
--gen-n |
3 | n-best候选数量 |
--max-rounds |
3 | 最大反思轮次 |
--trajectory-topk |
3 | 首次生成检索案例数 |
--reflection-topk |
2 | 反思阶段检索案例数 |
--trajectory-threshold |
0.6 | 相似度阈值 |
--resume |
False | 启用断点续跑 |
--save-every |
5 | 每处理N条落盘一次 |
输出文件
系统自动生成带时间戳的输出文件:
plaintext
questions_filled_partial_result_20260413_142030.xlsx # 最终结果
sql_gen_success_20260413_142030.jsonl # 成功轨迹
sql_gen_failures_20260413_142030.jsonl # 失败轨迹
Excel输出格式:
| 表名 | 问题种类 | 问题 | sql | judge_is_correct |
|---|---|---|---|---|
| VIEW_PART_USER_HISTORY | 价格查询 | 查询零件价格 | SELECT ... | TRUE |
JSONL轨迹格式:
json
{
"question": "查询零件价格",
"table": "VIEW_PART_USER_HISTORY",
"category": "价格查询",
"attempts": [
{
"round": 1,
"sql": "SELECT PART_NM FROM ...",
"exec_result": {"success": false, "error": "Invalid column name"},
"judge": {
"is_correct": false,
"score": 0,
"error_tags": ["COLUMN_NAME_FIX"],
"advice": "将PART_NM改为PART_NAME_C"
}
},
{
"round": 2,
"sql": "SELECT PART_NAME_C FROM ...",
"exec_result": {"success": true, "row_count": 10},
"judge": {
"is_correct": true,
"score": 95,
"error_tags": [],
"comment": "SQL正确回答了问题"
}
}
]
}
性能评估
测试集指标
我们在内部测试集上进行了评估:
| 指标 | 数值 |
|---|---|
| 总样本数 | 150 |
| 成功率 | 87.3% |
| 平均反思轮次 | 1.42 |
| 平均裁判分数 | 82.5 |
关键发现:
- 首轮生成成功率:~72%
- 经过1轮反思后提升至:~85%
- 经过2轮反思后提升至:~87%
- 超过2轮后收益递减
消融实验
| 配置 | 成功率 | 说明 |
|---|---|---|
| 基线(无反思无检索) | 68.5% | 单轮生成 |
| + n-best(3候选) | 75.2% | 提升6.7% |
| + 反思纠错 | 83.1% | 再提升7.9% |
| + 轨迹检索 | 87.3% | 再提升4.2% |
结论: 三项技术叠加带来18.8%的绝对提升。
最佳实践
1. 轨迹库建设
- 冷启动阶段:先用人工标注的100-200条高质量案例初始化
- 持续积累:每次运行自动保存成功/失败轨迹
- 定期清洗:移除低质量案例(judge_score < 60)
- 增量索引:新案例追加到FAISS索引,无需重建
2. 参数调优建议
- 小数据集(<50条):关闭轨迹检索,使用传统few-shot
- 中等数据集(50-500条):开启内存模式轨迹检索
- 大数据集(>500条):使用FAISS向量库模式
- temperature设置:生成用0.01(确定性),反思用0.1(适度多样性)
3. 错误案例分析
常见错误类型及解决方案:
| 错误标签 | 占比 | 典型原因 | 解决策略 |
|---|---|---|---|
| COLUMN_NAME_FIX | 35% | 列名拼写错误 | 加强表结构描述 |
| SYNTAX_FIX_KEYWORD_TYPO | 20% | 使用了MySQL语法 | Prompt中强调SQL Server方言 |
| FILTER_CONDITION_FIX | 15% | WHERE条件过严 | 放宽筛选或改用LIKE |
| AGGREGATION_FIX | 12% | GROUP BY缺失 | 检查聚合函数使用 |
| TABLE_NAME_FIX | 8% | 表名错误 | 补充schema前缀 |
| 其他 | 10% | - | - |
未来展望
短期优化方向
- 动态n-best策略:根据问题难度自适应调整候选数量
- 多裁判投票:引入多个裁判模型,取多数意见
- 执行计划分析:结合EXPLAIN PLAN优化复杂查询
- 缓存机制:对相同问题复用历史结果
长期演进路线
- Schema链接增强:自动识别问题中的实体并映射到表列
- 多表JOIN推理:强化跨表查询能力
- 子查询分解:将复杂问题拆解为多个简单查询
- 在线学习:实时更新轨迹索引,形成闭环优化
总结
本文介绍了一个生产级的智能SQL生成系统,通过以下三大核心技术实现了高准确率:
- n-best候选生成:提高首轮命中率
- 反思纠错机制:自动诊断并修正错误
- 轨迹检索增强:从历史案例中学习
系统设计遵循模块化、可扩展、易维护原则,已在实际业务场景中验证有效性。我们相信,这种"生成-验证-反思-学习"的闭环架构,不仅适用于Text-to-SQL任务,也可推广到其他需要高精度生成的AI应用场景。
技术栈: Python 3.8+, OpenAI API, SQL Server, FAISS, sentence-transformers
后记:
最近忙着毕业一直没更,上班完善了一套我去年做的代码。发现还有很多问题,一个是对案例数据库的高度依赖,一旦案例数据库里面没有的案例种类,就效果不太好。另一方面我发现想提高text2sql第一步的生成效果,输入的表的Schema很关键。把一些值很少的列直接全部传值给LLM,会很大地提高正确率。
另一个需要完善的地方是失败轨迹目前没有利用上,还有成功轨迹里面,先失败再反思,最后成功这个过程里面,是怎么改变SQL的这个至关重要的部分,还是没有用上。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)