之前的文章《15天学会AI应用开发(十三)上下文与RAG的阶段性总结》概述了检索RAG知识库的主要步骤,现在采用LangChain将其改造为更智能的本地RAG系统。
接下来基于LangChain框架与Chroma向量数据库,结合离线的文本嵌入模型和大语言模型,从零开始搭建本地的RAG检索系统。
一、下载RAG需要的离线大模型
首先下载离线的大语言模型,比如在命令行窗口执行下面命令,即可下载国产大模型qwen2:1.5b:
ollama pull qwen2:1.5b
接着下载构建向量数据库需要的文本嵌入模型,比如在命令行窗口执行下面命令,即可下载国产嵌入大模型bge-m3:
ollama pull bge-m3
等待两个大模型下载完毕,在命令行窗口执行下面命令,即可启动Ollama并加载离线大模型:
ollama serve
二、安装RAG需要的LangChain包
使用LangChain实现RAG检索功能用到了多个LangChain包,下面分别说明需要安装的LangChain包及其用途。
首先在命令行窗口执行下面命令,即可安装LangChain及其用到的Ollama插件:
pip install langchain langchain-core langchain-ollama
其次执行下面命令安装Splitters插件,用于对文档内容切分文本块:
arduino
pip install langchain-text-splitters
再次执行下面命令安装Chroma插件,用于让LangChain操作向量数据库Chroma:
pip install langchain-chroma chromadb
还要执行下面命令安装LangChain的社区包,用于引入PyPDFLoader以便加载PDF文档:
pip install langchain-community
三、编写RAG的检索实现代码
基于LangChain与Chroma实现RAG功能的时候,主要经过四个步骤:解析PDF文档、创建RAG检索器、构建提示词模板、启动RAG链执行问答操作,依次说明如下:
1、解析PDF文档
首先准备好待解析的PDF文档比如test.pdf放在python工程目录下,接着使用PyPDFLoader加载PDF文档,再引入RecursiveCharacterTextSplitter切分文本块。
对应的Python代码及其导包语句如下:
ini
# PDF文档加载
from langchain_community.document_loaders import PyPDFLoader
# 文本分割工具
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 加载PDF文档
loader = PyPDFLoader("./test.pdf")
docs = loader.load()
# 切分文本块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=80,
separators=["\n\n", "\n", "。", ",", " "]
)
split_docs = text_splitter.split_documents(docs)
2、创建RAG检索器
首先使用OllamaEmbeddings加载离线的文本嵌入模型bge-m3,接着引入Chroma从文档内容构建向量数据库,注意数据库文件保存在persist_directory参数指定的chroma_db目录;然后调用数据库实例的as_retriever方法创建RAG检索器,该方法的search_kwargs结构可指定最多引用几个文档块。
对应的Python代码及其导包语句如下:
ini
# 文本嵌入模型
from langchain_ollama import OllamaEmbeddings
# 向量数据库
from langchain_chroma import Chroma
# 引用离线的文本嵌入模型bge-m3
embedding = OllamaEmbeddings( model="bge-m3" )
# 从文档内容构建向量数据库,数据库文件保存在chroma_db目录
vectordb = Chroma.from_documents(
documents=split_docs,
embedding=embedding,
persist_directory="./chroma_db"
)
# 创建RAG检索器,最多引用三个文档块
retriever = vectordb.as_retriever( search_kwargs={ "k": 3 } )
3、构建提示词模板
首先编写字符串形式的提示词内容,说清楚要让AI做什么事、返回什么样的回答,关键要说明上下文来自context字段,且用户问题来自question字段。然后调用PromptTemplate的from_template方法从提示词文字构建提示词模板。
对应的Python代码及其导包语句如下:
ini
from langchain_core.prompts import PromptTemplate
# 自定义提示词。很重要,必须提示context和question,这样AI才会有效回答
prompt_template = """
你是专业文档问答助手,只能根据提供的上下文回答问题,禁止编造信息。
上下文:
{context}
用户问题:
{question}
请提供包含主语、谓语、宾语在内的准确、简洁的一句话回答(50字以内)。
"""
# 从提示词文字构建提示词模板
prompt = PromptTemplate.from_template(prompt_template)
4、启动RAG链执行问答操作
首先使用ChatOllama加载离线的大语言模型qwen2:1.5b,接着封装RAG执行链,注意输入第三步提示词要求的context和question两个参数,其中context字段依次输入第二步的RAG检索器和拼接文档需要的format_docs函数。然后调用RAG执行链的invoke方法,根据输入的问题获取RAG检索返回的答案。
对应的Python代码及其导包语句如下:
python
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_core.output_parsers import StrOutputParser
from langchain_ollama import ChatOllama
# 工具函数:拼接检索到的多段文档
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 加载离线的大语言模型qwen2:1.5b
llm = ChatOllama(model="qwen2:1.5b") # 改成你本地的模型
# 构建RAG执行链,输入context和question两个参数
rag_chain = (
{
"context": retriever | RunnableLambda(format_docs),
"question": RunnablePassthrough()
}
| prompt | llm | StrOutputParser()
)
# 令RAG链执行问答操作
query = "公共收益是什么?"
answer = rag_chain.invoke(query)
print("=== AI回答 ===\n", answer)
运行上面整合后的Python代码,根据提示输入问题内容"公共收益是什么?",输出日志结果如下:
diff
=== 你的问题 ===
公共收益是什么?
=== AI回答 ===
公共收益是指利用住宅小区业主共有部分以外的共用设施设备开展经营活动而产生的收益。
由日志信息发现,AI应用能够返回PDF文档中的正确答案。可见引入Chroma向量数据库之后,LangChain成功实现了来自PDF文档的RAG知识检索功能。
本系列的AI应用开发文章目录为《15天学会AI应用开发全目录(零基础小白,零Token消耗)》。