跳转至

关键设计决策 (Architecture Decision Records)

概述

本文档以 ADR(Architecture Decision Record)格式,记录项目中所有关键架构决策。 每条决策包含 上下文(Context)决策(Decision)后果(Consequence) 及相关文件引用。


决策总览

ADR 决策名称 影响范围 关键文件
ADR-1 双面板分离 (Two-Plane Separation) 整体架构 semantic-plane/, data-plane/
ADR-2 R/V/G 三引擎架构 (Triple-Engine) 数据存储 backend/app/agents/semantic_plane/
ADR-3 Supervisor 轻量化 Agent 编排 backend/app/agents/supervisor.py
ADR-4 显式路由决策 (Explicit Routing) Agent 编排 backend/app/agents/sub_agents/router.py
ADR-5 SQL 护栏多层验证 安全 backend/app/agents/sub_agents/guardrails.py
ADR-6 Chat-First API 迁移 API 层 backend/app/api/chats.py
ADR-7 多模型注册与降级链 模型层 backend/app/models/registry.py
ADR-8 语义版本化元数据 治理流程 scripts/check_version_bump.py
ADR-9 双数据库连接池 运行时 backend/app/main.py, backend/app/db/engine.py
ADR-10 SQL-Only 缓存策略 性能 backend/app/memory/sql_cache.py
ADR-11 治理叠加层 (Governance Overlays) 多租户 backend/app/skills/governance.py
ADR-12 三层治理模型 元数据治理 semantic-plane/
ADR-13 双语必备 (Bilingual Requirement) 元数据规范 semantic-plane/
ADR-14 PII 分级暴露 安全与隐私 semantic-plane/, backend/app/agents/sub_agents/
ADR-15 LLM 驱动查询分解 (LLM Query Decomposition) 检索管道 backend/app/skills/query_decomposer.py
ADR-16 术语绑定强路由 (Term Binding Strong Routing) 全管道 backend/app/skills/rag.py, backend/app/skills/reranker.py
ADR-17 Better Auth 统一认证 (Unified Auth via Better Auth) 认证与授权 web/lib/auth.ts, backend/app/security/auth.py

ADR-1: 双面板分离 (Two-Plane Separation)

状态:已采纳

上下文 (Context)

元数据治理需要版本控制、代码审查和可追溯的变更历史;而运行时查询则需要低延迟、高吞吐的数据库访问。这两种诉求在迭代节奏和扩缩容策略上存在根本差异。

决策 (Decision)

将系统拆分为两个独立面板:

  • Semantic Plane:治理资产以 YAML 文件形式存储在 Git 仓库中,通过 PR 审查流程管理变更
  • Data Plane:运行时执行层使用 PostgreSQL(本地)/ Redshift(生产)提供高性能查询

后果 (Consequence)

正面 负面
Git 原生的版本控制与审查流程 需要额外的同步管道
治理资产可独立于服务部署 存在短暂一致性窗口
开发者可用熟悉的 IDE 编辑元数据 需要 CI 校验保证 YAML 合法性

相关文件

semantic-plane/          # 治理资产(YAML + Git)
data-plane/              # 运行时执行层
backend/app/agents/semantic_plane/  # 同步管道:S3 → R+V+G pipeline

ADR-2: R/V/G 三引擎架构 (Triple-Engine Architecture)

状态:已采纳

上下文 (Context)

NL2SQL 场景下存在三种截然不同的检索模式:

  1. 精确匹配:通过 ID/名称查找具体表或指标定义
  2. 语义召回:通过自然语言相似度检索相关元数据
  3. 关系推理:沿实体关系图谱发现关联和路径

单一存储引擎无法同时优化这三种模式。

决策 (Decision)

采用 Relational + Vector + Graph 三引擎并存架构:

