第 3 章 关键设计决策(ADR)¶
本章学习目标 - 理解 TTD 18 个关键架构决策的上下文、选择与后果 - 对每个决策掌握"反思与改进方向",理解哪些是主流实践、哪些是工程权衡 - 学会用 ADR 格式记录架构决策,便于在自己的项目中复用
前置知识:第 2 章 整体架构 关联代码:散布于
backend/、semantic-plane/、data-plane/、scripts/
决策总览¶
| ADR | 决策名称 | 影响范围 |
|---|---|---|
| ADR-1 | 双面板分离 (Two-Plane Separation) | 整体架构 |
| ADR-2 | R/V/G 三引擎架构 (Triple-Engine) | 数据存储 |
| ADR-3 | Supervisor 轻量化 | Agent 编排 |
| ADR-4 | 显式路由决策 (Explicit Routing) | Agent 编排 |
| ADR-5 | SQL 护栏五层验证 | 安全 |
| ADR-6 | Chat-First API 迁移 | API 层 |
| ADR-7 | 多模型注册与降级链 | 模型层 |
| ADR-8 | 语义版本化元数据 | 治理流程 |
| ADR-9 | 双数据库连接池 | 运行时 |
| ADR-10 | SQL-Only 缓存策略 | 性能 |
| ADR-11 | 治理叠加层 (Governance Overlays) | 多租户 |
| ADR-12 | 三层治理模型 | 元数据治理 |
| ADR-13 | 双语必备 (Bilingual Requirement) | 元数据规范 |
| ADR-14 | PII 分级暴露 | 安全与隐私 |
| ADR-15 | LLM 驱动查询分解 | 检索管道 |
| ADR-16 | 术语绑定强路由 | 全管道 |
| ADR-17 | Better Auth 统一认证 | 认证与授权 |
| ADR-18 | 语义查询规划器 (Advisory Mode) | SQL 管道 |
ADR 稳定性评级¶
| 稳定性 | 含义 | ADR |
|---|---|---|
| 稳定 | 已固化,短期无变更计划 | ADR-1(双面板)、ADR-2(三引擎)、ADR-5(五层护栏)、ADR-6(Chat-First)、ADR-8(语义版本化)、ADR-9(连接池)、ADR-12(三层治理)、ADR-13(双语)、ADR-17(Better Auth) |
| 可能修订 | 有已知演进方向,但当前仍有效 | ADR-3(Supervisor 回访)、ADR-4(路由反馈环)、ADR-7(嵌入维度统一)、ADR-10(缓存策略扩展)、ADR-11(RLS 硬隔离)、ADR-14(PII 策略细化)、ADR-18(规划器默认路径) |
| 已知演进 | 明确计划变更 | ADR-15(查询分解已部分实现)、ADR-16(术语绑定 AST 化) |
ADR 决策依赖关系¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart TB
%% 核心依赖链(治理→安全)
ADR1[ADR-1<br/>双面板分离]
ADR12[ADR-12<br/>三层治理模型]
ADR16[ADR-16<br/>术语绑定强路由]
ADR5[ADR-5<br/>SQL 五层护栏]
%% 第一层派生
ADR1 --> ADR2[ADR-2<br/>三引擎架构]
ADR1 --> ADR11[ADR-11<br/>治理叠加层]
ADR11 --> ADR12
ADR12 --> ADR16
ADR16 --> ADR5
ADR12 --> ADR8[ADR-8<br/>语义版本化]
%% 第二层派生
ADR2 --> ADR7[ADR-7<br/>多模型注册]
ADR7 --> ADR10[ADR-10<br/>SQL 缓存]
%% 编排链
ADR3[ADR-3<br/>Supervisor 轻量]
ADR4[ADR-4<br/>显式路由]
ADR15[ADR-15<br/>查询分解]
ADR3 --> ADR4 --> ADR15 --> ADR16
%% 安全链
ADR17[ADR-17<br/>Better Auth]
ADR14[ADR-14<br/>PII 分级暴露]
ADR17 --> ADR14
%% 规划器链
ADR18[ADR-18<br/>语义规划器]
ADR18 --> ADR5
classDef bpSuccess fill:#e6f4ea,stroke:#1e8e3e,stroke-width:2px,color:#202124
classDef bpInfo fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124
classDef bpError fill:#fce8e6,stroke:#d93025,stroke-width:2px,color:#202124
classDef bpProcess fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
class ADR1,ADR12,ADR16,ADR5 bpSuccess
class ADR2,ADR3,ADR4,ADR17 bpInfo
class ADR7,ADR8,ADR10,ADR11,ADR14 bpProcess
class ADR15,ADR18 bpError
关键依赖链:ADR-1(双面板)→ ADR-12(三层治理)→ ADR-16(术语绑定)→ ADR-5(五层护栏) 是治理到安全的完整链条——治理资产定义术语,术语绑定驱动检索与重排,护栏在生成后兜底校验。任何一环变更都会影响下游。
如何阅读本章
每条 ADR 包含:上下文(为什么需要决策)→ 决策(选了什么)→ 后果(正负面)→ 反思与改进方向(批判性评估)。 带 标记的决策涉及明显的工程权衡,存在已知的演进方向。
ADR-1: 双面板分离 (Two-Plane Separation)¶
上下文¶
元数据治理需要版本控制、代码审查、可追溯的变更历史;运行时查询需要低延迟、高吞吐的数据库访问。两者在迭代节奏与扩缩容策略上存在根本差异——治理是"慢节奏、高质量、人类审查",查询是"快节奏、低延迟、机器执行"。
决策¶
将系统拆分为两个面板:
- Semantic Plane:治理资产以 YAML 存储在 Git,通过 pre-commit hooks + CI 校验 + PR 审查管理变更
- Data Plane:运行时执行层,用 PostgreSQL(本地)/ Redshift(生产)提供查询
后果¶
| 正面 | 负面 |
|---|---|
| Git 原生版本控制与审查 | 需要额外的同步管道 |
| 治理资产可独立于服务部署 | 存在短暂一致性窗口 |
| 开发者用熟悉 IDE 编辑元数据 | 需要 CI 校验保证 YAML 合法性 |
反思与改进方向¶
这是业界主流且正确的决策,与 dbt 的"代码即配置"理念一致。当前实现有一个不足:同步管道是单向且非实时的,从 YAML 发布到运行时生效有延迟(轮询 S3 manifest hash)。
改进方向
引入变更通知(如 S3 事件 → webhook → Backend 立即同步),将一致性窗口从"下次轮询"压缩到秒级。同时支持回滚到任意 Git commit 对应的快照。
ADR-2: R/V/G 三引擎架构 (Triple-Engine)¶
上下文¶
NL2SQL 存在三种截然不同的检索模式:精确匹配、语义召回、关系推理。单一存储引擎无法同时优化——关系库做向量检索慢、向量库做关系查询弱、图库做精确查询不顺手。
决策¶
采用 Relational + Vector + Graph 三引擎并存,统一部署在同一 PostgreSQL 实例(PG Supernode),外加 Engine D(动态 Few-Shot)构成完整检索能力。
| 引擎 | 技术 | 用途 |
|---|---|---|
| Relational | PostgreSQL | 结构化 CRUD、精确查询 |
| Vector | pgvector | Embedding 语义检索 |
| Graph | Apache AGE | 关系图谱、路径推理 |
| Dynamic Few-Shot | pgvector | 在线学习 few-shot |
后果¶
| 正面 | 负面 |
|---|---|
| 每种检索模式获得最佳性能 | 每条资产存储多份(结构化+向量化+图化) |
| 单一 PG 实例减少运维开销 | 需维护三份写入一致性 |
| 可按需组合多种检索 | 调试需理解三种存储模型 |
反思与改进方向 ¶
这是一个有争议的工程权衡。业界更常见的做法是"专用引擎":向量用 Milvus/Qdrant,图用 Neo4j,关系用 PG。把它们都塞进 PG 扩展(pgvector + AGE)的好处是运维简单、事务一致;代价是 AGE 在大规模图上的性能远不如专用图库,pgvector 的 HNSW 索引也不如专用向量库高效。
但在当前规模(H&M 数据集、百级资产)下,PG Supernode 的一体化优势(单一连接池、共享事务、低运维)大于性能劣势。这是"先简单后优化"的工程策略。
改进方向
保持 PG Supernode 作为开发态"一体化"方案,但在生产态支持可选的存算分离:向量检索可切换到 Qdrant/Milvus,图推理可切换到 Neo4j。抽象出 VectorStore / GraphStore 接口,让引擎可插拔。详见 第 5 章 选型对比。
ADR-3: Supervisor 轻量化 (Lightweight Supervisor)¶
上下文¶
早期设计中 Supervisor 承载意图解析、路由、检索、生成全部逻辑,单文件超 1000 行,测试困难,修改任一功能需回归全链路。
决策¶
Supervisor 极轻量:只做 embedding 计算 + governance overlay 解析,然后委托 Query Understanding → Router。
后果¶
| 正面 | 负面 |
|---|---|
| 单一职责,易测试 | Supervisor 对全局上下文感知弱 |
| 修改路由不影响 Supervisor | 委托链变长,调试需跨节点 |
反思与改进方向 ¶
轻量 Supervisor 让代码清晰,但牺牲了全局上下文——Supervisor 不知道后续节点的状态,无法做跨节点的智能调度。例如,当检索结果很差时,理想情况下 Supervisor 应该能提前介入决策(换路由或要求澄清),但当前它只在入口出现一次。
改进方向
引入"Supervisor 回访"机制:在关键决策点(如检索后、SQL 生成后)让 Supervisor 或一个轻量"监督节点"评估状态,决定是否调整后续路径。这是向"自适应编排"演进的方向。
ADR-4: 显式路由决策 (Explicit Routing)¶
上下文¶
需要决定查询的执行路径。学习式路由(用 ML 分类器预测路由)更灵活但不可解释;显式映射(意图→路由表)可预测但覆盖面有限。
决策¶
采用 7 条显式路由(非 ML 学习),用 _INTENT_TO_ROUTE 映射表决定路径,确保可预测、可审计。
后果¶
| 正面 | 负面 |
|---|---|
| 路由决策完全可审计 | 无法处理预料之外的意图 |
| 新增路由只需加映射条目 | 意图分类错误会导致整条路径错误 |
| 无需训练数据 | 意图空间扩展需手动维护映射 |
反思与改进方向¶
显式路由在安全敏感场景是合理选择,但完全静态缺乏自学习。当用户意图不在预设 7 类中时,系统只能 fallback,无法自动发现新模式。
改进方向
保留显式路由作为默认,但增加"路由效果反馈环":记录每条路由的失败率与用户满意度,当某意图在某路由上频繁失败时,提示维护者考虑新增路由或调整映射。这是"确定性为主、数据驱动改进"的折中。
ADR-5: SQL 护栏五层验证¶
上下文¶
LLM 生成的 SQL 可能语法错误、含危险操作、引用不存在的列、违反术语绑定、或扫垮生产库。需要多层独立校验。
决策¶
五层校验:①语法解析 ②策略禁词 ③AST 列引用 ④术语绑定语义 ⑤EXPLAIN 成本估算。
后果¶
| 正面 | 负面 |
|---|---|
| 多层独立校验,纵深防御 | 五层串行增加延迟 |
| 每层可独立测试 | 层间有重复(如列校验与术语绑定) |
反思与改进方向¶
五层护栏是安全设计的良好实践,但当前部分层用正则表达式实现(如术语绑定校验用正则匹配 wrong_table.column),脆弱且易绕过。
改进方向
将正则校验升级为基于 sqlglot AST 的结构化校验——解析 SQL 为语法树后,在树上做列引用与术语绑定检查,既准确又能处理复杂 SQL 结构(子查询、CTE、窗口函数)。详见 第 6 章。
ADR-6: Chat-First API 迁移¶
上下文¶
早期 API 是单次查询式(/query + /stream),不支持多轮对话与历史管理。需要迁移到会话式 API。
决策¶
以 /api/v1/chats 为核心的 Chat-First API,SSE over POST 流式响应。
后果¶
| 正面 | 负面 |
|---|---|
| 原生支持多轮对话与历史 | SSE over POST 非标准 EventSource |
| SSE 流式体验好 | 无浏览器原生自动重连 |
反思与改进方向¶
Chat-First 是正确方向。SSE over POST(而非 GET EventSource)是因为需要传 body,但牺牲了浏览器原生 EventSource 的自动重连。
改进方向
SSE 重连可考虑用 Last-Event-ID 头实现断点续传,提升弱网体验。
ADR-7: 多模型注册与降级链¶
上下文¶
不同任务(理解/生成/洞察)适合不同模型;单一模型可能不可用,需要降级。
决策¶
任务驱动路由(非提供商驱动),代码默认 + Admin DB 覆盖两层配置,每个模型配 Fallback 链。
当前代码默认值¶
| Task | 默认模型 | Fallback |
|---|---|---|
| supervisor / intent_parsing / query_understanding / planner / follow_up | deepseek-v4-flash |
qwen3.7-max → deepseek-v4-pro |
| sql_generation / sql_repair | deepseek-v4-pro |
qwen3.7-max → deepseek-v4-flash |
| insights / visualization / sql_explanation / knowledge_qa | qwen3.7-max |
deepseek-v4-pro → deepseek-v4-flash |
| Embedding | text-embedding-v4(部署 schema 1024 维;builtin 默认 1536,待统一) |
— |
嵌入维度不一致
代码库存在维度不一致:backend/app/models/registry.py 的 builtin 默认值为 1536,但部署 schema(data-plane/sql/001_create_schema.sql)所有 *_embedding 表实际列为 vector(1024)。当前以部署 schema 的 1024 维为准。维度统一已列为改进项(第 16 章),详见 第 5 章 5.3.4 与 第 10 章 Schema 深潜。
版本说明
以上是 backend/app/models/registry.py 的当前代码默认值。支持 Admin DB 在线覆盖。
后果¶
| 正面 | 负面 |
|---|---|
| 按任务特性选最优模型 | 配置复杂度增加 |
| Fallback 提升可用性 | Fallback 模型质量可能下降 |
反思与改进方向 ¶
任务驱动路由是好的,但当前内置配置绑死 DashScope(DeepSeek/Qwen 都走 DashScope 的 OpenAI 兼容接口),迁移到其他供应商成本高。
改进方向
抽象 provider 层,让每个模型独立配置 provider/api_key/base_url,支持 OpenAI/Anthropic/本地模型(Ollama/vLLM)无缝切换。这也是脱离单一供应商依赖的必要演进。
ADR-8: 语义版本化元数据¶
上下文¶
元数据资产会变更,需要版本控制来判断是否需要重新向量化(MAJOR 变更触发全量重向量化)。
决策¶
SemVer 版本号 + CI 强制检查(check_version_bump.py),MAJOR 变更触发全量重向量化。
后果¶
| 正面 | 负面 |
|---|---|
| 变更影响可追踪 | 版本号管理增加心智负担 |
| 重向量化自动化 | 需维护 CI 校验 |
反思与改进方向¶
SemVer 对元数据是合理选择。但全量重向量化成本高,MAJOR 变更时可能影响在线服务。
改进方向
支持增量重向量化:只对变更的资产重新生成 embedding,而非全量。通过 changeset 的 diff 精确定位变更资产。
ADR-9: 双数据库连接池¶
上下文¶
ORM 操作(Admin 管理)与 raw SQL(Agent pipeline)有不同的连接需求,混用连接池会相互影响。
决策¶
双池分离:ORM 池(SQLAlchemy async)+ Raw 池(psycopg async)。
后果¶
| 正面 | 负面 |
|---|---|
| 互不影响,各自调优 | 连接数翻倍 |
| 职责清晰 | 需维护两套配置 |
反思与改进方向¶
这是合理的工程实践。需注意连接总数 = 实例数 × (ORM池 + Raw池),多实例时可能耗尽 PG 连接。
改进方向
引入连接池监控(已有 Prometheus 指标),根据负载动态调整池大小;或考虑用 PgBouncer 做连接复用。
ADR-10: SQL-Only 缓存策略¶
上下文¶
是否缓存查询结果?缓存结果会带来数据新鲜度问题;只缓存 SQL 则保证每次拿最新数据。
决策¶
只缓存生成的 SQL 文本(pgvector 相似度 ≥ 0.92 命中,TTL 15 分钟),不缓存执行结果。
后果¶
| 正面 | 负面 |
|---|---|
| 数据始终新鲜 | 命中缓存仍需执行 SQL |
| 避免脏数据 | 对"慢查询"加速有限 |
反思与改进方向¶
这是数据敏感场景的正确取舍——宁愿慢一点也不要脏数据。但对"昨天的数据"这类低新鲜度要求的查询,可以更激进。
改进方向
引入分级缓存策略:对明确标注"可容忍 N 分钟延迟"的查询(如历史报表),可选缓存结果;对实时查询,保持 SQL-Only。让缓存策略由治理资产声明,而非全局一刀切。
ADR-11: 治理叠加层 (Governance Overlays)¶
上下文¶
多租户/多区域场景下,不同上下文需要不同的治理约束(如某租户不能看某域数据)。
决策¶
按 tenant/region/industry/compliance_pack 注入治理上下文,在 Supervisor 阶段一次性解析,下游共享。
后果¶
| 正面 | 负面 |
|---|---|
| 治理上下文统一 | 叠加层只是上下文注入 |
| 一次解析全链共享 | 数据库层无强制隔离 |
反思与改进方向 ¶
叠加层是"软治理"——靠 LLM 遵守 prompt 中的约束,不是数据库层的硬隔离。在高安全场景这不够。
改进方向
叠加 PostgreSQL Row-Level Security(RLS)做硬隔离:即使 LLM 生成越权 SQL,数据库层也会拒绝。这是从"软治理"到"纵深防御"的必要升级。
ADR-12: 三层治理模型¶
上下文¶
企业知识有不同层次:结构定义、术语消歧、业务规则。混在一起管理会混乱。
决策¶
三层治理:Layer 1 元数据契约 / Layer 2 术语治理 / Layer 3 业务规则与上下文。
后果¶
| 正面 | 负面 |
|---|---|
| 层次清晰,职责分离 | 资产类型多(9 类),学习成本高 |
| 各层可独立演进 | 跨层引用需 CI 校验 |
反思与改进方向¶
三层模型是合理的认知框架,但9 类资产对新手偏多。详见 第 9 章。
改进方向
提供资产编写的脚手架工具(CLI 交互式生成 + LLM 辅助补全),降低冷启动成本;合并使用频率低的资产类型。
ADR-13: 双语必备 (Bilingual Requirement)¶
上下文¶
系统需支持中英双语用户与多语言 LLM 检索。
决策¶
所有元数据同时维护 _zh/_en 字段,CI 强制校验成对存在。
后果¶
| 正面 | 负面 |
|---|---|
| 多语言检索与展示 | 编写工作量翻倍 |
| CI 保证一致性 | 维护两套描述易漂移 |
反思与改进方向¶
双语对国际化场景必要,但维护成本高。
改进方向
引入 LLM 辅助翻译:写一种语言,CI 自动生成另一种语言的候选,人工校验。降低双语维护成本。
ADR-14: PII 分级暴露¶
上下文¶
不同列的敏感度不同,需要对 LLM 区分暴露策略。
决策¶
llm_exposure_policy: visible/masked/hidden,hidden 列完全从 LLM 上下文移除(不参与 embedding)。
后果¶
| 正面 | 负面 |
|---|---|
| PII 保护精细 | 需人工标注每列策略 |
| hidden 列不泄露给 LLM | masked 的脱敏样本可能误导 |
反思与改进方向¶
分级暴露是好的设计。但masked 的脱敏样本可能让 LLM 误解数据分布。
改进方向
masked 策略应附带"这是脱敏样本,真实分布不同"的明确提示,避免 LLM 基于脱敏数据做错误推断。
ADR-15: LLM 驱动查询分解¶
上下文¶
早期用正则分词提取搜索关键词,语言相关、领域相关,效果差。
决策¶
用 LLM(复用 Query Understanding 输出)提取搜索关键词,零额外 LLM 调用。
后果¶
| 正面 | 负面 |
|---|---|
| 语言无关、领域无关 | 依赖 QU 的 LLM 输出质量 |
| 零额外延迟 | QU 失败时无 fallback |
反思与改进方向¶
复用 QU 输出是巧妙的优化。但 QU 失败时检索会受影响。
改进方向
保留一个轻量的正则 fallback,当 QU 输出异常时退化到正则分词,保证检索不中断。
ADR-16: 术语绑定强路由 (Term Binding Strong Routing)¶
上下文¶
术语(如"GMV")到物理列的映射需要贯穿全管道,否则 LLM 可能把"价格"引用到错误的表。
决策¶
mapped_asset_id 贯穿检索→重排→Prompt→护栏→自愈五层,作为强路由信号。
后果¶
| 正面 | 负面 |
|---|---|
| 术语消歧强制正确 | 需预先维护术语绑定 |
| 全链路可追溯 | 绑定错误会污染全链 |
反思与改进方向¶
这是 TTD 的核心创新点之一,显著提升了 SQL 准确性。是值得保留的好设计。
改进方向
增加术语绑定的"置信度"概念:当术语匹配模糊时,不强绑定而是提供候选,让 LLM 在约束下选择。避免错误绑定的硬污染。
ADR-17: Better Auth 统一认证¶
上下文¶
需要统一身份源,避免 Backend 维护第二套认证。
决策¶
Better Auth 托管在 Next.js,签发 RS256 JWT,Backend 通过 JWKS 本地验签,admin/user 两级角色。
后果¶
| 正面 | 负面 |
|---|---|
| 单一身份源 | 认证耦合到 Next.js |
| 本地验签低延迟 | 角色模型简单(仅 admin/user) |
反思与改进方向¶
Better Auth 是合理选择。但角色模型过简单,无 Organization/Team/RBAC。
改进方向
演进到基于角色的访问控制(RBAC)+ 资源级权限,支持多团队协作。这与 ADR-11 的 RLS 升级配套。
ADR-18: 语义查询规划器 (Advisory Mode)¶
上下文¶
纯 LLM 生成 SQL 在复杂 join 上易出错;纯确定性编译 SQL 灵活性不足。
决策¶
Advisory 模式:用 KMB Steiner 树做 join 路由 + 代数重写提供建议,不直接编译 SQL,而是增强 LLM 生成。失败安全回退为空。
后果¶
| 正面 | 负面 |
|---|---|
| 确定性规划 + LLM 灵活性 | Advisory 太弱,LLM 可能忽略建议 |
| 失败不阻塞管道 | 建议采纳率难度量 |
反思与改进方向 ¶
Advisory 模式是务实的折中,但"建议"可能被 LLM 忽略,削弱了规划器的价值。
改进方向
两步走:短期增加"建议采纳率"指标,量化规划器效果;长期在 Phase 4 让规划器成为默认路径,LLM SQL 仅用于 fallback/repair(这与现有 Rollout 路线图一致)。详见 第 6 章。
小结¶
本章要点:
- 18 条 ADR 覆盖架构、编排、安全、模型、治理、性能各维度。
- 多数决策是业界主流实践(ADR-1/5/9/10/17),部分是工程权衡(ADR-2 三引擎一体、ADR-3 轻量 Supervisor、ADR-11 软治理、ADR-18 Advisory 模式)。
- 每条 ADR 都附反思与改进方向,这些改进点汇总在 第 16 章 演进路线。
下一章:第 4 章 Agent 编排与状态机——进入 AI Agent 内核,详解 LangGraph 图拓扑、7 路由、状态机与修复循环。