支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


Text2SQL 比赛实战心得:从难点到解决方案

发布日期:2025-04-18 10:32:21 浏览次数: 1586 作者:howard周
推荐语

参加Text2SQL比赛的心得分享,带你深入了解自然语言转SQL查询技术的应用和挑战。

核心内容:
1. Text2SQL技术介绍及其商业价值
2. “2024金融行业·大模型挑战赛”概况及数据集特点
3. Text2SQL技术难点分析及解决方案探讨

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家


一、Text2SQL 是啥?比赛又是咋回事?

先说说 Text2SQL

简单来说,Text2SQL(或者叫 NL2SQL)就是把我们平时说的话(自然语言)自动翻译成数据库能懂的查询语言(SQL)。这样一来,就算你完全不懂 SQL,也能直接问数据库问题,拿到你想要的数据。这技术能:

  • 降低数据查询门槛:让业务同学、运营人员也能轻松自己查数据。
  • 提高工作效率:数据分析师不用再吭哧吭哧写那么多 SQL 了。
  • 让数据更普及:公司里更多人能方便地用上数据。
  • 改善 BI 工具体验:在报表平台里提问查数,更自然。

总之,商业价值挺大的。

我参加的比赛

我参加的是“2024 金融行业・大模型挑战赛”。这个比赛由清华大学基础模型研究中心主办,智谱 AI 提供支持,聚焦大模型在金融领域的应用。

比赛方提供了挺“豪华”的金融数据集:77 个数据表,3000 多个字段,涵盖 A 股、港股、美股、基金、财务报表等等。

题目也分了难度:初级(简单查个数)、中级(带点统计分析)、高级(复杂的金融分析),挺全面地考察我们用大模型处理金融数据的能力。

感兴趣可以去官网看看:(2024金融行业·大模型挑战赛) https://competitions.zhipuai.cn/matchDetail?id=120241202000000003

二、Text2SQL 难在哪?(都是痛点)

搞 Text2SQL 并不容易,我总结了几个主要的难点:

2.1 找对表和字段太难(DB召回难)

这是第一道坎。用户问个问题,模型得先弄明白该去哪些表里、找哪些字段才能回答。

  • 找少了:缺胳膊少腿,没法生成正确的 SQL。
  • 找多了:信息太杂,模型容易“看花眼”,选错字段。
  • 数据库太大:几十上百个表,关系错综复杂,理清它们本身就很难。
  • 术语对不上:用户说的话和数据库里的专业名词 spesso (often) 不是一回事。

比如,用户问“最近三年盈利能力最强的科技公司”,模型得知道去哪找“科技公司”的定义,怎么关联“财务数据”,还得理解啥叫“盈利能力”,涉及哪些字段,怎么计算。

2.2 听懂人话不容易(用户提问理解难)

用户的提问方式千奇百怪,而且往往不够精确:

  • 省略信息:问“去年营收多少?”,没说哪个公司;问“对比一下?”,没说跟谁比,比什么。时间、排序、范围经常省略。
  • 术语模糊:“表现好”可以指股价涨得多、营收增长快,也可能是别的指标。
  • 指代不清:多轮对话里,“这些公司”、“它的竞争对手”到底指谁?
  • 追问太短:“那增长率呢?”单独看完全不知道问啥,必须结合上下文。
  • 条件变化:聊着聊着,用户可能会改主意,模型得知道哪些条件要更新,哪些保留。

用户很少一次就把需求说明白,都是边问边想,这对模型的理解力是巨大考验。

2.3 复杂问题不好解(复杂问题求解难)

特别是金融领域,很多问题不是一条简单 SQL 能搞定的:

  • 计算复杂:比如“计算连续三年净利润增长的公司的平均股价”,得分好几步。
  • 逻辑嵌套深:各种 WHEREGROUP BYHAVING 嵌套,还得用聚合函数。
  • 时间序列分析:同比增长、环比变化、算 N 日平均线等,需要跟历史数据比。
  • 多表关联路径多:从 A 表到 C 表,可以通过 B 表,也可以通过 D 表,选哪条路?
  • 中间结果难处理:第一步查出来的结果,怎么在第二步 SQL 里用?

