资讯动态

多策略召回+精排:智能问数系统让大模型生成更准SQL

发布时间:2026/10/21 12:05:30 来源:尧图企业网站定制
先讲个我印象挺深的场景。数据团队同事一早跑过来说业务用户问“上季度华东区回款完成率怎么样”系统生成了一条SQL——表名对、时间对、维度对就是回款口径里的分子分母弄反了最后结果差了两位小数。这种问题不是大模型不够聪明而是你在链路里没给它足够多的“正确路径”可走。智能问数这个方向表面看是NL2SQL实际上是一个典型的检索增强生成问题先召回出一批靠谱的候选再做精排最后才交给大模型去写SQL。这篇文章不聊概念把我自己实践下来的多策略召回、精排方案和踩坑记录整理出来希望对正在搭智能问数系统的同行有帮助。1. 先拆清楚一件事智能问数到底难在哪1.1 用户输入和结构化数据之间的鸿沟很多人一开始做智能问数以为把表结构文档丢给大模型就行。真实场景根本不是这样。企业内部数据平台动辄几百张表、上千个字段每个字段背后还有口径说明、枚举值、单位、权限归属。用户问“最近卖得好的产品”这句话里没有指定表名没有指定“卖得好”是按销售额还是毛利没有指定“最近”是近7天还是近30天。要让大模型在这种情况下生成一条正确可执行的SQL光靠模型“临场发挥”是非常不稳定的。用户语言和数据模型之间至少隔了三层鸿沟语义鸿沟、结构鸿沟、口径鸿沟。语义鸿沟是指同一个说法在不同业务语境下含义不同“回款完成率”在财务口径和销售口径里可能是两套公式结构鸿沟是指用户描述的是业务对象底层的表结构可能是星型模型、宽表、甚至多张物理表拼接出来的逻辑视图口径鸿沟更麻烦同样的字段名在不同团队里有不同含义漏掉一个过滤条件结果就完全变样。1.2 为什么“直接甩给大模型”不够我见过不少团队在第一版demo里直接让大模型根据整库schema写SQL效果看着还行因为测试问句都比较规整。一旦放到生产环境问题就集中爆发表名和字段名幻觉、join条件瞎编、条件值写错、口径不统一甚至有时候模型生成了语法正确的SQL但执行结果明显不符合业务预期。选择“召回精排”的框架来兜底核心思路是不要逼大模型从几百张表里大海捞针而是先用轻量级策略圈定一个候选集合再用排序模型或大模型本身去挑最可能的几个候选最后让生成模型只对着这几个候选去做SQL合成。这本质上是信息检索里的经典漏斗结构只不过把“文档召回”换成了“schema召回候选SQL精排”。1.3 召回精排框架的定位这个框架不是要取代大模型而是给大模型配一个“导航员”。召回负责解决“全不全”的问题精排负责解决“准不准”的问题生成负责解决“对不对”的问题。三个阶段目标不同评价指标也不同不能混在一起调优。在实际工程里我习惯把这套链路拆成五个模块查询理解、多策略召回、精排过滤、SQL生成、执行校验与解释。每个模块独立迭代比如召回部分可以单独评测Recall20精排部分单独评测MRR只要每一层都能稳住整体正确率就比较容易往上涨。2. 多策略召回让候选集既全又稳2.1 召回不是“搜相似”是“找对路”召回阶段的首要目标不是精确而是“别漏”。漏掉正确的表、字段或SQL示例后面精排和生成再怎么努力都救不回来。但光追求全也不行如果召回结果太杂比如用户问“订单金额”你召回了一堆跟“合同额”“开票额”相关的字段反而会给精排阶段制造噪音。我实践下来的原则是多路召回每路负责一种“匹配信号”最后做融合去重。这样即使某一路失明其他路还能兜底。2.2 策略一基于元数据字典的规则召回规则召回是最容易被忽视但性价比最高的一路。它的核心是把数据平台已有的元数据利用起来做关键词和别名的匹配。具体来说每个字段可以维护一组“检索词”包含字段名、字段注释、业务别名、常用枚举值等。比如“sales_amount”这个字段检索词集合可以是销售额、销售金额、sales amount、GMV、收入。用户问“上个月收入怎么样”如果只做字面匹配根本匹配不到sales_amount但加上业务别名“收入”就能命中。规则匹配不是简单的字符串包含就行。我一般会做几层处理先做小写化和全半角归一化再做分词和词干化最后用编辑距离或最长公共子序列做近似匹配。比如用户输入“销售额统计”分词后是“销售”“额”“统计”“销售”能命中“销售额”的别名但“统计”这种泛化词不能作为匹配信号否则会召回一堆无关字段。还需要注意枚举值的匹配。用户问“已关闭的订单有多少”如果订单状态字段的枚举值是“closed”“cancelled”而注释里只有中文规则召回时要先把枚举值和中文标签做映射否则根本查不到。2.3 策略二向量化语义召回规则召回解决字面匹配解决不了“意思像但字不像”的问题。比如用户问“哪些品类的利润率最高”字段注释是“品类毛利占比”字面上没有共同词但语义是相关的。这时候需要用向量召回。做法不复杂把每个字段、表、甚至一份示例SQL的注释和检索词离线编码成向量用户过来一条query在线编码成一个向量然后做embedding相似度检索取topK。大模型时代这里的encoder可以用通用embedding模型也可以用领域微调过的双塔模型。向量召回的坑在于如果在全库字段上直接检索很容易被那些语义宽泛的字段干扰。比如“备注”“更新时间”这类字段基本上跟任何query都有一定相似度。我的处理是给不同字段类型加权重前缀把约束类字段和度量类字段分开建立索引用户query里包含“多少”“率”“占比”这些度量词时优先从度量字段索引里找。2.4 策略三历史命中与Few-shot示例召回第三路召回来自历史数据我把它叫做“经验召回”。企业里很多问数是反复出现的比如每个月都有人问“本月签单额”只是时间条件在变。如果能基于历史query和对应SQL做示例检索把类似的历史问题以及它的SQL片段作为候选召回生成阶段会非常稳。具体实现上我会维护一个“query-SQL样本库”每条样本包含规范化后的query、SQL、涉及的实体表、命中的字段和口径标签。召回时用当前query和历史query计算相似度既可以继续用向量也可以叠加BM25这种词法方法。这一路的召回结果不需要完美对应当前query只需要提供可参考的“骨架”比如用户问“上季度华东区回款完成率”历史样本里恰好有一条“上季度回款完成率”那它的SQL骨架中的表、口径逻辑可以直接复用。2.5 召回结果融合与去重多路召回的结果怎么融合直接决定候选集质量。最简单的做法是各路分数直接加权求和但不同路的分数尺度不一样规则召回可能只有0/1命中向量召回是0到1的相似度历史召回的相似度区间可能又是另一套分布。我的经验是先用RRFRanked Reciprocal Fusion做无参融合它不关心分数绝对值只关心排名位置。这样能把各路的“排序信号”合并成一个最终排序。然后做两步清洗第一步跨路去重同一个逻辑实体表、字段的多路召回去掉只保留最高分那条第二步是约束过滤比如字段权限不满足的用户直接剔除该字段字段类型与query意图明显冲突的用户要数值却召回文本字段也剔除。融合后的候选集大小我一般控制在“表级别top10、字段级别top50”左右。太大精排压力大太小容易漏。具体取值可以结合你们schema复杂度调但总体原则是召回宁全勿缺把“硬过滤”留给精排前面那个轻量校验层。3. 精排把正确的SQL/答案顶到最前面3.1 精排要解决什么召回做完候选集里可能还有一堆“看起来相关”的表和字段但真正适合当前问句的可能只有一两项。这就像搜索网页召回给你一万条结果但用户只关心前三条。精排的任务就是把正确的候选用更精细的信号顶到最前面。精排可以作用在两个层级一个是schema候选精排另一个是候选SQL精排。前者发生在“生成SQL之前”后者发生在“生成SQL之后”。这两个层级的能力要求不同实践中需要区分对待。3.2 打分式精排轻量模型做粗分类schema级别的精排我建议先用轻量级cross-encoder打分。输入是query和召回回来的schema条目表名、字段名、注释、枚举值等输出一个相关度分数。cross-encoder相比双塔向量模型有个优势query和schema条目可以在深层交互捕捉到“字段注释里有query的同义词”这种细腻信号。这里有个容易被忽略的细节schema条目要“拼得合理”。如果只把字段名和注释拼在一起模型能用的信息太少我习惯把“表名字段名字段注释常见枚举值字段类型”拼成一个短文本让精排模型看到尽可能完整的上下文。比如[表] sales_order [字段] status [类型] string [注释] 订单状态枚举created(已创建)、closed(已关闭)、cancelled(已取消)这样的输入会让cross-encoder理解得更好。打分结果出来后我不会直接砍掉所有低于阈值的候选而是保留topN进下一阶段阈值只用来剔除明显不相关的。3.3 生成式精排让LLM自己“挑”schema精排之后剩下的候选仍然可能让大模型犯难。我还会加一层“LLM精排”尤其是在候选SQL这个层级。做法是先用生成模型基于召回精排出的schema生成多个候选SQL再把这些SQL和用户的query一起交给大模型让它按“正确程度”排序并挑出最可信的那条。生成式精排的prompt不要写得太虚。我实际用的思路是让大模型做三件事先判断候选SQL是否符合用户意图再检查SQL里的表、字段、过滤条件是否真实存在于给定的schema片段里最后检查是否存在明显口径问题。这三步对应“意图匹配、语义合法性、口径一致性”。如果大模型在第一步就判断某条SQL与用户意图不符直接置底不需要再纠结它的语法多完美。这里有个技巧把候选SQL先做一次脱敏和格式化去掉多余空行、把大小写统一再丢给精排模型。不然精排模型很容易被候选SQL的风格差异带偏误以为写得更漂亮的SQL就是更正确的。3.4 精排与生成的联动技巧精排结果不能只是拿来“展示”给用户更要反馈给生成模型。具体做法是把精排后top1的候选schema作为一个固定前缀写进SQL生成prompt的schema上下文里。比如schema片段不再是一大堆表的罗列而是精排后的三张表和十个字段的紧凑描述并明确标注“这些是从数据目录中检索出的最相关对象请优先基于它们生成SQL”。这个操作看起来简单但对生成准确率的提升非常明显。联动时还要控制候选SQL生成的多样性。生成阶段如果总用贪心解码可能每次都生成相似但有微小错误的SQL我建议用多次采样比如temperature设为0.7采样5次让生成模型尝试不同的SQL写法增加“正确答案出现”的概率然后靠精排把它挑出来。4. 一套可落地的系统链路设计与核心实现4.1 整体流程一次问数的完整路由整套系统的处理流程大致是用户输入问句 → 查询理解模块做意图分类和槽位抽取 → 多策略召回模块拉出候选表、字段、历史示例 → 轻量校验层做字段存在性/权限/类型过滤 → schema精排 → SQL候选生成 → SQL精排 → 执行引擎校验如EXPLAIN→ 结果返回与解释。每一步之间建议用标准化的数据结构传递比如召回阶段统一输出一个“SchemaItem列表”精排阶段在此基础上增加分数和rank字段生成阶段再把topN的SchemaItem序列化成上下文文本。这样每个模块可以独立测试和替换。实际部署时我把召回和精排做了缓存设计。同一类问句比如“XX季度XX地区回款率”在短时间内会被不同用户高频询问缓存历史精排结果能节省大量LLM调用成本。缓存key尽量用语义归一化后的query而不是原始query否则“上季度”和“上个季度”都会被分别缓存。4.2 schema linking的实现要点schema linking是把用户问句中的实体和意图映射到底层schema对象的过程也是召回精排的基础。最粗的做法是把“用户说到的词”和“字段名/注释”做匹配但有三个改进点很关键第一优先做“表-字段”两级约束。用户问“订单金额”不能只召回“订单总额”这一个字段更应该把“订单表”这个主表作为第一候选这样后续生成SQL时join逻辑才有立足点。我的做法是先召回候选表表级分数较高再基于候选表召回相关字段而不是在全库字段里乱找。第二把外键关系纳入召回信号。如果用户问句里涉及两个业务对象比如“客户”和“订单”合理的查询大概率需要join这两张表。召回阶段如果把两张表都召回来同时把外键关系也作为候选上下文一并提供生成阶段就有机会生成正确的join条件。第三对字段注释做“口径摘要”。很多内部表的字段注释写得又长又乱我建议离线把注释拆成“含义摘要口径公式业务模板”比如“回款率回款金额/合同金额*100%统计口径为已验收项目”生成prompt时只放摘要避免上下文被无效信息撑爆。4.3 候选生成与多路机制的实现示例这里给一段精简的伪代码展示生成阶段如何结合召回精排结果。实际生产代码会复杂很多但核心结构是这样的def generate_candidates(user_query, top_schema_items, sample_pool): context build_schema_context(top_schema_items) # 精排后的schema片段 examples retrieve_similar_samples(user_query, sample_pool, k3) prompt build_generation_prompt( user_queryuser_query, schema_contextcontext, fewshot_examplesexamples, safety_notes禁止使用未出现在Schema上下文中的表或字段, ) candidates [] for _ in range(5): # 多样采样 sql llm_generate(prompt, temperature0.7, max_tokens512) candidates.append(sql) reranked_sql llm_rerank(user_query, candidates, top_schema_items) return reranked_sql这里有个非常关键的细节fewshot示例的选择本身也是一种“召回”。样本库里可能有上千条历史SQL随便塞几条进prompt反而会误导模型。我的做法是先从样本库里向量召回top20条历史样本再用轻量交叉编码器挑最相关的3条作为fewshot。它和schema召回共用同一套精排思路只不过对象从schema变成了query-SQL对。4.4 效果评估与指标设计没有评测集的智能问数系统就是盲人摸象。我建议团队从第一周就开始建评测集至少覆盖几类典型问题简单查询单表聚合、多表关联、时间条件变化、口径歧义、多轮指代、极端口语化。每条评测样本标注“标准SQL预期结果摘要”。指标上我实际主要盯四个Recall20召回正确字段/表的比例、MRR精排后正确答案的排名位置、执行准确率生成的SQL能执行且结果与标注SQL一致的比例、人工满意度业务用户打分。这四个指标分别对应召回、精排、生成、体验四个阶段缺一不可。有一个很容易踩的坑只统计“SQL是否生成成功”而不校验“结果是否准确”。有的系统生成的SQL能执行但查出来的结果和业务口径完全不同。所以执行准确率这个指标一定要拆成“语法可执行率”和“结果正确率”两层后者才是智能问数的核心目标。5. 上线后真实会遇到的问题与排查记录5.1 召回为空同义词和缩写词典得长期养上线初期最常出现的现象是用户问“库存周转天数”系统召回为空。原因往往是字段注释里写的是“inventory_turnover_days”别名库没有“库存周转天数”这个词。这类问题只能靠长期维护同义词词典和别名库来解决。我的做法是建立“埋点回收机制”每一条查询不管生没生成成功SQL都把用户原始query记录下来。每天晚上离线跑一遍“未命中字段”的分析把高频率未命中词整理出来人工确认后补进别名库。这样词典会随着用户使用逐渐长大而不是一开始就追求完美覆盖。5.2 精排打分漂移定期回归评测集精排模型上线一段时间后可能发现MRR掉了。最常见的原因是业务新增了字段或表schema分布变了精排模型在没见过的新schema上表现下降。我遇到过一次某平台加了一批“合约类”新表精排模型对“合约”相关的query打分普遍偏高因为新表注释里带了很多“合同”“协议”之类的词和“合约”字面高度重合但不是用户真正想要的。排查方式还是回归评测集。我建议每周跑一次精排回归对比本周和上月MRR。一旦发现下降首先检查新增schema是否改变了候选分布其次检查是不是用户query出现了新的说法比如上个月还没人问“结转比例”这个月开始高频出现。5.3 用户问法太口语化需要查询理解前置处理“给我看下这个月跟上个月比咋样”这种问法直接丢给召回模型效果一般。倒不是模型不聪明而是“这个月跟上个月比”对应的是时间维度上的环比不是说一句“这个月”就能表达的。我的经验是先做一层查询理解把时间词、比较词、指标词做结构化改写。比如“这个月跟上个月比咋样”可以改写成“本月与上月的指标对比需要分别计算本月值和上月值并计算变化率”。改写后的query再进召回精排效果会好很多。这个改写规则初期用规则引擎就能覆盖大部分场景后续再考虑用大模型做泛化改写。5.4 数据权限与列裁剪精排必须结合权限信息智能问数系统如果忽略数据权限基本没法在生产环境落地。用户问“全公司员工薪资”系统拼出一张包含薪资明细的表但该用户实际只能看汇总数据这就属于越权。权限的处理时机要卡在“召回之后、生成之前”。每个SchemaItem上要带一个permission标签精排打分完成后过滤掉当前用户无权访问的项。之后生成prompt里只能出现有权限的schema避免生成出越权SQL。这个点宁可做得保守也不能冒险数据安全问题上没有后悔药。5.5 多轮对话与指代消解别把所有问题都压到召回阶段用户接着说“那华北区呢”如果没有上下文召回和精排根本无从下手。我通常在前置层做多轮会话的“上下文改写”把“那华北区呢”改写为“对比华北区与华东区的销售额”改写之后的query再进入召回精排链路。这里有个实践细节上下文改写时不要把上一轮的完整历史都拼接进去否则query过长会稀释关键信息。更好用的是维护一个“槽位上下文”只保留上一轮识别出的指标、维度、时间、过滤条件然后合并到当前轮。这样既保留了上下文信息又不会让prompt爆炸。5.6 常见问题速查表现象可能原因优先排查项召回结果为空同义词/别名缺失检查别名库和未命中埋点日志召回了无关表向量召回被宽泛字段干扰检查字段类型加权与索引划分精排把正确项排在后面cross-encoder没见过新schema回归评测集更新精排训练数据SQL能执行但结果错口径处理错误或条件缺失人工比对标准SQL与生成SQL的过滤条件SQL语法错误频繁召回schema不完整检查schema上下文是否包含字段类型和枚举值多轮问题答非所问上下文改写失败检查槽位上下文合并逻辑查询超时表数据量过大增加LIMIT限制、执行超时熔断排查思路排序很关键先看是哪一层的问题再决定改哪里。召回层的空结果改精排是没用的生成层的口径错误改向量索引也没用。先把问题定到层再动手。最后再分享一个个人体会智能问数项目做久了最容易产生的误解是“只要模型够强什么问句都能答”。实际上生产环境里的智能问数更多时候是被召回和精排这两个看起来没那么性感的环节决定成败的。召回决定了系统能力的天花板精排决定了用户感知到的准确率大模型只是把前两个环节的信号翻译成SQL。我在实际落地中会把至少一半的迭代时间花在检索质量和评测数据上效果涨得比单纯调prompt快得多。如果你也在做类似系统建议先从一层小小的别名库和一百条评测样本开始跑通之后再逐步加深。做厚地基比换一个更大的模型更能让系统走远。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