尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

泰迪杯B题财报智能问数方案:Text-to-SQL与大模型知识库实战解析

泰迪杯B题财报智能问数方案:Text-to-SQL与大模型知识库实战解析 简介自然语言转SQLText-to-SQL是当前企业数据分析和商业智能领域的热门技术方向其核心价值在于让用户通过自然语言直接查询数据降低数据使用门槛。实现这一能力通常需要依赖大模型对语义的理解并结合知识工程对业务术语、字段口径进行结构化约束以确保生成SQL的准确性与稳定性。在实际落地中文本到SQL技术常被应用于财报分析、经营驾驶舱等场景帮助分析师快速获取指标数据。针对2026年泰迪杯B题“上市公司财报智能问数助手”业内通用的解决方案是构建“数据预处理知识增强模式链接SQL生成执行校验”的完整链路以此应对复杂财务口径与多表关联挑战。本文从技术实践出发详细拆解了该类系统的设计思路与调优经验为相关竞赛和工程应用提供参考。 刚拿到2026年泰迪杯B题“上市公司财报‘智能问数’助手”的赛题说明时我的第一反应是这题终于从“让参赛者做数据分析”进化到“让参赛者做一个能回答数据分析问题的AI系统”了。如果你准备过最近的泰迪杯或其他数据挖掘竞赛应该能感觉到出题方在刻意往大模型应用方向靠——不要求你训练一个多大的模型而是考察你如何用现有的大模型能力配合数据处理、知识工程和工程化手段把一套完整的“问数”链路搭起来。这篇题解不会只丢给你一堆代码而是把我的完整解题思路、代码实现、踩过的坑、调参心得全部拆开讲。无论你是第一次参加泰迪杯、想冲国奖还是想借这个题练手Text-to-SQL技术栈这篇文章应该都能给你一个可以直接落地的方案。整套资源我也打包好了文末会说明获取方式。1. 赛题拆解这届B题到底在考什么1.1 从“数据分析”到“智能问数”竞赛命题的底层变化往年的泰迪杯B题风格很固定给你一份或者几份业务数据让你做清洗、特征工程、模型训练最后生成一份分析报告再回答几个指定问题。说白了考的是你“分析数据”的能力。但2026年这道题明显变了——“智能问数”这四个字把整个考察重心从“分析数据”挪到了“理解数据驾驭大模型”。什么叫“智能问数”就是用户用自然语言提问比如“2025年净利润排名前10的上市公司是哪些”“哪家公司的毛利率连续三年上升”系统自动解析问题、写出SQL、查数据库、把结果用自然语言和图表呈现出来。这本质是一个Text-to-SQL自然语言转SQL任务但加了财报场景的限定。这个变化背后其实是工业界数据分析工具的主流演进方向企业对BI报表的需求正在从“人工看板”转向“自然语言交互”。所以这道题不是凭空出的它对应的是当前数据中台和商业智能产品里最热的那股潮流。出题方希望参赛者通过这个题目提前接触真实产品中会遇到的工程问题——不是训练一个模型就完事而是要把“能用”变成“好用”。1.2 赛题的核心难点SQL生成、口径理解、交互闭环我把B题拆成了三个层面的难点这也是我后续方案设计的出发点。第一层是“自然语言到SQL的映射”。用户的问题五花八门但数据库里的字段是有限的。比如“哪些公司利润最高”这句话哪家模型都可能把“利润”直接映射成“净利润”但财务里还有“营业利润”“利润总额”“归母净利润”到底取哪个SQL里怎么写聚合要不要排序这个映射错一步答案就废了。第二层是“业务口径的理解”。财报领域最怕的是口径不统一。同一个“增长率”有同比、环比、复合增长同一个“净资产收益率”有加权和摊薄两种算法。如果系统不理解这些业务口径生成的SQL就是“看似正确实则跑偏”。这一点是我在整个方案里花最多精力去解决的也是后面我要重点展开的知识库构建部分。第三层是“交互闭环”。题目要求的不只是生成SQL还要把结果返回给用户大概率会涉及结果解释和可视化输出。这意味着你的系统不能只是一个“SQL生成器”还得有“把表格结果转成自然语言”的能力最好还能画图。另外还有一个隐形的难点评测。竞赛的评测通常分两块一块是系统生成的SQL是否正确另一块是最终呈现的结果是否准确。但“准确性”的判定标准是什么官方大概率有一个人工标注的问答集。所以你的系统必须在“标准性问题”上足够稳这比堆花活重要得多。1.3 整体方案选型为什么我放弃了“纯提示词工程”拿到题目后我最初的方案特别朴素把整库的建表语句和字段说明丢给大模型让它直接写SQL。这个流程跑通只需要半天但一测就发现问题schema太长几十张表的字段全部塞进去Prompt超过10K token模型注意力严重分散经常把两个表的字段搞混。字段名是拼音缩写“jlr”到底是“净利润”还是“经利润”模型全凭猜。同一个问题换个问法结果就变稳定性差。模型偶尔会“编字段”——SQL里出现一个数据库里根本不存在的列名。所以我的结论是纯提示词工程在这个题上行不通至少撑不过中高阶测试题。最终我把方案升级成了“数据预处理 知识增强 模式链接 SQL生成 执行校验”五段式架构。核心思路是不让大模型直接面对整个数据库而是先通过规则和检索把“候选字段”缩小到一个很小的范围再让大模型在这个受限空间里生成SQL最后用执行器校验兜底。这套方案的本质是“减少大模型的自由发挥空间”。我把能确定的事情全部前置——字段映射、同义词扩展、口径公式、常用聚合模板——只把真正需要“理解自然语言意图”的部分留给大模型。事实证明这个取舍方向是对的。后面第3章我会给出完整实现第4章的对比数据也能说明这个方案带来的实际提升。2. 数据准备与知识库构建答对题的前提是听懂专业词汇2.1 财报数据的结构分析与字段语义梳理B题数据集的构成大概率是典型的上市公司财报数据资产负债表、利润表、现金流量表可能还会带一些基础的公司信息表。先别急着写代码拿到数据以后我用了一个下午把每一张表的字段逐个过了一遍做了三件事字段去重、单位确认、语义标注。字段去重是因为很多报表设计得很不规范。同一个“营业总收入”在不同的表里可能叫“营业总收入”“营业收入”“主营营业收入”对应到数据库里可能是不同的字段。如果不提前把这种关系理顺后面无论用什么方案都会撞车。单位确认是财务数据里最容易忽略但最致命的坑。有的表字段单位是“元”有的是“万元”有的甚至是“亿元”。如果SQL里直接做跨表运算结果会差几个数量级。我最终的做法是写一个字段元信息表把每个字段的单位、所属表、字段类型、业务含义全部记录下来作为后续知识库的基座。语义标注是最费时间的但也是整个方案里性价比最高的一步。我给每个字段都补了一份“别名清单”比如字段“归母净利润”别名可以包括“净利润”“归属于母公司所有者的净利润”“归母净利”“公司赚了多少钱”字段“销售毛利率”可能被问成“毛利率”“赚钱能力”“产品盈利水平”字段“营业总收入”可能被问成“收入”“营收”“销售额”“营业收入”别小看这个工作它相当于给大模型配了一张“财务术语翻译表”。你越早做后面Schema Linking的准确率就越高。2.2 构建财报领域词典与同义词映射表我构建的知识库核心是一张四层的同义词映射结构用户问法层、业务概念层、标准字段层、SQL实现层。举个例子。用户问“哪个公司去年赚得最多”系统要做的事情是把“去年”解析成时间条件比如2024财年。把“赚得最多”映射到业务概念“净利润”。把“净利润”在知识库中匹配到标准字段“归母净利润”或“净利润”根据上下文选择一个最合理的。生成SQL时用标准字段名去写而不是用用户的原话。这张映射表我用JSON格式维护具体长这样{ 净利润: { 标准字段: net_profit_parent, 所属表: income_statement, 同义词: [归母净利润, 净利润, 归属于母公司股东的净利润, 净利, 赚了多少钱], 单位: 元, 计算方式: 直接查表, 备注: 当用户未明确说合并或母公司时默认取归母净利润 } }这套词典的覆盖面直接决定了系统对用户问题的容忍度。我最后整理的版本大约覆盖了300多个财报常用业务概念足够应对常规测试题了。2.3 数字口径与计算指标的规则沉淀除了字段映射还有一类问题没法直接查表得“算”。比如“毛利率”“净资产收益率”“营收同比增长率”这些指标数据库里不一定有现成的字段。如果你指望大模型自己理解“毛利率(营业收入-营业成本)/营业收入”那结果多半要翻车。我的做法是把这类计算指标单独做成一个“指标库”每条指标直接写成可执行的SQL片段作为生成SQL时的“配方”供模型查询调用。比如{ 毛利率: { 公式: (operating_revenue - operating_cost) / operating_revenue, 所属表: income_statement, 口径说明: 毛利率按单年计算不取平均值, 适用场景: 用户问毛利率、盈利水平、产品利润空间 } }这样做有一个特别大的好处大模型不需要“算”毛利率它只需要“选”毛利率。它要做的是判断用户问的是不是毛利率然后把对应的SQL片段拼到最终查询里。把推理问题降维成选择问题整条链路的稳定性会高很多。再补充一点口径的定义要尽量细化但也不能过度。比如“同比增长率”我明确设定了计算方式为“本期数-去年同期数/去年同期数”并且在Prompt里给了模型一个强制约束——如果用户问“同比增长”必须使用指标库中“同比增长率”的模板不允许自行展开计算。这个约束看着很霸道但在竞赛场景里非常有效因为它把模型幻觉的空间压到了最小。3. 核心实现SQL生成与结果校验的完整链路3.1 基于模式链接Schema Linking的候选字段筛选所谓模式链接就是在大模型生成SQL之前先把用户问题里涉及的表和字段找出来。我在这套系统里用了两种方式双管齐下规则过滤和向量召回。规则过滤很好理解。把用户问题做分词之后去同义词映射表里匹配。如果问句里出现了“净利润”的同义词就直接把“net_profit_parent”加入候选字段集合。这种硬匹配速度最快准确率也最高覆盖不了的是那些“用了说法但从没见过”的问法这时候就需要向量召回兜底。向量召回我用的是一个开源的文本Embedding模型把所有字段的“业务含义示例问法”提前向量化用户问题来了以后算余弦相似度取Top N作为候选字段。技术实现不复杂import numpy as np from sentence_transformers import SentenceTransformer # 提前加载模型和字段向量 model SentenceTransformer(moka-ai/m3e-small) field_vecs np.load(field_vectors.npy) field_info load_field_info() # 字段元信息 def schema_linking(question, top_k5): q_vec model.encode(question, normalize_embeddingsTrue) scores field_vecs q_vec.T # 余弦相似度 top_idx np.argsort(scores)[-top_k:][::-1] candidates [field_info[i] for i in top_idx] return candidates为什么要把候选字段的筛选独立出来因为这是整条链路里性价比最高的一步。做过Text-to-SQL的人都知道大模型面对十几个字段时容易混淆但面对三五个字段时表现会好很多。把候选集缩小到5个以内等于直接把错误的概率砍掉了大半。3.2 让大模型稳定输出SQL模板约束与结构化Prompt大模型生成SQL最怕的是什么是不稳定。今天能生成的SQL明天换个说法就不行了。我的应对方法是不让它自由发挥设计SQL结构而是给它一套强约束的Prompt模板再配几张“示例表”。模板分为三部分角色定义、候选字段说明、输出格式约束。角色定义就是告诉模型“你是一名财务数据分析师你需要根据用户的问题和提供的字段信息生成SQL”。这部分是为了限定模型的回答风格别答非所问。候选字段说明是经由Schema Linking筛出来的几个字段我会把这个字段的业务含义、所属表、单位、计算方式都写清楚。字段少了模型就能把注意力集中在真正需要的地方。输出格式约束是最关键的。我要求模型以JSON格式返回包含SQL和中文解释两个部分并且明确禁止模型输出其他内容。这样我后续解析的时候不用清洗多余文本直接JSON.loads就行。prompt_template 你是财务数据分析助手。请根据用户问题从候选字段中生成SQL查询。 候选字段信息 {field_info} 用户问题{question} 要求 1. 只返回JSON不要任何多余文字。 2. SQL中的表名、列名必须来自候选字段禁止编造。 3. 如果问题涉及指标计算优先使用指标库中的SQL片段。 4. 返回格式{{sql: 你的SQL, explanation: 对这个查询的简要解释}} 这个Prompt看起来简单但我试过很多版本最终稳定的关键是“候选字段信息”这一块。字段说明越详细模型选错的可能性越小。特别是遇到了那种“两个字段长得像但含义不同”的时候你必须在字段说明里写清楚差异。比如“营业总收入”和“营业收入”你不解释区别模型就可能选错。3.3 执行校验与“三遍自查”机制我见过太多参赛队伍在SQL生成上纠结半天却忽略了一个更实际的环节SQL生成完了怎么保证它能跑通、结果是对的我的做法是加了一个执行校验层分三步。第一步是语法校验用sqlparse解析SQL能过解析器不一定是好SQL但过不了解析器的一定是坏SQL。第二步是执行校验用一个只读数据库连接去执行SQL重点看两个问题一个是字段名是否真实存在另一个是查询是否超时。第三步是结果合理性校验这步最容易被忽略——把查询结果拿回来以后检查空值比例、数据范围、条数是否合理。比如用户问“净利润排名前10的公司”结果你返回了100行那大概率是有问题可能是SQL里少了LIMIT。如果校验不通过我会把报错信息原样反馈给大模型让它自己“改错”。这招在竞赛里特别管用def generate_sql_with_retry(question, candidates, max_retry3): for i in range(max_retry): resp call_llm(prompt_template, question, candidates) sql extract_sql(resp) err validate_and_execute(sql) if err is None: return sql, resp.explanation # 把报错信息塞回Prompt让模型修正 candidates.append(上一次SQL执行报错 err) return None, None把报错信息塞回Prompt这一招本质上是给模型一次“自我纠错”的机会。实测下来第二次生成的成功率一般能提到90%以上。另外要提醒一句执行SQL的时候一定要用只读账号给系统加一层防呆保护。比赛环境中如果因为SQL写错导致数据被改那是灾难级的翻车。4. 从基线到成品的调优记录与常见问题排查4.1 第一版Demo的翻车现场直接问、直接答的坑我第一次搭出完整Demo的时候既没有知识库也没有字段筛选就是把所有表结构拍给大模型让它自己折腾。测试的结果惨不忍睹我记录了三个典型翻车案例第一个用户问“哪家公司最赚钱”模型生成了一条SQLSELECT * FROM company_info ORDER BY profit DESC LIMIT 1。结果数据库里根本没有profit这个字段直接报错。这就是典型的“模型编造字段”没有Schema约束的时候它反而会一本正经地胡来。第二个用户问“毛利率超过50%的公司”模型把“毛利率”当成了一个数据库字段去查询但实际数据库里根本没有这个字段需要实时计算。这就是“业务指标识别失败”的典型案例。第三个用户问“2024年营收同比增长最快的公司是哪家”模型生成的SQL里直接写了“operating_revenue”但忘了把“同比”这个计算逻辑加进去。结果返回的是“营收绝对值最大”不是“增长最快”。这就是典型的“看到了关键词但没理解语义”。这三个翻车案例让我下决心改架构也就是前面说的把所有能前置的知识全部前置只把最核心的“意图判断”留给大模型。4.2 性能对比字段筛选与自纠错机制带来的实际提升为了验证方案改进的效果我整理了一个50条问题的测试集覆盖了单表查询、多表关联、指标计算、排序聚合等常见类型。问题全部是我人工构造的模拟可能出现在竞赛评测中的典型问法。测试结果如下方案SQL生成准确率最终答案准确率平均耗时秒纯Prompt生成SQL46%38%1.2加入知识库和Schema Linking78%72%0.8再加执行校验与自纠错86%83%1.5完整方案知识库字段筛选自纠错结果解释86%88%1.8从数据里能明显看出来Schema Linking的加入是提升最大的一步准确率直接涨了30多个百分点。自纠错机制虽然耗时增加了但胜在能“救回来”一部分失败案例对最终答案准确率也有明显贡献。有一点我要特别说明最终答案准确率高于SQL生成准确率并不是统计口径错了而是因为有一部分问题即使SQL生成得不够完美经过结果解释层加工后用户看到的总还是“答对了”。这也说明最终交付的质量不只看SQL还看你后期的“包装”能力。4.3 踩坑清单从字段撞名到日期断言在整个开发过程中我踩了不少坑挑几个有代表意义的写在下面希望能帮后来的人省点时间。第一个坑是字段撞名。多家公司、多张表里都有类似“备注”或者“净利润”的字段如果Schema Linking只按字段名匹配很容易选错表。解决办法是给每条候选字段加上“所属表”的前缀标记并在Prompt里明确要求模型必须使用“表名.字段名”的完整格式。第二个坑是日期断言。用户问“去年”“最近一年”“2024年”的时候如果不做统一格式化模型偶尔会把日期字符串当成文本处理。我的办法是在知识库里专门维护一份“时间词对应表”把“去年”映射成具体的年份在进入Prompt之前就完成替换。这个步骤放在问题预处理阶段不依赖大模型。第三个坑是单位换算。前面提过不同字段单位不同但用户问题里不会告诉你“净利润单位是元”。我建议在做结果解释的时候统一把数字转成“亿元”或“万”展示而不是把数据库里的原始值直接抛给用户。这一步需要在SQL查询后增加一个单位换算层千万别直接拿原始数字做回答。我见过有队伍查询的净利润数值是几百万结果解释成“几百万亿元”直接就崩了。第四个坑是闭源模型的随机性。同一个PromptGPT和DeepSeek跑出来的SQL可能不一样。所以我的建议是在提交之前固定一个模型版本不要频繁切换。比赛的评测环境不一定能访问外网你在本地如果用了闭源模型API最好提前想好降级方案——我最终在本地准备了一个7B级别的小模型作为备份虽然效果差一些但至少能兜底。4.4 问题分类与针对性策略比一个万能Prompt更好用最后分享一个我做这题最大的心得不要试图用一个万能Prompt解决所有问题。把问题分好类再为每个类别定制策略整体效果好得多。我把用户问题分成了五类单表查询类典型问法“某公司2024年的营业收入是多少”。这类最简单直接生成SQL不需要特殊的指标计算。多表关联类典型问法“各公司2024年净利润和资产负债率对比”。需要从多张表取数关键是字段关联的准确性。指标计算类典型问法“毛利率超过30%的公司”。必须走指标库不能靠模型发散。排序TopN类典型问法“净利润前10的公司”。要特别注意LIMIT和多条件排序的组合。比较分析类典型问法“A公司和B公司哪个赚钱多”。需要拆解成两个查询再对比这步最考验系统的分析能力。处理的方式是在主流程里先对用户问题做一个简单的分类然后为每一类配置不同的Prompt模板和候选字段抽取策略。这种方法比一个“包治百病”的Prompt稳定得多因为大模型在特定模式下犯错的概率远低于自由发挥的模式。另外提醒一句最终提交的完整项目最好包含一个小型的Web演示界面——用Gradio或者Streamlit都行。泰迪杯评测通常很看重系统演示效果评委如果能直接在页面里输入问题、看到返回的表格和图表印象分会高很多。我自己的项目里就加了一个Streamlit页面做成了类似对话助手的交互形式。我个人在实际操作中的一个体会是做这类竞赛题最重要的不是把大模型调得多聪明而是把能控制的部分全部控制住。字段映射、同义词扩展、口径定义、SQL校验这些虽然看着“不酷”但恰恰是决定你系统能不能用的关键。真要等模型自己在测试的时候犯了错再去补救代价就太大了。如果你最近也在准备泰迪杯欢迎把我这套方案当成一个起点。先用最小链路跑通再逐步加模块优化大概率比一开始就搭一个庞大架构要扎实得多。本文还有配套的精品资源点击获取
返回列表