RAG 技术深度解析
RAG(检索增强生成)是 2023 年最流行的基于 LLM 的应用系统架构。有许多产品几乎完全建立在 RAG 之上,覆盖了结合网络搜索引擎和 LLM 的问答服务,到成千上万个数据聊天的应用程序。很多人将 RAG 和 Agent 作为大模型应用的两种主流架构,但什么是 RAG 呢?RAG 又涉及了哪些具体的技术呢?
1. 什么是 RAG
RAG 即检索增强生成,为 LLM 提供了从某些数据源检索到的信息,并基于此修正生成的答案。RAG 基本上是 Search + LLM 提示,可以通过大模型回答查询,并将搜索算法所找到的信息作为大模型的上下文。查询和检索到的上下文都会被注入到发送到 LLM 的提示语中。
嵌入式搜索引擎可以通过 Faiss 来实现,向量搜索领域成为了 RAG 的一个助力。像 Pinecone 这样的向量数据库可以构建开源搜索索引,为输入文本增加了额外的存储空间,还增加了一些其他工具。
面向 RAG 的开发框架,对于基于 LLM 的流水线和应用程序,有两个最著名的开源工具——LangChain 和 LlamaIndex,分别是在 2022 年 10 月和 11 月创建的,随着 ChatGPT 爆发,也在 2023 年获得了大量采用。LlamaIndex 和 LangChain 都是令人惊叹的开源项目,它们的发展速度非常快。
2. 基础的 RAG 技术
RAG 系统的起点一般是一个文本文档的语料库,简单看起来是这样的:把文本分割成块,然后把这些分块嵌入到向量与 Transformer 编码器模型,把所有这些向量建立索引,最后创建一个 LLM 提示语,告诉模型回答用户的查询,给出在搜索步骤中找到的上下文。在运行时,我们用相同的编码器模型完成用户查询的向量化,然后执行这个查询向量的索引搜索,找到 top-k 的结果,从数据库中检索到相应的文本块,并提供给 LLM 提示语 Prompt 作为上下文。
在 OpenAI 平台上,提示词 Prompt 可以是这样的:
def question_answering(context, query):
prompt = f""" my query text... """
response = get_completion(instruction, prompt, model="gpt-3.5-turbo")
answer = response.choices[0].message["content"]
return answer
关于提示词和提示词工程的更多介绍可以参考 OpenAI 的提示词工程手册。
显然,尽管 OpenAI 在 LLM 市场上处于领先地位,但还是有很多替代方案,比如 Anthropic 的 Claude,还有最近流行的更小但功能强大的模型,比如 Mistral,微软的 Phi-2 以及许多开源选项,比如 Llama2,OpenLLaMA,Falcon 等都可以用来开发面向 RAG 的大模型产品。
3. RAG 中的高级技术
尽管并不是所有 RAG 系统中的高级技术都可以轻松地在一张图中可视化,但给出一个描述核心步骤和算法的方案还是有意义的。
3.1. 分块和矢量化
首先,要创建一个向量索引表示我们的文档内容,然后在运行时搜索所有这些向量和查询向量之间最小距离对应的最接近语义。
由于 Transformer 模型有固定的输入序列长度,即使输入上下文的窗口很大,一个或几个句子的向量也比一个在几页文本上取平均值的向量更能代表它们的语义意义,所以数据分块是一个有意义的技术。把初始文档分成一定大小的块,同时又不失去它们的意义,也就是把文本分成句子或段落,而不是把一个句子分成两部分。而且,已经有了各种能够执行此任务的文本分割器实现。例如,在 LlamaIndex 中,NodeParser 就提供了一些高级选项,如定义自己的文本分割器、元数据、节点/块关系等。
数据块的大小是一个需要考虑的参数,它取决于使用的嵌入模型及其 token 容量,标准的 Transformer 编码模型,如 BERT 的句子转换器,最多只能使用 512 个 token,OpenAI ada-002 能够处理更长的序列,如 8191 个 token,但这里的折衷是足够的上下文,让 LLM 能够推理以及特定的足够文本嵌入,以便有效地执行搜索。
下一步是选择一个模型来生产所选块的嵌入,同样有很多方法,例如搜索优化的模型(bge-large 或者 E5 系列),MTEB 排行榜可以得到最新的一些方法信息。
3.2. 搜索的索引
面向 RAG 的大模型应用的关键部分是用于搜索的索引,它存储前面得到的向量化内容。当然,查询总是首先向量化,对于 top k 分块也是一样的。最简单的实现使用一个平铺的索引,在查询向量和所有块向量之间进行距离计算并遍历。


