Files

651 lines
14 KiB
Markdown
Raw Permalink Normal View History

# 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 自动变得精准。