
聊到数据分析这个方向我这两年听到最多的一个词就是“对话式分析”。简单说就是让业务人员直接用自然语言问数据系统自动翻译成 SQL去数据仓库里查再把结论用图表或摘要还回来。像 BigQuery 这类云原生数据仓库本身跑在云端、弹性伸缩天然适合做这种智能体的底座。我自己从去年开始就在折腾这套东西今天把整体落地思路、技术选型和踩过的坑一并整理出来给同样在做数据智能方向的朋友做个参考。这个智能体能解决什么问题其实就一句话把数据分析师从“提数机”的角色里解放出来。业务人员想查个数据不用再发工单、等排期打开对话窗口直接问“华东区上个月的退货率环比变化”系统自己就能去数仓里算算完还能追问“那上海为什么涨了”。不是替代分析师而是把大量简单重复的查询消化在对话里让分析师专注在口径复杂、结论需要判断的任务上。适合谁来参考数据工程师、BI负责人、数据产品经理以及想把大模型真正用进企业数据流程的团队都可以从这篇文章里找到对应的模块。1. 智能体的整体架构与设计思路1.1 先聊聊企业里做数据分析的真实痛点传统分工是业务提需求、分析师写SQL、报表做好以后需求又变了。这种模式在数据量小、业务稳定的阶段够用但一旦业务节奏加快问题就会集中爆发。口径不一致同一个“销售额”财务按开票算运营按订单算两个团队吵了几个月最后各出一版数。响应慢分析师排期两三天等业务拿到数据那个决策窗口已经过了。报表僵化固定报表只能回答预设问题业务在报表里翻不到答案只能再提需求又进入下一个排队循环。自助式BI工具拖拽图表那种解决了一部分问题但对没受过训练的业务人员依然不友好。维度、度量、过滤条件是数据分析的“行话”业务人员大概率不知道“退货率”应该拖哪个字段去算。对话式分析为什么能起来是因为自然语言本身就是业务人员最熟悉的表达方式——你说“退货率”模型理解你指的是什么指标然后自己去拼接计算逻辑。1.2 五个核心模块的分工我习惯把智能体拆成五个部分每个部分职责清晰出了问题也好排查。接入层负责会话管理、多轮上下文维护、用户身份识别。业务问“那上海呢”系统要能知道“那”指的是前一句问题里的某个维度。规划层把用户的口语问题转化成可执行的查询意图。包括意图识别是查询、是追问、还是闲聊、改写补全把指代还原成完整表达、查询拆解复杂问题分成多个步骤。这个层不直接生成SQL它负责决定“怎么查、查什么”。语义层给大模型提供元数据和指标口径。模型不知道你的表里有什么字段、枚举值是什么、退货率怎么算语义层就是模型和物理表之间的“翻译词典”。这是整个智能体的灵魂。执行层负责SQL的生成、校验、执行和结果格式化。翻译成SQL只是第一步SQL能不能跑、跑出来对不对、结果怎么转成图表和摘要都在这一层。治理层权限、审计、成本监控。谁可以问什么数据、一次查询最多扫描多少数据、每一步操作能不能被追溯这层决定了系统能不能真正走进生产环境。你可以理解成接入层是“前台”规划层是“大脑”语义层是“知识库”执行层是“双手”治理层是“保安”。缺了任何一个系统要么没法用要么不敢用。1.3 为什么先做语义层而不是急着接大模型很多人拿到大模型API直接丢一句“帮我写SQL”结果惨不忍睹。模型会一本正经地编造不存在的字段名把“订单日期”写成“order_date”还是“odate”完全靠猜。这不是模型不行而是你什么都没给它。语义层的本质是把你数据库里的信息“翻译”成模型能理解的语言。一个标准的字段描述至少要包含字段名、业务含义、数据类型、枚举值、单位、取值范围。比如{ field: order_amount, description: 订单实付金额含运费不含退款, type: DECIMAL, unit: 元, sample_values: [12.90, 399.00, 2999.00] }只有把这些信息喂给模型它才知道“最近三个月”应该去过滤哪个字段“实付金额”和“商品金额”到底有什么区别。语义层做得越细模型生成SQL的准确率就越高这是整个项目里投入产出比最高的模块。2. 语义层的构建与数据口径治理2.1 字段元数据的结构化表达做这个项目我一开始犯过一个错误靠人肉写说明文档然后整段拼进提示词。结果上下文又长又乱模型反而更容易被无关信息干扰。后来改成结构化录入每个字段单独维护记录按需加载。字段元数据至少包含这些项项说明举例字段名物理表里的字段order_date业务名称业务人员习惯的叫法下单日期、订单日期业务含义这个字段记录了什么用户提交订单的日期下单即记数据类型字段类型DATE/DECIMAL/STRING枚举值字段可能的取值[pending, paid, shipped, cancelled]单位与精度数值的单位和精度元保留两位小数敏感等级PII/内部/公开内部脱敏展示关键点是“按需加载”——不是把所有表的描述都塞给模型而是先识别用户问题涉及的主题只加载相关几张表的元数据。否则几百张表的描述一进来光上下文就超出模型窗口了而且无关表越多模型越容易选错。2.2 注册标准指标和常用查询模板字段解决了指标还没解决。“销售额”和“成交额”在很多公司口径不同但在对话里业务人员可能混着用。所以语义层里必须有一本“指标字典”。指标字典的形式很简单指标名: 销售金额 口径: 已支付订单的商品金额之和不含退款订单 涉及表: orders.order_items 计算公式: SUM(order_items.item_amount) WHERE orders.payment_status paid 可关联维度: 日期、地区、渠道、商品类目这里有个需要坚持的原则能定义的就先定义不明确口径的指标宁可先不上线。我见过太多项目指标口径一团乱麻团队内部讨论几个月都没定论结果智能体上线的第一天就把某个“还没定”的指标给用户算了。用户不会管你内部口径有没有吵清楚他只会觉得“这个AI算错了”。常用查询模板也很值得做。业务上高频的问题就那么几十种“某段时间的销售趋势”“本周Top10商品”“各区域的对比”……把这些问题的SQL预先写好当用户问法匹配到模板时直接套用模板再加上具体参数准确率比现场生成高得多。模板不是死板地匹配字符串而是用语义相似度判断比如“卖得最好的几个产品”和“Top10商品排行榜”匹配到同一个模板。2.3 用规则引擎对SQL做二次校验即使有了语义层模型生成的SQL也不可能100%正确。我的做法是在SQL真正执行之前过一道规则引擎。规则校验分两类。第一类是静态检查在SQL语法解析后直接判断表名和列名是否存在于元数据里是否存在可疑的笛卡尔积两张无关表直接JOIN没带ON条件SELECT里的字段和GROUP BY里的字段是否一致聚合函数嵌套是否合法第二类是估算检查预测这条SQL会扫描多少数据。BigQuery 是按扫描量计费的产品一条忘加 WHERE 条件的全表查询可能一次就烧掉几十上百块钱。规则引擎可以在执行前估算分区过滤情况如果预估扫描超过某个阈值直接拦截并返回“问题太宽泛请缩小时间范围或增加筛选条件”。我自己实现时规则引擎用了一组表达式FORBIDDEN_PATTERN: 匹配: SELECT\\s.*\\sFROM\\s 说明: 必须包含WHERE或LIMIT排除全表扫描 action: 阻断并提示用户添加条件这种硬性规则看起来粗暴但反而是生产环境里最可靠的一道防线。大模型的能力体现在“理解和表达”规则的可靠性体现在“确定性的约束”两者配合才能既灵活又安全。3. 对话式规划与Text-to-SQL实现细节3.1 让模型学会“先给方案再写SQL”这里有一个很关键的流程设计不要要求模型一次性从自然语言跳到SQL而是拆成两步。第一步模型先输出“查询计划”——它打算查哪个主题、涉及哪些指标、怎么过滤。比如用户问“最近一个月A商品的销量和库存”模型的计划可以是主题: 商品销售与库存 指标: 销量SUM(sales_qty)、当前库存MAX(stock_qty) 维度: 商品名称、日期 过滤: 商品名称 A商品, 日期 当前日期-30天 表: sales_daily, inventory_daily第二步系统先校验这个计划里的表名、指标名是否在语义层里确认无误后再让模型生成SQL。这个“先方案后SQL”的设计好处很直接。如果模型连计划都是错的就没有必要生成SQL免得白跑一趟。而且计划是半结构化文本规则引擎可以像校验JSON一样逐项检查——表存在吗指标注册过吗维度合法吗都不通过直接返回修改意见用户重新问一次就行了而不是等SQL执行完才发现结果是一堆垃圾数据。3.2 提示词模板的写法业务智能体的提示词和写作文的提示词完全是两回事它更像一份“岗位说明书”。我用的系统提示词模版长时间迭代下来长这样你是企业数据分析助手。你的任务是把用户的中文问题转换为可以在BigQuery上执行的SQL查询。 规则 1. 只能使用语义层提供的表和字段禁止猜测字段名。 2. 日期一律先用自然语言描述再换算成具体日期范围。 3. 分组统计必须包含分组字段聚合结果要有明确含义。 4. 金额类指标默认保留两位小数除特殊说明外不使用四舍五入。 5. 遇到查不到的数据或者不明确的指标口径直接回复“无法解答”禁止编造数据。 查询计划输出格式 - 主题 - 指标含计算公式 - 维度含粒度 - 过滤条件 - 涉及表 确认计划后再输出SQL。这里面的每一句话都是踩过坑之后加进去的。比如“禁止猜测字段名”是因为早期模型经常把order_date写成orderTime规则再加元数据都拦不住这种“自信的幻觉”。后来在系统提示词里专门加了一条效果立刻不一样。少样本示例few-shot也要放几个到上下文里尤其是新增了某张表、某个指标定义的时候。示例不用多覆盖三种典型情况就行一个简单的聚合查询、一个多表JOIN、一个带窗口函数的对比查询。这三个示例能让模型很清楚地“对齐”输出风格和SQL语法粒度。3.3 少样本示例的动态加载既然语义层讲究按需加载少样本示例也不应该固定不变。我最早是手写十个示例硬塞在提示词里后来发现用户的高频问题一直在变固定示例覆盖不到的时候生成准确率明显下降。后来改成动态加载维护一个“问题-SQL”样例库每收到一个新问题先用向量检索把样例库里最相似的几条翻出来拼接进当前上下文。检索用的是文本嵌入模型对问题做向量化然后按余弦相似度取Top3或Top5。这套机制上线后SQL生成准确率提升了差不多10个点。道理也简单模型看到跟当前问题长得像的问题、SQL对输出的格式和逻辑就有参照。如果你问“上周各城市的销量”示例里恰好有“上月各渠道的销售额”模型一下子就知道用户要的是“维度聚合指标”输出结构几乎不会跑偏。4. 权限管控与企业级安全落地4.1 数据权限如何传递到SQL执行层对话式分析真正让人提心吊胆的不是模型写得对不对而是数据安不安全。业务人员能问数据那就意味着模型在替用户访问数据仓库。如果权限没控制好一个普通销售就能通过对话问出全公司的工资表这个项目直接变成安全事故。我的做法是三层权限身份层用户登录后从身份系统拿到用户所属部门、角色、数据范围。语义层加载元数据时只加载该用户有权访问的表和字段。没权限的表连字段名都不让模型看见。执行层在SQL真正跑之前做一次权限注入——把用户的部门、区域等维度自动拼成过滤条件确保SQL结果限制在授权范围内。执行层的权限注入是最后一道兜底。比如一个销售区域经理登录系统在生成SQL时自动加上WHERE region 华东无论模型怎么发挥最终结果只会包含华东区的数据。这个条件用户是看不到的也不该能通过对话绕过。列级权限我用视图机制来实现。把物理表包一层业务视图视图中只暴露该角色可见的列敏感字段直接不开放。用户对视图执行查询物理表的原始数据始终不可见。4.2 拦截危险操作与限制资源消耗数据仓库不是随便跑的玩具对话式分析尤其要把“执行”这扇门看好。我在执行层做了四类拦截拦截项规则说明写操作拒绝所有INSERT/UPDATE/DELETE/ALTER智能体只读不允许任何写操作全表扫描无过滤条件的全表查询拦截防止单次查询扫描TB级数据扫描量限制按用户设定单次扫描上限超过后提示用户补条件而不是直接执行并发限制每个用户最多N个并发查询防止某些操作拖垮整个集群这里要特别提一下扫描量限制很多团队第一次跑对话式分析最大的惊吓来自账单。模型生成的SQL没带分区过滤一次查询就扫了全表几TB数据那个月账单一出来项目差点被叫停。所以扫描量限制宁紧勿松哪怕偶尔误伤几个复杂查询也比月底看账单强。4.3 审计日志与问题回溯对话式分析这种场景每一次查询都必须可追溯。我要求系统把每个问题对应的完整链路写入审计表用户是谁、问了什么、模型输出了什么计划、生成了什么SQL、实际扫描了多少数据、花了多少钱、结果返回了什么。这条链路在排障时尤其关键。当业务方说“AI算错了”第一步就是打开审计记录回放当时的SQL和数据结果看问题出在哪个环节。我印象很深的一次业务反馈“昨天问的销售数据和我们部门日报对不上”结果审计记录一查发现SQL用的过滤条件把退款订单也包含进去了口径不是模型错了是数据口径本身就两个定义。审计还有一个好处是持续改进。每周把审计表拉出来跑一遍看看哪些查询经常失败、哪些问题让系统产生了错误的SQL集中优化语义层和示例库比被动等用户反馈要主动得多。5. 性能调优与成本账单里的门道5.1 会话缓存是目前性价比最高的优化对话式分析有一个特点用户经常会问“差不多”的问题。同一个销售经理上午问了一遍上周华东区销售下午又问一遍一样的口径、想确认是不是更新了。这种情况下如果每次都重新生成SQL再重新跑数仓成本完全重复。我在会话内加了结果缓存层。判断逻辑很简单用户问题的向量相似度超过阈值查询涉及的表、过滤条件完全一致数据时效允许缓存比如日更表设置缓存有效期到当天首次更新后会话ID相同三个条件都满足直接从缓存返回上一次结果。实测下来会话内缓存命中率能做到30%以上这意味着有三分之一的查询根本没碰数仓。成本降下来了响应速度也从“跑30秒”变成“秒回”对用户体验的提升远比模型算法优化来得明显。5.2 模型层的轻量化改造对话式分析刚做的时候容易陷入“什么都是最强大模型”的思维定式。后来我看了一下账单推理开销其实相当可观不是所有的对话都需要满血版模型。我加了意图分类前置层先用一个轻量模型判断用户问题属于“简单聚合、趋势查询、多维度对比、复杂归因分析”中的哪一类再决定走哪条链路。简单汇总比如“上个月订单总数”走小模型直接查预聚合表速度快也便宜。归因分析这种需要多轮推理、多表JOIN的复杂问题才走大模型完整链路。通过这个分流推理成本下降了差不多一半但用户能感知到的回答质量几乎没有变化。5.3 预聚合表是LLM加速的隐形加速器LLM生成SQL的时候如果你让它去查一个几十亿行的明细表扫描量大、耗时长模型还容易在复杂的聚合逻辑里翻车。一个更聪明的做法是提前准备好不同粒度的预聚合表。比如订单明细表可以按“日期区域渠道”预聚合生成一张日度汇总表。用户问“华东上个星期每天的销量”LLM只需要查这张聚合表数据量小了三个数量级SQL简单扫描成本低速度还快。我一般建三层日聚合日期核心维度周聚合周核心维度月聚合月份核心维度预聚合表建好以后在语义层里把“推荐表”标记为聚合表元数据加载时优先加载模型自然会优先用它们。这个操作看似是数仓建模的老本行但在对话式分析项目里反而是影响体验最深远的一步——模型用到的每张表都又小又干净生成SQL的成功率和执行速度一起上去了。6. 实测中的坑与排查记录6.1 常见问题速查表这套系统从搭建到跑起来中间踩过的坑远比想象中的多。挑几个有代表性的整理成表现象根因解决方案模型把order_date写成orderTime语义层没加载或字段名没写进系统提示词在提示词里增加“禁止猜测字段名”约束跨天日期过滤结果少一天时区换算不一致数据表存储UTC模型按本地时间过滤在字段元数据里标注时区提示词里加入日期换算规则多张表里都有“金额”字段模型选错语义层字段描述没有说清上下文字段描述里加上“订单实付金额”并在指标字典里注册别名一条SQL扫描了好几个TB未加分区过滤模型生成了全表扫描设置单次查询扫描量上限规则引擎拦截输出SQL用了数仓不支持的函数给模型的方言规范不够明确在提示词中固化支持的函数列表、Window函数写法问“上周数据”模型按自然周算业务按周一到周日算周起始日口径没有在语义层定义在指标字典里明确周口径业务自定义和周ISO两种都配置6.2 一个指标口径不一致的典型事故有一次业务方在对话里问“退货率”模型给出了一个数。第二天业务方把数甩给我说和手工统计差了两倍。我第一反应是模型又幻觉了日志一查SQL完全正确问题出在“退货率”这个指标本身有两种定义。一种定义是退货订单数除以订单总数。另一种是退货商品件数除以销售商品总件数。业务方在对话里问的是第二种而我在指标字典里注册的是第一种。系统没报错因为两种口径都合法只是用户期望的和你系统里定义的不是同一个。从那以后我强制规定指标上线的必要条件是在语义层里写清楚计算公式和口径来源没有注册的指标模型必须回答“该指标未定义”而不是自己发挥。宁可让用户觉得“这个AI还不够强”也不能让他觉得“这个AI在胡说”。6.3 防幻觉的兜底策略任何时候都不要完全信任模型能处理好没遇到过的问题防幻觉不是靠“更聪明的模型”而是靠系统设计。第一层是语义层限定模型只能基于提供的表和字段操作不存在的对象直接拒答。第二层是规则校验SQL语法、表结构、扫描量都过一遍再执行。第三层是回答兜底如果查询结果返回空、字段没有业务上下文、或者计算过程触发了任何规则拦截系统输出统一话术“我暂时无法回答这个问题请尝试换个说法”。这套兜底设计看起来保守甚至有点“笨”但正是这种笨让智能体在企业里真正站住了。用户问十个问题哪怕是错了那么一两个只要有清晰的路由到人工支持业务方依然能接受。真正不能接受的是AI一本正经地瞎编一个不存在的数字那会让整个系统失去信任。如果让我重新做一遍这个项目我会把更多的精力花在一开始的数据治理上。大模型的代码生成能力已经足够强大真正拉开差距的是你给你的数据准备了多少“可被理解”的上下文。每张表的字段描述、每个指标的计算口径、每个维度的枚举含义这些听起来琐碎无聊实际效果却远超换一个更强的模型。另一个经验是从窄场景起步。先在两三个高频问句上做到高准确率再慢慢扩展。别指望像演示视频里那样让AI随口回答公司所有的经营问题那通常意味着它什么都答不好。先把“华东区销售趋势”“Top10爆款商品”“库存周转天数”这三个问题做到业务方天天在用再从日志里去挖掘第二个十、第三个十。数据质量永远是前提。如果你的仓库里本身脏数据横行AI只会把这些错误更快、更广地传开。对话式分析不是数据质量问题的救世主它是数据治理做好之后释放价值的放大器。