引擎 技术选型 用途
Relational Aurora PostgreSQL 结构化 CRUD、精确查询、JOIN 规则
Vector pgvector 扩展 Embedding 存储、语义相似度检索
Graph Apache AGE 扩展 实体关系图谱、路径推理、血缘追溯

统一数据库实例

三个引擎共享同一 PostgreSQL 实例(通过扩展实现),降低运维复杂度。

后果 (Consequence)

正面 负面
每种检索模式获得最佳性能 每条资产存储 3 次(结构化、向量化、图化)
单一 PG 实例减少运维开销 Backend pipeline 需维护三份写入一致性
可按需组合多种检索方式 调试时需理解三种存储的数据模型

相关文件

backend/app/agents/semantic_plane/  # 关系 upsert + AGE 图谱同步 + Embedding 生成
data-plane/sql/001_create_schema.sql  # DDL(含 pgvector + AGE 初始化)

ADR-3: Supervisor 轻量化 (Lightweight Supervisor)

状态:已采纳

上下文 (Context)

早期设计中 Supervisor 承载了意图解析、路由、检索、生成等全部逻辑,导致单文件超过 1000 行,测试困难,修改任一功能需回归全链路。

决策 (Decision)

Supervisor 仅执行两项职责:

  1. Embedding 生成:对用户输入进行向量化
  2. Governance Overlay 解析:注入租户/区域/合规约束

所有业务逻辑委派给独立的 sub-agent。

Supervisor → Query Understanding → Router → [Sub-agents...]

后果 (Consequence)

正面 负面
各 sub-agent 可独立单元测试 调用链更长,需要良好的 tracing
新路由/新 agent 可独立开发 状态传递依赖 LangGraph State
Supervisor 职责清晰,代码简洁 初学者需理解整体编排拓扑

相关文件

backend/app/agents/supervisor.py              # Supervisor 入口
backend/app/agents/sub_agents/query_understanding.py  # 意图结构化
backend/app/agents/sub_agents/router.py       # 路由决策

ADR-4: 显式路由决策 (Explicit Routing)

状态:已采纳

上下文 (Context)

"学习型路由"(如 classifier 模型)虽然灵活,但在出错时难以定位原因,且训练数据不足时表现不稳定。对于关键业务系统,可解释性和确定性优先于灵活性。

决策 (Decision)

Router sub-agent 基于结构化意图分析,显式选择以下 7 条命名路由之一:

路由名称 用途
kpi_lookup 指标快速查询
nl2sql_query 标准 NL2SQL 生成
deep_analysis_workflow 深度分析流程
analytical_workflow 多步分析工作流
graph_reasoning 图谱推理
business_knowledge_qa 业务知识问答
clarification_required 需要澄清

后果 (Consequence)

正面 负面
路由决策完全可解释、可审计 新增路由需手动维护映射表
调试时可直接定位失败节点 边界场景需人工定义归属
可针对单一路由做性能优化 不如学习型路由自适应

相关文件

backend/app/agents/sub_agents/router.py   # 路由决策逻辑
# 内部关键映射:_ROUTE_PLANS, _INTENT_TO_ROUTE

ADR-5: SQL 护栏多层验证 (SQL Guardrails)

安全关键决策

上下文 (Context)

LLM 生成的 SQL 可能包含:

  • 语法错误导致执行失败
  • DELETE/UPDATE/DROP 等破坏性操作
  • 全表扫描或笛卡尔积导致资源耗尽
  • 术语绑定违规 — 将绑定列引用到错误的物理表(如"价格"绑定 fact_transaction.price,却生成 dim_article.price)

需要在执行前进行多层防护。

决策 (Decision)

实施五层验证流水线:

层级 验证内容 工具/方式
Layer 1 语法解析 SQL 语法正确性 sqlparse AST 解析
Layer 2a 安全扫描 DML 操作检测、注入检测 禁止关键词匹配 + 模式识别
Layer 2c 列引用校验 引用不存在的列 AST 列提取 + 白名单对比
Layer 2d 术语绑定校验 绑定列被引用到错误表 正则 + term_bindings 元数据
Layer 3 成本估算 预估行数/开销超阈值拦截 EXPLAIN 执行计划分析

