Files
geo/docs/claude_architecture_review_3.md
T
root de30497f59 feat: add tenant and user management with migrations, handlers, and tests
- Implemented tenant and user management features including:
  - Tenant creation and management with associated migrations.
  - User creation and management with associated migrations.
  - Tenant membership management with associated migrations.
  - Platform user roles management with associated migrations.
  - Quota management with associated migrations.
  - Article and template management with associated migrations.
- Added HTTP handlers for templates and workspaces.
- Created tests for protected and public routes.
- Introduced a script to check tenant scope in SQL queries.
- Documented task plan for backend completion and frontend foundation.
2026-04-01 00:58:42 +08:00

319 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GEO 平台技术架构深度审查报告(第三轮)
> 在前两轮审查(功能覆盖度 + 5 万并发初审)基础上,交叉审查全部 6 份文档(共 ~4700 行),聚焦**跨文档一致性、数据流完整性、实施风险**三个维度。
---
## 一、跨文档一致性审查
现有文档体系:
| 文档 | 行数 | 定位 |
| --- | --- | --- |
| [geo-platform-ops-admin-tech-architecture-v1.md](file:///d:/project/geo/docs/geo-platform-ops-admin-tech-architecture-v1.md) | 1257 | 🏗️ 主架构 |
| [geo-platform-prd-v1.md](file:///d:/project/geo/docs/geo-platform-prd-v1.md) | 1079 | 📋 租户端 PRD |
| [geo-platform-ops-admin-prd-v1.md](file:///d:/project/geo/docs/geo-platform-ops-admin-prd-v1.md) | 652 | 📋 平台管理端 PRD |
| [media-binding-and-publish-v1.md](file:///d:/project/geo/docs/media-binding-and-publish-v1.md) | 562 | 🔌 插件方案 |
| [qdrant-rag-architecture-v1.md](file:///d:/project/geo/docs/qdrant-rag-architecture-v1.md) | 651 | 🧠 RAG 方案 |
| [question-driven-monitoring-design-v1.md](file:///d:/project/geo/docs/question-driven-monitoring-design-v1.md) | 495 | 📊 数据监控方案 |
### 问题 1:RAG 组件未纳入主架构图和交互图
[qdrant-rag-architecture-v1.md](file:///d:/project/geo/docs/qdrant-rag-architecture-v1.md) 定义了完整的检索链路:`query rewrite → filter → Qdrant 检索 → rerank → 上下文拼装 → 提交模型`。但主架构中:
- §8 交互图中 `worker-generate → Qdrant` 的箭头缺失(只有 `worker-knowledge → QD`
- 文章生成链路(§10.1)只提到"调用模型"**没有 RAG 检索步骤**
- 后端模块职责中无 `shared/rag``shared/retrieval` 模块
> [!WARNING]
> **影响**`worker-generate` 生成文章时需要从 Qdrant 检索品牌知识,这条数据流在主架构中完全不可见。开发时容易遗漏 RAG 集成。
>
> **建议**
> 1. 交互图中为 `worker-generate → Qdrant` 增加箭头
> 2. §10.1 生成链路在步骤 3 和 4 之间插入 "RAG 检索品牌知识上下文"
> 3. 后端目录中增加 `internal/shared/retrieval` 模块
### 问题 2media-binding 插件协议与主架构定义存在分歧
| 对比项 | media-binding-v1.md | 主架构 §13.8 |
| --- | --- | --- |
| 绑定落库 | 前端调用后端 API 保存 | 插件**双通道**:回传前端 + 回调 API 落库 |
| 会话管理 | 无 `plugin_session` 概念 | 有 `plugin_sessions` 表 + `session_token` |
| 回调路径 | `POST /api/platform-accounts/bind` | `POST /api/callback/plugin/bind` |
| 发布记录 | `POST /api/publish-records` | `POST /api/callback/plugin/publish` |
> **结论**:主架构 §13.8 的设计更完善(双通道 + 会话令牌 + 审计),但 media-binding-v1.md 还是旧方案。**两份文档需要统一**,否则前端、插件、后端开发人员会看不同的接口文档。
### 问题 3:数据追踪的数据模型字段不一致
| 对比项 | question-driven-monitoring-v1.md | 主架构 §13.11.6 |
| --- | --- | --- |
| 汇总表名 | `question_model_daily_metrics` | 未出现此表(但有 `keyword_model_daily_metrics` |
| `question_monitor_runs` 字段 | 有 `run_at``task_batch_id` | 缺少 `run_at``task_batch_id` |
| `question_monitor_parse_results` 字段 | 有 `citation_platform_count``parser_version` | 缺少这两个字段 |
| 保留策略 | 明确 30/90/180 天分级 | 未提及保留策略 |
> **建议**:主架构 §13.11.6 的 Schema 应与 [question-driven-monitoring-design-v1.md](file:///d:/project/geo/docs/question-driven-monitoring-design-v1.md) 完全对齐,特别是补充 `question_model_daily_metrics` 表和缺失字段。
### 问题 4RAG 中的 `knowledge_chunks_meta` 表未出现在主架构 Schema
[qdrant-rag-architecture-v1.md](file:///d:/project/geo/docs/qdrant-rag-architecture-v1.md) §6.1 提到 PostgreSQL 需保存 `knowledge_chunks_meta`(chunk 元数据和状态),但主架构 §13.11.4 知识库 Schema 中**没有这张表**。这意味着:
- 每个文档被切成多少 chunk、每个 chunk 的 token 数、status 在 PostgreSQL 中无处存储
- chunk 重建/版本化时没有元数据支持
> **建议**:在 §13.11.4 增加 `knowledge_chunks_meta` 表:
> ```
> | knowledge_chunks_meta | id, knowledge_item_id, chunk_index, token_count, qdrant_point_id, item_version, status |
> ```
### 问题 5RAG 中的 `retrieval_logs` 和 `rag_eval_cases` 未纳入主架构
RAG 文档要求记录每次检索的 query/filter/rerank 结果用于 badcase 分析,但主架构没有对应表和模块。V1 如果不记录,后续无法评估 RAG 精准度。
> **建议**:如果目标是"足够简单",V1 可以先将检索日志写入**文件日志**而非数据库表,后续再升级。在架构中明确这一策略。
### 问题 6PRD 中定义了 `kol` 角色但无实际权限描述
主架构 §6.2.2 新增 `kol`(用于某个专家的 prompt 从底层共享),但:
- 租户端 PRD 只定义了 管理员/编辑/只读 三种角色
- `kol` 角色的具体权限范围(能看什么、能做什么)未在任何文档中定义
- PRD 中的 `viewer`(只读成员)角色被删除
> **建议**:保留 `viewer`、额外增加 `kol`,并在权限矩阵中补充 `kol` 的能力边界。
### 问题 7`article_templates` 表的 `tenant_id` 字段暗示模板是租户级的
主架构 §13.11.2 中 `article_templates``tenant_id`。但:
- 平台端 PRD §8.5 明确有"系统模板管理"功能
- 平台级模板应 `tenant_id = NULL` 或专门的 `scope` 字段
- 参考截图中工作台的模板卡片(Top X / 产品评测 / 研究报告)看起来是**平台级**预设
> **建议**`article_templates` 表增加 `scope` 字段(`platform` / `tenant`),平台级模板 `tenant_id = NULL`,租户可在此基础上自定义。
### 问题 8ops-admin-web PRD 中的"平台接入管理"路由在主架构 §13.4.2 中已有但 §5.3.3 中缺失
§13.4.2 ops-admin-web 路由有 `/platform/integrations/*`,§13.5.4 模块也有 `platform/integration`,但 §5.3.3 原始 API 路由列表中没有 `/api/platform/integrations/*`
> **影响较小**,因为 §13.6.2 覆盖了。但前后不一致容易造成混乱。
---
## 二、关键数据流深度审查(面向 5 万并发 + 简单)
### 数据流 1:文章生成全链路
```
用户 → admin-web → gateway → tenant-api(额度校验 + 任务入队)
→ RabbitMQ → worker-generateRAG 检索 + 模型调用 + 结果存储)
→ PostgreSQL + Redis → SSE/轮询 → 前端更新
```
**关键问题:LLM 调用超时与成本**
- 一篇文章生成可能调用 LLM 3-20 秒(取决于文章长度和模型)
- 5 万用户中假设 1% 同时在生成 = **500 并发生成任务**
- 每个任务占用 1 个 Worker goroutine + 1 个 LLM API 连接
- 如果 LLM 上游限频(如 DeepSeek 300 RPM),Worker 会排队堆积
> [!CAUTION]
> **架构中缺少**
> 1. LLM 调用超时策略(多久算超时?超时后任务状态如何?)
> 2. LLM 上游限流适配(令牌桶/重试退避)
> 3. 生成任务的优先级控制(VIP 租户优先?)
> 4. Worker 对 LLM 的并发连接池设计
### 数据流 2:知识库入库全链路
```
用户上传 → tenant-api → 对象存储 → RabbitMQ
→ worker-knowledge(提取 → 清洗 → 切片 → Embedding → Qdrant 写入 + PG 元数据)
```
**关键问题:Embedding 调用成本**
- 每个文档可能切出 50-200 个 chunk
- 每个 chunk 需要一次 Embedding API 调用
- 50 万租户 × 平均 10 个知识文档 = 500 万文档 → 潜在 2.5-10 亿 chunk
> 但 V1 阶段用户量有限,这主要是演进方向的考量。当前建议在架构中明确 **Embedding 批量调用**策略(一次请求多个 chunk),减少 API round-trip。
### 数据流 3:定时任务调度全链路
```
scheduler 每分钟扫描 → schedule_tasks(到期的) → 生成 generation_tasks → 入队 RabbitMQ
→ 生成 analytics 任务 → 入队 RabbitMQ
```
**关键问题:调度雷群效应**
- 很多租户的定时任务可能设在同一时刻(如每天 08:00)
- 某分钟可能扫描出数千个到期任务
- 全部同时入队可能瞬间打爆 `q.generate` 队列
> **建议**
> - Scheduler 支持 **平滑派发**(将同时到期的任务分散到 5-10 分钟窗口内入队)
> - 或租户创建定时任务时自动 **加随机偏移量**(±15 分钟)
### 数据流 4:数据追踪采集全链路
```
scheduler → 为每个启用的 question_version × model 生成采集任务 → RabbitMQ
→ worker-analytics(调用模型 → 解析回答 → 存解析结果)
→ 日级聚合 → 刷新缓存
```
**关键问题:采集任务与生成任务共享模型调用**
- 数据追踪的采集本质上也是"调用 LLM 获取回答"
- 与文章生成共用 LLM 额度/限频
- 但采集任务优先级应**低于**用户直接触发的生成任务
> **建议**
> - 采集任务使用独立的 LLM 通道/频次限制
> - 或在共享通道中标记优先级,用户直接任务优先
### 数据流 5:插件发布全链路
```
admin-web → 创建 plugin_session → browser-extension 执行发布
→ 同时:回传前端(即时反馈)+ 回调 /api/callback/plugin/publish(可靠落库)
→ tenant-api 存储 publish_records → SSE 广播
```
**问题:插件离线时的降级处理**
- 如果用户没装插件、或插件版本过低,点击"发布"会发生什么?
- 主架构中未定义**插件检测**机制(前端如何知道插件是否可用?)
- media-binding-v1.md §12 提到只注入本系统后台域名,但没定义版本检测协议
> **建议**:前端在页面加载时检测插件安装状态和版本,未安装或版本过低时灰掉发布按钮并引导安装。在 `packages/shared-types` 中定义最低兼容插件版本常量。
---
## 三、实施风险与工程注意事项
### 风险 1`worker-generate` 中 LLM 超时导致 goroutine 泄漏
Go 中如果 HTTP 请求到 LLM API 设置了很长的超时(如 60s),大量排队任务会占用大量 goroutine。如果 LLM 侧故障,可能导致 goroutine 数量爆炸。
> **建议**
> - 对 LLM 调用设置 `context.WithTimeout`(建议 30s
> - Worker 设置最大并发 goroutine 数(如基于 `semaphore` 限制并发 50
> - 超时任务标记为 `failed`,进入重试队列
### 风险 2PostgreSQL 事务边界不清晰
生成任务涉及多表写入:`articles` + `article_versions` + `generation_tasks` + `tenant_quota_ledgers`。如果不在同一事务中:
- 额度扣了但文章没写入 → 额度泄漏
- 文章写了但额度没扣 → 超额使用
> **建议**:文档中明确关键事务边界——至少"额度扣减 + 任务创建"必须在同一 PostgreSQL 事务中。Worker 侧的"结果写入 + 额度确认"也应事务保护。
### 风险 3migration 策略缺失
30+ 张表的 Schema 定义好了,但没有 migration 策略:
- 如何管理 Schema 版本?(golang-migrate / Atlas / sponge 内置?)
- 线上 DDL 变更如何无停机执行?
- 是否有 down migration
> **建议**:明确使用 `golang-migrate` 或 `Atlas`,并在 `server/migrations/` 目录约定命名规则。
### 风险 4`tenant_id` 数据隔离的执行保障
5 万用户意味着大量租户。所有租户数据共库共表,`tenant_id` 过滤是唯一隔离手段。如果某个查询漏加 `tenant_id`,就是数据泄露。
> **建议**
> - 在 repository 层封装 `TenantScope(ctx)` 自动注入 `WHERE tenant_id = ?`
> - 在 CI 中运行静态分析检查所有查询是否带 `tenant_id`
> - 文档中将此列为**强制编码规范**
### 风险 5:对象存储的清理策略
知识库文档、原始模型回答、发布请求/响应 payload 持续写入对象存储。无清理策略意味着存储成本线性增长。
> **建议**
> - 已删除知识文档的对象存储文件应在 T+7 天后清理
> - 原始模型回答按 `question-driven-monitoring` 建议保留 30-90 天
> - 定期运行 GC 任务扫描"孤儿文件"
### 风险 6Sponge 框架的 code-gen 与手写代码冲突
Sponge 的核心卖点是代码生成(从 protobuf/sql 生成 handler/service/dao)。但主架构的分层是自定义的 `transport/app/domain/repository`。如果使用 sponge 的代码生成:
- 生成的代码放在哪个层?
- 手写的 domain 逻辑如何与生成的 dao 共存?
- 重新生成时是否覆盖手写代码?
> **建议**:明确 sponge 仅用于**脚手架初始化**(项目初始化、数据库连接、中间件配置),**不使用**其默认的 handler/service/dao 代码生成,由团队按自定义分层手写业务代码。
### 风险 7SSE 与 Kubernetes HPA 的兼容性
Kubernetes 默认负载均衡(基于 Service → Pod 的 round-robin)会将新请求分配到不同 Pod。但 SSE 是长连接,如果用户的 SSE 连接在 Pod A,但任务结果写入后通知只发到 Pod B,用户收不到。
> **建议**
> - SSE 推送使用 **Redis Pub/Sub** 作为跨 Pod 广播层
> - 或使用 **Sticky Session**Ingress `nginx.ingress.kubernetes.io/affinity: cookie`
> - 文档中需要明确选择哪种方案
### 风险 8`brand_question_versions` 的碎片化
每次用户修改问题文案就新增一个版本。如果用户频繁微调(改错字、加时间等),版本表会快速碎片化。监控计划如果绑定 `question_version_id`,可能产生大量只使用 1-2 天的版本。
> **建议**
> - 对问题修改增加 **debounce**(如 10 分钟内多次修改只产生一个版本)
> - 或者只在"发布/启用"时才生成新版本,编辑中间态不算版本
### 风险 9:批量生成的额度预扣与回滚
用户提交"批量生成 100 篇"时:
- 是预扣 100 次额度?
- 还是每篇生成成功时扣 1 次?
- 如果预扣后有 30 篇生成失败,如何退回额度?
> **建议**:文档应明确**额度策略**:推荐"提交时预扣 + 失败后按条退还 + 定期对账"模式。
### 风险 10`viewer` 角色缺失导致 PRD 不满足
PRD 明确定义了"只读成员"角色,但主架构删除了 `viewer` 替换为 `kol`。如果按当前架构开发:
- "只读成员"无法创建——PRD 交付不通过
- 前端权限配置缺少对应的角色枚举
---
## 四、"足够简单"维度的优化建议
| 可简化选项 | 当前方案 | 简化建议 | 风险 |
| --- | --- | --- | --- |
| SSE 推送 | 全场景 SSE | **V1 全部用轮询**10s),V1.5 引入 SSE | 延迟略高但架构极度简化 |
| Gateway 独立进程 | `cmd/gateway` | **V1 把网关逻辑合并到 tenant-api 的中间件**,省一个进程 | 后续拆分需重构 |
| Rerank 模块 | cross-encoder reranker | **V1 跳过 rerank**,只用 dense + filter + top5 | RAG 精度降低 |
| 独立 Scheduler | `cmd/scheduler` | **用 cron job 或 pg_cron** 代替独立进程 | 灵活性降低 |
| `question_model_daily_metrics` | 问题级汇总表 | **V1 只保留 `keyword_model_daily_metrics`** | 丢失问题级穿透能力 |
> [!NOTE]
> 以上是**可选简化**,不是必须。如果团队人力充足,保持当前设计无问题。但如果"足够简单"是硬约束,可以选择部分简化。
---
## 五、修改建议汇总
### P0(必须修复)
| # | 问题 | 预计工作量 |
| --- | --- | --- |
| 1 | 主架构中补充 RAG 检索步骤(交互图 + 生成链路 + 模块定义) | 30min |
| 2 | 统一 media-binding-v1.md 与主架构 §13.8 的接口定义 | 1h |
| 3 | 补充 LLM 调用超时/限流/并发池设计 | 30min |
| 4 | 明确关键事务边界(额度扣减 + 任务创建 + 结果写入) | 15min |
| 5 | 恢复 `viewer` 角色 + 定义 `kol` 角色权限 | 15min |
### P1(重要补充)
| # | 问题 | 预计工作量 |
| --- | --- | --- |
| 6 | §13.11.4 补充 `knowledge_chunks_meta` 表 | 10min |
| 7 | §13.11.6 与 question-driven-monitoring 对齐字段 | 20min |
| 8 | `article_templates` 增加 `scope` 字段区分平台/租户模板 | 5min |
| 9 | 补充 SSE 跨 Pod 广播方案(Redis Pub/Sub 或 Sticky Session | 20min |
| 10 | 补充额度预扣/退还/对账策略 | 15min |