ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WrenAI实战:用语义模型让Text-to-SQL从Demo走向生产

WrenAI实战:用语义模型让Text-to-SQL从Demo走向生产 开源界的Text-to-SQL工具不少WrenAI是其中一个思路比较特别的。我第一次跑通它的时候第一反应不是“SQL生成得有多溜”而是“原来这个问题应该这样拆”。很多团队遇到过类似的情况拿到一个Text-to-SQL工具demo阶段惊艳全场真接进生产环境却没人敢用。WrenAI给我的感觉是它没有试图让大模型变成一个无所不知的SQL生成器而是先把数据仓库整理成一份“业务菜单”再让模型照着菜单回答问题。这篇文章我会从它的架构逻辑、本地部署、语义建模、实际查询效果到踩过的坑按真实使用顺序完整过一遍适合正在选型Text-to-SQL方案的数据工程师也适合想自己搭一套数据问答服务的开发者。1. Text-to-SQL为什么一直“看起来很美”问题不在生成SQL本身1.1 直接喂给大模型一段建表语句会发生什么把建表语句、字段注释直接塞给大模型让它写SQL这大概是很多人玩Text-to-SQL的第一步。坦白说这个方案在本地测试时效果相当唬人——它确实能写出语法正确的SQL复杂点的join也能给你拼出来甚至还能附赠一段“这段SQL的思路是……”的解释。但真正放到业务场景里问题很快就暴露了。先说最常见的一种字段歧义。比如订单表里有个“status”字段既有订单状态又有支付状态而业务同学问“上个月成功订单的数量”模型很可能把“status paid”和“status completed”混着用SQL生成的语法完全没问题但结果就是错的。这不是大模型笨而是它的上下文里只有物理表结构没有业务口径。再一个是表关系不明确导致的选错表。一张用户表、一张订单表、一张退款表问“哪些用户退货最多”模型有时候会直接把三张表join成笛卡尔积有时候会漏掉退款表单独用订单表硬算。数据量大一点这种查询跑起来就是几十秒起步最后拿到的数还是错的。权限问题也拦住了很多人。大模型写SQL需要看到完整表结构但生产环境里不是所有字段都适合让AI去自由发挥。物理表层面做行级权限控制很麻烦很多团队最后图省事只给AI一个只读账号敏感字段根本隔离不了。所以你看Text-to-SQL真正难的地方从来不是“把自然语言翻译成SQL语法”而是把自然语言里的业务概念准确映射到复杂、零散、充满历史包袱的物理模型上。直接跨过这层去做翻译demo越惊艳生产环境翻车越惨烈。1.2 WrenAI的解法让查询发生在“语义层”而不是“物理表”上WrenAI的思路和上面那种“裸奔式”方案不一样。它先让你构建一个语义模型——把一堆物理表抽象成业务模型而不是一上来就让大模型对着原始schema自由发挥。我用一个生活化的类比你去餐厅点菜如果服务员递给你一本全是原材料名的仓库清单比如“猪里脊500g、花生油20ml、豆瓣酱3勺”你大概率点不出想吃的菜。但如果你拿到的是“鱼香肉丝、麻婆豆腐、宫保鸡丁”这种菜单事情就简单多了。语义模型就是这份菜单物理表就是那份仓库清单。WrenAI做的事情是先把数据库整理成一份业务同学看得懂、AI也更容易处理的菜单再让大模型在这份菜单的范围内写SQL。这个设计带来的实际好处是大模型不再需要从几十个字段里猜“活跃用户”是什么意思语义模型里已经明确定义了“活跃用户 近30天有登录且完成过订单的用户”它也不需要从零推断用户表和订单表怎么关联语义模型里已经声明了关系和join条件。所以WrenAI的准确率不是靠某个特定大模型有多强而是靠预先建模把模糊空间压缩到足够小。这也是我后来对比其他方案时感受最深的一点它把Text-to-SQL从一个“纯AI问题”变成了一个“AI工程纪律问题”后者的成功率显然更可控。2. 拆开WrenAI看内部结构三个核心组件各管哪一段2.1 Wren Engine把表变成可以聊天的模型Wren Engine是整套系统里最核心的部分承担的是语义建模和数据源管理的脏活累活。你在UI上建模型、定义字段、配置关系背后都是它在处理。引擎本身理解起来不复杂它把你的数据源连接信息、表结构、语义模型定义统一管理起来对外暴露一套查询能力。当你向系统提问时它会根据问题先去匹配语义模型里最相关的模型和字段组合出上下文再交给AI服务生成SQL。这一步很关键因为它决定了AI能“看到”什么、不能“看到”什么。我特别喜欢它的一个细节定义模型时你可以给每个字段起一个业务名称、写一段描述甚至可以设置计算字段。比如原始表里只有“订单时间”和“订单金额”你可以在模型里直接定义一个“月销售额”指标AI在生成SQL时会优先使用这个定义好的计算方式而不是自己临时拼逻辑。关系管理也是Engine的核心职责。用户表和订单表是一对多订单表和退款表是一对一这些关系如果不定义清楚AI生成的join就全靠猜。我在使用中发现关系定义得越完整SQL准确率提升越明显这比换更强的模型还事半功倍。2.2 Wren AI服务让大模型在限定轨道里写SQLWren AI服务负责自然语言到SQL的生成与校验。它和直接调用OpenAI或本地模型写SQL的区别在于它前面接的是语义层的上下文而不是原始schema。从实际反馈看它做了一件很聪明的事在生成最终SQL之前会有一个“语义理解”阶段先把问题拆成“涉及哪些模型、哪些字段、需要什么过滤条件”然后再让LLM基于这些限定信息生成SQL。这样即使某个词有歧义模型也不会在整库范围内瞎找而是在已经定义好的语义模型里做选择。生成SQL之后WrenAI还会执行校验。它会先跑一条查询确认没有语法错误再决定要不要把结果返回给用户。我在测试中遇到过几次它给出的SQL其实不够优化的情况比如明明只查近7天数据却把整张表扫描了但至少语法层面和执行层面它比“裸聊”式方案要可靠得多。这里我多说一句WrenAI的AI服务本身是可配置的你可以接入不同的模型服务。我用过几种模型体感是模型能力越强在复杂语义理解上的表现越好但模型能力再强也弥补不了语义模型建得粗糙的问题。它和2.1里的Engine是两条腿缺一条都走不动。2.3 Wren UI建模、数据目录和问答合在一起的入口Wren UI在整个架构里很容易被当成“只是个界面”实际上它是工作流的中枢。你在UI里完成数据源配置、语义建模、测试问答平时的查询结果和管理界面也都在这里。建模界面做得很直观。你可以像在数据库管理工具里一样看到自己导入的各个表然后把它们拖成一个业务模型也可以直接写语义模型定义文件再导入。我第一次用的时候感觉上手成本非常低基本不需要看文档照着界面提示点就行。数据目录这部分值得单独提一下。模型建好之后每个字段的描述、类型、关联关系都整整齐齐列在那里业务同学可以自己进去看“这个指标到底是怎么算的”。这点在日常协作中特别加分——很多口径问题不需要再单独去问数据团队目录本身就是一份活的文档。问答界面则是业务同学日常使用的地方。输入一个自然语言问题系统会展示生成的SQL、执行结果、甚至数据预览图表。查询历史会被保存下来同一个问题下次直接就能看到结果不用反复跑。整体交互逻辑很像在和一个懂数据的同事聊天这种体验对非技术用户相当友好。3. 本地部署与第一次建模从零跑通一条查询3.1 一条docker compose命令拉起整套环境WrenAI的部署比我想象中简单很多官方仓库提供了一个完整的docker-compose编排一般会包含Engine、AI服务、UI以及元数据库这几个关键容器。本地跑测试环境基本就是# 拉取编排文件和配置 git clone 官方仓库地址 cd wrenai # 启动所有服务 docker compose up -d # 查看服务日志确认没有报错 docker compose logs -f等待容器状态都变成healthy之后打开浏览器访问UI对应的端口就能看到欢迎页面。第一次登录需要设置管理员账号接下来就能连数据源了。我建议第一次玩的时候不要用生产大库先准备一份几十万行以内的样例库比如订单、用户、商品三张表把流程跑通再说。有两点想提醒一下一是本机要预留足够的资源整体跑起来大概要吃4GB左右内存模型调用还会额外占用一些二是端口冲突问题如果你的机器上已经跑了别的服务记得提前改好映射否则启动阶段会报端口占用。这些细节在官方文档里都有但很多人不看直接启动然后卡在日志里才回头找。3.2 连接数据源类型、账号权限、时区这些细节数据源配置这一步决定了后面所有查询的稳定性和安全性建议认真对待。WrenAI支持包括PostgreSQL、MySQL、DuckDB在内的一批主流数据源UI上选择类型、填连接信息就行。连接账号的权限是一个容易埋坑的地方。虽然你也可以用一个高权限账号把整个实例给WrenAI但我强烈建议给WrenAI单独建一个只读账号并且只授予它访问本次建模涉及的schema的权限。因为AI在生成SQL时会做一些探索性的查询如果账号权限过大一个失误就可能导致全表扫描数据库直接被拖慢。最小权限原则在这里非常实用。时区这块我吃过亏。如果源库存的时间是UTC而业务同学在北京时间下提问生成的查询结果就会差八小时。WrenAI支持在数据源级别配置时区但很多人忽略了看到时间对不上第一反应是AI写错SQL回头查半天才发现是时区没配。做时间相关的查询之前先把数据源时区校准好这是经验之谈。schema数据库模式作用域也要注意。如果你的数据库里有多个schemaWrenAI默认可能只会识别到当前账号默认的schema其他schema里的表看不到。我遇到过明明表就在库里建模时却找不到的情况后来才发现是schema没选对。把这个配置项检查一遍能省很多排查时间。3.3 建模实操模型、关系和指标三件套连接好数据源之后就进入WrenAI最重要的一步语义建模。一个业务模型通常包含三个层次的配置模型Model、关系Relationship和指标Measure/计算字段。模型这一步最简单就是把物理表“翻译”成业务模型。比如订单表你可能不想暴露所有字段只需要保留订单ID、订单金额、订单时间、用户ID、订单状态这几个核心字段并且在模型里给每个字段配上业务描述。UI上直接操作就行也可以通过语义模型配置文件来写类似下面这种简化结构model: name: order table_reference: public.orders columns: - name: order_id description: 订单唯一编号 - name: order_amount description: 订单金额单位元 - name: created_at description: 下单时间 measures: - name: total_amount expression: SUM(order_amount) description: 订单总金额 - name: order_count expression: COUNT(order_id) description: 订单数量关系配置是AI生成join的关键。用户和订单是一对多订单和退款是一对一在UI里把关系类型和关联字段选好。特别是做跨表查询的时候关系定义错误会直接导致连错方向、结果翻倍或者变成笛卡尔积。计算字段这一层最花心思也最值钱。比如“客单价”如果表里没有现成字段你可以在模型里定义一个指标总销售额 / 订单数。“退款率”就是退款订单数 / 总订单数。把这些常用指标事先定义好AI在回答时就会优先使用而不是临时猜公式。建完模型之后在问答界面输入一句测试问题比如“上个月每天的订单总金额是多少”系统会给出SQL和结果。到这个阶段整套链路就算真正跑通了。我第一次跑通的时候最大的感受是原来每次回答背后都有清晰的语义依据而不是模型凭空猜。4. 用真实问题检验效果哪些查询很稳哪些会翻车4.1 基础聚合和多表关联稳定区域我拿自己搭的一套模拟零售数据做了两轮测试数据大概覆盖了用户、订单、商品、退款四张表规模在几十万行左右。第一轮是基础查询——单表聚合、分组、简单时间过滤这类问题WrenAI的表现非常稳定。比如我问“上个月各商品类别的销售额Top10”它生成的SQL会用商品表join订单明细表按类别分组排序并限制到前10整条SQL逻辑完全没问题结果也正确。再比如“最近一周每天的订单量”这类纯单表聚合基本秒出结果连多问几句都不会出错。这个阶段我觉得是WrenAI使用价值最清晰的地方。高频取数里有一大批就是这类“不会错但这辈子重复无数遍”的固定问题业务同学自己提问拿到结果就省去了排队等数据团队写SQL的时间。基础问题稳定是最基本的要求也是它做得让我放心的地方。4.2 多表关联和时间趋势有时候会绕弯能力边界在第二轮测试里就出现了。遇到更复杂的多表关联和时间趋势分析生成的SQL经常“逻辑对但不够聪明”需要人工介入才能得到最优方案。举个例子我问“今年每个月客单价的变化趋势”它需要关联订单和用户两张表然后用订单金额除以订单数算出客单价再按月分组。WrenAI生成的SQL结果是正确的但join的顺序和过滤条件下推做得不够好可能先join完所有数据再按月过滤而不是先按月收缩再join导致查询跑得偏慢。再比如“对比上个月和上上个月的销售额计算环比增长率”这种涉及自关联子查询的复杂问题它偶尔会生成一段管状子查询嵌套过多、可读性很差的SQL。执行层面不会报错但维护起来很痛苦。我的经验是真正复杂的报表指标与其靠AI临场发挥不如在语义模型里预置成计算字段让AI只做“取数”而不是“推导口径”。4.3 业务口径的“隐藏规则”需要在语义模型里提前说清楚第三轮测试我没有用标准问题而是故意模拟真实业务里最常见的“口语化提问”。比如我问“上个月的成功退款情况怎么样”这里“成功退款”到底是指“退款申请通过”还是“退款已经到账”如果不在语义模型里做区分AI大概率会直接选一个它认为合理的解释然后很自信地给你一个结果。这个现象非常典型。业务同学问问题的时候默认对方和自己共享同一个世界里的口径。但AI没有这个默认它只能从语义模型给出的描述里挑最可能的意思。我在另一个场景里问“最近的活跃用户数”如果没有提前定义“活跃”它可能用“30天内有登录”算也可能用“30天内有订单”算两个结果都不一样。所以我在用了两轮之后把WrenAI使用铁律总结成一句语义模型里的每一个模糊概念都会在回答时变成一次随机猜测。想要稳定可靠就得把这些概念提前固化下来。这个工作没有捷径但一遍做扎实后面所有提问都受益。它能把你对口径的管理从“人脑”转移到“系统”这正是它区别于普通AI问答工具的核心价值。5. 使用一段时间后的经验和踩坑记录5.1 元数据刷新与模型变更的节奏用得久了最常踩的坑反而不是SQL生成而是数据源表结构变化后没有同步到语义模型。源库加了新字段、删除旧字段、调整了字段类型WrenAI不会自动感知它仍按照旧模型来回答导致明明数据已经变化查询结果还是老样子。我建议把元数据刷新纳入固定的维护节奏。每周定时去检查一次数据源结构有没有变化如果有就在WrenAI里同步更新对应模型和关系配置。模型字段如果改名了旧的保存问题会直接失效记得顺手清理或更新避免业务同学点了历史记录才发现无结果。模型变更本身也建议走版本化思路。我在本地维护了一份语义模型定义文件每次改动都先备份再改因为UI上直接改虽然方便但改错了想回退比较麻烦。养成“改前备份、改后测试”的习惯能少踩很多坑。5.2 限制返回行数和执行超时别让AI替你“烧钱”AI生成的SQL有一个特点它在执行前不会自动预判查询代价。一个毫不起眼的问题比如“统计所有商品的销量”如果商品表有几百万行而且没建索引这条查询可能直接拖垮线上数据库。WrenAI的语义层可以帮你在模型层面限制返回行数配置execution limit和timeout这一步别省。我第一个月用的时候没有做限制某次测试生成了一条全表join的SQL直接把本地库跑满了。从那以后我养成了几个习惯给源库账号只读权限在模型层设置单次查询的最大返回行数配置执行超时时间重要的数据源尽量指向只读副本而不是生产主库。这些措施不是不信任AI而是当成安全护栏来对待。另外一个经验是别把WrenAI直接暴露给整个公司而不加审计。查询历史要定期看把那些特别快、特别准的高频问题固定成“推荐问题”把总是出错的问题找出来反向改进语义模型。它不只是一个查询工具更是一个逐步沉淀数据口径的系统。5.3 我对WrenAI的定位判断它是团队的数据放大器不是查询替代者用了几个月之后我对WrenAI的定位有了清晰的结论它适合当一个团队的自助式数据问答加速器而不是取代数据分析师的神器。它最大的价值是把大量重复高频、口径清晰的取数请求从数据团队手里接走让业务同学自己就能拿到确定性答案这是实打实的效率提升。它也有明确的适用边界。那些真正复杂的专项分析、需要业务判断才能定义的口径、或者要从多套数据源里临时组合数据的研究型问题目前还得靠人来主导。这也是我建议大家理性看待任何Text-to-SQL工具的原因——它放大的是你已经建模清楚的那部分能力而不是凭空解决所有数据问题。我个人现在的工作方式是把它当成团队数据基础设施的一部分数据团队维护好模型和指标业务同学通过自然语言自助取数。每次新业务上线前先花几天把语义模型建好后面就能持续稳定地输出答案。如果你的团队也经常被困在重复取数和口径争议里WrenAI值得认真试一轮但记住一条打磨好语义模型是它真正好用的前提。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进