关键配置

TTD_FORBIDDEN_KEYWORDS=DELETE,UPDATE,DROP,TRUNCATE,ALTER,INSERT,GRANT,REVOKE
TTD_MAX_ESTIMATED_ROWS=1000000
TTD_MAX_ESTIMATED_COST=50000

后果 (Consequence)

正面 负面
有效阻止数据变更和资源滥用 复杂合法查询偶尔被误拦截
自动注入 LIMIT 兜底 EXPLAIN 增加约 10-50ms 延迟
术语绑定语义保障 — 防止 LLM 将度量落到错误的维度表 Layer 2d 仅对有 mapped_asset_id 的术语生效
五层独立,可按需启停 需维护禁止词列表更新

相关文件

backend/app/agents/sub_agents/guardrails.py  # 五层验证主逻辑
backend/app/skills/sql_column_validator.py   # Layer 2c AST 列校验
backend/app/security/guard.py                # 安全扫描模块

ADR-6: Chat-First API 迁移 (Chat-First API Migration)

状态:已采纳

上下文 (Context)

早期 API 采用单次请求/响应模式(/query/stream),无法支撑:

  • 多轮对话上下文保持
  • 对话历史回放
  • 中间结果的增量推送

决策 (Decision)

  • 主 API/api/v1/chats 会话式端点,支持完整对话生命周期
  • 流式传输:SSE(Server-Sent Events)通过 POST 请求(非 GET EventSource)
  • 向后兼容:保留 /query + /stream 作为 legacy 接口

后果 (Consequence)

正面 负面
原生多轮对话,上下文自动维护 前端需实现 SSE over POST
对话历史持久化,可回放 会话管理增加服务端状态
统一入口简化客户端集成 Legacy 接口仍需维护一段时间

相关文件

backend/app/api/chats.py     # Chat-First 主端点
backend/app/api/router.py    # API 路由注册(chats 为 primary)
backend/app/session/          # 会话管理模块

ADR-7: 多模型注册与降级链 (Multi-Model Registry with Fallback)

状态:已采纳

上下文 (Context)

依赖单一 LLM 提供商存在风险:

  • 单点故障导致全系统不可用
  • 不同任务对模型能力要求不同(推理 vs 编码 vs 速度)
  • 成本优化需灵活切换

决策 (Decision)

采用 任务驱动的模型路由(非提供商驱动),配置可降级链:

任务类型 首选模型 降级模型
SQL 生成 qwen3-coder-plus deepseek-v3.2
意图理解 claude-4.5-sonnet qwen3-max
通用推理 deepseek-v3.2 qwen3-max
代码审核 claude-4.5-sonnet qwen3-coder-plus

运行时覆盖

管理员可通过数据库配置动态覆盖模型路由,无需重启服务。

后果 (Consequence)

正面 负面
单模型宕机时自动降级 需维护多提供商 API Key
按任务优化成本和质量 不同模型输出格式需统一适配
可灵活接入新模型 降级链配置需持续调优

相关文件

backend/app/models/registry.py  # 模型注册表与降级链配置

ADR-8: 语义版本化元数据 (Semantic Versioning for Metadata)

状态:已采纳

上下文 (Context)

元数据变更会触发下游效应:

  • MAJOR 变更(SQL 逻辑修改)需要重新向量化
  • MINOR 变更(描述更新)可增量更新 embedding
  • PATCH 变更(typo 修复)无需触发任何管道

需要一种机制区分变更影响级别。

决策 (Decision)

所有元数据资产遵循 SemVer 规范,CI 强制检查版本递增:

