首先,RAG 的全称是检索增强生成(Retrieval-Augmented Generation),它的核心是通过构建并利用知识库来增强大语言模型的回答能力。但在其标准的流程中,会衍生出很多概念和技术细节,这些细节在许多地方都至关重要。
我们先看一个最经典的 RAG 流程是怎么样的:
数据处理阶段:
- 整理知识,汇总文档对文档进行切分,这一步称为分块(Chunking),产物是Chunks对切分后的段落(Chunks)选择合适的Embedding 模型进行向量化(Embedding)将向量化的数据存放到向量数据库(Vector Database)
用户查询阶段:
- 用户提问将问题同样通过 Embedding 模型进行向量化到向量数据库中进行语义检索(Retrieve)把检索到的相关内容(Context)汇总成一个提示词(Prompt),提交给大语言模型(LLM)LLM 根据 Prompt 生成答案并返回
这是一个非常经典的 RAG 流程。不过,其中有很多细节值得探讨。这篇文章一方面是科普,另一方面也想探索一些最佳实践以及可以在项目中应用的技术。
一、文档处理(Parsing)
这一步其实是一个非常棘手的问题。在真实的业务场景下,会存在各种格式的文档,例如:
- Markdown (
.md
) 文档纯文本 (.txt
) 文档PDF (.pdf
) 文档Word (.docx
) 文档Excel (.xlsx
) 数据PPT (.pptx
) 文档...对于这些文档,很多常规的知识库构建方法可能只是简单地进行 OCR 或纯文本提取,但这样肯定是不可取的,因为会丢失非常多的结构化语义信息(如标题层级、表格、列表等)。
现在比较好的做法是利用专门的文档解析工具(如Unstructured.io, LlamaParse),它们能理解文档的内部结构,然后将所有异构文档统一转换为一种富文本格式(如 Markdown),以便进行标准化的后续处理。
除了上面提到的工具,这里还强烈推荐下微软开源的多模态处理工具 markitdown。
二、文本分块(Chunking)
这一步同样至关重要。因为 Embedding 模型一次能处理的上下文长度是有限的,此外,如果文本长度过长,也可能导致 LLM 产生幻觉或引入过多噪音。因此,合理的切分至关重要。
传统的做法,这里以 Dify 平台的功能来举例说明,有:
- 基于长度:例如按 1000 个字符来进行切分。弊端很明显,太容易切断完整的语义,导致上下文信息丢失。基于正则或段落:按换行符或特定符号切分。好处是能保留段落的常见语义信息,但对于上下文联系紧密的跨段落内容,依然会丢失关联。基于整篇文章:如果文章长度超出 Embedding 模型限制再进行切分。好处是信息更加密集,但缺点依然存在:对于超长文章,切分后上下文关联仍可能丢失;同时,召回的单个 Chunk 内容过长,需要后续进行更精细的处理。
为了改进上述方式,一些框架也引入了**段落重叠(Overlap)**的机制,即把上一段结尾的部分内容加入到下一段的开头,再进行向量化。但在我的项目经验来看,这种机械重叠带来的实际效果提升有限。
我想表达的是,没有“万能的银弹”,还是要结合你的项目情况来选择合理的 Chunking 方法。下面我们讨论一种目前比较有前景的技术——智能分块(Agentic Chunking)。
如果你使用过 Coze 这样的平台,上传一篇文章后会很疑惑,为什么没有设置相关的切分选项,它就自动帮你处理好了?我推测其背后很可能就是使用了 Agent 来做处理。
它的原理是给定一个 Prompt,让 LLM 来执行切分任务,例如:请对用户给定的文档进行合理的语义切分,确保每个分块都是一个独立的、完整的意义单元,同时长度不能超过2000个token。
当然,这只是一个简单的示例,实际的 Prompt 会比这个复杂得多。
目前来看,通过 LLM 来做切分是效果较好的做法,但也存在一些挑战:
- 需要更长的处理时间和额外的计算资源(API 调用成本)。对于超长文档,需要引入更复杂的机制来处理,因为 LLM 本身的上下文窗口也是有限的。在切分超长文档时,如何保证分块间的连贯性?是继续遵守段落重叠,还是引入某种记忆机制来引导下一轮的切分呢?
综合来看,Agentic Chunking 前景广阔,特别适合对精度和准确性要求极高的文档切分场景。
三、向量化(Embedding)
选择合适的 Embedding 模型至关重要,这直接决定了语义向量化的效果。但这里也存在一个矛盾:
- 高精度模型:通常性能更好,但其生成的向量维度更高,对运行和存储的要求也更高,意味着服务器和存储成本会增加,推理时间也会变长。低精度或性能较差的模型:成本低,但可能导致向量化的意义丢失,其语义搜索效果甚至可能不如传统的关键词搜索算法。
这里的 Embedding 模型可以去 Hugging Face 的**MTEB (Massive Text Embedding Benchmark)**排行榜上查看和选择,这是业界的黄金标准。
四、查询处理(Query Transformation)
这一步也经常被忽略,但在真实场景下,用户的提问可能是天马行空的,或者与知识库的主题无关,这时直接检索效果会很差。我的建议是在检索前增加一个 LLM 预处理步骤。
- 意图识别与路由:对提问进行分类。如果是在知识库范畴内,则执行检索;对于其他任务(如闲聊、查询天气),则可以礼貌地拒绝或路由到其他工具。查询改写/扩展:对原始提问进行精简或优化,甚至可以从不同角度生成几个新的问题,然后用这几个问题同时去检索,以提高召回的全面性和准确性。
五、重排(Reranking)
这一步也叫重排序,是一个非常重要的优化点。向量数据库返回的结果是一个基于向量相似度粗略检索出的候选集。
通常我们会让向量数据库召回一个比最终需要的多一些的结果,也就是控制Top K
的数量,比如召回 20 条。Reranking的作用就是利用一个更强大的、专门用于判断相关性的重排模型(Reranker),对这 20 条结果进行更精细的打分和重新排序。在 Dify 等平台中,还可以设置一个分数阈值,低于该分数的直接忽略。
所以总结一下,Reranking 这一步是为了对粗召回的结果进行二次精选,确保后续组合 Prompt 时,提供给 LLM 的是最高质量的上下文。
六、检索内容整合(Prompt Engineering)
这一步就是把我们 Reranking 后的结果进行整合,设计成一个合理的 Prompt,方便 LLM 理解并生成答案。
这一步没有太多固定的讲解,需要根据具体任务和模型特性,进行合理的设计即可。
七、答案生成与审查(Generation & Guardrails)
这是流程的最后一步,但通常会面临两个问题:
- 选择什么 LLM? 在大多数场景下,使用强大的通用大模型(如 GPT-4o, Claude 3 等)就足够了。但在特定领域下,比如为了结合公司的业务风格和专业术语,我们可能会选择一个经过微调(Fine-tuned)的专用模型。内容审查与合规:这一步其实是对 LLM 最终生成的内容进行一次审查,可以通过规则或另一个 LLM 来判断答案是否有据可依(Groundedness Check)、是否包含不当内容,确保返回给用户的结果是安全、可靠且负责任的。
衍生点
向量数据库
可以看到我们 RAG 中非常重要的一环就是向量数据库,除了在 RAG 中使用,对于一些常见的文章搜索也可以广泛使用,例如你有一个博客,通常你的搜索可能是基于关键词或者模糊搜索,但是这些都没有脱离文字搜索,但是结合向量数据库你可以做到语义化的返回查询内容。
还有比如电商平台,你搜索关键词可能返回的商品会更加符合你的喜好,因为图片或者简介包含了我们需要搜索的内容。
最后
完成上述的所有步骤和优化,我们的 RAG 流程才算得上健壮和完整。这里面包含了一些我自己的理解和经验,如果有不对的地方,欢迎一起交流和探讨。