基于RAG的文档QA系统 | 豆包MarsCode AI刷题

205 阅读4分钟

基于RAG的文档QA系统概念与原理详解

1 技术介绍

1. 文档切分(Indexing)

在RAG问答系统中,文档切分是将整个文档分解成更小的、可管理的单元(chunk)的过程。这一步骤至关重要,因为它直接影响到后续检索的准确性和效率。切分可以基于句子、段落或者固定长度的文本块。切分的目的是为了在保持语义完整性的同时,提高检索的粒度和效率。例如,使用RecursiveCharacterTextSplitter,我们可以将文档分割成固定字符长度的块,同时设置一定的重叠区域以保持上下文连贯性。

2. 语句嵌入(Embeddings)

语句嵌入是将文本转换为向量空间中的点,这些点可以捕捉文本的语义信息。在RAG系统中,我们通常使用预训练的语言模型(如OpenAI的Ada)来生成这些嵌入。嵌入向量使得我们可以在向量空间中进行高效的相似度计算。通过OpenAIEmbeddings,我们可以将文本块转换为嵌入向量,这些向量随后被存储在向量数据库中,用于检索。

3. 召回搜索(Retrieval)

召回搜索是RAG系统中的核心步骤,它涉及根据用户的查询从向量数据库中检索最相关的文档块。这一步骤通常基于向量之间的相似度计算。在RAG系统中,有两种主要的相似度度量方法:余弦相似度和欧几里得距离。

  • 余弦相似度:衡量两个向量之间的夹角,从而评估它们的相似性。当两个向量的方向越接近,它们的余弦相似度越接近1,表示它们越相似。余弦相似度适用于捕捉语义相似性,因为它关注的是向量的方向而非大小。
  • 欧几里得距离:衡量两个向量在空间中的实际距离。欧几里得距离越小,表示两个向量越接近。它适用于那些向量的大小直接相关的场合,但在文本相似度计算中不如余弦相似度常用。

在RAG系统中,通常会选择余弦相似度作为相似度度量,因为它更适合于捕捉文本的语义相似性。通过计算查询向量和文档块向量之间的余弦相似度,系统可以检索出与查询最相关的文档块。

4. 拼接Prompt进行问答(Generation)

一旦检索到相关的文档块,下一步是将这些块与原始查询拼接成一个prompt,然后输入到语言模型中生成答案。这个prompt通常包括查询和检索到的文档块,有时还会包括一些指导性的语言,告诉模型如何结合这些信息生成答案。这个过程是RAG系统生成准确、相关回答的关键。

通过上述步骤,RAG系统能够有效地结合检索到的外部知识,减少“幻觉”现象,提高问答系统的准确性和可靠性。这种结合检索和生成的方法,使得RAG成为处理复杂问答任务的强大工具。

2. 整体架构流程

2.1 加载文档

首先,我们需要加载鲜花公司管理手册的文档。这可以通过使用langchain的文档加载器来实现。例如,如果文档是PDF格式的,我们可以使用PyPDFLoader

python
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("path_to_management_manual.pdf")
pages = loader.load_and_split()
2.2 文档切分

接下来,我们将文档切分成更小的段落,以便进行处理。使用RecursiveCharacterTextSplitter进行切分:

python
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=100)
paragraphs = []
for page in pages:
    paragraphs.extend(text_splitter.create_documents([page.page_content]))
2.3 语句嵌入

将文档段落转换为向量形式,以便存储在向量数据库中。使用OpenAIEmbeddings进行嵌入:

python
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
vectors = [embeddings.embed_text(paragraph) for paragraph in paragraphs]
2.4 召回搜索

使用向量数据库进行召回搜索,找到与查询最相关的文档段落。这里我们使用Chroma作为向量数据库:

python
from langchain_community.vectorstores import Chroma
db = Chroma.from_documents(paragraphs, OpenAIEmbeddings())
retriever = db.as_retriever()
docs = retriever.get_relevant_documents("查询内容")
2.5 拼接Prompt进行问答

最后,我们将查询和相关文档段落拼接成prompt,然后使用语言模型生成回答:

python复制
prompt_template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
{info}
用户问:
{question}
请用中文回答用户问题。
"""
from langchain.prompts import PromptTemplate
template = PromptTemplate.from_template(prompt_template)
prompt = template.format(info=docs[0].page_content, question="查询内容")
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
response = llm.invoke(prompt)
print(response.content)