变更类型 版本变化 触发动作
格式修正、typo、reviewed_at 更新 PATCH
描述更新、新增同义词、exceptions MINOR 增量 re-embedding
SQL 逻辑、rule_expressionbound_assets MAJOR 全量 re-vectorize + 审批

后果 (Consequence)

正面 负面
精确控制下游管道触发时机 贡献者需理解版本规则
MAJOR 变更可触发额外审批流程 偶尔出现版本争议
变更历史清晰可追溯 CI 检查增加 PR 合入时间

相关文件

scripts/check_version_bump.py  # CI 版本检查脚本
.github/workflows/             # 校验 Workflow

ADR-9: 双数据库连接池 (Dual Database Pool)

状态:已采纳

上下文 (Context)

后端存在两类数据库访问模式:

  1. CRUD 操作:会话管理、审计日志等,适合 ORM
  2. AI 管道:高吞吐 embedding 检索、批量向量查询,ORM 开销不可接受

决策 (Decision)

维护两个独立连接池:

连接池 技术选型 用途
ORM Pool SQLAlchemy async pool CRUD、会话、审计
Raw Pool psycopg async pool Embedding 检索、向量查询、图谱遍历

后果 (Consequence)

正面 负面
AI 管道无 ORM 序列化开销 两套连接池需分别监控
连接池参数可独立调优 代码中需明确选择使用哪个池
ORM 层仍享有 Schema 验证便利 数据库总连接数需合理规划

相关文件

backend/app/main.py         # Lifespan 中初始化两个连接池
backend/app/db/engine.py    # 连接池配置与管理

ADR-10: SQL-Only 缓存策略 (SQL-Only Cache)

状态:已采纳

上下文 (Context)

缓存策略需要在性能和数据新鲜度间权衡:

  • 缓存执行结果:快但可能返回过时数据
  • 缓存生成的 SQL:仍需执行但保证数据实时性
  • 完全不缓存:每次重新生成 SQL,延迟高

决策 (Decision)

仅缓存生成的 SQL 语句,使用 pgvector 语义相似度匹配:

缓存匹配流程

用户问题 → Embedding → pgvector 相似度搜索
→ 相似度 ≥ 0.92 → 命中缓存 SQL → 重新执行获取最新数据
→ 相似度 < 0.92 → 生成新 SQL

关键配置

参数 说明
TTD_SQL_CACHE_SIMILARITY 0.92 语义相似度阈值
TTD_SQL_CACHE_TTL_MINUTES 15 缓存过期时间

后果 (Consequence)

正面 负面
数据始终是最新的 仍需执行 SQL,无法跳过查询开销
避免 LLM 重复生成,降低延迟和成本 语义相似度阈值需精细调优
缓存失效简单(TTL 即可) 同义问法可能未命中缓存

相关文件

backend/app/memory/sql_cache.py  # SQL 缓存实现

ADR-11: 治理叠加层 (Governance Overlays)

状态:已采纳

上下文 (Context)

多租户场景下,不同租户/区域/合规要求需要不同的数据访问约束,但不希望通过代码分支实现差异化。

决策 (Decision)

在 Supervisor 入口处注入 Governance Overlays,作为约束条件贯穿后续所有 sub-agent:

请求 → Supervisor → 解析 tenant/region/compliance
                  → 注入 overlay 约束到 State
                  → Sub-agents 感知并遵守约束

Overlay 类型包括:

  • Tenant Overlay:数据范围隔离
  • Region Overlay:地域合规(如 GDPR、数据出境)
  • Compliance Overlay:行业合规(如金融监管)

后果 (Consequence)

正面 负面
多租户无需代码分支 Overlay 规则需持续维护
新约束可热加载 Sub-agent 需正确解读 overlay
统一入口便于审计 复杂 overlay 组合需充分测试

相关文件

backend/app/skills/governance.py  # Overlay 解析与注入
backend/app/agents/supervisor.py  # Supervisor 中 overlay 初始化