这类问题需要模型有规划能力,把大问题拆成小步骤去执行。

2.4 领域知识门槛高(领域知识与业务理解难)

光懂技术不行,还得懂业务:

  • 指标计算有讲究:PE、PEG、ROE 这些金融指标有特定公式,不是数据库字段直接相加。
  • 行业术语得翻译:“高成长”、“价值洼地”这些概念怎么对应到数据查询?
  • 数据结构藏着业务逻辑:财务报表里的资产负债表、利润表、现金流量表之间怎么关联?
  • 业务规则影响 SQL:比如交易日怎么判断?涨跌停限制怎么处理?财报啥时候发布?
  • 术语有多义性:同一个词在不同场景下意思可能不同。

缺了领域知识,就算 SQL 语法没错,结果也可能完全没意义。

2.5 系统又慢又不稳(系统效率与鲁棒性难)

要把 Text2SQL 做成好用的系统,工程挑战也不小:

  • 大模型太“贵”:每次调用都要消耗 Token,上下文长了成本就高。
  • 复杂 SQL 跑得慢:数据库大了,一个复杂查询可能半天没响应。
  • 模型老犯错:生成的 SQL 可能语法不对,或者逻辑有问题,需要自我修复。
  • 用户输入“不讲武德”:各种奇怪的问法、边缘情况都得能处理。
  • 模型产生“幻觉”:瞎编不存在的表或字段,或者给出错误答案。

一个靠谱的系统,不仅要准,还得快、稳、省钱,能扛住各种压力。

三、我们是怎么解决的?(一些思路和方案)

面对上面这些难点,我们在比赛中尝试了一些解决方案,效果还不错:

3.1 应对 DB 召回难:两套组合拳

怎么让模型在茫茫多的表和字段里找到对的?我们试了两种方法:

3.1.1 思路一:让 LLM 层层筛选(多级召回)

这就像“缩小包围圈”,让 LLM 分几步走:

  1. 第一步:根据问题,先选出最可能相关的几个数据库(如果有多库)。
  2. 第二步:在选中的库里,再挑出相关的数据表。
  3. 第三步:最后,从这些表里选出需要的字段。
  • 好处:避免信息爆炸(不用一次把所有 schema 给 LLM),省 Token,思路比较清晰。
  • 坏处:对 LLM 理解能力要求高(得懂业务),错误会累积(第一步错了后面全完),得多调几次 LLM,慢。

3.1.2 思路二:LLM 假扮用户提问 + 向量检索(推荐!)

我们发现,直接把表名、字段名和它们的描述(比如 revenue_growth_rate: 营收增长率)做成向量,然后去检索,效果很差。

