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

16 KiB
Raw Blame History

GEO 平台技术架构深度审查报告(第三轮)

在前两轮审查(功能覆盖度 + 5 万并发初审)基础上,交叉审查全部 6 份文档(共 ~4700 行),聚焦跨文档一致性、数据流完整性、实施风险三个维度。


一、跨文档一致性审查

现有文档体系:

文档 行数 定位
geo-platform-ops-admin-tech-architecture-v1.md 1257 🏗️ 主架构
geo-platform-prd-v1.md 1079 📋 租户端 PRD
geo-platform-ops-admin-prd-v1.md 652 📋 平台管理端 PRD
media-binding-and-publish-v1.md 562 🔌 插件方案
qdrant-rag-architecture-v1.md 651 🧠 RAG 方案
question-driven-monitoring-design-v1.md 495 📊 数据监控方案

问题 1:RAG 组件未纳入主架构图和交互图

qdrant-rag-architecture-v1.md 定义了完整的检索链路:query rewrite → filter → Qdrant 检索 → rerank → 上下文拼装 → 提交模型。但主架构中:

  • §8 交互图中 worker-generate → Qdrant 的箭头缺失(只有 worker-knowledge → QD
  • 文章生成链路(§10.1)只提到"调用模型"没有 RAG 检索步骤
  • 后端模块职责中无 shared/ragshared/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_attask_batch_id 缺少 run_attask_batch_id
question_monitor_parse_results 字段 citation_platform_countparser_version 缺少这两个字段
保留策略 明确 30/90/180 天分级 未提及保留策略

建议:主架构 §13.11.6 的 Schema 应与 question-driven-monitoring-design-v1.md 完全对齐,特别是补充 question_model_daily_metrics 表和缺失字段。

问题 4RAG 中的 knowledge_chunks_meta 表未出现在主架构 Schema

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_logsrag_eval_cases 未纳入主架构

RAG 文档要求记录每次检索的 query/filter/rerank 结果用于 badcase 分析,但主架构没有对应表和模块。V1 如果不记录,后续无法评估 RAG 精准度。

建议:如果目标是"足够简单",V1 可以先将检索日志写入文件日志而非数据库表,后续再升级。在架构中明确这一策略。

问题 6PRD 中定义了 kol 角色但无实际权限描述

主架构 §6.2.2 新增 kol(用于某个专家的 prompt 从底层共享),但:

  • 租户端 PRD 只定义了 管理员/编辑/只读 三种角色
  • kol 角色的具体权限范围(能看什么、能做什么)未在任何文档中定义
  • PRD 中的 viewer(只读成员)角色被删除

建议:保留 viewer、额外增加 kol,并在权限矩阵中补充 kol 的能力边界。

问题 7article_templates 表的 tenant_id 字段暗示模板是租户级的

主架构 §13.11.2 中 article_templatestenant_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 中定义最低兼容插件版本常量。


三、实施风险与工程注意事项

风险 1worker-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-migrateAtlas,并在 server/migrations/ 目录约定命名规则。

风险 4tenant_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 SessionIngress nginx.ingress.kubernetes.io/affinity: cookie
  • 文档中需要明确选择哪种方案

风险 8brand_question_versions 的碎片化

每次用户修改问题文案就新增一个版本。如果用户频繁微调(改错字、加时间等),版本表会快速碎片化。监控计划如果绑定 question_version_id,可能产生大量只使用 1-2 天的版本。

建议

  • 对问题修改增加 debounce(如 10 分钟内多次修改只产生一个版本)
  • 或者只在"发布/启用"时才生成新版本,编辑中间态不算版本

风险 9:批量生成的额度预扣与回滚

用户提交"批量生成 100 篇"时:

  • 是预扣 100 次额度?
  • 还是每篇生成成功时扣 1 次?
  • 如果预扣后有 30 篇生成失败,如何退回额度?

建议:文档应明确额度策略:推荐"提交时预扣 + 失败后按条退还 + 定期对账"模式。

风险 10viewer 角色缺失导致 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