- 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.
16 KiB
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/rag或shared/retrieval模块
Warning
影响:
worker-generate生成文章时需要从 Qdrant 检索品牌知识,这条数据流在主架构中完全不可见。开发时容易遗漏 RAG 集成。建议:
- 交互图中为
worker-generate → Qdrant增加箭头- §10.1 生成链路在步骤 3 和 4 之间插入 "RAG 检索品牌知识上下文"
- 后端目录中增加
internal/shared/retrieval模块
问题 2:media-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 完全对齐,特别是补充
question_model_daily_metrics表和缺失字段。
问题 4:RAG 中的 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 |
问题 5:RAG 中的 retrieval_logs 和 rag_eval_cases 未纳入主架构
RAG 文档要求记录每次检索的 query/filter/rerank 结果用于 badcase 分析,但主架构没有对应表和模块。V1 如果不记录,后续无法评估 RAG 精准度。
建议:如果目标是"足够简单",V1 可以先将检索日志写入文件日志而非数据库表,后续再升级。在架构中明确这一策略。
问题 6:PRD 中定义了 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,租户可在此基础上自定义。
问题 8:ops-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-generate(RAG 检索 + 模型调用 + 结果存储)
→ PostgreSQL + Redis → SSE/轮询 → 前端更新
关键问题:LLM 调用超时与成本
- 一篇文章生成可能调用 LLM 3-20 秒(取决于文章长度和模型)
- 5 万用户中假设 1% 同时在生成 = 500 并发生成任务
- 每个任务占用 1 个 Worker goroutine + 1 个 LLM API 连接
- 如果 LLM 上游限频(如 DeepSeek 300 RPM),Worker 会排队堆积
Caution
架构中缺少:
- LLM 调用超时策略(多久算超时?超时后任务状态如何?)
- LLM 上游限流适配(令牌桶/重试退避)
- 生成任务的优先级控制(VIP 租户优先?)
- 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,进入重试队列
风险 2:PostgreSQL 事务边界不清晰
生成任务涉及多表写入:articles + article_versions + generation_tasks + tenant_quota_ledgers。如果不在同一事务中:
- 额度扣了但文章没写入 → 额度泄漏
- 文章写了但额度没扣 → 超额使用
建议:文档中明确关键事务边界——至少"额度扣减 + 任务创建"必须在同一 PostgreSQL 事务中。Worker 侧的"结果写入 + 额度确认"也应事务保护。
风险 3:migration 策略缺失
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 任务扫描"孤儿文件"
风险 6:Sponge 框架的 code-gen 与手写代码冲突
Sponge 的核心卖点是代码生成(从 protobuf/sql 生成 handler/service/dao)。但主架构的分层是自定义的 transport/app/domain/repository。如果使用 sponge 的代码生成:
- 生成的代码放在哪个层?
- 手写的 domain 逻辑如何与生成的 dao 共存?
- 重新生成时是否覆盖手写代码?
建议:明确 sponge 仅用于脚手架初始化(项目初始化、数据库连接、中间件配置),不使用其默认的 handler/service/dao 代码生成,由团队按自定义分层手写业务代码。
风险 7:SSE 与 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 |