Naive RAG
RAG技术概况
当前,RAG技术已发展出三种主要类型:Naive RAG(朴素RAG)、Advanced RAG(高级RAG)和Modular RAG(模块化RAG)。
尽管RAG在成本效益上优于原生大语言模型(LLM),但它仍存在若干局限性。这也让人感到:RAG 虽然入门容易,但要想做好却颇具挑战。
为了克服Naive RAG中的不足,Advanced RAG与Modular RAG应运而生,代表了RAG技术进一步演进的重要方向。
Naive RAG
Naive RAG涵盖一个传统的流程,包括索引、检索和生成。它也被称为"检索-阅读"框架。
朴素RAG的典型工作流程图:
使用langchain构建简单RAG
我们利用 Python 用的本地部署ollama+千问2、Chroma向量数据库以及OpenAI 嵌入模型(实际上是使用开源embedding模型)来实现一个检索增强生成(RAG)流程。使用 LangChain来进行整件编排。
了解LangChain 或Chroma可以通过他们的官方文档
https://python.langchain.com/v0.2/docs/introduction/
https://www.trychroma.com/
环境准备
在开始之前,请确保你的系统中已安装以下 Python 包:
- langchain —— 用于整件编排
- openai —— 提供嵌入模型和大语言模型 (LLM),我们这儿用openAI版示,但实际使用中可能是用ollama本地部署
- chroma —— 向量数据库
安装chroma
安装chroma轻量级向量数据库,因为它轻量并且支持windows,不需要wsl,不需要docker
pip install chromadb # 安装
chroma run # 运行
安装其他必要库
#conda创建python=3.10版本的虚拟环境
conda create-n llmrag python=3.10
#激活conda创建的名字11mrag的虚拟环境
conda activate llmragpytorch安装
conda instal1 pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=12.1 -c pytorch -c nvidia#安装依赖#用openai的模型
pip install openai#如果是本地部等ollama,langchain对ollama的支持
pip install langchain-ollama#通义千问线上版
pip install -U langchain_openai#支持chroma
pip install langchain_chroma
pip install -U langchain-community
使用openai的模型
如果使用openai的模型来生成,你需要在项目的根目录下的 .env 文件中设置相关的环境变量,要获取 OpenAI 的 API 密钥,你需要注册 OpenAI 账户,并在https://platform.openai.com/account/api-keys页面中选择"创建新的密钥"。
然后在项目中创建一个.env文件,在里面填写以下信息:
OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"
通义千问
注册地址:https://help.aliyun.com/zh/model-studio/obtain-api-key-app-id-and-workspace-id
兼容openai文档地址:https://help.aliyun.com/zh/model-studio/compatibility-of-openai-with-dashscope
跟上面同样的操作:
DASHSCOPE_API_KEY="YOUR_DASHSCOPE_API_KEY"
项目通用加载环境变量
完成这些设置后,运行下面的命令来加载你所设置的环境变量。
import dotenv
dotenv.load_dotenv()
使用ollama
安装并运行Ollama服务,同时下载推理所需的模型。
#https://ollama.com/library/qwen2:7b-instruct-q4_0
#比如已经下载好了qwen2 7b的模型
ollama run qwen2:7b-instruct-q4_0
建立向量数据库
建立一个向量数据库,作为一个外部知识源,分三步:
- 收集数据并将其加载到系统
- 将您的文档进行分块处理
- 分块内容进行嵌入,并存储这些块
选择 BAAI/bge-small-zh-v1.5作为embedding模型,因为它是开源的模型,而且体积较小,性能也不差,还有GPU算力的机器和CPU也能跑得动该模型。
另外大语言模型是兼容Openai API的国产通义千问,因为国外的大模型比如ChatGPT, Claude等对中国用户都不是很友好,因此选择跟些能跟与Openai API兼容的国产模型,这样我们就可以使LangChain框架来使用国产大模型了。
在这个例子以及后面的例子中,遇到库我们都将使用百度百科关于aigc的知识:https://baike.baidu.com/item/AIGC?fromModule=lemma_search-box
为了加载这些数据,可以利用 LangChain 提供的众多 DocumentLoader 之一。Document 是一个包含文本和元数据的字典。为了加载文本,可以使用 LangChain 的 TextLoader。
import requests
from langchain.document_loaders import WebBaseLoaderloader = WebBaseLoader("https://baike.baidu.com/item/AIGC?fromModule=lemma_search-box")
documents = loader.load()
其次,需要对文档进行分块 — 由于 Document 的原始大小超出了 LLM 处理窗口的限制,因此需要将其切割成更小的片段。LangChain 提供了许多文本分割工具。对于这个简单的示例,你可以使用 RecursiveCharacterTextSplitter,设置 chunk_size 大约为 500,并且设置 chunk_overlap 为 50,以确保文本块之间的连贯性。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
print(chunks[0].page_content)
最后一步是嵌入并存储这些文本块。为了实现对文本块的语义搜索,你需要为每个块生成向量嵌入,并将它们存储起来。生成向量嵌入时,你可以使用 OpenAI 的嵌入模型;而存储它们,则可以使用 Chroma 向量数据库。通过执行 .from_documents() 操作,就可以自动将这些块嵌入向量数据库中。
from langchain.embeddings import OpenAIEmbeddingsfrom langchain_chroma import Chroma
from langchain_community.embeddings import HuggingFaceBgeEmbeddings#openai embedding
#rag_embeddings=OpenAIEmbeddings()#创建BAAI的embedding
rag_embeddings = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-small-zh-v1.5")#embed嵌存到chroma向量数据库
vector_store = Chroma.from_documents(documents=chunks, embedding=rag_embeddings,persist_directory="./chroma_langchain_db")
第一步:检索
根据用户查询与已嵌入的文本块之间的语义相似度,来检索出最外的上下文信息。
retriever = vectorstore.as_retriever()
第二步:增强
接下来,你需要准备一个提示模板,以便用额外的上下文信息来增强原始的提示。可以根据下面显示的示例,轻松地定制这样一个提示模板。
from langchain.prompts import ChatPromptTemplatetemplate = """您是问答任务的副理。
使用以下检索到的上下文来回答问题。
如果你不知道答案,就说你不知道。
最多使用三句话,不超过100字,保持答案简洁。
Question: {question}
Context: {context}
Answer:
"""prompt = ChatPromptTemplate.from_template(template)print(prompt)
第三步:生成
在 RAG (检索增强生成) 管道的构建过程中,可以通过将检索器、提示模板与大语言模型 (LLM) 组结合来形成一个序列。定义好 RAG 序列之后,就可以开始执行它。
from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParserfrom langchain_ollama.llms import OllamaLLM#使用ollama服务
llm = OllamaLLM(model="qwen2:7b-instruct-q4_0")...#使用通义千问
llm = ChatOpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"), # 如果实没有配置环境变量,请在此处用自己的API Key进行替换base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 填写DashScope base_urlmodel="qwen-plus"
)
...#使用openai
#llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)rag_chain = ({"context": retriever, "question": RunnablePassthrough()}| prompt| llm| StrOutputParser()
)query = "艾伦•图灵的论文叫什么"
resp=rag_chain.invoke(query)
print(resp)
从用户问"艾伦•图灵的论文叫什么"开始,通过检索问题数据库并返回文本,接着进行提示增充,最终生成回答的检索增强生成。
完整代码
import requests
from langchain.document_loaders import WebBaseLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain.prompts import ChatPromptTemplatefrom langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParserfrom langchain_ollama.llms import OllamaLLMfrom langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings#准备知识库数据,建索引
def prepare_data():loader = WebBaseLoader("https://baike.baidu.com/item/AIGC?fromModule=lemma_search-box")documents = loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)chunks = text_splitter.split_documents(documents)print(chunks[0].page_content)return chunks#embedding 知识库,保存到向量数据库
def embedding_data(chunks):#openai embedding#rag_embeddings=OpenAIEmbeddings()#创建BAAI的embeddingrag_embeddings = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-small-zh-v1.5")#embed保存知识到向量数据库vector_store = Chroma.from_documents(documents=chunks, embedding=rag_embeddings,persist_directory="./chroma_langchain_db")retriever = vector_store.as_retriever()return vector_store,retriever#使用ollama服务
llm = OllamaLLM(model="qwen2:7b-instruct-q4_0")
template = """您是问答任务的助理。
使用以下检索到的上下文来回答问题。
如果你不知道答案,就说你不知道。
最多使用三句话,不超过100字,保持答案简洁。
Question: {question}
Context: {context}
Answer:
"""prompt = ChatPromptTemplate.from_template(template)chunks = prepare_data()vector_store,retriever = embedding_data(chunks)#生成答案
def generate_answer(question):prompt = ChatPromptTemplate.from_template(template)chunks = prepare_data()vector_store,retriever = embedding_data(chunks)#生成答案
def generate_answer(question):...#使用通义千问llm = ChatOpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"), # 如果没有配置环境变量,在此处用自己的API Key进行替换base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 填写DashScope base_urlmodel="qwen-plus")...#使用openai#llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)rag_chain = ({"context": retriever, "question": RunnablePassthrough()}| prompt| llm| StrOutputParser())resp=rag_chain.invoke(question)print(resp)query = "艾伦•图灵的论文叫什么"generate_answer(query)