本文档汇总了关于 PaiSmart RAG 系统实现细节的常见疑问及其技术解答。
- 问题背景: 在
SearchService中,向量化使用的是原始query,而 ES 的文本检索使用的是去噪后的normalized。 - 解答:
- 向量化 (Embedding): 原始
query保留了完整的语气、句法结构和上下文(如疑问句式、逻辑连接词)。高质量的向量模型(如 DeepSeek、豆包)能够理解这些细微的语义,若是去噪反而会丢失信息。 - 文本检索 (BM25): 传统的倒排索引对“的”、“是”、“请问”等高频停用词非常敏感,不去噪会导致大量无关文档被召回。
- 结论: 向量用全句(保语义),BM25 用关键词(保精准),这是混合检索的最佳实践。
- 向量化 (Embedding): 原始
- 问题背景: 当前代码将文本匹配 (
match) 放在了must子句中。 - 解答:
- 最新策略: 我们已将关键词匹配策略恢复为严格模式 (
minimum_should_match: 100%)。 - 原因: 相比单纯降低阈值引入噪声,我们引入了更高级的 Query 重写与扩写。通过 LLM 将模糊、口语化的输入转换为多条精准的书面查询,即使关键词匹配严格,也能通过多路并发检索覆盖所有可能的表达方式。
- 最新策略: 我们已将关键词匹配策略恢复为严格模式 (
- 问题背景: 代码中构建了一个针对
phrase的should查询。 - 解答:
- 这是为了给连续短语匹配加分。
- 如果文档中不仅包含查询词,而且这些词是连续出现的(即完全匹配短语),ES 会通过
match_phrase给该文档一个很高的额外分数(Boost 3.0)。 - 这是一种轻量级的精排机制:保证了“零散匹配”能搜到,“精确匹配”排前面。
- 问题背景: 代码中构造了
knnjson 对象,但计算在哪里发生? - 解答:
- 完全由 Elasticsearch 完成。
- Go 代码仅负责构造查询 DSL(JSON),ES 收到请求后利用底层的 HNSW 索引在向量空间中进行最近邻搜索。ES 在这里充当了向量数据库的角色。
- 问题背景:
chat_service.go中的流式响应逻辑。 - 解答:
它是 RAG 的“最后一公里”,负责:
- 组装上下文 (Prompt Engineering): 将搜到的 10 个文档片段格式化为
[1]... [2]...形式,拼接到 Prompt 中。 - 调用 LLM: 将完整的上下文发给 DeepSeek 模型。
- 流式转发 (Streaming): 利用
wsWriterInterceptor拦截 AI 生成的每一个字符,并通过 WebSocket 实时推送给前端,实现这“打字机”效果。
- 组装上下文 (Prompt Engineering): 将搜到的 10 个文档片段格式化为
- 解答:
- 它是一个向量化客户端。
- 负责调用外部 API(如阿里云 DashScope/豆包),将文本转化为数学向量 (float32 array)。
- 它充当了 RAG 系统的“翻译官”,连接了非结构化文本与数学向量空间。
- 问题背景: 提到混合检索是 Vector + BM25,但代码里只有
match。 - 解答:
- Elasticsearch 的
match查询默认就是使用 BM25 算法进行打分的。 - 代码中的
must match(初筛) 和rescore match(重排) 底层都在调用 ES 的 BM25 评分器。 - 由 ES 服务端内部实现,不需要 Go 客户端显式调用。
- Elasticsearch 的
- 问题背景: 既然已经做了混合检索,为什么还要引入重排序?
- 解答:
- BM25 (字面匹配): 只能看字面,不懂“苹果手机充电”和“吃苹果补充体力”的区别。
- Vector (语义匹配): 压缩了信息,可能丢失“不推荐”、“避免”等否定语义。
- Cross-Encoder (重排序): 它是一个交互式模型,像专家一样逐字阅读“问题”和“文档”,能精准判断相关性。
- 结论:
- BM25+Vector 负责海选 (Top 50,快但粗)。
- Rerank 负责决赛 (Top 10,慢但准)。这是提升 LLM 回答质量的关键一步。
- 解答:
- 绝对需要。
- Rerank 速度太慢,无法处理百万级文档,必须依赖 BM25+Vector 进行快速初筛 (First-stage Retrieval)。
- BM25 擅长精确关键词匹配(如错误码、专有名词),是向量检索的重要互补(向量容易漂移)。
- 二者是**前鋒(召回)与守门员(精排)**的关系,缺一不可。
- 渠道融合 vs 终极评审:
- RRF (裁判员):它的职责是“合并名单”。它把来自“向量检索”和“关键词检索”这两份完全不同维度的名单,根据各自的排名公平地捏合到一起,确保两路优秀的结果都能出线。它解决的是异构数据如何汇总的问题。
- Rerank (面试官):它的职责是“择优录用”。它是对 RRF 汇总后的名单(如前 50 名)进行逐一面试,用更昂贵的语义模型(Cross-Encoder)重新打分,把真正准确的文档推到最顶端。
- 协作模式:RRF 保证了名单的多样性与广度,Rerank 保证了结果的极端精准度。
- 问题背景: 为什么系统能听懂“那怎么用”或者把口语转换得非常专业?
- 解答:
- 核心机制:在
SearchService执行检索前,增加了一个 LLM 预处理层。 - 实现步骤:
- 历史注入:将最近 3 轮对话历史连同当前问题一起发给 LLM,实现指代消解(如把“那”替换为前文提到的具体产品名)。
- 语义重写:通过精心设计的 Prompt 引导 LLM 将口语(如“咋整”)重构为标准检索词(如“处理规程”)。
- 多路扩写:LLM 一次生成 3 个语义互补的查询变体,以此覆盖文档中可能存在的不同表达方式。
- 严格兜底:因为 Query 质量变高,系统配合使用了
minimum_should_match: 100%进行严格过滤,实现了“高召回”与“高精准”的统一。
- 核心机制:在
- 问题背景: 如果不清理,重新上传(覆盖)文档后,搜索结果里偶尔会出现“未知文件”或过时的内容片段。
- 解答:
- 残留数据 (Ghost Data): RAG 系统将文档切分为多个分块(Chunks)。Elasticsearch 的默认逻辑是根据
vector_id执行 upsert(更新或插入)。 - 问题成因: 如果新版文档生成的切片数量减少了(例如删减了文字),或者分词器的分片逻辑发生了变化,原先多出来的旧切片 ID 将无法被覆盖,从而残留在索引库中。这些残留切片往往缺少新的元数据(如
file_name字段)。 - 解决方案: 在
Processor处理流水线中,我们在入库前引入了物理 Purge 机制。根据文件的 MD5,系统会首先调用DeleteByQuery强制清空该文档在 Elasticsearch 和 MySQL 中的所有既有记录。 - 结论: 这种“先破后立”的幂等性设计,确保了每次入库的数据都是绝对纯净的,彻底杜绝了 RAG 系统中常见的“僵尸数据”干扰问题。
- 残留数据 (Ghost Data): RAG 系统将文档切分为多个分块(Chunks)。Elasticsearch 的默认逻辑是根据