主要技术:Apache DataFusion、sqlglot、PyArrow
项目目录
项目简介
WrenAI 为现有 Agent 提供业务语义层和查询引擎。用户可以在熟悉的 Agent 中提出经营问题,由 Agent 利用模型与业务上下文编写 SQL,再通过 Python SDK 和 Rust 语义引擎展开、执行,返回数据结果。
仓库提供 jaffle_shop 示例:订单模型映射到原始订单表,度量定义订单金额与笔数,关系描述订单怎样关联客户。围绕“哪些客户的销售额最高”一类问题,Agent 可引用逻辑模型查询;同一份指标和关系定义可以供多次问数复用。
README 另提供 GenBI 技能指南,由已有 Agent 在查询结果上继续构建可筛选、可分享的仪表盘,并部署到使用者自己的托管账号。
项目亮点
- 可执行的业务语义模型: 以
YAML和SQL文件维护模型、字段、关系、度量与维度,编译为MDL。业务口径参与查询规划,而不只是写在模型提示词里。 - 按依赖展开模型与方言:
WrenEngine从查询提取相关模型和视图,借助Rust/DataFusion规划,再由sqlglot注入CTE、生成目标数据库方言,连接逻辑模型与物理表。 - 规划与执行分层检查: 输入查询经过只读策略;显式启用
strict_mode后另限制模型范围。dry_plan返回展开语句,dry_run委托连接器检查,query执行并返回PyArrow数据表。 - 模型构建与 Agent 工具衔接: 项目定义构建为
target/mdl.json,LangChain工具在后续调用中重读产物并创建查询引擎;可选 memory 提供结构说明与查询示例,辅助 Agent 选择模型和写法。
技术架构
Python 层负责加载项目定义、构建 MDL、组织工具和访问数据源。查询引用的模型经过依赖抽取,通过 PyO3 连接 Rust 引擎,利用 Apache DataFusion 展开语义,再输出目标方言 SQL。原生连接器执行查询,结果以 PyArrow 表返回调用方。
Agent 集成负责把自然语言问题变成模型查询,语义引擎负责展开已维护的定义。当前代码提供 SDK、CLI 与工具集成;已声明的关系和度量仍需用业务数据核对粒度与计算结果。
| 代码位置 | 主要职责 |
|---|---|
examples/v5-jaffle/ |
展示模型、物理映射、关系及度量维度的项目结构 |
core/wren/src/wren/context.py、core/wren/src/wren/context_cli.py |
读取与校验项目定义,生成 MDL 构建产物 |
core/wren/src/wren/engine.py、core/wren/src/wren/mdl/cte_rewriter.py |
执行查询策略、依赖抽取和目标方言改写 |
core/wren-core/、core/wren-core-py/ |
语义规划与面向 Python 的 Rust 绑定 |
sdk/wren-langchain/、core/wren/src/wren/connector/ |
向 Agent 提供工具,并连接实际数据源 |
如何写进简历
订阅罗盘 · 订阅经营指标服务 | 订阅软件服务场景示例
项目描述:面向订阅业务运营团队的经营指标服务,统一实收净额、到期客户批次和续费指标的查询口径,返回计算依据与所用模型版本。
技术栈:Python、Rust、Apache DataFusion、sqlglot、PyArrow
主要工作:
- MDL 指标建模与退款粒度:以
YAML、ref_sql将订阅、收款及退款编译为MDL,保留退款发生期和原支付单,在各自统计粒度汇总后关联;分别表达本期现金净额与收款批次净额,避免多次退款放大金额或丢失跨期归属。 - Cube 聚合与到期批次分析:以
Cube的度量、维度和时间字段组织套餐、期间聚合,由CubeQuery生成语义查询;到期客户按合同沿袭关系归入固定批次,结合宽限期与观察截止日统计续费,未成熟批次单列。 - Python 与 Rust 协同语义规划:通过
PythonSDK 调用Rust/DataFusion规划器,按查询抽取模型及关系依赖,再由sqlglot注入CTE并生成目标数据库SQL,使不同订阅查询复用同一份指标定义。 - 受限查询与执行阶段诊断:直接构造
WrenEngine并传入WrenConfig(strict_mode=True),限定可查询模型并执行只读检查;以dry_plan和dry_run分别检查语义展开与数据库兼容性,随结果保留指标版本和统计截止日。
指标易算 · 生产质量指标服务 | 工业制造场景示例
项目描述:面向制造企业质量分析人员的指标服务,分别统计批次、抽检样本和缺陷事件,按生产时点的产线及产品归属比较首检、复检与缺陷分布。
技术栈:Python、Rust、Apache DataFusion、sqlglot、PyArrow
主要工作:
- 质量事实粒度与语义模型:将批次、抽检样本和缺陷事件建成独立
MDL模型,样本合格率按完成检测的唯一样本计算,缺陷分布按事件计数;先归并多缺陷记录再关联批次,避免关联扇出改变统计分母。 - 复检语义与跨模型查询规划:通过
ref_sql区分首检和截止时点最后有效结果,查询时由Rust/DataFusion抽取相关模型与关系,经CTE改写生成目标方言SQL,将返工复检规则落实到可展开的计算路径。 - 模型构建与版本化查询重建:将产线、产品历史归属及质量口径写入
YAML/SQL模型并构建MDL;通过 SDK 后续调用重读模型、创建引擎,以dry_plan核对变更后的字段和关联,查询返回所用版本,减少旧口径快照沿用。
面试题与回答
2 题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()成功、模型名称合法、数据库能够解析,都不能替代这些运行约束与业务验收。