RAG工程

本文基于GitHub - AlibabaCloudDocs/aliyun_acp_learning内容整理。

1. RAG(检索增强生成)

RAG(Retrieval-Augmented Generation,检索增强生成) 就是实现上下文工程的强大技术方案。

核心思想:

  • 检索:检索与私域知识相关的知识片段。

  • 增强:与用户的问题合并喂给大模型。

  • 生成:生成大模型返回的回答。

构建RAG主要为2个阶段:

1.1. 建立索引

建立索引是为了将私有知识文档或片段转换为可以高效检索的形式。通过将文件内容分割并转化为多维向量(使用专用 Embedding 模型),并结合向量存储保留文本的语义信息,方便进行相似度计算。向量化使得模型能够高效检索和匹配相关内容,特别是在处理大规模知识库时,显著提高了查询的准确性和响应速度。

建议索引主要包含以下步骤:

  1. 文档解析:需要将各种格式的文档(pdf, word)解析成大模型可理解的格式。

  2. 文本分段:对解析后的文档进行分类分段,以便可以快速找到。

  3. 文本向量化:对文本进行数字化,以便进行相似度比较和寻址。

  4. 存储索引:将向量化后的数据存储到向量数据库,增加后续的查找速度。

1.2. 检索与生成

检索生成是根据用户的提问,从索引中检索相关的文档片段,这些片段会与提问一起输入到大模型生成最终的回答。这样大模型就能够回答私有知识问题了。

  1. 检索:检索会把问题同向量数据库的进行相似度比较,并找出最相关的段落。

  2. 生成:将问题和检索的内容生成提示词喂给大模型,利用大模型的总结能力返回答案。

1.3. 实践RAG应用

1、基于公司的制度文件创建RAG应用,步骤如下:

  1. 解析文本文件

  2. 创建索引

  3. 创建提问引擎(设置流式输出)

  4. 输入问题并输出答案

# 导入依赖
from llama_index.embeddings.dashscope import DashScopeEmbedding,DashScopeTextEmbeddingModels
from llama_index.core import SimpleDirectoryReader,VectorStoreIndex
from llama_index.llms.openai_like import OpenAILike

# 这两行代码是用于消除 WARNING 警告信息,避免干扰阅读学习,生产环境中建议根据需要来设置日志级别
import logging
logging.basicConfig(level=logging.ERROR)

print("正在解析文件...")
# LlamaIndex提供了SimpleDirectoryReader方法,可以直接将指定文件夹中的文件加载为document对象,对应着解析过程
documents = SimpleDirectoryReader('./docs').load_data()

print("正在创建索引...")
# from_documents方法包含切片与建立索引步骤
index = VectorStoreIndex.from_documents(
    documents,
    # 指定embedding 模型
    embed_model=DashScopeEmbedding(
        # 你也可以使用阿里云提供的其它embedding模型:https://help.aliyun.com/zh/model-studio/getting-started/models#3383780daf8hw
        model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2
    ))
print("正在创建提问引擎...")
query_engine = index.as_query_engine(
    # 设置为流式输出
    streaming=True,
    # 此处使用qwen-plus模型,你也可以使用阿里云提供的其它qwen的文本生成模型:https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u
    llm=OpenAILike(
        model="qwen-plus",
        api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        is_chat_model=True
        ))
print("正在生成回复...")
streaming_response = query_engine.query('我们公司项目管理应该用什么工具')
print("回答是:")
# 采用流式输出
streaming_response.print_response_stream()

2、RAG程序优化,保存和加载索引

由于创建索引的时间比较长,如果提前创建索引并保存到本地,则可以提升大模型回答的速度。

# 将索引保存为本地文件
index.storage_context.persist("knowledge_base/test")
print("索引文件保存到了knowledge_base/test")

# 将本地索引文件加载为索引
from llama_index.core import StorageContext,load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="knowledge_base/test")
index = load_index_from_storage(storage_context,embed_model=DashScopeEmbedding(
        model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2
    ))
print("成功从knowledge_base/test路径加载索引")

封装:

from chatbot import rag

# 引文在前面的步骤中已经建立了索引,因此这里可以直接加载索引。如果需要重建索引,可以增加一行代码:rag.indexing()
index = rag.load_index(persist_path='./knowledge_base/test')
query_engine = rag.create_query_engine(index=index)

rag.ask('我们公司项目管理应该用什么工具', query_engine=query_engine)

2. RAG工作流程优化

rag的工作流程如下:

  1. 解析与切片

  2. 向量存储

  3. 检索召回

  4. 生成答案

RAG各个环节优化策略

2.1. 文档准备阶段

2.2. 文档解析与切片阶段

2.2.1. 问题分类及改进策略

2.2.2. 借助工具解析pdf到markdown

2.2.3. 使用多种文档切片方法

  1. Token切片

  2. 句子切片

  3. 句子窗口切片

  4. 语义切片

  5. markdown切片

2.3. 切片向量化与存储阶段

文档切片后需要建立索引,可以使用嵌入(Embedding)模型将切片向量化,并存储到向量数据库中。

2.3.1. 了解Embedding与向量化

Embedding模型将文本转换为高维向量,向量之间夹角越小说明相似度越高。

2.3.2. 选择合适的Embedding模型

不同的Embedding模型对相同文字得到的向量可能完全不同,越新的Embedding模型其表现越好。在实践中,单纯升级Embedding模型就可以显著提升检索质量。

2.3.3. 选择合适的向量数据库

向量存储方案从简单到复杂如下:

  • 内存向量存储:优点快速上手,开发测试;缺点数据无法持久化,受限于内存大小。

  • 本地向量数据库:例如Milvus、Qdrant 等。这些数据库提供了数据持久化和高效检索能力。优点是功能完整、可控性强;缺点是需要自行部署维护。

  • 云服务向量存储:例如阿里云的向量检索服务(DashVector),向量检索服务 Milvus 版,已有数据库的向量能力。优点是按量付费,成本可控,无需运维。

选择建议:

  • 开发测试选择内存向量存储

  • 小规模应用选择本地向量数据库

  • 生产环境使用云服务。

2.4. 向量召回阶段

2.3.1. 问题改写

  • 使用大模型扩充问题

  • 将单一查询改为多步查询

  • 用假设文档来增强检索

2.3.2. 提取标签增强检索

在向量检索的基础上我们可以增加标签过滤:

  1. 建立索引时,从文档切片中提取结构化标签

  2. 检索时,从用户问题中提取对应标签进行过滤

2.2.3. 重排序

从向量数据库中检索出20条文档切片,通过文本排序模型进行重新排序,删选出最相关的3条信息。

2.5. 生成答案阶段

大模型生成的答案不及预期,可以通过以下方式解决:

  1. 选择合适的大模型

  2. 充分优化提示词模板:明确要求不编造答案;添加内容分隔标记;根据问题类型调整模板。

  3. 调整大模型参数

  4. 调优大模型

参考: