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