去年年初我们数据团队接了一个让我头疼很久的活儿:业务部门每天都在钉钉群里追着要数,今天问"华东区上个月退货率为什么涨了",明天问"新客首单转化掉了几个点",后天又问"帮我拉一下最近90天高价值用户的复购明细"。这些需求单看都不难,但架不住量大、口径杂、时段急,几个分析师每天不是在写SQL,就是在解释"为什么数跟BI看板不一样"。我们自己内部也上过自助BI、做过指标平台,但业务同学普遍反馈"不会用""不会写条件""看板是固定的、问题却是活的"。后来我们决定试试一条新路:把LLM塞进自助分析流程里,让用户直接用大白话问数。这个项目前后做了三个多月,踩了不少坑,今天把这套智能化自助分析系统的搭建过程、架构取舍和上线后的真实表现完整记录下来,希望给正在考虑同样方案的同学一些参考。
1. 为什么"自助分析"喊了这么多年,最后还得靠LLM来补这一环
先说清楚一个背景:市面上不是没有自助分析工具,Tableau、Power BI、帆软,包括我们自研的指标平台,都算成熟产品。但"自助"这两个字,对业务同学来说一直是个伪命题。工具的筛选、联动、钻取功能再强,前提是用户脑子里得先有一个"该看什么维度、该切什么条件、该对什么口径"的框架。可现实是,业务同学脑子里装的是经营问题,不是数据结构。他问的是"为什么这个月新客少了",背后隐含的其实是"新客定义是什么、跟哪个表关联、时间怎么对齐、和上月比还是和年初比"。这个从业务语言到技术语言的翻译过程,传统工具一直做得不好,只能靠人来弥补——于是取数需求还是源源不断流向分析师。
1.1 一线观察:取数工单的真实构成和浪费在哪里
我翻过我们团队半年的工单记录,发现一个规律:大约70%的需求其实是"结构化程度很高"的查询。什么意思呢?就是用户要的表、字段、过滤条件基本都说得清楚,只是他不会写SQL,或者不想学BI工具。比如"查一下1月到3月每个区域每个品类的销售额,按降序排",这种需求本质是固定模板,但每天换个时间段、换个维度就变成一个新工单。剩下30%才是真正需要分析师手动加工的复杂分析,比如归因、预测、对比解读。
这里就藏着第一层机会:如果LLM能把那70%的模板化查询直接消化掉,分析师就能把时间省下来去做那30%的深度活儿。我们自己统计过,改造后取数工单量下降了六成,分析团队的人均深度分析产出提升了将近一倍。所以我一直跟别人讲,评估这个系统值不值得做,别盯着"智能"两个字看,先算算你团队里有多少人力被固定SQL占走了。
1.2 LLM在这里的角色不是"变出结果",而是"听懂问题、校准口径、生成可执行查询"
很多朋友一听"LLM做自助分析",第一反应是"那不就是让AI直接写SQL吗?"其实没那么简单。LLM当然可以写SQL,但"写出来"和"写对"之间隔着指标口径、表关系、权限规则三道坎。
我习惯用Transformer注意力机制里的三个角色来理解这件事:Key、Query、Value。Query是你想知道什么,对应业务同学那句"华东区退货率为什么涨了";Key是系统能提供什么,对应我们元数据层里预置好的表结构、字段注释、指标定义、维度枚举;Value是你最终能拿到的答案,对应查询出来的真实数据。如果Key这一侧给得不全、不清晰,模型就只能靠猜,猜出来的SQL大概率是错的。所以LLM在系统里的定位,本质是一个"翻译器"和"编排器",它不负责发明业务口径,只负责把用户的问题对齐到我们已经定义好的数据资产上。
想明白这一点之后,整个项目的架构思路就清晰了——不能把宝全押在模型能力上,要把确定性知识做扎实,让模型在一个被约束好的框架里发挥弹性。
2. 系统整体架构:我给这个"智能"划了三层边界
很多人的第一版设计是拿一个LLM API直接接数据库,用户在对话框里问一句,模型生成SQL、执行、返回。我们第一版也这么干过,效果只能用"翻车"来形容。后来重构成了三层架构:接入层、语义层、执行层。每一层各管一摊事,模型只是语义层里的一个组件,而不是整个系统。
2.1 三层架构的落地形态和每层的核心职责
接入层负责统一入口。我们做了Web端和企微机器人两个入口,背后是同一个服务的两个适配器。这一层要处理的其实是工程问题:登录鉴权、租户隔离、会话保持、限流配额。特别是限流,LLM接口的Token成本不是固定的,一个用户疯狂连问20个问题,Token消耗可能抵上别人一天的量,所以接入层必须做预算控制。
语义层是核心,负责把用户输入的原始问题,一步步转成"可执行的查询意图"。它内部包含:意图识别(是查数、看趋势、还是做对比)、参数抽取(时间范围、维度、筛选条件、指标)、指标对齐(把用户说的"成交额""销售额""GMV"映射到指标字典里的唯一ID)、多轮上下文管理、以及RAG检索(出了问题不知道怎么办,先查知识库里有没有类似的历史解法)。
执行层相对"笨",它不碰模型,只做三件事:校验语义层输出的查询计划、通过只读账号去数据源执行查询、把结果做后处理和可视化返回。为什么要单独拆执行层?因为安全策略必须落在确定性的代码里,不能依赖模型的自觉性。权限过滤、行级限制、超时中断这些如果交给模型去判断,迟早会出事。
2.2 系统搭建的边界感:哪些逻辑永远不该交给LLM
这是我在这个项目里总结出的最重要的一条经验——模型负责"意图理解",代码负责"规则执行",这两者必须物理隔离。
举几个具体的例子。第一,数据权限。用户是A部门的,他就不该看到B部门的客户明细。这个判断如果让LLM来做,哪怕你精心设计了prompt,也可能在某个多轮对话的上下文拐弯处出错。我们的做法是,在语义层生成查询计划时,权限信息已经作为硬性过滤条件注入到SQL里,模型没有决定权。第二,指标口径。我们指标字典里定义了"月活跃用户"等于"当月至少登录一次的去重用户ID数",这个定义一旦写死,模型负责的只是把用户问题里的"月活"匹配到这条定义上,不允许它自己发挥解释。第三,DDL和写操作。执行层直接用只读账号+语法白名单双保险,从物理上杜绝了模型生成DELETE或UPDATE的可能性。
3. 语义层是灵魂:让LLM看得懂你的指标体系和数据模型
整个系统里最难做、也最容易被低估的,就是语义层。模型本身的能力差距其实没有想象中那么大,真正拉开体验差距的,是你喂给它的"上下文"质量。
3.1 指标字典和元数据注入:相当于给模型发了一张"开卷考试的小抄"
我们内部有一句话:你把多少数据资产告诉模型,模型就能还给你多少准确率。任何不经过元数据注入、直接让模型裸写SQL的方案,都等于让一个刚入职的实习生不看任何文档就去写生产查询,能跑但大概率是错的。
我们的做法是维护了一张指标字典,每一行包含:指标名称、业务口径描述、来源表、涉及字段、常用聚合方式、常见筛选条件。用户问"查一下近7天各门店的坪效",语义层会先把"坪效"这个词去指标字典里做模糊匹配和同义词扩展,匹配到"坪效=销售额/营业面积"这条定义之后,再把对应的表结构、字段说明、联表关系拼装成上下文,和用户问题一起送给LLM生成查询计划。
这里有个非常关键的细节:每次请求注入的元数据不是全量的,而是"按需检索"出来的。全库表结构可能有几百张表,Token塞不下,塞下了也全是噪声。我们做了一个轻量级的元数据检索器,先用用户问题里的业务词去表名、字段注释里做关键词召回,只把命中的表和关联表注入到上下文里。这个设计让生成准确率直接从刚过半提升到了接近九成。
3.2 RAG、GraphRAG与LLM Wiki知识库:我分别用在了三个不同环节
聊到LLM应用,RAG是个绕不开的话题。但很多人把RAG当成一个"万能外挂",什么知识都往里塞。我的经验是:要区分知识的形态,不同形态用不同的检索方案。
第一类是非结构化的经验知识,比如"用户问'环比'时,系统建议输出与上个周期的对比并提供差值",这类说明文档、FAQ、最佳实践,我放在了一个LLM Wiki式的知识库里,按主题沉淀历史问题与标准解法,用向量检索召回。第二类是关系型知识,比如"门店表通过org_id关联区域表,区域通过region_id关联大区表",这类表与表之间的血缘关系、层级隶属关系,我建了一个轻量级本体模型(Ontology),用图结构来组织,效果比纯向量检索好很多。第三类是经典的RAG场景,用于检索SQL模板示例,比如"近30天销售额趋势"这种高频问题,直接从模板库里拿到骨架再套实际参数。
我给这三种方案划了一条选型线:文档说明用RAG,业务查询用向量检索,关系推理用GraphRAG或本体。别指望一个方案通吃,也别嫌维护三个库麻烦——它们解决的是不同性质的问题,维护成本换来的是准确率上的明显回报。
3.3 多轮对话与上下文状态管理:最容易翻车的地方全在这里
用户问"华东区上个月销售额是多少",系统回答完,用户紧接着一句"那华南呢?",如果系统不记得"上个月""销售额"这些前提,就会生成一个残缺SQL。这算是多轮对话最简单的场景了。
难的是用户在中途改条件。比如先问"统计各区域上季度的销售额",看到结果后又问"算了,不看季度了,改成上个月吧"。此时系统需要理解,这不是新增一个条件,而是替换时间粒度。我们在语义层里用了一套轻量级的槽位填充机制:把时间范围、维度、指标、筛选条件做成槽位,每一轮对话先把新的参数和已有槽位做比对,判断是新增、覆盖还是置空,再决定是直接生成查询还是向用户澄清。
这里有个经验之谈:不要让模型自己决定"该不该问用户"。我们设了一个规则:关键槽位缺失时,如果置信度低于阈值,系统必须生成一句澄清问题,而不是赌一个答案。比如用户只说"看看销售情况",没说时间、没说维度,模型直接猜"近30天"其实是很危险的行为,因为它猜错了用户也不知道,看到结果不对劲才反馈,一来一回体验反而更差。宁可让系统多问一句"您想看哪个时间段?",也好过给一个错误的确定性结果。
4. Text-to-SQL与查询执行:从"能生成"到"敢执行",中间隔着一整条安全链
语义层把用户问题转成查询计划之后,就到了Text-to-SQL这一步。很多项目死在这一步——不是模型写不出来,而是生产环境不敢执行模型写的SQL。
4.1 SQL生成的三种路线对比:我最终选了"模板+参数"的组合方案
业内做Text-to-SQL大体有三条路线。第一条是纯模型生成,让LLM直接根据表结构写完整SQL。这条路线最灵活,但风险也最高,模型可能写出不存在的字段、错误的JOIN条件,甚至语法都不对。第二条是模板填充,预先写好一批高频查询模板,模型只负责把用户条件映射成参数。这条路线最稳,但覆盖不了长尾需求。第三条是语义改写,模型把自然语言改写成结构化的条件表达式,SQL骨架由确定性模板生成。
我推荐的是"模板+参数"的组合方案,具体操作是:先做一次意图分类,判断用户问题是否命中高频模板。比如"某区域某时间段的某指标趋势"这种结构,直接走上层模板,模型只负责填参数。如果没命中模板,再走语义改写路线:模型把问题里的指标、维度、时间、过滤条件抽取成结构化对象,然后由代码把对象拼装成SQL。这样做的好处是,最终执行的SQL里,表名、字段名、聚合函数这些关键部分全部来自我们的受控字典,模型只负责填参数值,从根本上压缩了模型自由度带来的风险,又保留了长尾问题的覆盖能力。
4.2 保护数据安全的四道防线:一条都不能少
执行层我设计了四道防线,任何一个新来的工程师我都会先带他过一遍这个清单。
第一道,只读数据库账号。系统里所有查询统一走一个仅有SELECT权限的账号,从数据库层面杜绝写操作。第二道,强制LIMIT。所有生成的SQL自动拼接LIMIT子句,默认返回上限1000行,防止用户问了个"全表"把数据库内存打爆。第三道,查询超时与资源组。我们给自助分析系统单独划分了计算资源组,查询超过30秒自动终止,不让一个异常SQL拖垮整个集群。第四道,语法与结构的规则校验。代码层面用SQL解析器校验模型输出的查询计划,只允许SELECT、只允许存在的表字段、禁止多语句注入,任何一条不满足就直接拒绝并提示用户换一种问法。
这四道防线缺一不可。我见过太多团队只做了第一道,结果模型生成了一个笛卡尔积JOIN,直接把生产库查挂了。自助分析系统一旦上线,面对的用户可是完全不懂SQL的业务同学,他们不会意识到自己问的"所有数据"背后是多大的查询量,系统必须替用户兜住这个底。
4.3 返回结果的"可读化包装":让用户信得过的关键一步
执行成功不等于交付完成。最初我们直接把表格丢给用户,业务同学反馈"看不懂、不知道这个数跟BI上的是不是一回事"。后来我们加了一层结果解释模块:用LLM对查询结果做一个自然语言摘要,回答用户最关心的"涨了还是跌了、异常在哪、和上次比变化多少",同时附上所用指标口径和数据来源。更重要的是,我们在每条结果后面放了一个"查看生成SQL"的入口,分析师点开就能核对查询条件是否准确,这既是可追溯审计,也是在用户心里建立信任感的必要手段。
这一步的工程细节是:解释文本要基于查询结果来生成,而不是让模型凭空总结。我们先把结果表格摘要、对比数据、关键异常值计算好,再让模型基于这几项结构化输入做文本润色,避免模型为了"通顺"而编出数据里没有的结论。
5. 生成式分析的幻觉防护与数据安全:这一章决定系统能不能活过试用期
把用户问题变成SQL这件事,最大的天敌是幻觉。模型一本正经地告诉你"根据数据分析,退货率上升是因为天气原因",结果数据表里根本没有天气字段,这种案例在真实环境里比比皆是。做LLM应用,防幻觉的优先级永远排在效果优化前面。
5.1 幻觉最容易出现的三个环节:字段幻觉、口径幻觉、解读幻觉
我总结了三类高发幻觉,排查问题时习惯先对号入座。
字段幻觉是最常见也最好发现的。模型生成了表里不存在的字段名,SQL执行直接报错。这类幻觉用"执行前校验"就能拦下来,我们让生成的SQL先走EXPLAIN验证字段是否存在,不通过就触发重写。口径幻觉隐蔽得多,模型可能把"近30天"理解成自然月,而我们的定义是滚动30天,SQL能执行但结果是错的。这类幻觉靠元数据注入来防——把口径说明作为prompt上下文的一部分,让模型有据可依。解读幻觉最危险,SQL是对的、结果也是对的,但LLM在生成总结时脑补了一个数据里不存在的"原因"。我们应对解读幻觉的办法是:绝不让模型直接看原始数据表,只让它基于我们预设好的统计指标做表述,把它的"自由发挥"限制在措辞层面,而不是事实层面。
5.2 落地一套"模型自检+规则兜底"的组合校验机制
防幻觉不能只靠一层。我们的组合拳是这样:第一层,对用户问题进行意图分类,分类置信度低就直接转人工兜底,不硬答。第二层,SQL执行前做字段和语法校验,不通过就重写或澄清。第三层,SQL执行后做一个结果合理性校验,把关键查询结果和指标平台里的同维度历史值做差异比对,差异率超过阈值就标记"结果异常,请谨慎参考"并附上比对说明。
这个组合机制上线之后,系统的整体可用率从不到七成提升到了九成以上。虽然多花了一点推理成本,但换来的是用户对结果的信任。我见过很多项目在POC演示时很惊艳,一上生产就露馅,核心原因就是没有把幻觉的兜底机制做到位。
5.3 权限与数据隔离:模型不该知道的事情,一个字都不给它
这是数据安全里最容易被忽视的一点。很多人以为做了行级权限就安全了,但LLM应用的隐患在于:如果Prompt里注入了不该出现的数据,模型就可能顺着用户的追问泄露出去。所以我们从源头上做了隔离——元数据检索时,只检索用户有权限访问的表和字段;RAG知识库召回时,按租户做好了数据分区;生成查询计划时,用户的权限维度自动作为硬编码过滤条件拼进SQL。
另外还有一个工程细节:所有模型请求和SQL执行都做全链路审计,记录用户ID、原始问题、注入的上下文、生成的SQL、执行结果、返回给用户的解读。这样万一出了权限问题,能快速定位是哪一层漏了。
6. 模型选型与部署实践:开源模型真的够用吗
聊完系统设计,说说更底层的模型选型。我们团队既用过商业API,也折腾过私有化部署,这块的经验分享出来,能帮你省不少冤枉时间。
6.1 从通用榜单到垂直场景:评价指标才是真正该盯的东西
很多人选模型,第一件事是刷Open LLM Leaderboard之类的公开榜单,看谁分高选谁。但我要泼一盆冷水:通用榜单排名高,不代表在你的Text-to-SQL场景里表现好。榜单测的是综合能力,而你的场景要求的是"准确地理解指标口径、生成结构正确的SQL、不瞎编结论"。我们做了个小范围的对比测试,把60条真实业务问题同时喂给几个主流模型,结果发现排名靠后的开源模型在某些SQL范式上反而更稳,因为它的训练数据里刚好覆盖了我们常用的数据库方言。
正确的做法是,尽快建立你自己的评测集,用真实场景跑一轮对比,再确定主模型。评测集怎么建?下面第7章专门讲。这里只说选型结论:在7B到14B规模的开源模型里,经过良好微调或配合完善的元数据注入,处理大部分结构化查询已经够用;只有遇到特别复杂的分析推导问题,才需要调度到更大的云端模型。
6.2 私有化部署与量化:把模型塞进自有服务器的几条经验
出于数据合规和稳定性的考虑,我们的核心场景选择了私有化部署。模型是开源权重,用量化方案压到可以接受的显存占用。这里补充一点基础认知:LLM本质上是深度学习模型的一种,它的特殊之处在于规模和训练方式带来的涌现能力,但在推理层面,它依然遵循神经网络前向传播的基本逻辑。理解了这一点,你就能明白为什么可以用INT8量化来显著减少显存占用——把权重精度从FP16压到INT8,精度损失在可控范围内,但显存需求几乎减半。
在具体实现上,我们有两条路可以选:一条是走ONNX导出再用ONNX Runtime做GPU推理,工程链路成熟、和现有CI/CD体系好集成;另一条是直接用llama.cpp这类推理框架,胜在部署简单,CPU也能跑。我们的取舍是:大模型(14B以上)走ONNX+GPU,追求吞吐;小模型(7B以下)用轻量框架跑CPU,省成本。这里有个经验:别追求一次性把模型能力拉满,先跑通Mini版本再逐步升级。我们用7B模型跑了两个月,积累了大量真实Prompt和修正Case,后来换了14B模型,效果立刻又上一个台阶——因为评测集和知识库都已经准备好了,模型升级的收益能立刻被验证。
6.3 LLM网关:多模型路由和省钱的关键组件
项目跑了半年之后,我愈发觉得LLM网关是个被严重低估的组件。它在接入层和模型之间加了一层代理,负责三件事:路由、配额、回退。路由是指同一个请求根据业务类型、数据敏感度、复杂度分发到不同模型——敏感数据走私有化模型,复杂推导走云端大模型,这样既保安全又省成本。配额是给不同租户设定Token预算,防止单个部门把预算烧光。回退是指主模型调用失败时自动切换到备选模型,不影响用户使用。
把这一层做扎实之后,系统的可运维性提升了一个量级。发布新模型、调整配额、观察各模型质量差异,都不需要改业务代码,在网关层就能完成。
7. 评估体系与上线踩坑:怎么科学地证明系统"真的能用"
最后这部分,是我觉得最值得分享的。智能系统的效果评估,比普通软件难得多。你说它"好用",得先定义什么是"好"。我们花了整整一周时间建了一套评估体系,后来所有优化排期都靠这套体系来驱动。
7.1 评测集的构建:从历史真实工单里"挖矿"
评测集是整个评估体系的地基。我们的做法是:从过去一年的真实取数工单里,按业务主题分桶抽样,人工标注标准SQL和预期结果,形成首批200条评测样本。每一批新迭代,再从最新反馈中抽取增量样本补充进去,保证评测集能反映真实需求变化。
有个细节是:评测集要分难度分层。我们把问题按"单表简单过滤、多表关联聚合、复杂条件嵌套、模糊意图澄清"四档打标。上线初期,要求前两档准确率必须达标,后两档允许转人工;成熟期再逐步提高后两档的通过率。这样既保证核心体验,又不被长尾问题拖死。
7.2 核心评估指标:语义准确率、执行可用率、澄清兜底率
我定义三个核心指标,会看板的团队可以直接抄作业。
第一个是语义准确率:模型生成的SQL是否忠实反映了用户意图,这个由标注人员人工判定,比较费人力,但必不可少。第二个是执行可用率:生成的SQL能否成功执行并返回非空结果,这个完全自动化计算,反映的是基础干活能力。第三个是澄清兜底率:系统正确识别自己不懂、并主动向用户澄清的比例,这个指标看着不起眼,其实决定体验下限。一个从不澄清、硬着头皮给错误答案的系统,比一个老老实实说"没听懂"的系统危险得多。
我们在内部定了一个及格线:语义准确率不低于85%、执行可用率不低于90%、澄清兜底率不低于30%。这三个指标综合起来,才能说明系统"既能干活、又知道边界"。
7.3 灰度上线阶段最容易踩的三个坑
作为收尾,我把上线初期踩过的坑浓缩成三条,供你参考。
坑一:全量开放给所有部门。正确做法是先挑一两个配合度高、需求简单的业务团队做种子用户,灰度跑两周,集中处理反馈。坑二:只评测SQL生成,不评测结果解读。用户最终看的是解读文本,解读如果撒谎,SQL再准也没用。结果解释模块一定要纳入评测范围。坑三:反馈闭环断了。用户点了"回答不满意"之后,后面没有人跟进分析为什么不满意、如何改进。我们后来专门建了一个反馈工单池,每周固定时间Review不满意样本,直接驱动Prompt和评测集迭代。
我个人在实际操作中最大的体会是:做智能化自助分析系统,真正难的不是模型调优,而是"让模型在确定的边界内发挥弹性"。模型能力会持续进步,但指标口径、权限边界、安全防线、评测方法这些确定性的东西,才是项目能否落地的根基。别指望一步到位,先用MVP满足Top 20的高频需求,让人力从重复取数中解放出来,再去扩大覆盖面,这个节奏最稳。