灵光一闪的创新点:让 LLM 帮忙“编”问题!

  1. 让 LLM 给每个字段写“模拟问题”:想象一下用户可能会怎么问到这个字段?

  • “哪些公司增长最快?”
  • “业绩提升明显的企业有哪些?”
  • “销售额上涨幅度大的公司?”
  • “营收增速如何?”
  • 比如 revenue_growth_rate 这个字段,用户可能问:
  • 我们让 LLM 自动生成很多这类问题。
  • 向量化这些“模拟问题”:注意,是向量化这些问题,而不是原始的字段描述。

  • 用户提问时,用向量检索:用户的真实问题进来后,我们去匹配最相似的“模拟问题”,就能找到背后对应的字段了。

  • 聪明的召回结果整合

    直接按字段分数排序选 top N 个字段?效果不好。因为一个模拟问题可能对应多个字段,而且有些表可能整体相关性很高,但单个字段分数不算最高。

    我们的做法是:

    1. 表级别聚合:把每个字段的得分,加权算到它所属的表上。
    2. 两层筛选:先选出得分最高的 Top N 个表,再从这几个表里,分别选出得分最高的 Top M 个字段。
    3. 平衡与控制:这样既保证了相关表的覆盖度,又控制了总字段数量,不会太多也不会太少。

    对比一下

    • 方案一(多级召回)得调两次 LLM 才确定表,而且可能把表里所有字段都选进来,信息还是太多。
    • 方案二(向量检索)一次召回就能拿到比较精准的候选表和字段列表,效率高,字段数量也更可控。

    两种方案都需要“最后确认”:无论用哪种方法召回,最后都需要再让 LLM 从候选表和字段里,最终确认哪些是真正回答问题所必需的。这一步很关键,因为无关字段太多会严重干扰 LLM 生成 SQL。我们的向量检索方案因为前期召回更准,最后这一步的压力也小很多。

    3.2 应对用户提问理解难:简单粗暴有时更好

    多轮对话里,用户经常问得很简洁,比如前面问了 A 公司的营收,接着问“那增长率呢?”。

    方案一:让 LLM 重写问题(尝试过,效果一般)

    我最初的想法是:

    • 把历史对话和用户的新问题一起喂给 LLM。
    • 让 LLM 生成一个包含了所有上下文信息、能独立理解的“完整问题”。

    比如,把“那增长率呢?”,根据上下文改写成类似“A 公司的营收增长率是多少?”。

    但是,这个方法有坑!

    • LLM 有可能在重写时理解错,歪曲用户的原意。
    • 一旦问题理解错了,后面生成的 SQL 肯定也是错的,就像导航一开始就设错了目的地。

    方案二:直接拼接上下文(推荐!)

    后来发现,一个更简单直接的方法效果反而更好:

    • 把历史的几轮问答,原封不动地拼在用户新问题的前面。
    • 不做任何改写,直接把“聊天记录”丢给 LLM。
    • 让 LLM 自己从完整的对话历史中去理解用户当前问题的意图。

    实践证明:

    1. 避免了“问题重写”引入的误解风险。
    2. 信息是原汁原味的,最全。
    3. 减少了中间处理环节,降低了出错概率。

    这个经验告诉我们:处理多轮对话理解,有时保持信息的完整性比做过多“聪明”的加工更靠谱。

    3.3 应对复杂问题:让 AI “边想边做”(迭代思考框架)

    遇到那种一步到位的 SQL 很难写出来、甚至写不出来的复杂问题,怎么办?我用了一个简单但很有效的迭代思考框架。

    构建“思考 → 执行 → 再思考”的循环

    就像我们人解决复杂问题一样,我让 AI:

    1. 先思考(Think):分析用户问题,判断我现在需要哪些信息?第一步该查什么?
    2. 写个 SQL(Write SQL):不用想着一步登天,先写个 SQL 查一部分需要的数据。
    3. 执行 SQL 看结果(Execute SQL & Observe):看看查回来的数据是啥。
    4. 根据结果再思考(Think Again):好了,现在我知道了这些信息,下一步还需要什么?再写个 SQL 去查?还是信息够了,可以回答用户了?

    这个过程可以循环几次,直到 AI 认为信息足够回答用户问题为止。

    让 AI 把思考过程“说出来”

    关键是让模型在每一步都明确地输出它的思考过程,比如:

    • "根据用户问题和已知信息,我现在需要查询 X 表的 Y 字段,来获取 Z 信息。"
    • "上一步查询结果是 A,但这还不够,我还需要结合 B 表的 C 字段,下一步准备执行..."
    • "信息已经足够,我现在可以整合结果回答用户了。"

    通过这种方式,AI 能更好地拆解复杂任务,一步步逼近答案。

    为什么这方法有效?

    • 化繁为简:把一个大难题拆成一连串小问题。
    • 带纠错功能:每一步都能看到中间结果,如果发现有问题(比如查出来是空的,或者结果不对),可以及时调整策略。
    • 逻辑更清晰:每一步的思考和 SQL 都很明确,方便调试和优化。
    • 模拟人类思维:更符合我们解决问题的直觉。

    对于需要多表连接、复杂计算、多重条件嵌套的问题,这个“边想边做”的方法特别管用。

    3.4 应对领域知识难:给模型“喂”知识

    模型不懂金融术语和业务规则怎么办?硬塞肯定不行,得讲究方法:

    3.4.1 通用术语解释放 Prompt 里(少而精)

    一些最常用、最基础的业务术语,可以直接加到 System Prompt 里。

    • 原则:只加通用且必要的。比如解释一下什么是“交易日”。
    • 注意:别加太多! 无关的解释不仅浪费 Token,还可能分散模型注意力,让它忽略了核心指令。实测发现,Prompt 里塞太多东西,效果反而会变差。

    3.4.2 给数据库字段加“注释”(按需加载)

    这是个好方法:在定义数据库 Schema 时,就给重要的字段加上详细的业务含义解释。

    • 比如 PE_TTM 字段,注释可以写:“滚动市盈率,衡量股价相对于每股收益的倍数,常用于估值”。
    • 当这个字段被前面的召回步骤选中后,我们自动把它的注释补充到 Prompt 里。
    • 好处:针对性强,不浪费 Token,只在需要时提供相关知识。

    3.4.3 用 SQL 样例做 Few-shot(最有效!)

    这招效果最好:提前准备一些典型问题的 SQL 样例。当用户提出类似问题时,动态地把最相似的几个 SQL 样例(问题+SQL)塞到 Prompt 里,作为示例(Few-shot)给模型看。

    为什么有效?

    • 光用文字解释“你应该怎么思考怎么关联表”,模型很难学会。
    • 但给它看一个具体的、相似问题的正确 SQL,模型就很容易“照猫画虎”,举一反三。

    通过这几种方法组合,就能比较好地解决领域知识不足的问题了。

    3.5 应对效率和鲁棒性难:省钱还得能扛事

    怎么让系统跑得快点、稳点、省点钱?

    3.5.1 省 Token 的召回策略

    前面提到的 DB 召回方案其实已经在省 Token 了:

    • 多级召回:避免一次性加载所有 Schema。
    • 向量检索:更精准地筛选少量相关表和字段给 LLM。

    这两个都是为了在保证效果的前提下,尽量减少喂给大模型的信息量,从而降低 Token 消耗。

    3.5.2 提高稳定性的“安全带”

    在上面提到的“迭代思考”框架里,我们加了几道保险:

    • 设置最大迭代次数:防止模型陷入死循环(比如因为模型能力不足、数据有问题、或者召回不全导致一直没法得到最终结果),迭代超过一定次数就强制终止。这个很重要,因为总有意外情况。
    • SQL 执行结果必须反馈:无论 SQL 执行成功、失败还是超时,都要把完整的执行信息(包括错误信息)反馈给 LLM。这样模型就有机会知道自己上一步 SQL 写的有问题,从而进行自我纠错。这对鲁棒性至关重要。
    • 检测“复读机”行为并熔断:我们发现 LLM 有时会“卡壳”,连续几次生成完全一样的、错误的 SQL。我们加了个检测机制,如果发现模型连续重复犯同一个错误,就直接熔断,不再继续尝试,避免无效消耗。

    这些机制就像安全带,保证了系统在遇到各种异常时,不会彻底崩溃。

    4. 代码也开源了!

    为了方便大家交流学习,我把这次比赛用到的核心代码框架整理开源了。

    代码仓库

    GitHub 链接:(FinGLM2-semi-final) 

    https://github.com/Jinglever/FinGLM2-semi-final

    代码结构

系统架构


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询