WrenAI

为现有 Agent 提供业务语义和查询引擎
反馈(在 GitHub 新标签页打开)

主要技术: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 协同语义规划:通过 Python SDK 调用 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 题
  1. Text-to-SQL 如何区分表结构与业务规则知识,并适应表结构变更?

    展开收起

    核心回答

    WrenAI 把物理结构与业务语义通过逻辑模型连接起来:数据库表列说明数据存在哪里,MDL 语义模型说明对外使用什么名称、怎样关联和计算。 当前架构由 Python SDK/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。这样,下游已确认的度量可以继续引用逻辑字段。若新字段已经变成含税金额或统计范围改变,就不能只加别名维持表面兼容,还要修改度量、说明及相关查询。

    更新过程需要区分三件事:

    1. 修改源定义。 检查物理映射、列类型、连接条件、指标公式和业务说明的影响范围。
    2. 重新构建与索引。 构建新的 target/mdl.json;使用语义检索时,按项目工作流更新相应 memory 索引,使 Agent 读取的模型说明与执行定义一致。
    3. 让调用方加载新定义。 直接创建的 WrenEngine 保存传入的 manifest_str,不会因为 YAML 改动自动热更新。当前 LangChain SDK 的 ProjectMDLSource 则在加载时重读 target/mdl.json,工具调用据此创建引擎,因此外部构建的新文件可在后续调用中被拾取。不能把“改文件”“构建完成”“调用已使用新版本”视为同一时刻。见SDK 的 manifest 读取。

    验证时先看规划后的物理 SQL 是否使用新字段与预期关系,再用固定数据检查总额、分组、空值和多表连接结果。还应保留一个会发生一对多放大的样例,核对聚合粒度。物理映射保持兼容,可以降低结构变化对上层查询的影响;业务口径本身改变时,则需要明确版本和生效范围,不能由兼容映射替业务做决定。

    参考资料

  2. 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() 成功、模型名称合法、数据库能够解析,都不能替代这些运行约束与业务验收。