不接全库:用 Codex 为 Wide World Importers 创建第一版 AI 问数语义层

85 阅读7分钟

把数据库交给 Codex 后,生成一条能运行的 SQL 并不难。真正麻烦的是:销售额该取哪个字段,时间按下单还是开票,结果又该拿什么核对。

上一篇《从0开始,一句话启动AI DataAgent》用内置 SQLite 样例跑通了安装和 MCP 调用。这一次,我把 Microsoft Wide World Importers 恢复到本机 SQL Server,让 Codex 从销售分析开始建立语义层。

先把这段话交给 Codex

这里默认已经按照上一篇启动了本地 DataAgent,并把 Wide World Importers 恢复到本机 SQL Server,准备好只读连接。

第一次开始时,不需要先查清所有表名、字段名和指标口径。我给 Codex 的任务只有这些:

请使用 foggy-ai-analysis Skill,连接我本机 Docker SQL Server 中的 WideWorldImportersDW 只读 datasource。

第一版先从销售分析开始,不接全库。我希望能查询销售趋势、客户排名、商品利润、库存情况,以及订单金额和开票销售额的差异。

请先检查相关表结构和数据范围,列出需要我确认的问题。确认后再建立第一版语义模型,并准备一组可以核对结果的题库。

这段任务只提供数据库、分析方向和希望回答的问题。后面的表结构检查、候选字段定位、模型生成和题库准备由 Codex 推进;用户或业务人员负责确认销售额、时间范围等关键口径,并验收最终结果。

第一版只建立了三个可查询模型(QueryModel),准备了 26 条题库,其中 21 条执行通过,5 条用于记录需要澄清或停止的边界。下面是具体过程。

不接全库,先选销售分析

Wide World Importers 包含 OLTP 库 WideWorldImporters 和分析库 WideWorldImportersDW,覆盖客户、订单、发票、商品、库存和日期等常见业务对象。相比临时准备几张 demo 表,它更接近实际业务系统。

但第一版没有接入所有表,只选择了 WideWorldImportersDW 的销售分析域。仅这个范围,就有几组容易混淆的口径:

  • 订单金额和开票销售额不是一回事。
  • 含税金额、不含税金额和利润不能混用。
  • 下单日期和发票日期会得到不同的统计结果。
  • 客户榜单里要不要包含 Unknown 会直接影响 TopN 结果。
  • 数据库里有日期维表到 2016 年底,但销售事实数据只到 2016-05-31。

如果不事先约定,同一个问题可以生成多种都能执行、但含义不同的 SQL。因此,我没有让 Codex 从全库自由选择,而是先用问题限定范围。

建模之前,先看清数据范围

我把官方 .bak 恢复到本机 Docker SQL Server,并为 DataAgent 创建了只读账号。读取查询可以正常运行,建表操作会被 SQL Server 拒绝。生产环境还需要补充审计和数据权限,本次示例不展开。

数据库恢复后,我没有马上生成模型,而是先检查表规模和数据日期范围:

对象行数日期范围
Fact.Sale2282652013-01-01 到 2016-05-31
Fact.Order2314122013-01-01 到 2016-05-31
Dimension.Date14612013-01-01 到 2016-12-31
Fact.Stock Holding227当前库存快照

这里很快发现了第一个问题:日期维表覆盖了完整的 2016 年,但销售和订单事实只到 5 月 31 日。

也就是说,查询即使能按日期维表筛选全年,也不能把结果称为“2016 全年销售额”。后面的模型和题库统一使用“2016 年样例事实期”,或者直接标明 2016-01-01 到 2016-05-31。

先列问题,再定模型

接着,我没有继续浏览更多表,而是先整理了一组需要验收的问题:

  • 2016 年样例事实期销售额、利润、数量是多少?
  • 2016 年样例事实期销售额最高的客户是谁?
  • 按月份看销售额趋势是什么?
  • 哪些商品利润最高?
  • 当前哪些库存低于补货线?
  • 订单金额和开票销售额有什么差异?
  • 2016 年 1-5 月相比 2015 年同期,哪些客户下降最多?
  • 如果用户问“2016 全年销售额”,系统应该怎么澄清?

这些问题最终对应三个 QueryModel。没有被题库用到的业务域,暂时不接:

QueryModel作用
wwi_sales_analysis默认销售分析模型,基于 Fact.Sale,使用发票日期和开票销售额
wwi_order_analysis订单金额口径对照,基于 Fact.Order,不替代销售额
wwi_inventory_signal当前库存、补货线、目标库存和低库存信号

默认销售模型 wwi_sales_analysis 明确了三个口径:

默认销售日期:Fact.Sale.[Invoice Date Key]
默认销售额:Fact.Sale.[Total Including Tax]
默认客户榜单:排除 Dimension.Customer.Customer = 'Unknown'

题库还保留了一条包含 Unknown 的对照查询。结果很直观:Unknown 会以 10224728.14 的销售额排在第一名。因此,默认客户榜单将它排除,用户明确要求时再纳入统计。

订单则单独建模。2016 年样例事实期内:

开票销售额来自 Fact.Sale
订单金额来自 Fact.Order

两者的结果也不同:开票销售额含税金额是 25971029.11,订单含税金额是 26848038.92。这也是订单和销售需要分开建模的直接证据。

26 条题库,既测答案也测边界

模型生成后,我把问题固化成题库,并运行模型校验、查询执行和业务断言。

题库共 26 条:21 条执行通过,5 条边界题按计划不执行,没有失败项。

这 5 条边界题记录第一版不执行或需要澄清的问题:

  • 预测下个月销售额。
  • 把下降客户写回数据库并标记高风险。
  • 编造利润下降原因。
  • 查询联系人或登录信息。
  • 用户问“2016 全年销售额”时,需要澄清数据只到 2016-05-31。

这 5 条没有进入查询执行,因此不计入失败。这里记录的是项目边界,不能解释成系统已经执行并自动拒绝了这些请求。

其余 21 条覆盖汇总、TopN、趋势、商品利润、库存异常、同比下降,以及订单与销售口径对照。它们不复杂,但都能用手写 SQL 核对,适合第一版验收。

最后得到的不是一条 SQL

一条 SQL 能运行,不代表它回答的是用户心里的那个问题。日期是否完整、销售额采用哪个口径、订单和开票能否对比,都要在建模和验收阶段明确。

在这套流程中,语义层固定指标和维度的含义,题库检查结果与项目边界。Codex 可以检查字段、生成语义模型、执行验证和整理证据;是否通过验收,仍取决于结果能否复核、口径是否一致。

下一步

下一篇不再重复建模,而是直接使用这三个模型分析一个具体问题:近一个月哪些客户的销售量明显下降?我会拆解时间窗口、客户范围、销售口径和下降判定,看看从基础问数走向分析时还缺哪些约束。

参考资料和内部验证记录:

  • Microsoft Wide World Importers 官方说明:https://learn.microsoft.com/en-us/sql/samples/wide-world-importers-what-is
  • Microsoft SQL Server samples 仓库:https://github.com/microsoft/sql-server-samples/tree/master/samples/databases/wide-world-importers
  • Fabric Data Agent example queries 方法参考:https://learn.microsoft.com/en-us/fabric/data-science/data-agent-example-queries
  • docs/wide-world-importers-dataagent-evidence/2026-07-07-wide-world-importers-sqlserver-restore.md
  • docs/wide-world-importers-dataagent-evidence/2026-07-07-wide-world-importers-dataagent-validation.md
  • docs/wide-world-importers-dataagent-evidence/2026-07-08-wide-world-importers-question-bank-expansion-research.md