跳转至

Apache Ossie 是什么?为什么用 Dosi?

Apache Ossie(原名 Open Semantic Interchange,简称 OSI)是一个开放规范,用于在 BI 工具、AI Agent 和数据平台之间交换语义模型、指标、维度和业务上下文。

一份 Ossie 模型就是一个普通 YAML 文件。指标在里面定义一次,所有支持 Ossie 的工具 读的都是这同一份定义,不再各自重写。Dosi 是首个 Apache Ossie 原生的语义层引擎:加载 Ossie 模型,按上游规范校验,再把指标请求编译成 16 种数仓方言各自正确的 SQL。

Apache Ossie 速览

是什么 开放、厂商中立的语义模型规范
原名 Open Semantic Interchange(OSI)
格式 YAML(或 JSON)
描述内容 数据集、关系、字段与维度、指标,以及各对象上的 AI 上下文
交给引擎的部分 怎样把指标变成某个数仓上正确的 SQL
上游 ossie.apache.org 与 apache/ossie:核心规范、参考校验器、格式转换器
本站的引擎 Dosi:校验、编译并执行 Ossie 模型

问题:人人都在重新定义"营收"

从头写一句"8 月营收"的 SQL,至少有三处会出错:JOIN(营收在哪张表,连上 以后会不会扇出、把总数悄悄翻倍)、维度(按下单日期还是发货日期,算毛额 还是净额,日粒度还是月粒度)、表达式(是 SUM(amount),还是从明细表算 SUM(unit_price * quantity)——却漏了折扣列,还是要用 SUM(CASE WHEN status != 'refunded' THEN amount ELSE 0 END) 才能扣掉退款)。 任何一处出错,算出来的数字看着照样像那么回事。

这正是 BI 看板、财务报表、手写查询算出三个不同"营收"的原因:一处过滤了 已取消订单,另一处没过滤,第三处 JOIN 了一张表、把总额悄悄翻倍。AI Agent 拿着裸表名写查询,也会犯一模一样的三种错误,只是更快、更有把握。结果大家 都熟悉:两个都"正确"却对不上的数字,外加一场"该信哪个"的会。

语义模型的解法是把每个指标只定义一次:revenue 是什么意思、来自哪张表、 表与表如何关联,作为唯一事实来源。之后所有查询都从这份定义派生,不再各写各的。 Apache Ossie 就是把这份模型写下来的一种开放、标准的方式。

Apache Ossie 模型长什么样

模型里列出数据集(表)及其字段、表与表之间的关系,以及用普通 SQL 表达式 写成的指标。下面这份是完整、合法的模型:

version: "0.2.0.dev0"

semantic_model:
  - name: orders_model
    description: Orders, the customers who placed them, and revenue
    datasets:
      - name: orders
        source: main.orders
        primary_key: [order_id]
        fields:
          - name: order_id
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: order_id
          - name: customer_id
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: customer_id
          - name: order_date
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: order_date
            dimension:
              is_time: true
          - name: amount
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: amount
      - name: customers
        source: main.customers
        primary_key: [customer_id]
        fields:
          - name: customer_id
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: customer_id
          - name: region
            expression:
              dialects:
                - dialect: ANSI_SQL
                  expression: region

    relationships:
      - name: orders_to_customers
        from: orders
        to: customers
        from_columns: [customer_id]
        to_columns: [customer_id]

    metrics:
      - name: revenue
        description: Total order amount
        expression:
          dialects:
            - dialect: ANSI_SQL
              expression: SUM(orders.amount)
        ai_context:
          synonyms: [sales, order revenue]
  • 数据集与字段:对应物理表,以及可以分组、过滤的列;dimension: {is_time: true} 标记时间维度。
  • 关系:说明表怎么 JOIN,消费方不用再猜关联键。
  • 指标:基于这些字段的 SQL 聚合表达式,只写一次。
  • ai_context:业务上下文(同义词、说明、示例),给读模型的 AI Agent 用。

Ossie 描述的是指标是什么。至于怎么把它们变成某个数仓的正确 SQL,规范刻意不管。 这正是 Dosi 要补的那块。

是交换标准,不只是一种格式

原名里的 Interchange(交换)依然是关键:上游 Apache Ossie 项目维护着 Ossie 与其他 语义层格式之间的双向转换器 —— dbt、Snowflake、Databricks、Salesforce/Tableau、GoodData、Honeydew、Omni 等。一个中立的枢纽,省掉格式之间两两点对点的迁移。

Dosi 直接读取标准 Ossie 模型,只要指标是 SQL 表达式(见 S-EXPR-1), 转换器的产出拿来就能用:模型今天可以从 dbt 转进来, 明天再转去 Snowflake,指标定义不会被扣在任何一家的格式里。定义一次,自由迁移。

Dosi 与 Apache Ossie 的关系

Apache Ossie 是契约,Dosi 是执行它的引擎。Dosi 没有自己的模型格式:输入就是 Ossie 模型;它额外提供的能力,要么发生在查询时,要么放在规范自带的扩展点里。

Apache Ossie 定义 Dosi 补充
数据集、关系、字段,以及用 SQL 表达式写的指标 带扇出保护的 JOIN 规划,16 种方言的 SQL
参考校验器 dosi validate --osi-basic 在自身检查之外一并运行它
custom_extensions,规范的厂商扩展点 Datus 扩展(窗口、派生指标、JOIN 类型、空值填充、时间粒度)就放在里面
每个对象上的 AI 上下文 原生 MCP 服务,把模型提供给 Agent,错误结构化
与其他格式互转的转换器 指标为 SQL 的转换结果原样作为输入

