☰
业务指标字典(Metrics Layer)设计:让大模型看懂统一指标计算口径
2026/10/9 4:33:57 网站建设 项目流程

“大喜,为什么大模型跑出来的‘GMV’比财务报表整整多了三个亿?”

周一早上九点半,业务线负责人举着两张打印出来的报表直接杀到我工位前。我家那只英短猫 Null 还在工位软垫上打盹,被这句咆哮吓得耳朵一立。

我抓过他手里的 SQL 扫了一眼,嘴角忍不住抽搐:大模型在生成 SQL 时,直接把退款订单、拼团未成团订单以及测试商户的流水全算进了 GMV 汇总里,甚至把跨境关税也一股脑算作了平台毛利。

大模型本身没有商业常识,它只是在拿着提示词里的表名和字段去碰概率。如果你的数仓底座没有一层严格的指标语义层(Metrics Layer),让大模型直接面对几百张底层宽表和零散视图去写聚合逻辑,无异于让醉酒的实习生在生产数据库里盲打。


为什么直接让大模型读 DDL 会崩盘

很多做 ChatBI 的团队在初期都图省事:把数据库里的CREATE TABLE语句、字段注释打包塞进 System Prompt,或者做个简单的 RAG 检索,就天真地以为大模型能自己写出符合业务口径的 SQL。

现实往往给团队上一堂沉重的概率论毒打课:

  1. 同名异义与同义异名:order_amount在订单明细表里是未支付总额,在结算主表里是扣除优惠券后的实收金额。大模型只看字段名字面意思,随机抓取。
  2. 隐藏的过滤条件:业务上计算“有效支付金额”,通常需要附带status IN (2, 4, 7) AND is_test = 0 AND refund_type IS NULL。大模型如果漏掉任何一个谓词,汇总结果就会出现几百万的偏差。
  3. 计算公式层级错乱:比率型指标比如“支付转化率”、“客单价”,绝不能简单先求平均再求和。一旦涉及维度聚合切换,必须展开为原子指标的基础聚合运算。

大模型在处理结构化查询时,最擅长的是语法翻译(把自然语言意图转为逻辑 AST),最不擅长的是在没有约束的前提下猜测企业内部黑话。因此,我们必须把“口径定义权”从大模型的脑子抽离出来,下沉到指标层统一管理。


语义模型的三层骨架:原子指标、衍生指标与维度矩阵

在我们的架构设计里,指标字典不是一张写给业务人员看的静态 Wiki 表格,而是一个具备强类型约束的、大模型可机读的语义拓扑网络。

整个指标字典的核心抽象分为三级:

+-------------------------------------------------------------+ | 复合 / 衍生指标 (Derived Metrics) | | 客单价 = GMV / 支付买家数 | +-------------------------------------------------------------+ | 依赖 +-------------------------------------------------------------+ | 原子指标 (Atomic Metrics) | | GMV: SUM(pay_amount) | | 支付买家数: COUNT(DISTINCT user_id) | +-------------------------------------------------------------+ | 绑定 +-------------------------------------------------------------+ | 维度与切片 (Dimensions & Slices) | | 时间、区域、商品类目、渠道渠道 | +-------------------------------------------------------------+

1. 原子指标(Atomic Metric)

不可再分的最小聚合事实,由基础事实表的数值字段配合确定性的聚合函数(SUM、COUNT、AVG、MAX、MIN)定义,并强制绑定底层事实过滤条件。

2. 维度与修饰词(Dimension & Modifier)

维度是切片视角(时间粒度、地理区域、用户标签);修饰词则是限定条件(如“近7天”、“iOS端”、“新客首单”)。修饰词本质上是参数化的 WHERE 谓词。

3. 衍生与复合指标(Derived & Composite Metric)

由原子指标叠加修饰词,或多个原子指标通过四则运算组合而成的最终呈现指标。复合指标绝不单独落库物理字段,完全依赖运行时动态拼装。


大模型可机读的指标定义规范

为了让大模型在做意图解析(Intent Parsing)时能以最少 Token 理解最大语义,我们摒弃了臃肿的 JSON Schema,采用声明式 YAML 规范指标。大模型检索到相关指标后,直接按图索骥提取口径。

以下是交易域指标字典的核心定义切片:

version: 2.1 domain: trade_dw dimensions: - name: order_date type: temporal column: dt formats: [day, week, month] description: "订单创建的业务日期,分区字段" - name: platform_type type: categorical column: client_os valid_values: ["iOS", "Android", "MiniProgram", "Web"] description: "下单客户端系统" atomic_metrics: - id: raw_pay_amount name: "成交支付金额" table: dwd_trade_order_pay_di expression: "SUM(actual_pay_amt)" base_filter: "order_status IN (2, 4) AND is_test_account = 0" unit: "分" description: "扣除全场红包与积分折扣后的实际在线支付净额,不含纯退款未支付流水" - id: distinct_buyers name: "支付用户数" table: dwd_trade_order_pay_di expression: "COUNT(DISTINCT buyer_id)" base_filter: "order_status IN (2, 4) AND is_test_account = 0" unit: "人" description: "有过至少一笔有效支付成功的去重买家数" derived_metrics: - id: trade_atv name: "笔单价" calculation: "raw_pay_amount / NULLIF(distinct_orders, 0)" required_atomics: [raw_pay_amount, distinct_orders] description: "平均每笔有效支付订单的成交金额" - id: user_arpu name: "客单价" calculation: "raw_pay_amount / NULLIF(distinct_buyers, 0)" required_atomics: [raw_pay_amount, distinct_buyers] description: "每个去重支付买家产生的平均支付金额"

