WrenAI
Canner/WrenAI项目目录
项目简介
WrenAI 为现有 Agent 提供业务语义层与查询能力,当前由 Python SDK、CLI 和 Rust 查询引擎组成。指标、维度、模型关系和业务说明保存在可审阅文件中,查询经模型抽取和 SQL 重写后交给目标数据源执行。
如何写进简历
数衡 · 订阅业务经营指标服务 | 场景化示例
项目描述:基于 WrenAI,供订阅业务运营与财务分析人员复用订阅实收、续费和退款口径,为 Agent 与报表提供语义查询服务。
技术栈:Python、Rust、Apache DataFusion、sqlglot、PyArrow
主要工作:
- 订阅指标建模与编译:基于
WrenAI将订阅实收、到期客户续费率和退款的粒度、度量与关联写入YAML模型,编译为MDL,供 Agent 与报表复用同一套指标定义。 - 语义 SQL 规划与改写:基于
WrenEngine接入语义查询,按SQL引用抽取最小模型,经Rust/DataFusion规划、CTE和目标方言改写生成数据库SQL,连接指标定义与底层执行。 - 查询范围与执行验证:将只读检查、
strict_mode模型范围限制与数据源dry_run分开接入查询入口,配合dry_plan暴露目标SQL,便于定位关联和聚合偏差。
面试题与回答
Text-to-SQL 如何区分表结构与业务规则知识,并适应表结构变更?
展开收起
核心回答
WrenAI把物理结构与业务语义通过逻辑模型连接起来:数据库表列说明数据存在哪里,MDL 语义模型说明对外使用什么名称、怎样关联和计算。 当前架构由PythonSDK/CLI 加载模型、组织查询并连接数据源,Rust语义引擎基于Apache DataFusion参与规划,将模型引用展开为目标数据库可执行的SQL。表结构改变后,更新物理映射和受影响的字段、关系或度量定义,重新构建 manifest,再让查询调用方使用新版本。仅改数据库,或者只改一段业务说明,都不会自动完成这条更新链;编译和查询可执行也不等于业务口径正确。
YAML 中哪些是结构,哪些是业务规则
项目把模型、关系、视图和聚合定义保存为可审阅的
YAML与SQL文件:- 模型与字段:
metadata.yml声明逻辑字段、类型和主键等信息,ref_sql.sql将逻辑模型映射到物理表或查询。类型是结构事实,对外名称、描述和计算表达式则带有语义选择。 - 度量与维度: cube 的
measures定义聚合或计算,dimensions定义按什么字段分组。它们明确“怎样算、按什么粒度看”,并非从字段类型自动推导。 - 关系:
relationships.yml保存模型间的连接条件及join_type,让可复用关系进入规划,而不是每次让模型仅凭同名字段猜连接。
原仓库的示例很具体:逻辑模型
orders通过ref_sql.sql读取public.raw_orders,声明每行是一笔订单;order_metrics以orders为base_object,定义SUM(amount)和COUNT(*),以customer_id为维度。orders_customer则声明订单到客户的MANY_TO_ONE关系。这里金额如何聚合、按谁分组、使用哪个连接条件,都是明确维护的定义。见示例度量与维度。cube 在这条链中是逻辑聚合定义:查询先转换为普通
SQL,再交给引擎执行,不意味着已经自动生成物化聚合表。类似地,声明MANY_TO_ONE也不证明客户键在实际数据中唯一。关系粒度或数据质量不符合声明时,仍可能重复加总。定义怎样进入查询规划
加载器读取模型目录的
metadata.yml,合并同目录的ref_sql.sql,并加载关系、视图与 cube。wren context build默认先检查项目结构,再调用build_json(),将定义转成引擎使用的命名形式并写入target/mdl.json。它不是把原始 YAML 直接塞进提示词。见模型加载与构建入口。查询时,
WrenEngine.dry_plan()解析对逻辑模型的引用,提取相关 manifest,借助语义引擎与改写器展开模型和视图,生成目标方言的SQL;query()再通过对应连接器执行。cube 查询也先由cube_query_to_sql()生成基于其base_object的查询,再走相同引擎链。因此,业务定义不只作为自然语言提醒,还会参与实际计算与连接的规划。Rust 侧能够检查部分模型依赖,例如 cube 基对象是否存在、派生度量是否成环、层级是否引用已声明维度;但“退款是否应该扣除”“金额单位是否一致”仍需先由业务定义并用数据核对。
结构变化后怎样更新
如果物理字段从
amount改为gross_amount,而含义和单位不变,可以修改模型映射,例如用gross_amount AS amount继续暴露逻辑字段amount。这样,下游已确认的度量可以继续引用逻辑字段。若新字段已经变成含税金额或统计范围改变,就不能只加别名维持表面兼容,还要修改度量、说明及相关查询。更新过程需要区分三件事:
- 修改源定义。 检查物理映射、列类型、连接条件、指标公式和业务说明的影响范围。
- 重新构建与索引。 构建新的
target/mdl.json;使用语义检索时,按项目工作流更新相应 memory 索引,使 Agent 读取的模型说明与执行定义一致。 - 让调用方加载新定义。 直接创建的
WrenEngine保存传入的manifest_str,不会因为 YAML 改动自动热更新。当前LangChainSDK 的ProjectMDLSource则在加载时重读target/mdl.json,工具调用据此创建引擎,因此外部构建的新文件可在后续调用中被拾取。不能把“改文件”“构建完成”“调用已使用新版本”视为同一时刻。见SDK 的 manifest 读取。
验证时先看规划后的物理
SQL是否使用新字段与预期关系,再用固定数据检查总额、分组、空值和多表连接结果。还应保留一个会发生一对多放大的样例,核对聚合粒度。物理映射保持兼容,可以降低结构变化对上层查询的影响;业务口径本身改变时,则需要明确版本和生效范围,不能由兼容映射替业务做决定。参考资料
- 国内:飞致云:SQLBot 最佳实践:参考结构说明、人工关系和业务术语分别维护的方式,解释仅有数据库表列不足以确定计算与连接口径。
- 国内:阿里云 Quick BI:数据准备:采用字段描述、数据形态和业务定义需要分别准备的分析,说明物理类型与分析语义不能混同。
- 国内:阿里云 Quick BI:问数知识管理:依据业务定义、同义词和生效范围的分工,说明结构兼容不等于口径兼容,业务变化仍需明确适用范围。
- 国内:NineData:Text2SQL / Chat2SQL:参考生成结果要核对对象、枚举、关联与聚合的要求,组织变更后的结果验证。
- 国外:AWS 与 Cisco:Enterprise-grade natural language to SQL generation using LLMs:采用用稳定结构抽象复杂物理关系、同时补充领域规则的思路,解释逻辑模型与物理映射的分工。
- 国外:dbt Labs:Semantic models:参考实体、维度和聚合定义共同构成语义层的分工;WrenAI 的实际字段、加载和规划方式以固定源码为准。
- 模型与字段:
Text-to-SQL 在 Agent 中如何应用?存在哪些风险和限制?
展开收起
核心回答
WrenAI可以作为现有 Agent 的语义查询层:Agent 根据问题和业务上下文,编写引用MDL模型的SQL;Python SDK负责策略检查、规划与连接器执行,Rust引擎负责展开模型、计算字段及关联语义。模型中维护的指标与关系可以复用,Agent 不必每次从物理表重新猜测口径。这条链路要区分三种正确性:查询符合执行策略、模型定义展开正确、最终统计符合业务要求。 只读检查和模型对象限制主要约束前一项;语义模型帮助稳定计算定义,但不能证明模型定义本身、Agent 选择的日期条件或聚合粒度一定正确。
Agent 怎样把问题交给语义层
当前代码提供
SDK、CLI及 Agent 工具集成。以LangChain集成为例,WrenToolkit暴露列出模型、规划和查询等工具;启用项目记忆后,还能读取相关结构与业务上下文、召回已确认的查询示例。Agent 据此写引用模型名称和字段的SQL,交给wren_query,而不是把自然语言直接交给WrenEngine自动完成全部推理。工具集成。例如问“本月订阅实收净额”,应先确定订阅付款、退款及其时间归属,再选相应模型和指标。业务说明提供理解依据,
MDL中可执行的表达式承接已建模的计算;仅在说明文件里写一句口径,不等于查询引擎已经强制实现了该口径。只读与严格模式分别限制什么
WrenEngine._plan()用目标数据库方言解析输入,调用validate_sql_policy()。其中只读检查始终执行:除检查查询根节点外,还遍历整棵语法树,拒绝写入、建表、修改会话状态、SELECT INTO、写意图锁等节点,因此不会只看开头是不是SELECT。规划入口、只读检查。strict_mode默认是False。开启后,额外检查表引用是否对应MDL中的模型或视图,并检查已知的数据读取型函数,防止通过文件、外部数据库等入口绕过模型范围;denied_functions则是另行配置的函数禁用集合。这些策略不能等同于任意函数和方言都安全的沙箱,模型对象检查也不能替代按用户配置的行列权限和数据库授权。默认配置、严格模式与函数检查。模型怎样展开成数据库查询
策略检查后,程序解析查询引用的模型或视图,抽取所需的
MDL;抽取还会保留关联模型、视图依赖及相关规则依赖。CTERewriter识别实际使用的模型和列,调用经PyO3暴露的Rust语义引擎展开模型定义,将结果作为CTE注入查询,再输出目标数据库方言。模型抽取、CTE 重写、逐模型展开。策略错误会直接抛出,不会靠回退绕过。模型抽取遇到普通异常时,仅在未开启严格模式且没有函数禁用配置的情况下,才允许改用完整模型继续规划。改写后还会调用
validate_planned_sql()检查展开内容中的写操作;但该函数遇到输出重解析失败会直接返回,不能把它描述为所有方言下都严格拒绝不确定输出的完整安全证明。抽取回退与输出检查、输出重解析边界。dry_plan()返回展开后的查询,不访问数据库;dry_run()再调用连接器做数据库侧检查;query()才执行并返回PyArrow表。例如当前MySQL连接器的dry_run()使用普通EXPLAIN,它不会验证业务统计值是否正确,也不等同于真正运行查询测量耗时。三个入口、MySQL 检查方式。通过规划后为什么仍可能算错
模型展开不会自动纠正 Agent 在外层查询中写错的连接条件、过滤条件或聚合。假设一笔订阅付款有两笔退款,直接连接后累加付款金额,可能重复计算实收;即使引用的模型都合法、查询成功执行,订阅实收净额仍然错误。
业务集成应明确订阅付款与退款粒度、日期归属和空值处理,必要时先按各自粒度聚合,再按确定的键组合。用一对多退款、无退款、跨月退款等样本与参考查询比较,检查最终数值;语义模型变更后也复查这些样本。这是验证设计,不是已经取得的效果数据。
生产执行还要依据真实用户配置访问范围,限制查询时间、并发及返回结果。只读查询仍可能读取敏感数据或进行昂贵扫描;
dry_plan()成功、模型名称合法、数据库能够解析,都不能替代这些运行约束与业务验收。