ADR-12: 三层治理模型 (Three-Layer Governance Model)

状态:已采纳

上下文 (Context)

不同类型的元数据有不同的变更频率、负责人和审批流程:

  • 表/列定义相对稳定,由 DBA 维护
  • 术语网络持续演进,由 Data Steward 维护
  • 业务规则频繁变化,由业务专家维护

决策 (Decision)

将治理模型分为三层:

层级 范围 负责人 变更频率
Layer 1 规范元数据 表、列、指标、JOIN 规则、Few-shot DBA / Data Engineer 低(认证后稳定)
Layer 2 术语治理 术语生命周期、消歧、关系网络 Data Steward 中(持续演进)
Layer 3 业务知识 业务规则、上下文假设、约束 Domain SME 高(随业务变化)

后果 (Consequence)

正面 负面
权责清晰,各层独立演进 跨层引用需严格校验
Layer 1 稳定性保障查询质量 三层协调需要治理流程支撑
Layer 3 可快速响应业务变化 新贡献者需理解层级划分

相关文件

semantic-plane/domains/             # Layer 1: 表/指标
semantic-plane/terms/               # Layer 1+2: 术语
semantic-plane/term_relationships/  # Layer 2: 术语关系
semantic-plane/business_rules/      # Layer 3: 业务规则
semantic-plane/business_contexts/   # Layer 3: 业务上下文
scripts/validate_cross_layer_refs.py  # 跨层引用校验

ADR-13: 双语必备 (Bilingual Requirement)

状态:已采纳

上下文 (Context)

  • 终端用户主要使用中文提问
  • 系统内部(SQL、代码、API)使用英文
  • LLM 需要同时理解中英文语义

决策 (Decision)

所有元数据字段必须提供 _zh_en 双语对:

# 示例
business_name_zh: "订单总金额"
business_name_en: "Total Order Amount"
description_zh: "包含已完成订单的总交易金额(含税)"
description_en: "Total transaction amount of completed orders (tax inclusive)"

规范要求

  • 描述必须阐释业务语义,不可仅描述物理存储
  • _zh_en 字段必须成对出现,缺一不可
  • CI 校验会检查双语字段完整性

后果 (Consequence)

正面 负面
LLM 可根据用户语言偏好选择引用 元数据编写工作量翻倍
中文用户体验自然流畅 翻译质量需人工把控
Embedding 可分别为两种语言生成 双语一致性维护成本

相关文件

semantic-plane/schemas/          # JSON Schema 中定义双语字段约束
scripts/validate_all_yaml.py     # CI 双语字段完整性检查

ADR-14: PII 分级暴露 (PII Tiered Exposure)

安全关键决策

上下文 (Context)

NL2SQL 系统中,LLM 需要了解数据库 Schema 才能生成正确 SQL,但部分列包含 PII(个人可识别信息),如客户姓名、手机号、身份证号等。需要在 SQL 生成质量和隐私保护之间取得平衡。

决策 (Decision)

为每个列定义 llm_exposure_policy,实施三级暴露策略:

策略 LLM 可见性 适用场景 示例
visible 完全可见 非敏感业务字段 order_id, amount
masked 仅可见脱敏样本 需要理解格式但不需真实值 phone138****1234
hidden 完全不可见 高敏感 PII id_card_number
# 元数据定义示例
columns:
  - column_id: col_customer_name
    sensitivity_level: pii
    llm_exposure_policy: hidden

  - column_id: col_order_amount
    sensitivity_level: internal
    llm_exposure_policy: visible

强制执行

hidden 策略的列不参与 embedding 生成,从源头杜绝 PII 泄露到 LLM 上下文。

后果 (Consequence)

正面 负面
PII 从源头阻断,不进入 LLM hidden 列上的查询可能失败
策略声明式,易于审计 需要为每列正确标注策略
masked 兼顾格式理解和隐私 脱敏逻辑需按类型定制

相关文件

