解密RAG系统:检索增强生成全攻略

发布时间:2026/7/29 10:55:11
解密RAG系统:检索增强生成全攻略 这篇文章我们来剖析下rag系统。一、RAG系统简介定义RAG 是一种结合了检索和生成的技术。它通过从外部知识库中检索相关信息并将其作为上下文提供给大语言模型LLM从而增强模型生成答案的准确性和可靠性减少幻觉。核心流程索引阶段 数据加载 - 文本分块 - 向量化 - 存入向量数据库。检索阶段 用户提问 - 问题向量化 - 在向量库中相似度检索Top-K- 获取相关文档片段。生成阶段 将用户问题和检索到的文档片段组合成 Prompt - 输入 LLM - LLM 生成最终答案。为什么需要 RAG它有什么能力解决了什么问题1.知识时效性 LLM 的训练数据是截止的无法获取最新信息RAG 可连接实时数据库。2.私有数据/领域知识 LLM 未训练过企业内部私有数据或特定垂类数据。3.幻觉问题 LLM 可能会一本正经地胡说八道RAG 强制模型基于检索到的事实回答降低幻觉。4.可解释性 可以引用参考来源追溯答案依据。二、向量数据库向量数据库的作用是存储和索引非结构化数据的向量表示。它支持高效的相似度搜索快速找出与查询语义最接近的数据片段。向量数据库用于将数据与人工智能模型集成。 使用它们的第一步是将数据加载到向量数据库中。 然后当将用户查询发送到AI模型时首先检索一组类似的文档。 然后这些文档作为用户问题的上下文与用户的查询一起发送给AI模型。在springAI框架中提供了对向量数据操作的接口使得向量化操作变得很容易。1.VectorStoreRetriever 只读接口它只公开文档检索功能FunctionalInterfacepublic interface VectorStoreRetriever {ListDocument similaritySearch(SearchRequest request);default ListDocument similaritySearch(String query) {return this.similaritySearch(SearchRequest.builder().query(query).build());}}2.VectorStore 接口扩展了VectorStoreRetriever并增加了更多的操作功能public interface VectorStore extends DocumentWriter, VectorStoreRetriever {default String getName() {return this.getClass().getSimpleName();}void add(ListDocument documents);void delete(ListString idList);void delete(Filter.Expression filterExpression);default void delete(String filterExpression) { ... }default T OptionalT getNativeClient() {return Optional.empty();}}接下来简单看下向量化操作ComponentSlf4jpublic class DocumentIndexer {Autowiredprivate VectorStore vectorStore;public void load(String sourceFile){log.info(开始加载文件: {}, sourceFile);TextReader textReader new TextReader(new FileSystemResource(sourceFile));ListDocument documents textReader.get();log.info(从文件读取到 {} 个文档, documents.size());documents.forEach(doc - {log.info(文档内容长度: {}, doc.getText().length());log.info(文档元数据: {}, doc.getMetadata());});this.vectorStore.add(documents);log.info(文档已成功加载到VectorStore);}}上面实现了文档加载功能即把文档向量化到数据库。ComponentSlf4jpublic class DocumentRetriever {Autowiredprivate VectorStoreRetriever retriever;public ListDocument retrieveDocuments(String query) {log.info(开始检索文档查询词: {}, query);SearchRequest request SearchRequest.builder().query(query).topK(5) // Return top 5 results.similarityThreshold(0.5) // Only return results with similarity score 0.5.build();ListDocument filteredDocuments retriever.similaritySearch(request);log.info(检索到 {} 个文档, filteredDocuments.size());return filteredDocuments;}}上面实现了文档检索功能从向量数据库检索语义相近的文档。在向量化的时候数据处理与预处理是怎样的 如何进行文本分块一般有如下策略1.固定大小 按字符数如 512/1024 token切分通常保留重叠窗口如 50 token以维持上下文连续性。2.语义分块 根据句子结构、段落或语义变化进行切分如递归字符切分。3.专用分块 针对 Markdown, LaTeX, 代码等特定格式进行结构化分块。4.父文档检索 索引时切小块用于精准检索返回时引用大块用于提供完整上下文。文档的分块很重要直接影响到检索准确度过大过小就会导致检索偏差过小 可能丢失上下文信息导致语义不完整检索噪音大。过大 包含无关信息过多稀释了关键信息的密度导致 LLM 注意力分散且可能超过 LLM 的 Context Window 限制检索精度也可能下降。RAG系统常用的相似度计算方法包括1.余弦相似度 最常用关注向量方向而非大小不受向量长度影响。2.点积 考虑方向和大小常用于 OpenAI Embeddings因为其归一化后等同于余弦。3.欧氏距离 衡量向量空间中的绝对距离。三、RAG系统检索优化如果你自己准备去实现一个rag系统那么在实现的过程中你会遇到很常见的一个问题向量检索效果不好有时候回答的不是很精确甚至直接跑偏。遇到这种情况我们可以从以下几方面改善混合检索 结合关键词检索BM25和向量检索。关键词擅长匹配专有名词、实体向量擅长匹配语义。重排序 先检索出 Top 50-100 个文档然后使用 Cross-Encoder如 BGE-Reranker, Cohere Rerank进行精细打分筛选出 Top 5-10 给 LLM。查询改写 用户的问题可能不清晰使用 LLM 将问题重写、扩展或分解为多个子问题。调整索引参数 调整 Chunk 大小、重叠度、Top-K 值。上面这几点其实都很好理解我们来看下检索和重排序检索 第一阶段快速从海量数据中粗略筛选出可能相关的候选文档通常是召回追求速度。重排序 第二阶段对召回的候选文档进行更深度的语义理解和打分重新排序只保留最相关的文档追求精度计算成本较高所以只对少量数据进行。除了上面的一些优化点我们还可以进行一些辅助手段杜绝LLM的幻觉Prompt 优化 在 Prompt 中明确指令“请仅基于以下上下文回答如果上下文中没有答案请说不知道”。引用验证 生成后检查答案是否包含来源引用。约束解码 技术上限制模型只能生成上下文中出现的词较少用影响流畅性。我在实现公司线上rag的时候遇到了查询不精确甚至不对的场景我是通过以下方法优化的1.辅助业务同事尽可能描述清楚自己的问题使的问题更工程化。比如我让业务输入xx系统的xx菜单下的xx菜单的我的订单详情页页面的xx部分数据是怎么来的。2.对文档的分块进行调整首次创建的时候存在语义相反的文档片段被切分到一起了这样导致冲突的观点数据放到一起模糊匹配异常。举个例子用户创建订单注意事项和用户删除订单取消订单输入事项作为了一个文档片段。3.对于初步召回的文档引入重排序的机制对文档进行精确层面的语义相关性进行评分排序返回密切相关数据文档。