de30497f59
- 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.
651 lines
14 KiB
Markdown
651 lines
14 KiB
Markdown
# GEO 平台 Qdrant 版 RAG 架构设计 V1
|
||
|
||
## 1. 文档信息
|
||
|
||
| 项目 | 内容 |
|
||
| --- | --- |
|
||
| 文档名称 | GEO 平台 Qdrant 版 RAG 架构设计 V1 |
|
||
| 文档版本 | V1.0 |
|
||
| 文档状态 | 待评审 |
|
||
| 创建日期 | 2026-03-25 |
|
||
| 适用范围 | GEO 平台 V1 知识库与高精度 RAG 检索 |
|
||
| 关联文档 | `docs/geo-platform-tech-architecture-v1-50k.md` |
|
||
| 关联文档 | `docs/geo-platform-prd-v1.md` |
|
||
|
||
## 2. 设计目标
|
||
|
||
本文档用于定义 GEO 平台在 V1 阶段的 RAG 技术方案,目标是:
|
||
|
||
- 让知识库检索结果足够精准,支持品牌内容生成和问答增强。
|
||
- 将事务型业务数据与向量检索能力解耦。
|
||
- 在 5 万在线用户前提下,保证知识库查询链路稳定、可扩展。
|
||
- 为后续多品牌、多租户、多知识源、多平台内容生成打好基础。
|
||
|
||
## 3. 设计结论
|
||
|
||
V1 的 RAG 主方案建议为:
|
||
|
||
- `PostgreSQL` 负责知识元数据、权限、品牌、分组、任务状态
|
||
- `Qdrant` 负责向量索引和向量检索
|
||
- `对象存储` 负责原始文档和解析中间产物
|
||
- `Worker` 负责解析、切片、Embedding、写入、重建索引
|
||
|
||
检索链路建议为:
|
||
|
||
1. 查询预处理
|
||
2. 过滤条件构造
|
||
3. Qdrant dense / hybrid 检索
|
||
4. rerank 精排
|
||
5. 上下文拼装
|
||
6. 提交给大模型生成
|
||
|
||
## 4. 为什么选 Qdrant
|
||
|
||
### 4.1 业务需求决定
|
||
|
||
GEO 平台的 RAG 检索不是简单的“全文找相似段落”,而是带强业务约束的检索:
|
||
|
||
- 只检索当前租户数据
|
||
- 只检索当前品牌或选定品牌数据
|
||
- 只检索指定知识分组或文档类型
|
||
- 需要按更新时间、来源、文档权重做过滤或排序
|
||
- 后续要支持 hybrid search 和 rerank
|
||
|
||
因此检索系统必须同时擅长:
|
||
|
||
- 高效向量召回
|
||
- 高效 payload filter
|
||
- 后续高精度搜索演进
|
||
|
||
### 4.2 相比 pgvector 的优势
|
||
|
||
Qdrant 更适合做 V1 主方案,原因如下:
|
||
|
||
- 向量检索从 PostgreSQL 主交易链路中解耦
|
||
- metadata filter 能力更适合 RAG 场景
|
||
- 后续扩容路径清晰
|
||
- 更方便演进到 hybrid search、多向量和 rerank 方案
|
||
|
||
### 4.3 pgvector 的角色
|
||
|
||
`pgvector` 仍可作为轻量备选方案:
|
||
|
||
- 数据量较小
|
||
- 团队只有单数据库运维能力
|
||
- 只是验证 MVP
|
||
|
||
但如果目标是“RAG 要精准好用”,Qdrant 更适合作为长期主方案。
|
||
|
||
## 5. RAG 场景定义
|
||
|
||
### 5.1 主要使用场景
|
||
|
||
#### A. 文章生成增强
|
||
|
||
在生成品牌文章时,从品牌知识库中召回相关背景材料、产品说明、FAQ、历史内容和网页资料,为模型生成提供高质量上下文。
|
||
|
||
#### B. 品牌问答增强
|
||
|
||
当系统生成“问题集答案”“趋势分析”“竞品分析”类内容时,先检索品牌知识和文档片段,再交给模型组织答案。
|
||
|
||
#### C. 内容优化增强
|
||
|
||
对已有文章做优化时,检索品牌标准话术、产品卖点、平台规范、历史高质量文章片段。
|
||
|
||
### 5.2 不适合 V1 的场景
|
||
|
||
- 跨品牌全局开放检索
|
||
- 实时抓取网页后秒级入库并立刻高质量检索
|
||
- 十亿级向量规模
|
||
- 强知识图谱推理
|
||
|
||
## 6. 数据分层设计
|
||
|
||
## 6.1 PostgreSQL 存储内容
|
||
|
||
PostgreSQL 中保存:
|
||
|
||
- `knowledge_bases`
|
||
- `knowledge_groups`
|
||
- `knowledge_items`
|
||
- `knowledge_parse_tasks`
|
||
- `knowledge_chunks_meta`
|
||
- `brand`
|
||
- `tenant`
|
||
- `document_acl`
|
||
- `retrieval_logs`
|
||
- `rag_eval_cases`
|
||
|
||
### PostgreSQL 的职责
|
||
|
||
- 保存业务主数据
|
||
- 控制权限与租户隔离
|
||
- 提供后台列表页查询
|
||
- 保存 chunk 元数据和状态
|
||
- 保存评测数据和命中日志
|
||
|
||
## 6.2 Qdrant 存储内容
|
||
|
||
Qdrant 中保存:
|
||
|
||
- chunk 向量
|
||
- chunk payload
|
||
- 文档级过滤字段
|
||
- chunk 排序辅助字段
|
||
|
||
### Qdrant 的职责
|
||
|
||
- 相似度召回
|
||
- filter + ANN 检索
|
||
- hybrid search
|
||
- rerank 前候选集召回
|
||
|
||
## 6.3 对象存储内容
|
||
|
||
- 原始 PDF / DOCX / TXT / HTML
|
||
- 提取后的 Markdown / 纯文本
|
||
- 清洗后的中间文件
|
||
|
||
## 7. Collection 设计
|
||
|
||
## 7.1 设计原则
|
||
|
||
V1 不建议每个品牌一个 collection,也不建议每个租户大量拆 collection。
|
||
|
||
推荐策略:
|
||
|
||
- 按业务域建立少量 collection
|
||
- 通过 payload filter 做租户、品牌、分组隔离
|
||
- 避免 collection 数量过多导致管理复杂
|
||
|
||
## 7.2 推荐 collection
|
||
|
||
### collection 1: `kb_chunk_main`
|
||
|
||
用途:
|
||
|
||
- 存放知识库标准 chunk
|
||
- 支撑文章生成、优化、品牌问答等主要检索
|
||
|
||
### collection 2: `content_example_main`
|
||
|
||
用途:
|
||
|
||
- 存放历史优质文章片段
|
||
- 支撑风格参考、结构参考、示例生成
|
||
|
||
V1 如果要尽量简单,也可以先只保留一个 `kb_chunk_main`。
|
||
|
||
## 7.3 向量字段建议
|
||
|
||
### 主向量
|
||
|
||
- `dense_vector`
|
||
- 维度按所选 embedding 模型决定
|
||
- 距离建议优先 `Cosine`
|
||
|
||
### 预留向量
|
||
|
||
后续可扩展:
|
||
|
||
- `title_vector`
|
||
- `summary_vector`
|
||
- `sparse_vector`
|
||
|
||
V1 可以先只落一个 dense vector。
|
||
|
||
## 8. Payload 设计
|
||
|
||
## 8.1 必须字段
|
||
|
||
每个 chunk 在 Qdrant 中建议至少带以下 payload:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `tenant_id` | keyword | 租户隔离 |
|
||
| `brand_id` | keyword | 品牌过滤 |
|
||
| `kb_id` | keyword | 知识库 ID |
|
||
| `group_id` | keyword | 分组过滤 |
|
||
| `item_id` | keyword | 文档 ID |
|
||
| `chunk_id` | keyword | chunk 唯一 ID |
|
||
| `doc_type` | keyword | 文档类型 |
|
||
| `source_type` | keyword | 来源类型,如上传文档/网页/手输 |
|
||
| `language` | keyword | 语言 |
|
||
| `status` | keyword | 是否可检索 |
|
||
| `priority` | integer | 文档优先级 |
|
||
| `published_at` | integer | 发布时间时间戳 |
|
||
| `updated_at` | integer | 更新时间时间戳 |
|
||
| `token_count` | integer | chunk token 数 |
|
||
| `title` | text/keyword | 文档标题 |
|
||
| `tags` | keyword[] | 标签 |
|
||
|
||
## 8.2 推荐扩展字段
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `is_official_doc` | bool | 是否官方资料 |
|
||
| `is_product_doc` | bool | 是否产品文档 |
|
||
| `author` | keyword | 作者 |
|
||
| `source_url` | keyword | 来源链接 |
|
||
| `quality_score` | float | 文档质量分 |
|
||
| `freshness_score` | float | 时效性分 |
|
||
| `manual_boost` | float | 人工加权 |
|
||
|
||
## 8.3 索引建议
|
||
|
||
Qdrant 中对高频 filter 字段建立 payload index,优先包括:
|
||
|
||
- `tenant_id`
|
||
- `brand_id`
|
||
- `group_id`
|
||
- `doc_type`
|
||
- `status`
|
||
- `source_type`
|
||
|
||
如果查询高频使用时间和标签,也可以增加:
|
||
|
||
- `updated_at`
|
||
- `tags`
|
||
|
||
## 9. Chunk 设计
|
||
|
||
## 9.1 核心原则
|
||
|
||
RAG 准确率很多时候不是输在向量库,而是输在 chunk 切分。
|
||
|
||
切分原则:
|
||
|
||
- 按语义结构切,不按固定字数硬切
|
||
- 保持 chunk 语义完整
|
||
- 控制 chunk 粒度不要过大
|
||
- 保留上下文关联信息
|
||
|
||
## 9.2 推荐切分策略
|
||
|
||
### 文档型内容
|
||
|
||
优先按以下结构切分:
|
||
|
||
- 一级标题
|
||
- 二级标题
|
||
- 段落簇
|
||
- 列表块
|
||
- 表格说明块
|
||
|
||
### 网页型内容
|
||
|
||
优先清洗掉:
|
||
|
||
- 导航
|
||
- 页脚
|
||
- 广告
|
||
- 无意义重复区域
|
||
|
||
保留:
|
||
|
||
- 标题
|
||
- 摘要
|
||
- 正文
|
||
- FAQ
|
||
- 参数表
|
||
|
||
## 9.3 V1 chunk 参数建议
|
||
|
||
- 目标 token:`300 ~ 600`
|
||
- overlap:`50 ~ 100`
|
||
- 对 FAQ 场景可更短:`100 ~ 250`
|
||
- 对长报告场景可按标题段落聚合到 `500 ~ 800`
|
||
|
||
### 说明
|
||
|
||
- 太短会导致上下文碎片化,召回噪音高
|
||
- 太长会导致 embedding 表达不准,且浪费上下文窗口
|
||
|
||
## 9.4 特殊处理建议
|
||
|
||
### FAQ 文档
|
||
|
||
按 `问题 + 答案` 成对切 chunk,不要拆开。
|
||
|
||
### 产品参数文档
|
||
|
||
按“模块 + 参数块”切分,并保留型号、版本、发布时间。
|
||
|
||
### 品牌官网介绍
|
||
|
||
品牌介绍、品牌故事、产品卖点、售后说明应分开切分。
|
||
|
||
## 10. 入库流程设计
|
||
|
||
## 10.1 标准流程
|
||
|
||
1. 用户上传文档或录入网页。
|
||
2. 文件进入对象存储。
|
||
3. 创建解析任务。
|
||
4. Worker 提取文本并清洗。
|
||
5. 按规则切 chunk。
|
||
6. 为每个 chunk 生成 embedding。
|
||
7. chunk 写入 PostgreSQL 元数据表。
|
||
8. 向量和 payload 写入 Qdrant。
|
||
9. 更新知识条目状态为可用。
|
||
|
||
## 10.2 幂等要求
|
||
|
||
- 同一文档重复上传要可识别
|
||
- chunk 重建时采用版本号或软删除策略
|
||
- 文档更新后需支持全量替换对应 chunk
|
||
|
||
## 10.3 文档版本策略
|
||
|
||
推荐方案:
|
||
|
||
- `knowledge_item` 保存文档主记录
|
||
- `knowledge_item_version` 保存版本记录
|
||
- `knowledge_chunk_meta` 带 version 字段
|
||
- Qdrant payload 中写入 `item_version`
|
||
|
||
检索时仅查询当前生效版本。
|
||
|
||
## 11. 检索流程设计
|
||
|
||
## 11.1 标准检索链路
|
||
|
||
1. 接收用户查询或文章生成任务上下文
|
||
2. 做 query rewrite
|
||
3. 构造 filter 条件
|
||
4. 从 Qdrant 召回 topK 候选 chunk
|
||
5. 使用 reranker 精排
|
||
6. 去重与多样性控制
|
||
7. 拼接最终上下文
|
||
8. 返回给大模型
|
||
|
||
## 11.2 查询预处理
|
||
|
||
建议包含:
|
||
|
||
- 去除无意义词
|
||
- 标准化品牌名、产品名、简称
|
||
- 同义词扩展
|
||
- 根据任务类型追加意图词
|
||
|
||
例如:
|
||
|
||
- “帮我写某品牌数字时尚趋势文章”
|
||
- 可以扩展为:
|
||
- “数字时尚 品牌趋势 新趋势 消费者关注点 产品风格 设计趋势”
|
||
|
||
## 11.3 filter 策略
|
||
|
||
RAG 结果想精准,必须优先加 filter,而不是裸搜。
|
||
|
||
推荐默认过滤条件:
|
||
|
||
- `tenant_id = 当前租户`
|
||
- `status = active`
|
||
|
||
可选过滤条件:
|
||
|
||
- `brand_id in 当前品牌集合`
|
||
- `group_id in 指定知识分组`
|
||
- `doc_type in 允许文档类型`
|
||
- `updated_at > 某时间`
|
||
|
||
## 11.4 召回策略
|
||
|
||
### V1 基础版
|
||
|
||
- dense retrieval
|
||
- topK = 30 ~ 50
|
||
|
||
### V1 推荐版
|
||
|
||
- dense retrieval + BM25 或 sparse retrieval
|
||
- hybrid merge
|
||
- rerank top 30
|
||
- 最终输出 top 5 ~ 10
|
||
|
||
## 11.5 去重与多样性
|
||
|
||
召回后应做:
|
||
|
||
- 同文档 chunk 去重
|
||
- 相邻 chunk 合并
|
||
- 标题级去重
|
||
- 不同来源混排
|
||
|
||
防止最终上下文全部来自同一篇文档的连续片段。
|
||
|
||
## 12. rerank 设计
|
||
|
||
## 12.1 为什么必须 rerank
|
||
|
||
向量召回解决的是“先找一批可能相关的候选”,不是最终最准排序。
|
||
|
||
如果不做 rerank,常见问题包括:
|
||
|
||
- 语义相近但不回答问题
|
||
- 命中了品牌,但不是当前主题
|
||
- 召回结果太泛
|
||
|
||
## 12.2 V1 rerank 方案
|
||
|
||
推荐两级:
|
||
|
||
### 方案 A:轻量版
|
||
|
||
- dense topK 召回
|
||
- 用 cross-encoder reranker 排序
|
||
- 取前 5 到 8 个 chunk
|
||
|
||
### 方案 B:增强版
|
||
|
||
- dense + sparse hybrid
|
||
- rerank
|
||
- 基于来源权重、官方文档权重、时效权重做分数融合
|
||
|
||
## 12.3 打分建议
|
||
|
||
最终分数建议由以下部分组成:
|
||
|
||
- semantic_score
|
||
- rerank_score
|
||
- quality_score
|
||
- freshness_score
|
||
- manual_boost
|
||
|
||
可采用线性融合,V1 不必一开始做复杂学习排序。
|
||
|
||
## 13. 上下文拼装策略
|
||
|
||
## 13.1 拼装原则
|
||
|
||
- 少而准优于多而杂
|
||
- 优先保留完整意义块
|
||
- 优先官方和高质量来源
|
||
- 记录引用来源,方便可解释
|
||
|
||
## 13.2 推荐拼装方式
|
||
|
||
每个返回 chunk 建议带:
|
||
|
||
- 文档标题
|
||
- 来源类型
|
||
- 来源 URL
|
||
- chunk 正文
|
||
- 更新时间
|
||
|
||
拼装给模型时,建议按主题分组,而不是简单拼接长文本。
|
||
|
||
## 13.3 token 控制
|
||
|
||
不同任务控制不同上下文预算:
|
||
|
||
- 短文章生成:`1000 ~ 2000 tokens`
|
||
- 长文章生成:`2000 ~ 5000 tokens`
|
||
- 问答增强:`800 ~ 1500 tokens`
|
||
|
||
## 14. 多租户与权限设计
|
||
|
||
## 14.1 隔离原则
|
||
|
||
所有检索必须带 `tenant_id` 过滤,不允许裸查询。
|
||
|
||
## 14.2 品牌隔离
|
||
|
||
如果一个用户一次只操作一个品牌,检索时默认加:
|
||
|
||
- `brand_id = current_brand`
|
||
|
||
如果是跨品牌分析,再显式放开。
|
||
|
||
## 14.3 文档权限
|
||
|
||
如果后续支持团队协作,可增加:
|
||
|
||
- 可见范围
|
||
- 部门范围
|
||
- 角色范围
|
||
|
||
V1 可以先在 PostgreSQL 中控制可见性,再把最终允许检索的 payload 写入 Qdrant。
|
||
|
||
## 15. 性能设计
|
||
|
||
## 15.1 查询延迟目标
|
||
|
||
| 项目 | 目标 |
|
||
| --- | --- |
|
||
| query rewrite | < 50ms |
|
||
| Qdrant topK 检索 | < 100ms |
|
||
| rerank | < 200ms |
|
||
| 检索总耗时 | < 400ms |
|
||
|
||
### 说明
|
||
|
||
以上是服务端目标,前端或模型生成耗时不包含在内。
|
||
|
||
## 15.2 并发策略
|
||
|
||
- 热门品牌查询走缓存
|
||
- 高频 filter 字段建立 payload index
|
||
- rerank 单独走线程池或独立服务
|
||
- query rewrite 和 rerank 限并发,避免放大成本
|
||
|
||
## 15.3 写入策略
|
||
|
||
- 文档上传与知识学习异步处理
|
||
- 向量写入批量提交
|
||
- 文档更新采用分批重建
|
||
|
||
## 16. 精准度评测体系
|
||
|
||
## 16.1 为什么必须做评测
|
||
|
||
RAG 不评测,精准度一定会失控。
|
||
|
||
V1 至少需要建立一套小型评测集。
|
||
|
||
## 16.2 评测集构成
|
||
|
||
每个品牌至少准备:
|
||
|
||
- 20 个高频问题
|
||
- 20 个内容生成主题
|
||
- 10 个产品对比问题
|
||
- 10 个品牌介绍问题
|
||
|
||
总计每品牌建议至少 50 条评测 query。
|
||
|
||
## 16.3 评测指标
|
||
|
||
推荐指标:
|
||
|
||
- Recall@5
|
||
- Recall@10
|
||
- Precision@5
|
||
- MRR
|
||
- NDCG
|
||
- Answer groundedness
|
||
- 人工满意度
|
||
|
||
## 16.4 人工评测维度
|
||
|
||
每条 query 至少看:
|
||
|
||
- 是否命中正确文档
|
||
- 是否命中正确片段
|
||
- 是否出现品牌错配
|
||
- 是否出现过期内容
|
||
- 是否出现无关噪音
|
||
|
||
## 16.5 评测流程
|
||
|
||
1. 固定查询集
|
||
2. 固定期望命中文档
|
||
3. 跑召回与 rerank
|
||
4. 输出命中率报表
|
||
5. 低分案例人工复盘
|
||
6. 调整 chunk、payload、topK、rerank 策略
|
||
|
||
## 17. 监控与日志
|
||
|
||
## 17.1 监控指标
|
||
|
||
- Qdrant 查询延迟
|
||
- Qdrant 查询 QPS
|
||
- payload filter 命中比例
|
||
- topK 平均耗时
|
||
- rerank 平均耗时
|
||
- 检索空结果率
|
||
- 检索后回答满意度
|
||
|
||
## 17.2 日志记录
|
||
|
||
每次检索建议记录:
|
||
|
||
- query
|
||
- rewrite_query
|
||
- filter 条件
|
||
- topK 召回结果
|
||
- rerank 结果
|
||
- 最终返回 chunk
|
||
- 生成任务 ID 或问答 ID
|
||
|
||
方便后续做 badcase 分析。
|
||
|
||
## 18. 风险与边界
|
||
|
||
### 18.1 常见风险
|
||
|
||
- chunk 切分不合理,导致检索效果差
|
||
- payload 设计不完整,导致过滤能力不足
|
||
- 只做召回不做 rerank,结果不稳定
|
||
- 知识库内容脏乱,导致“检索准但答案差”
|
||
|
||
### 18.2 V1 边界
|
||
|
||
- V1 不要求做复杂多跳推理
|
||
- V1 不要求做全自动 query rewrite 学习
|
||
- V1 不要求做复杂学习排序
|
||
|
||
## 19. V1 最终建议
|
||
|
||
如果你的目标是“RAG 精准好用”,V1 建议采用这套最实用组合:
|
||
|
||
- `PostgreSQL` 做业务主存储和知识元数据
|
||
- `Qdrant` 做向量检索
|
||
- `RabbitMQ` 做知识学习与检索相关任务队列
|
||
- `对象存储` 做原始文档存储
|
||
- `dense + filter + rerank` 做主检索链路
|
||
|
||
同时一定要配套以下机制:
|
||
|
||
- 合理 chunk 切分
|
||
- 完整 payload 字段
|
||
- payload index
|
||
- rerank
|
||
- 评测集
|
||
- badcase 回放
|
||
|
||
没有这些,单纯换向量库也不可能让 RAG 自动变得精准。
|
||
|