# 01 · 技术选型与对抗式审查

> 方法：每个关键决策列出候选方案，然后从「攻击者视角」批判性审视（找失败模式、找反例、找过度设计），通过审查得出最优解。**约束**：Java 11 后端、已有 MaxCompute 账单表+维度表、LLM 用 DeepSeek。

---

## 决策 1：本体推理引擎

**候选方案**
- A. Apache Jena（Java，RDFS/OWL 规则推理 + SPARQL）
- B. RDF4J（Java，RDF 框架）
- C. Neo4j（图数据库，非本体，用传递闭包模拟层级）
- D. Python owlready2（需引入 Python 栈）

**对抗式审查**
- 攻击 A：Jena 的 `OWL_MEM_RULE_INF` 是**规则推理**，不是完整描述逻辑（DL）推理。指标本体若需要 `owl:equivalentClass`、传递性 `partOf`、子类推理——规则推理**够用**；完整 DL 推理机（Pellet/HermiT）是过度设计，且性能差。Jena 内存推理对概念规模敏感，但指标本体通常 < 1000 概念，内存毫无压力。
- 攻击 C：Neo4j 是图数据库不是本体，传递闭包 ≠ OWL 推理（无等价类、无公理）。选它等于**退回关系表**，违背「本体驱动」的核心（这正是上一版被否掉的原因）。
- 攻击 D：引入 Python 栈增加运维负担，与 Java 后端割裂，维护两个运行时。

**结论**：**A Apache Jena**。规则推理恰好覆盖指标本体的推理需求（等价/层级/派生），Java 原生，生态成熟。规模增长后用 TDB + 增量推理兜底。

---

## 决策 2：本体建模方式

**候选方案**
- A. 纯手写 Turtle
- B. Protégé 可视化建模
- C. 代码从表结构 + 配置自动生成本体
- D. 混合（骨架手写 + 指标实例配置驱动生成）

**对抗式审查**
- 攻击 A：每次新增指标都要手写 Turtle，易出错、不可规模化，指标几十个后维护成本爆炸。
- 攻击 B：Protégé 是桌面工具，无法纳入 CI/自动化流程，与「AI Native 自动解析表结构」的定位冲突。
- 攻击 C：纯自动生成，本体的概念类、公理模板也自动生成，灵活性差，难以表达复杂公理（如复合指标的组成关系）。
- D 的审查：骨架（概念类、关系、公理模板）是**稳定的、低频变化的**，手写一次；指标实例（GMV、客单价）是**高频变化的、数据驱动的**，从配置（YAML）自动生成。两者职责分离，兼顾可控性和可维护性。

**结论**：**D 混合**。概念类体系 + 推理关系手写 Turtle 骨架；指标实例从 `metrics.yaml` 配置自动生成，新增指标只需加配置。

---

## 决策 3：架构形态

**候选方案**
- A. Spring Boot 单体 + 模块化分层
- B. 微服务（本体服务 / 查询服务 / LLM 服务拆分）

**对抗式审查**
- 攻击 B：本体推理、SQL 生成、LLM 调用在**一次查询内是强耦合的串行协作**（推理结果喂给 SQL 生成，SQL 结果喂给 LLM 归因），拆微服务引入网络调用、序列化、分布式一致性，纯增复杂度。指标查询不是高并发、高独立扩展场景。微服务是**过度设计**。
- 攻击 A：单体的风险是模块边界模糊导致代码腐化，但靠**模块化分层**（ontology/sql/llm/query 独立 package + 接口隔离）即可控制。

**结论**：**A 单体 + 分层**。用 package 边界和接口隔离保证清晰，拒绝微服务。

---

## 决策 4：SQL 生成方式

**候选方案**
- A. 纯模板 + 参数填充
- B. 模板 + 结构化 DSL（简单指标模板，复杂指标 DSL）
- C. 引入 dbt MetricFlow（语义层框架）

**对抗式审查**
- 攻击 A：纯模板对**简单指标**（单表 SUM/COUNT）完美，但复杂指标（多表 join、子查询、窗口函数）会让模板爆炸，每个复杂指标一个专属模板，退化成硬编码。
- 攻击 C：dbt 引入 Python + dbt 运行时，重、运维复杂，违背轻量原则；且 dbt 的语义层仍要自己建本体，两套语义模型打架。
- B 的审查：简单指标（90%）用模板，复杂指标（10%）用结构化 DSL（类似 QueryBuilder，声明式拼 SQL）。DSL 仍由代码拼接、不交 LLM，保持受约束。折中最优。

**结论**：**B 模板 + DSL**。核心原则不变：LLM 不写 SQL，模板和 DSL 都是确定性代码。

---

## 决策 5：LLM 集成

**候选方案**
- A. 直调 DeepSeek（OpenAI 兼容 HTTP）
- B. Spring AI 框架
- C. LangChain4j

**对抗式审查**
- 攻击 A：结构化输出（槽位提取 JSON）要自己处理。但 DeepSeek 支持 `response_format={type:'json_object'}` + JSON Schema，配合 Jackson 解析，完全可控，代码量不大。
- 攻击 B：Spring AI 结构化输出（StructuredOutputConverter）方便，但框架仍在快速迭代（API 不稳定），引入新依赖 + 学习成本，对"直调 + JSON Schema"这种简单需求是杀鸡用牛刀。
- 攻击 C：LangChain4j 功能全但重，且抽象层遮蔽了"LLM 只做槽位提取"这个简单职责，引入不必要的复杂度。

**结论**：**A 直调 DeepSeek**。MVP 少依赖、可控。若后续多模型切换或复杂 Agent 编排需求出现，再评估 Spring AI。

---

## 决策 6：语义关系存储

**候选方案**
- A. 全放本体（OWL）
- B. 本体管推理关系，配置管数据（分离）

**对抗式审查**
- 攻击 A：把口径表达式（`SUM(bill_amount)`）、映射（`dwd_bill.bill_amount`）、描述全放本体，会让本体**膨胀**——这些是**数据**不是**推理关系**，放本体污染语义层，且每次改口径都要动本体文件、重新推理。
- B 的审查：推理需要的关系（`equivalentClass`、`partOf`、`composedOf`）放本体；纯数据（口径表达式、字段映射、计算逻辑、描述）放 YAML 配置。本体保持精简（只推理），配置易维护（改数据不改本体）。职责清晰。

**结论**：**B 分离**。本体管推理，配置管数据。这是本方案区别于"把一切都塞进本体"的关键设计。

---

## 决策 7：部署形态

**候选方案**
- A. 标准 Spring Boot 可执行 jar
- B. 容器化（Docker/K8s）

**对抗式审查**
- 攻击 A：无容器编排能力，但本场景是**单机内网部署**（连 MaxCompute + 调 DeepSeek），无弹性伸缩、无多实例需求。
- 攻击 B：容器化是好的实践，但对单机部署的指标查询服务是过度工程。Jena 内存本体 + 无状态服务，jar 直接跑即可。

**结论**：**A jar 部署**，预留 Dockerfile 供未来容器化。

---

## 审查总结：被否决的选项

| 被否决 | 原因 |
|--------|------|
| Neo4j 模拟本体 | 图数据库 ≠ 本体推理，退回关系表 |
| Python owlready2 | 引入 Python 栈，割裂 Java 后端 |
| 微服务拆分 | 强耦合串行协作拆微服务是过度设计 |
| dbt MetricFlow | 引入 Python+dbt 运行时，双语义模型打架 |
| LangChain4j | 抽象层遮蔽简单职责，过度设计 |
| 全放本体 | 数据污染语义层，本体膨胀 |