semantic-plane/domains/    # 列定义中的 llm_exposure_policy
backend/app/agents/sub_agents/data_retrieval.py  # 运行时策略执行

ADR-15: LLM 驱动查询分解 (LLM Query Decomposition)

上下文 (Context)

Engine 1(术语精确匹配)需要将用户自然语言问题拆分为搜索关键词,逐词对 business_term 表做 ILIKE 匹配。 最初实现使用正则表达式(按中文虚词切分 + 英文空格分词),但这种方法:

  • 不通用 — 虚词列表硬编码,只覆盖了部分中文语法,对其他语言无效
  • 不灵活 — 口语化、缩写、行话无法正确切分
  • 反模式 — 在 data agent 中使用正则做 NLU 是 step backward;任何 case-by-case 的 regex 修补都会越来越脆弱

决策 (Decision)

  1. 创建独立 skill query_decomposer.py,封装两种关键词提取方式:
  2. from_query_understanding(qu) — 复用 QU 节点已经用 LLM 解析出的 entities.termsmetric_candidates 等,零额外延迟
  3. QueryDecomposer(registry).extract_search_terms(query) — 独立 LLM 调用(仅当 QU 未运行时使用)

  4. data_retrieval_node 优先从 state["query_understanding"] 提取搜索关键词传入 Engine 1

  5. exact_term_match() 接受 search_terms: list[str] | None 参数,不再包含任何分词逻辑

  6. 完全移除 _tokenize_for_term_match() 和相关正则常量

后果 (Consequence)

正面 负面
语言无关、领域无关 — LLM 天然理解任何语言和领域术语 如果 QU 节点跳过(极端路径),需 fallback 到独立 LLM 调用
零额外延迟 — 正常流程复用已有的 QU LLM 输出 QueryDecomposer 独立调用增加 ~200ms(仅 fallback)
不需要维护停用词/虚词列表 LLM 极低概率返回不合理关键词(有 fallback_split 兜底)
skill 可独立被其他节点复用(如 corrective_retrieval)

相关文件

backend/app/skills/query_decomposer.py          # 独立 skill
backend/app/agents/sub_agents/data_retrieval.py  # 消费 QU 输出
backend/app/skills/rag.py                        # exact_term_match 接受 search_terms

ADR-16: 术语绑定强路由 (Term Binding Strong Routing)

上下文 (Context)

Semantic Plane 中的 business_term 通过 mapped_asset_id 字段绑定到物理列或指标。 例如 term_hm_price → col_hm_txn_price(属于 fact_transaction)。 此前这些绑定信息虽然存在于元数据,但 runtime 并未将其用作强约束—— LLM 仍可能将"价格"错误地映射到 dim_article.price(该表不含此列,或此列含义不同)。

根因是信号在管道中逐步衰减: - Engine 1 命中术语后,绑定信息仅用于图遍历的入口点 - Reranker 不感知绑定关系 - Prompt 只给白名单,未明确说 "价格必须从 fact_transaction 取" - Guardrails 只校验列是否存在,不校验列是否在正确的表 - 自愈只按错误文本搜索,不按治理知识修复

决策 (Decision)

将术语绑定作为强路由信号贯穿整个管道:

  1. 检索阶段 — 构建 _term_bindings 视图注入 retrieval_results
  2. Reranker — Step 0 术语绑定加分/降权(绑定列 → 0.95,父表 → +0.25,非绑定表 → -0.10)
  3. Prompt Assembler — 新增 ## ⚠ 术语绑定约束 段落,生成 必须使用 table.column 强指令
  4. Guardrails Layer 2d — 拦截 wrong_table.column 引用
  5. Corrective Retrieval — 检测到绑定违规后,优先按绑定关系拉取正确表和列
  6. Engine R+ 无条件合并 — 关键词列命中不再受 vector 命中数量门控

后果 (Consequence)

