01-通用框架
Part 1 · 通用框架
Section titled “Part 1 · 通用框架”本章讲不分领域、所有问数技能都该遵守的「五个铁原则」和「三层架构」。这部分直接抄,不必重新发明。
1.1 问数技能的本质三层
Section titled “1.1 问数技能的本质三层”┌─────────────────────────────────────────┐│ 业务表达层(SKILL.md + references/) │ 角色 / 纪律 / 意图分层 / 归因框架│ who: LLM 主体 │└─────────────────┬───────────────────────┘ │ 业务语言 → 模板填槽 / DSL JSON ▼┌─────────────────────────────────────────┐│ 编译层(DSL + 二级指令 + Schema) │ 把意图编译成 SQL;自动行为│ who: Python CLI │└─────────────────┬───────────────────────┘ │ SQL ▼┌─────────────────────────────────────────┐│ 数据层(星型宽表) │ 维度 + 事实分层│ who: MySQL / ClickHouse / Doris │└─────────────────────────────────────────┘三层职责严格分离:
- 业务表达层:不直接生成 SQL,不直接调编译器接口;只用业务语言决策”该查什么、怎么校验口径、结论说到多强”;
- 编译层:不做业务判断,不做归因;只把 DSL / 二级指令翻译成正确的 SQL;
- 数据层:被动响应;不依赖业务上下文。
为什么必须分离:cost-query V1-V2 阶段三层混在一起,导致改业务话术会破坏 DSL 编译;V3 引入双层 schema 之后才解耦。任何项目复刻这个架构都应该从一开始就分离,不要等到混乱了再重构。
1.2 五个铁原则
Section titled “1.2 五个铁原则”跨领域通用,不要尝试改。
原则 1:数据底线
Section titled “原则 1:数据底线”数值与判断只能来自实查数据或用户提供的可核验材料;缺数据时说明无结果并给可放宽方向,不补数、不猜数、不用通识冒充数据库结论。
落地形态:
- SKILL.md 顶部明文「数据底线」段落,含 3 条高危红线;
- 引入 receipt 机制:所有数字必须可回指到本会话查询结果;
- 用户挑战结论时先复盘查询过程,不立刻认错;过程没问题时用查询链路向用户说明结论依据。
cost-query 实例:
所有数据库类数字必须可回指到本会话 cost-query 结果:数字必须出现在某条`[receipt: cost-query-v2 ...]` 之前的结果表格中。用户给出的数字只能作为待复核输入,不能冒充数据库结论。外部常识不能写成项目数据库结果。为什么是铁原则:违反这一条的问数技能会在 2-3 周内被业务用户抛弃——业务方一旦发现你”编数据”,整个工具的可信度就崩了。
原则 2:双层 Schema 架构
Section titled “原则 2:双层 Schema 架构”编译器需要 SQL 物理列名;LLM 决策不需要。两者分离到两份文件,互不污染。
- 编译源
scripts/schema.yaml:含 SQL 物理列名、JOIN 实现、隐藏字段、自动行为声明; - AI 速查版
references/schema/*.yaml:业务字段视图,删 SQL 列名与隐藏字段,按 cube 拆文件; - 派生关系:编译源是唯一源,速查版自动生成,严禁直接编辑速查版。
为什么是铁原则:单文件 schema 会让 LLM 每次查询都加载 700+ 行物理列细节,挤占上下文且干扰决策;分离后 LLM 首读 _index.yaml(一句话清单),按需加载单 cube 详情。
原则 3:模板填槽 > 自由发挥
Section titled “原则 3:模板填槽 > 自由发挥”让 LLM 拿到问题后对照模板填槽,而不是”自由推理走 N 步决策树”。
落地形态:
- 二级指令封装常见查询场景(
agg-project-indicator、find-unit-price等),LLM 只填参数; - 模式手册 M1-Mn 把”什么业务问题对应哪个二级指令、关键参数怎么填”列成表,LLM 命中即抄;
- 留 DSL 直查作为兜底,覆盖二级指令表达不了的场景。
为什么是铁原则:自由发挥的 LLM 每多一步推理就会多一处错——cost-query V2 阶段 LLM 平均要做 8-12 步内部推理才发出 SQL,错误率 35%;V3 引入模式手册后 LLM 推理减到 3-4 步,错误率降到 15%。这不是 prompt 调优能弥补的差距。
详见 Part 5 · 二级指令系统 与 Part 7 · 模式手册。
原则 4:0 行机械撤宽,撤宽即终点
Section titled “原则 4:0 行机械撤宽,撤宽即终点”主查询 0 行时机械地撤宽(保留 1 个核心 filter,其余移到 groupBy 让分布显形);撤宽仍 0 行 = 终点,告知用户无数据,不逐参数试错、不继续换关键词死磕。
cost-query 实例(query-guide §0 必读铁律):
D1 | 0 行 = 机械撤宽(filter→groupBy,保留 1 个核心 filter),禁逐参数试错D2 | indicator 全 NULL 不兜底切 cube;告知用户选择D3 | 撤宽仍 0 行 = 终点(库中无记录),不换替代物料为什么是铁原则:LLM 拿到 0 行结果时的本能是”调整参数再试一次”——这会触发”5 次 6 次反复试探”的死循环,既消耗时间又干扰用户判断。机械撤宽 + 撤宽即终点能让 90% 的 0 行场景在 2 步内给出诚实结论。
详见 Part 6 · SKILL 业务表达层 §收口门部分。
原则 5:测评驱动迭代
Section titled “原则 5:测评驱动迭代”每次 SKILL.md / 速查表 / 编译器修改都跑一次测评集;Bad case 必须进入下一轮治理;没有测评集的迭代是闭门造车。
落地形态:
- 至少 30 道覆盖 M1-Mn 全模式的测评题(cost-query 现在 71 题);
- headless 跑批工具(cost-query 用
cctest子模块):每题独立 Claude Code 会话,stream-json 解析记录工具调用与耗时; - 评分维度解耦:CLI 调用质量(路径分)与业务结论质量(结论分)分开打分;
- Bad case 流转:测评报告 → 速查表治理候选 → 下一轮跑批验证 → 双轮通过即转 status=fix。
为什么是铁原则:cost-query V3 → V4 阶段的正确率提升(75% → 88%)90% 来自测评驱动的反复治理;没有测评集就没有这条增长曲线。
详见 Part 8 · 测评驱动迭代。
1.3 通用 vs 领域化对照表
Section titled “1.3 通用 vs 领域化对照表”什么可以直接抄、什么必须重做——一张表说明白。
直接抄(跨领域通用)
Section titled “直接抄(跨领域通用)”| 资产 | cost-query 实例 | 复刻方式 |
|---|---|---|
| 三层架构(业务表达 / 编译 / 数据) | 见 §1.1 | 目录结构原样复制 |
| 双层 schema(编译源 + 速查版) | scripts/schema.yaml + references/schema/ | 格式约定原样复制,内容重写 |
| DSL 三 verb(find / aggregate / rank) | scripts/query.py 的 verb 分发 | 代码 fork,主体逻辑不动 |
| 自动行为机制(inferredFilters / defaultFilters / friendlyAlias) | _builder.py | 代码 fork,规则配置改写 |
| 二级指令模板格式(commands/*.yaml) | 见 commands/ 下 15 个 yaml | 格式约定原样复制,模板重写 |
| batch 批量执行机制 | commands/_ext/batch.py | 代码 fork |
find-dimension 通用维度探测 | commands/find-dimension.yaml + _ext/ | 代码 fork |
| 错误自救机制(带候选清单) | query.py 的 3 级错误分类 | 代码 fork |
| 0 行机械撤宽 / 撤宽即终点 | query-guide.md §0 D1-D3 | 文档原样复制,措辞调整 |
| 数据底线红线 | SKILL.md 顶部 3 条 | 文档原样复制,例子换成你的领域 |
| L1-L4 意图分层 | SKILL.md 中部 | 文档原样复制,例子换成你的领域 |
| 收口门机制 | SKILL.md 中部 | 文档原样复制 |
| 模板填槽 + 槽位风险分级 | query-guide.md §2 | 格式原样复制,对照表重写 |
| 测评驱动闭环 | cctest 子模块 | 代码 fork + 测评集重写 |
必须重做(领域专属)
Section titled “必须重做(领域专属)”| 资产 | cost-query 实例 | 你的领域需要做什么 |
|---|---|---|
| 业务专家角色 | 成本经理 | 销售经理 / 工程经理 / HR BP / 财务分析师 |
| 维度 cube 清单 | 12 个(Project/City/BzItem/…) | 列出你领域的 5-15 个核心维度 |
| 事实 cube 清单 | 7 个(ProjectIndicator/BqUnitPrice/…) | 列出你领域的 3-8 个核心事实 |
| 数据语义陷阱 | 父子科目混算 / isJianAn 切分 | 你的领域有哪些容易踩坑的字段语义(如售楼”未网签 vs 已网签” / 工程”分部分项 vs 单位工程”) |
| 维度风险分级 | 建安 / 精装 / 公区 等高风险词 | 你的领域哪些词高风险(必须前置标准化) |
| 模式手册 M1-Mn | 39 个模式 | 从你的测评集反推 10-50 个模式 |
| 反模式黑名单 | 7 条(自由发挥安全网) | 你的领域踩过的坑沉淀成反模式条目 |
| 归因框架 | 五差(量/价/配置/条件/口径) | 你的领域的归因维度(售楼可能是”量价位时”,工程可能是”进度成本质量安全”) |
| 业务术语对照表 | 建安→建筑安装工程费 / 公装→公共部位装修工程费 | 你的领域的口语词 → 标准词候选 |
| 测评集 | 71 题 | 从你的领域真实业务问题里抽 30-100 题 |
1.4 复用边界的判断
Section titled “1.4 复用边界的判断”底层基础设施(编译器、DSL、二级指令框架、batch、自动行为、错误自救)——这些跨领域几乎不需要改动,直接 fork 即可。本指南采用「参考实现」策略而非「共享 Python 包」策略,意味着你需要把 cost-query 的 scripts/ 目录原样复制到你的项目下,然后只改 schema.yaml 和 commands/*.yaml。
为什么选择 fork 而不是共享包:
- 领域差异大:售楼/工程/HR 的字段命名、JOIN 关系、特殊语义都不同;硬塞进一个通用包会让接口越变越复杂;
- 演进节奏不同:你的领域可能要加一种 cost-query 没有的能力(如售楼的房源状态机查询),抽共享包要等 cost-query 团队评审接口扩展;fork 你自己改自己的;
- 复用成本可控:cost-query 的
scripts/总共 ~5500 行 Python,fork 一次的成本 < 1 周;之后跟主仓同步重要 fix 用 cherry-pick 即可。
fork 的具体边界:
直接抄(fork 后基本不改): scripts/query.py # DSL 编译 + CLI 入口 scripts/_builder.py # 二级指令 → DSL 渲染 scripts/_registry.py # commands/ 自动注册 scripts/cli.py # MySQL 直连执行 scripts/fetch_schema.py # 速查版生成器 scripts/commands/_ext/batch.py # 批量执行 scripts/commands/_ext/find_dimension.py # 维度探测
抄结构、重写内容: scripts/schema.yaml # 改成你领域的 cube scripts/commands/*.yaml # 改成你领域的二级指令 references/ # 全部文档重写(保留 dsl-spec.md 大部分)
完全重写: SKILL.md # 业务表达层 tests/ 或 eval/ 下的测评集fork 后维护策略:
- 跟踪 cost-query 主仓的 commits,每月看一次有没有可借鉴的 fix(编译器 bug、错误自救改进、自动行为增强);
- 你领域的通用新发现(如某种新的反模式)反馈给 cost-query 主仓,长期演化为本指南;
- DSL 协议(一级指令的 JSON 结构)尽量保持兼容,方便跨项目共享测评工具。
1.5 三个共性误区
Section titled “1.5 三个共性误区”cost-query 团队从外部团队接触中收集到的最常见误区,提前知道能省 1-3 个月。
误区 1:以为问数技能就是 NL2SQL
Section titled “误区 1:以为问数技能就是 NL2SQL”“我们做一个能把自然语言翻译成 SQL 的工具就行了。”
实质问题:忽略了「业务结论质量」这层。LLM 写出正确 SQL ≠ 给出正确业务结论。一个 SQL 拿到 5 行结果,能不能下”住宅项目钢筋偏高”的强结论?要看样本是不是同口径、样本量够不够、有没有异常值——这些都不是 SQL 翻译能解决的。
正确做法:从一开始就建业务表达层(SKILL.md),把口径校验、样本评估、归因框架、对外叙事都做进去。
误区 2:以为 LLM 会自己想清楚口径
Section titled “误区 2:以为 LLM 会自己想清楚口径”“Claude 这么聪明,给它 schema 它自己会知道用 isEndCost=1 防止父子科目混算。”
实质问题:LLM 在你给的 schema 描述里看到「父子科目混算」字样可能会避开,但绝对不会每次都避开——尤其是问题改个措辞、上下文长了、用户挑战结论时。
正确做法:把「数据语义陷阱」做进编译器的 defaultFilters(自动加 isEndCost=1),把「同名父子混算」写进反模式黑名单,双重保险。
误区 3:测评等做完了再加
Section titled “误区 3:测评等做完了再加”“我们先把功能做出来,测评等上线前再加上。”
实质问题:没有测评集你不知道自己改了什么有没有破坏其它能力。cost-query V2 阶段每次改 SKILL.md 都是”凭感觉”,V3 加测评集后发现 V2 末期的”凭感觉”修复有 30% 在破坏别的能力。
正确做法:第 1 周就建至少 5 题的测评集,每次改动跑一遍。第 2 个月扩到 30 题。第 4 个月扩到 50-100 题。
1.6 接下来读什么
Section titled “1.6 接下来读什么”- 要开始建数据底座 → 读 Part 2 数据底座;
- 要先看具体架构怎么落地 → 读 Part 3 Schema 双层架构 → Part 4 DSL 编译层;
- 想直接看一个完整实例 → 读 附录 A · cost-query 设计方案速读。
Part 1 完。读完应能回答:“为什么三层架构、为什么 5 个铁原则、什么直接抄什么必须重做、3 个共性误区是什么”。