资讯动态

通义大模型驱动 ChatBI:对话式数据分析与 SQL 生成实践

发布时间:2026/9/18 12:48:27 来源:尧图企业网站定制
简介这是一份围绕通义大模型与对话式数据分析ChatBI的32页PPT讲义面向数据分析师、BI工程师、产品经理及希望把大语言模型落地到取数分析场景的技术人员。内容从传统数仓与BI取数流程的痛点切入梳理对话型数据分析的困难与挑战并给出析言GBI的整体解决方案包括产品架构、工作链路、多代理协作模式以及数据问答、Selector召回、SQL Generator Team、校验与改写等关键环节还涉及XiyanSQL在自然语言转SQL上的关键技术攻关与实验样例可帮助读者理解NL2SQL与智能体编排如何支撑自然语言问数、指标查询与报表生成。压缩包共1个文件为pptx演示文稿约5.16MB共32页页面结构便于按背景、方案、架构、关键技术、实验与最佳实践逐节阅读。目前已有123人学习下载适合需要快速把握ChatBI系统设计与落地方案的读者参考。1. 从 SQL 到一句话对话型数据分析为什么需要通义大模型业务群里最常见的一句话是「上周华东的退货率怎么突然涨了」。放在传统 BI 里它要经过需求登记、排期、建模、出报表快则一天慢则一周等图出来讨论热度已经过去了。对话型数据分析想压缩的正是这段时延业务方用人话提问系统自己完成指标理解、口径匹配、SQL 生成、执行取数、图表渲染。通义大模型在这里不是聊天外壳而是整条链路的语义中枢——把口语表达对齐到仓库里真实存在的字段和指标产出可执行 SQL再把结果翻译回人话。一份 32 页的分享材料要讲清的基本就是这条链路的每一环。后面按「链路拆解、语义层建设、落地实现、排错调优」推进面向想把取数入口搬进对话的分析师、要评估通义大模型能否接入现有数仓的 BI 工程师以及负责准确率兜底的数据平台研发每一步都给能跑的代码和参数。2. 通义大模型驱动 ChatBI 的链路拆解与最小调用2.1 一次问数要经过的四段链路把「一句话出图」当成一个黑盒调试时会非常痛苦因为出错之后你分不清是模型听错了、指标找错了还是 SQL 本身写错了。稳妥的做法是拆成四段每段都能单独打日志、单独回放。第一段是意图识别判断这句话是要取数、要解释已有结果、要下钻还是纯粹闲聊或者缺了时间范围必须先反问。第二段是指标召回从语义层里捞出候选指标和维度这一步决定了模型「知不知道你说的退货率是哪个口径」。第三段是 SQL 生成与校验模型基于 schema、口径和少量示例产出查询再由护栏程序解析、改写、拦截。第四段是执行与呈现跑数、判断结果形状、选图表类型、生成一句自然语言结论。四段分开的最大好处是排错有定位点。准确率掉了先看召回 Top5 里有没有正确指标召回没问题就去看 SQL 的 WHERE 条件是否被模型自己编了时间SQL 没问题再看图表是不是把两个量纲不同的指标画在了同一根轴上。2.2 通义大模型的最小可运行调用先把最基础的一次调用跑通再往上叠语义层。用 DashScope 的 Python SDK二十行以内就能验证账号、网络和模型是否正常# pip install dashscope import os import dashscope from dashscope import Generation dashscope.api_key os.environ[DASHSCOPE_API_KEY] # 从环境变量读不要硬编码进仓库 resp Generation.call( modelqwen-plus, # 通用对话与改写够用复杂 SQL 建议换更强的型号 messages[ {role: system, content: 你是数据助手只输出 SQL不要任何解释文字。}, {role: user, content: 查一下上周华东区的退货率按天看趋势}, ], result_formatmessage, # 返回结构化 message取字段更稳 temperature0.1, # 取数场景要确定性温度压低到 0.1 左右 seed1234, # 固定种子便于回归时逐字比对差异 ) print(resp.output.choices[0].message.content)参数里真正影响 ChatBI 效果的只有三个model决定能力上限和单次成本temperature决定同样的问法会不会每次给出不同 SQL取数场景必须压低seed让回归测试可复现改一版提示词就能 diff 出 SQL 变化。result_formatmessage是为了少写一层解析直接取message.content即可。前端是流式对话的话把stream打开并加上增量输出参数避免每个分片都重复推送全文responses Generation.call( modelqwen-plus, messages[{role: user, content: 上个月各品类的销售额占比}], result_formatmessage, streamTrue, incremental_outputTrue, # 只推增量片段前端直接做打字机效果 ) for chunk in responses: print(chunk.output.choices[0].message.content, end)提示流式输出适合自然语言结论部分SQL 生成建议关闭流式并做完整性校验避免半截语句被误执行。2.3 三种接入方式的取舍接入方式适用场景注意点原生 SDK 调用快速验证、内部工具依赖具体 SDK 版本升级前先看变更说明OpenAI 兼容模式已有 LangChain 等框架代码只需改base_url和api_key但部分高级参数不生效私有化部署数据不出域、强合规要求显存成本高量化后复杂 SQL 能力会下降我一般的判断是数据可以出域就先用托管服务把链路跑通把准确率的天花板摸清楚再评估是否值得为私有化牺牲一部分生成质量。反过来先做私有化很容易把「模型能力不够」误判成「方案不成立」。2.4 按任务分工选模型不同环节对模型的要求完全不同。意图识别是短文本分类快而便宜最重要SQL 生成要求结构严谨、字段不幻觉结果解释要求语言自然。把这三个环节用同一个模型跑成本高且某一环必然将就。常见做法是分流分类和改写用小模型SQL 生成用强模型解释结论回到中等模型。这个分工要写进配置而不是散在代码里否则调优时改一处忘一处。3. 语义层建设让通义大模型看懂你的业务指标3.1 为什么必须单独建一层语义层大模型见过海量公开语料但它没见过你们公司的口径。同叫「活跃用户」增长团队指七日内有登录商业化团队指七日内有付费行为同叫「销售额」有的含税有的不含税有的扣退款有的不扣。如果不把口径显式喂给模型它只能猜猜错的概率随指标数量线性上升。语义层的本质是把「业务语言」到「物理表字段 计算表达式」的映射固化成元数据指标名、同义词、口径说明、计算表达式、可用维度、责任人。模型每次生成 SQL 前先查这层映射拿到的是确定的口径而不是猜测。这层建好之后换模型、换提示词都不会动摇准确率的地基。3.2 指标元数据表的一份可落库 DDL元数据不必设计得很复杂下面这张表是我用得多、覆盖也够的结构CREATE TABLE meta_metric ( metric_code VARCHAR(64) NOT NULL, -- 指标唯一编码如 order_refund_rate metric_name VARCHAR(128) NOT NULL, -- 业务名称退货率 aliases TEXT, -- 同义词逗号分隔退款率,退货比例,退单率 biz_caliber TEXT, -- 口径说明签收后7天内退款单量 / 签收单量 expr_sql TEXT NOT NULL, -- 计算表达式直接可嵌入 SELECT default_dims TEXT, -- 默认可下钻维度region,category,dt time_col VARCHAR(64), -- 时间字段名用于强制补时间过滤 owner VARCHAR(64), -- 责任人口径变更时能找到人 status TINYINT DEFAULT 1, -- 1 生效 0 下线避免旧口径被召回 PRIMARY KEY (metric_code) );几个字段值得强调aliases直接决定召回率把业务同事在群里用过的口语说法都收集进去包括错别字和简称expr_sql存表达式而不是完整 SQL方便和不同维度自由组合time_col是护栏程序强制补时间条件的依据能挡掉相当一部分全表扫描status用来下线旧口径历史口径不清理是准确率长期劣化的主要原因之一。3.3 指标召回向量打底关键词和拼音兜底召回的目标是「用户说退货率Top5 里必须有 order_refund_rate」。纯向量检索对同义改写友好但对专有名词和编码类词不敏感纯关键词检索反过来。两者混合效果最稳# pip install dashscope numpy import os import numpy as np import dashscope from dashscope import TextEmbedding dashscope.api_key os.environ[DASHSCOPE_API_KEY] def embed(texts): r TextEmbedding.call(modeltext-embedding-v3, inputtexts) # 以控制台可用型号为准 return [d[embedding] for d in r.output[embeddings]] # 离线阶段把 metric_name aliases biz_caliber 拼成一句话算好向量存库 doc 退货率 退款率 退货比例 退单率 签收后7天内退款单量/签收单量 doc_vec np.array(embed([doc])[0]) # 在线阶段用户问题向量化后算余弦相似度 q_vec np.array(embed([上个月华东退货情况怎么样])[0]) score float(q_vec doc_vec / (np.linalg.norm(q_vec) * np.linalg.norm(doc_vec))) print(round(score, 4))向量分数拿到后不要直接取 Top1而是取 Top5 交给后续环节同时用关键词和拼音匹配做一路并行召回两路结果合并去重。用户打「thl」这种拼音缩写时向量几乎必然失效关键词兜底就派上用场。阈值上我的经验是相似度低于某个线实践里常在 0.6 到 0.7 之间就别硬猜直接触发反问「你是想看退货率还是退款金额」反问一次的成本远低于给错数的成本。3.4 塞进提示词的元数据要控制在多少召回回来的元数据会全部进提示词这里最容易失控。把整张指标表塞进去动辄上万 token既贵又会让模型注意力被无关指标稀释。实践中的做法是只放 Top3 到 Top5 的指标每个指标只保留metric_code、metric_name、biz_caliber、expr_sql、default_dims五个字段口径说明超过两句话就精简。维度列表同理别把几百个维度的全量表塞进去按主题域分组只给相关的那一组。注意提示词长度和准确率不是正相关。信息过载时模型更容易混用两个相似指标的口径反而比只给三个候选时错得更多。4. 对话型数据分析的落地实现从提问到图表4.1 意图识别先分流把取数请求和闲聊、解释类请求混在一起处理会让提示词互相干扰。分流一步用短提示词就能做且成本极低INTENT_PROMPT 你是数据问答路由判断用户问题属于哪一类只输出标签本身。 可选标签 - QUERY 需要查数、看趋势、看对比或占比 - EXPLAIN 对已有结果做归因、解释或下钻建议 - CLARIFY 指标名或时间范围缺失需要先反问 - CHITCHAT 与数据无关 用户问题{question} 输出CLARIFY这一类最容易被忽略却是体验分水岭。「看看销售情况」这种问法如果不反问就直接生成 SQL模型只能随便挑一个指标用户看到结果的第一反应是「这不是我要的」然后就不再信任这套系统。把反问做成显式分支宁可多问一句。4.2 SQL 生成的提示词模板与硬性约束提示词要写成「约束清单」而不是「描述」。下面这版结构我用了很久重点在最后的硬性约束和结构化输出SQL_PROMPT 你是一名 {dialect} 数据分析工程师根据表结构、指标口径和历史示例生成一条可直接执行的查询。 【表结构】 {schema_ddl} 【命中指标口径】 {metric_meta} 【可用维度】 {dim_list} 【历史示例】 {few_shot} 【硬性约束】 1. 只生成一条 SELECT禁止 INSERT/UPDATE/DELETE/DROP/ALTER。 2. 时间过滤必须使用 {time_col}且区间必须显式写出不允许使用 now() 之类的相对函数。 3. 禁止 SELECT *所有聚合列必须起英文别名。 4. 不确定的口径假设写进 assumptions不要自己发明字段。 5. 只输出 JSON{{sql: ..., used_metrics: [...], assumptions: [...]}} 用户问题{question}要求输出 JSON是为了让程序能稳定解析出used_metrics和assumptions。used_metrics用来做埋点统计哪些指标被问得最多assumptions用来在 UI 上提示「本次结果按含税口径计算」——把模型的假设暴露给用户比让它默默猜完再出错要好得多。few_shot不要放太多三到五条覆盖趋势、对比、占比三种形态即可示例过多会让模型倾向于照抄示例里的维度。4.3 执行前的三道护栏模型生成的 SQL 绝不能直接打到生产库。上线前至少要有语法解析、权限改写、行数限制三道# pip install sqlglot import sqlglot from sqlglot import exp FORBIDDEN (exp.Insert, exp.Update, exp.Delete, exp.Drop, exp.Alter, exp.Create) def guard(sql: str, dialect: str mysql, max_rows: int 5000, tenant_field: str tenant_id, tenant_id: str T001) - str: tree sqlglot.parse_one(sql, readdialect) if any(tree.find(t) for t in FORBIDDEN): raise ValueError(检测到非查询语句已拦截) if not isinstance(tree, exp.Select): raise ValueError(顶层节点不是 SELECT已拦截) # 行级权限没有租户条件的查询自动补上防止越权看到别家数据 if not tree.find(exp.Column, lambda c: c.name tenant_field): tree tree.where(f{tenant_field} {tenant_id}) if tree.args.get(limit) is None: tree tree.limit(max_rows) # 没写 LIMIT 就补一个防止全表扫描拖垮库 return tree.sql(dialectdialect)三个动作的逻辑parse_one把 SQL 变成 AST判断顶层是不是Select、有没有危险节点这是最可靠的黑名单方式比正则匹配强得多行级权限在 AST 上补WHERE条件业务代码不用关心每个用户能看到哪些数据补LIMIT是最后一道保险取数场景没人真的需要一次拉一百万行前端展示几十行就够了。4.4 结果到图表的自动选型规则SQL 跑完之后图表类型不该让用户选按结果集的形状判断即可结果特征推荐图表说明1 个维度 1 个度量维度为时间折线图趋势场景默认按时间升序1 个维度 1 个度量维度为类别柱状图类别超过 15 个时改横向条形图1 个维度 1 个度量单一结果行指标卡加同比环比数字更直观1 个维度 多个度量组合图或分组柱状图量纲差异大时必须用双轴2 个维度 1 个度量热力或透视表交叉分析优先给表格判断逻辑用返回值的列类型和行数就能实现先看维度列是不是时间类型再看度量列数量最后看行数是否超过阈值决定要不要截断。这套规则写在服务端前端只负责渲染同一份数据在不同终端上看到的图才是一致的。5. ChatBI 排错与准确率调优的实战技巧5.1 五类高频失败与排查动作准确率出问题时不要笼统地说「模型不行」按失败类型定位会快很多失败现象大概率原因排查动作答非所问指标完全错召回 Top5 未命中打印召回候选和相似度分数指标对但数字不对口径表达式或时间字段用错比对expr_sql与人工 SQL时间范围每次都不同提示词未禁用相对时间函数检查约束条款是否生效越权看到其他租户数据护栏未做行级权限改写用低权限账号跑一次回归多轮对话后跑偏上下文里塞了完整历史 SQL只保留上一轮的结果摘要其中最后一条最隐蔽。多轮对话里如果把每一轮的历史 SQL 全量带进上下文模型会被前面的写法带偏第三轮开始自己发明字段。稳妥做法是上下文只保留「上一轮问了什么指标、给了什么结果」的结构化摘要完整的 SQL 不进入下一轮提示词。5.2 用评测集把「感觉变准了」变成可回归的数字每次改提示词都靠人工试几个问题是没法持续迭代的。至少要攒一套 100 到 200 条的真实问题集每条标注出期望命中的指标和关键的 WHERE 条件然后做两个层面的自动比对指标召回是否命中可用准确率和 Top5 召回率两个指标看SQL 语义是否等价用 AST 归一化后比对表名、字段、过滤条件而不是字符串比对。改一版提示词就重跑一次指标召回率掉了 3 个点以上就该回滚。这套机制建起来之后团队才敢放心地换模型版本。5.3 一个具体技巧把 SQL 骨架缓存成模板复用高频问题高度重复与其每次都让通义大模型从头生成不如把稳定问法沉淀成模板。做法是正常走完一次链路后把生成的 SQL 做 AST 归一化——把具体的日期常量、地区常量替换成占位符得到一条「骨架」以骨架的哈希作为键缓存起来。下次用户问「这周华南的退货率」召回命中同一指标、维度组合一致时直接取骨架并把占位符替换成新参数跳过生成环节。这个技巧在真实场景里通常能覆盖三到五成的高频问法收益有三块响应从秒级降到毫秒级、单次成本降下来、这部分问法的准确率直接变成百分之百因为骨架是人工确认过的。缓存要设失效时间并在语义层的expr_sql或time_col变更时主动清空对应指标的骨架否则口径改了而缓存没改会出现一类极难复现的错数。最后一点骨架命中要在日志里单独打标签别和模型生成的混在一起统计不然准确率数据会虚高。本文还有配套的精品资源点击获取

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

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

免费获取报价