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 的审查:推理需要的关系(equivalentClasspartOfcomposedOf)放本体;纯数据(口径表达式、字段映射、计算逻辑、描述)放 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 抽象层遮蔽简单职责,过度设计
全放本体 数据污染语义层,本体膨胀