注意看trade_atv与user_arpu的定义。大模型在理解用户提问“上周 iOS 端的客单价是多少”时,它的思维链(Chain of Thought)不再是去翻看几张表里有没有叫avg_price的列,而是按照清晰的拓扑链执行推导:

  1. 识别目标指标:user_arpu(客单价)。
  2. 解构依赖原子指标:raw_pay_amount和distinct_buyers。
  3. 提取修饰限定条件:时间为“上周”(order_date BETWEEN ... AND ...),客户端为“iOS”(platform_type = 'iOS')。
  4. 拼装原子指标底层的过滤谓词order_status IN (2, 4) AND is_test_account = 0。
  5. 最终生成零口径歧义的 SQL 表达式。

语义层解析引擎:从自然语言到安全 SQL

在 ChatBI 的后端,我们设计了一个轻量级指标编译解析器。它的任务是:大模型负责输出结构化提取意图(JSON),解析器负责按照语义层字典把 JSON“编译”成安全的 SQL。大模型绝对不直接碰最后的 SQL 组装字符串。

以下是核心的编译拼装逻辑:

from dataclasses import dataclass from typing import List, Dict, Optional @dataclass class MetricQueryIntent: metric_id: str dimensions: List[str] time_range: Dict[str, str] filters: Dict[str, str] class SemanticCompiler: def __init__(self, metrics_registry: Dict, dimensions_registry: Dict): self.metrics = metrics_registry self.dims = dimensions_registry def compile(self, intent: MetricQueryIntent) -> str: if intent.metric_id not in self.metrics: raise ValueError(f"未受管的指标 ID: {intent.metric_id},请检查指标字典。") target_metric = self.metrics[intent.metric_id] # 针对衍生指标,递归展开为原子指标计算表达式 if "calculation" in target_metric: calc_expr = target_metric["calculation"] for atomic_id in target_metric["required_atomics"]: atomic_def = self.metrics[atomic_id] calc_expr = calc_expr.replace(atomic_id, atomic_def["expression"]) final_select_expr = f"{calc_expr} AS {intent.metric_id}" target_table = self.metrics[target_metric["required_atomics"][0]]["table"] base_filter = self.metrics[target_metric["required_atomics"][0]]["base_filter"] else: final_select_expr = f"{target_metric['expression']} AS {intent.metric_id}" target_table = target_metric["table"] base_filter = target_metric["base_filter"] # 收集维度与 GROUP BY dim_cols = [self.dims[d]["column"] for d in intent.dimensions if d in self.dims] # 收集过滤条件 where_clauses = [base_filter] if intent.time_range: where_clauses.append( f"{self.dims['order_date']['column']} BETWEEN '{intent.time_range['start']}' AND '{intent.time_range['end']}'" ) for k, v in intent.filters.items(): if k in self.dims: where_clauses.append(f"{self.dims[k]['column']} = '{v}'") # 组装最终查询 dims_part = f"{', '.join(dim_cols)}, " if dim_cols else "" group_part = f" GROUP BY {', '.join(dim_cols)}" if dim_cols else "" where_part = " AND ".join(f"({c})" for c in where_clauses) sql = f"SELECT {dims_part}{final_select_expr} FROM {target_table} WHERE {where_part}{group_part}" return sql

这套设计的威力在于:哪怕大模型偶发抽搐把修饰词搞混,底层的base_filter、NULLIF防除以零保护以及原子指标物理映射也永远锁死在编译代码中。业务规则的防御线被拉到了算法框架层,而不是靠提示词里的“你必须遵守以下50条规则”去撞运气。


避坑指南:指标字典落地的三道防线

在我们迭代了 3 个大版本之后,总结出指标字典落地的核心铁律:

1. 指标命名绝对禁止业务黑话简称

研发习惯写cnt_byr_7d_succ,业务习惯说“老客周复购”。如果指标字典里写简称,大模型在召回阶段会大量产生幻觉。指标 ID 采用系统命名,但必须在aliases字段里把各业务线的黑话、外号、口头禅全部登记在案。

2. 区分度量聚合与明细分析

很多业务人员提问并不是看宏观大盘,而是要导出“明细”。如果让同一个模型去既做指标聚合又做明细抽取,系统会陷入维度爆炸。我们把提问分流为两张路网:问“总览、趋势、占比、对比”的走 Metrics Layer;问“名单、明细、异常排查”的走明细生成 Agent。

3. 指标版本化与数仓 CI/CD 强校验

任何数据开发修改了底层事实表的枚举值(比如状态码从2改成了10),如果指标字典没更新,SQL 跑出来就是全空。我们将 YAML 指标定义纳入数仓代码仓库,每次数据管道上线前,必须跑一遍语义字典的自动化冒烟测试。

当指标字典从冰冷的数仓文档演进为与代码联动的可执行逻辑层,大模型才真正拥有了穿透数据迷雾的眼睛。不要再指望通用大模型直接看懂你们公司杂乱无章的 DDL,先把口径写进语义层,这才是 ChatBI 落地最坚固的基石。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询