构建智能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% - -

未来展望

短期优化方向

  1. 动态n-best策略:根据问题难度自适应调整候选数量
  2. 多裁判投票:引入多个裁判模型,取多数意见
  3. 执行计划分析:结合EXPLAIN PLAN优化复杂查询
  4. 缓存机制:对相同问题复用历史结果

长期演进路线

  1. Schema链接增强:自动识别问题中的实体并映射到表列
  2. 多表JOIN推理:强化跨表查询能力
  3. 子查询分解:将复杂问题拆解为多个简单查询
  4. 在线学习:实时更新轨迹索引,形成闭环优化

总结

本文介绍了一个生产级的智能SQL生成系统,通过以下三大核心技术实现了高准确率:

  1. n-best候选生成:提高首轮命中率
  2. 反思纠错机制:自动诊断并修正错误
  3. 轨迹检索增强:从历史案例中学习

系统设计遵循模块化、可扩展、易维护原则,已在实际业务场景中验证有效性。我们相信,这种"生成-验证-反思-学习"的闭环架构,不仅适用于Text-to-SQL任务,也可推广到其他需要高精度生成的AI应用场景。


技术栈: Python 3.8+, OpenAI API, SQL Server, FAISS, sentence-transformers

后记:
最近忙着毕业一直没更,上班完善了一套我去年做的代码。发现还有很多问题,一个是对案例数据库的高度依赖,一旦案例数据库里面没有的案例种类,就效果不太好。另一方面我发现想提高text2sql第一步的生成效果,输入的表的Schema很关键。把一些值很少的列直接全部传值给LLM,会很大地提高正确率。
另一个需要完善的地方是失败轨迹目前没有利用上,还有成功轨迹里面,先失败再反思,最后成功这个过程里面,是怎么改变SQL的这个至关重要的部分,还是没有用上。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