Dosi 有两种模式,每次调用时选择:

  • 基础模式(--osi-basic):严格的标准 Apache Ossie,模型的含义与规范完全一致; Datus 扩展忽略并给出警告。
  • Datus 模式(--osi-datus,默认):Apache Ossie 加上 Datus 扩展。

Datus 扩展都放在 custom_extensions 里,不会让模型变得不合法:其他 Ossie 工具照样 能读,不认识的扩展直接跳过。但 Datus 模式有一点比规范宽松:它还接受 POSTGRESQL 这类核心规范没有定义的厂商 SQL 方言标签。模型需要给其他 Ossie 工具使用时,请用 dosi validate --osi-basic 校验。

Dosi 由 Datus 开发,实现了 Apache Ossie 规范,但本身不是 Apache 软件基金会的项目。

Dosi 做什么

Dosi 是一个小巧快速的 Rust 引擎,读一份纯 Apache Ossie 模型,把一次指标请求变成对应数仓的 正确 SQL,还可以顺手执行:

flowchart LR
    A["你的 Apache Ossie 模型<br/><small>指标只定义一次</small>"] --> B["Dosi"]
    B --> C["正确的 SQL<br/><small>面向你的数仓</small>"]
    C --> D["结果"]

给出一个指标、几个分组维度、一种数仓方言,剩下的 JOIN、聚合,以及那个方言特有的 SQL 写法,都由 Dosi 算出来。同一份模型可以为 16 种方言生成正确 SQL: DuckDB、Postgres、MySQL、ClickHouse、Snowflake、BigQuery、StarRocks、Trino、 Databricks、Oracle 等。换数仓改一个参数,指标定义不动。

用法有四种:命令行工具、REST + Arrow 服务、面向 AI Agent 的原生 MCP 服务、Python 绑定。底下是同一个引擎,给出同样的答案。

AI Agent 需要指标体系回答什么

当指标的消费者从看板前的人变成 AI Agent,过去靠分析师经验拿捏的问题, 指标层必须给出明确答案。每个指标有四个属性,决定了它能被怎样安全地使用:

  • 可加(additive) —— 能否沿某个维度直接求和,还是一加就重复计数 (去重计数、比率)?
  • 可上卷(rollable) —— 日粒度的值能否直接卷成月值,还是必须从明细重算?
  • 可预测(predictable) —— 是可以外推预测的流量,还是外推毫无意义的存量?
  • 无扇出(fan-out-safe) —— JOIN 会不会让行数翻倍、总数虚高?

Dosi 把这些当作引擎必须掌握的属性,而不是分析师脑子里的约定。目前它会推断 每个指标的类型(聚合型 / 比率型 / 表达式型),并且绝不悄悄重复计数: 当一个指标要 JOIN 不同明细层级的表 —— 也就是会把营收翻倍的经典扇出 —— 要么各部分在各自粒度上正确计算,要么带着结构化错误停下。 它不会把一个虚高的总数交给你。

按指标类型内置归因:Agent 问"营收为什么跌了", 一次调用即可得到贡献拆解,由引擎用与该指标代数精确匹配的算法算出 —— 可加指标走维度归因,比率指标走 LMDI 结构/率值分解 —— 推理过程可观察、 可核对,而不是幻觉。

在生成侧,datus-agent(Datus 自家的开源数据 Agent)是目前最好的一方选择:它能直接产出符合 Dosi 规范、带 Datus 扩展的 Apache Ossie YAML —— 扩展里有更高级的窗口与 派生指标,以及比基础规范更完整的 JOIN 关系检测。消费侧保持开放:任何 Agent 都能通过 MCP 服务查询,比如 Claude Code。

这些数字凭什么可信

除了上面的指标代数,引擎还挡住了日常制造"对不上的数字"的错误:

  • 时间范围没有歧义。 2024 年 1 月 表示 [2024-01-01, 2025-01-01), 含起始、不含结束,月份边界上的天数不会被算两次。
  • 错误会告诉你怎么修。 指标名写错,换来的不是一段堆栈,而是一条列出最接近选项的 提示。--format json 下每个错误都带稳定错误码和修复建议,自动化工具和 AI Agent 据此就能自我纠正。

这些保证都精确写进了一份规范性契约,见语义契约。 不读也能用 Dosi;想确切知道引擎承诺了什么,再去查也不迟。

适合谁

  • 分析与数据工程师 —— 想要一份指标定义,在每个数仓、每个消费端都保持正确。
  • 正在迁移或多数仓并存的团队 —— 不愿为每个引擎重写一遍指标 SQL。
  • 以 Apache Ossie 为标准的团队 —— 需要一个直接运行现有模型的引擎,中间不夹私有格式。
  • 数据应用与 AI Agent 的开发者 —— 需要一个带结构化、机器可读错误的指标 API, 而不是自由格式的 SQL。

常见问题

OSI 和 Apache Ossie 是一回事吗?

是。Open Semantic Interchange(OSI)是这个规范的旧名,Apache Ossie 是现在的名字。 写着 OSI 的模型、工具和文档,指的都是同一个规范。

Dosi 能用任意 Apache Ossie 模型吗?

指标用 SQL 写的模型都能用。Ossie 还允许 MDX、TABLEAU、MAQL 表达式,但 Dosi 只编译 SQL:没有任何 SQL 写法的指标会报编译错误,字段则只给警告(见 S-EXPR-1)。dosi validate --osi-basic 除了 Dosi 自己的规则, 还会用上游 Apache Ossie 校验器检查一遍。Datus 扩展是可选的。

为什么 Dosi 的一些参数还叫 osi?

--osi-basic、--osi-datus 以及几个环境变量是在更名之前命名的。保留原名, 现有脚本和配置才能继续用。

下一步