正面 负面
从根源消除"度量列落到错误表"的幻觉类错误 需要术语正确绑定(脏元数据会导致误拦截)
五层强化互为兜底,单层失效不影响整体 管道节点间共享 _term_bindings 增加隐式耦合
自愈路径精准 — 直接告知正确 table.column Layer 2d 正则匹配对复杂别名场景有边界
不需要额外 LLM 调用 — 纯规则引擎

相关文件

backend/app/skills/rag.py                        # _build_term_bindings + R+ 无条件合并
backend/app/skills/reranker.py                   # _boost_term_bound_assets
backend/app/prompt/assembler.py                  # _build_term_binding_context
backend/app/agents/sub_agents/guardrails.py      # _validate_term_bindings (Layer 2d)
backend/app/skills/corrective_retrieval.py       # _correct_via_term_bindings

ADR-17: Better Auth 统一认证 (Unified Auth via Better Auth)

状态:已采纳

上下文 (Context)

原系统使用共享 HS256 JWT Secret 进行认证:前端签发 Token、后端验证。这种方式存在以下问题:

  1. 无用户管理:没有注册/登录流程,用户身份仅来自手动生成的 Token
  2. 无会话管理:Token 一旦签发无法撤销,无 refresh 机制
  3. 安全性低:HS256 共享密钥意味着前后端都持有签名能力,违反最小权限原则
  4. Admin 无保护:后台管理 API 缺乏角色校验,依赖 TTD_DEBUG 旁路

决策 (Decision)

引入 Better Auth 作为统一身份与会话权威,托管在 Next.js 内:

  1. Better Auth 作为唯一 IdP — 在 Next.js 内运行,管理注册、登录、会话、JWT 签发
  2. RS256 + JWKS 验签 — 放弃 HS256 共享密钥,改用公私钥对。FastAPI 通过 JWKS 端点获取公钥本地验签
  3. Better Auth user.id 作为全系统主体标识 — 直接映射到现有 Chat/Feedback 表的 user_id 字段
  4. 全局角色模型 — 仅 admin/user 两级,通过 Better Auth Admin plugin 管理角色
  5. 数据共用 PostgreSQL — Better Auth 的 5 张表 (user, session, account, verification, jwks) 存于同一 PG 实例
  6. JWT 仅作 API 访问令牌 — 浏览器主会话由 Better Auth cookie 管理,减少长期 Token 风险

后果 (Consequence)

正面 负面
完整的注册/登录/OAuth/会话管理 新增 5 张 auth 表(已用迁移隔离)
RS256 非对称签名,后端仅持有公钥 Better Auth 与 Next.js 强耦合
Admin 路由有强制角色校验 现有裸 Token 使用者需迁移
JWKS 公钥缓存,无逐请求 introspection 开销 需部署时确保 JWKS 端点可达
可渐进添加 OAuth provider (GitHub) 迁移期需维护 HS256 回退逻辑

相关文件

web/lib/auth.ts                     # Better Auth 服务端配置
web/lib/auth-client.ts              # 客户端 auth SDK
web/app/api/auth/[...all]/route.ts  # Auth API 路由处理器
web/app/(auth)/                     # 登录/注册页面
web/lib/api/client-config.ts        # Token 注入到 FastAPI 请求
backend/app/security/auth.py        # JWKS 验签 + UserContext + require_admin
backend/app/config.py               # better_auth_url, better_auth_issuer, jwks_cache_ttl
backend/app/api/admin/router.py     # Admin 路由保护 (Depends(require_admin))

附录:决策演进记录

版本说明

本文档随项目演进持续更新。新增 ADR 请遵循以下模板:

## ADR-N: 决策标题 (English Title)

### 上下文 (Context)
为什么需要做这个决策?

### 决策 (Decision)
具体选择了什么方案?

### 后果 (Consequence)
带来了哪些正面和负面影响?

### 相关文件
涉及哪些代码文件?