跳到主要内容 RAG 实操教程:基于 LangChain 与 Llama2 构建个人 LLM 应用 | 极客日志
Python AI 算法
RAG 实操教程:基于 LangChain 与 Llama2 构建个人 LLM 应用 本文介绍如何使用 LangChain 框架结合 Llama2 本地大模型构建检索增强生成(RAG)系统。通过 PyMuPDF 加载 PDF 文档,利用 Sentence Transformers 进行文本向量化,并存储至 ChromaDB 向量数据库。教程涵盖环境配置、文本分块、嵌入模型选择、LLM 服务启动及 Prompt 设置等关键步骤,最终实现基于私有数据的问答功能。此外,还补充了硬件要求、常见错误排查及性能优化建议,帮助开发者快速落地本地化 AI 应用。
PhpPioneer 发布于 2025/2/6 0 浏览
RAG 实操教程:基于 LangChain 与 Llama2 构建个人 LLM 应用
本文将逐步指导您创建自己的 RAG(检索增强生成)系统,使您能够上传自己的 PDF 文件并向 LLM 询问有关 PDF 的信息。本教程侧重于核心流程,即暂时不涉及 Gradio 前端交互。相关技术栈包括以下内容:
LLM : Llama2
LLM API: llama.cpp service
Langchain
Vector DB : ChromaDB
Embedding : sentence-transformers
核心在于 LangChain,它是用于开发由语言模型支持的应用程序的框架。LangChain 就像胶水一样,有各种接口可以连接 LLM 模型与其他工具和数据源。以下我将使用最简单的方式示范完整流程。
步骤 1. 环境设置 首先设置 Python 环境,建议使用 conda 创建独立环境,并安装以下库。示例在 Jupyter 环境中完成。
ipykernel
ipywidgets
langchain
PyMuPDF
chromadb
sentence-transformers
llama-cpp-python
注意:llama-cpp-python 可能需要编译依赖,请确保已安装 C++ 编译器及相应工具链。
步骤 2. 读入文件处理并导入数据库 首先我们要将外部信息处理后,放到 DB 中,以供之后查询相关知识。这一步对应文本拆分器(Text Splitter)和 Embedding 过程。
a). 使用文件加载器 LangChain 提供了很多文件加载器,包括 Word、CSV、PDF、Google Drive、Youtube 等。这里我创建一个虚拟人物 Alison Hawk 的 PDF 信息,并使用 PyMuPDFLoader 读取。请注意需要安装 PyMuPDF 才能使用。
from langchain.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("LangChain/Virtual_characters.pdf" )
PDF_data = loader.load()
b). 文本分割 文本分割器会将文档或文字分割成一个个 chunk,用以预防文档的信息超过 LLM 的 tokens。LangChain 提供两种主要方式:
RecursiveCharacterTextSplitter: 递归字符分割,默认推荐。
CharacterTextSplitter: 简单字符分割。
chunk_size: 决定分割文字时每个内存块中的最大字符数。
chunk_overlap: 决定分割文字时连续内存块之间重叠的字符数,避免语义被拆分后不完整。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100 , chunk_overlap=5 )
all_splits = text_splitter.split_documents(PDF_data)
c). 加载嵌入模型 然后使用嵌入将步骤 (b) 分割的块文本转换为向量。LangChain 提供了许多嵌入模型的接口,例如 OpenAI、Cohere、Hugging Face 等。这边我使用 Hugging Face 的 Sentence Transformers。
from langchain.embeddings import HuggingFaceEmbeddings
model_name = "sentence-transformers/all-MiniLM-L6-v2"
model_kwargs = {'device' : 'cpu' }
embedding = HuggingFaceEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs
)
d). 将 Embedding 结果汇入 VectorDB 我们会将嵌入后的结果存储在 VectorDB 中,常见的 VectorDB 包括 Chroma、Pinecone 和 FAISS 等,这里我使用 Chroma 来实现。Chroma 与 LangChain 整合得很好。
from langchain.vectorstores import Chroma
persist_directory = 'db'
vectordb = Chroma.from_documents(documents=all_splits, embedding=embedding, persist_directory=persist_directory)
指定 persist_directory 将会把嵌入存储到磁盘上,方便后续复用。
步骤 3. 启用 LLM 服务 你可以通过两种方法启动 LLM 模型并连接到 LangChain。一种是使用 LangChain 的 LlamaCpp 接口;另一种是用其他方式搭建 Llama2 的 API 服务。
a). 使用 LangChain 的 LlamaCpp 使用 LlamaCpp 接口加载 model,它会帮你启动 Llama 的服务。这方法较简单,直接使用下面代码就可以执行。model_path 指定到你的模型路径,例子中使用量化过后的 Llama2 Chat。注意这边要安装 llama-cpp-python。
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_community.llms import LlamaCpp
model_path = "llama.cpp/models/llama-2-7b-chat/llama-2_q4.gguf"
llm = LlamaCpp(
model_path=model_path,
n_gpu_layers=100 ,
n_batch=512 ,
n_ctx=2048 ,
f16_kv=True ,
callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]),
verbose=True ,
)
您可以尝试进行测试,看看 llm 服务是否已启动:
llm("What is Chain known for?" )
b). 使用 API 服务 如果你已经使用其他方式架设 LLM 的 API 服务,或者是使用 openai 的 API 的话,你需要使用 LangChain 的 ChatOpenAI 接口。这里示范是 llama.cpp 的 server 服务,它提供了类似 OpenAI 的 API,因此我们能直接用同一个接口来操作。
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
openai_api_key='None' ,
openai_api_base='http://127.0.0.1:8080/v1'
)
步骤 4. 设定你的 Prompt 一些 LLM 可以使用特定的 Prompt。例如,Llama 可使用特殊 token。我们可以使用 ConditionalPromptSelector 根据模型类型设定 Prompt。
from langchain.chains import LLMChain
from langchain.chains.prompt_selector import ConditionalPromptSelector
from langchain.prompts import PromptTemplate
DEFAULT_LLAMA_SEARCH_PROMPT = PromptTemplate(
input_variables=["question" ],
template="""<<SYS>> \n You are an assistant tasked with improving Google search results. \n <</SYS>> \n\n [INST] Generate THREE Google search queries that are similar to this question. The output should be a numbered list of questions and each should have a question mark at the end: \n\n {question} [/INST]""" ,
)
DEFAULT_SEARCH_PROMPT = PromptTemplate(
input_variables=["question" ],
template="""You are an assistant tasked with improving Google search results. Generate THREE Google search queries that are similar to this question. The output should be a numbered list of questions and each should have a question mark at the end: {question}""" ,
)
QUESTION_PROMPT_SELECTOR = ConditionalPromptSelector(
default_prompt=DEFAULT_SEARCH_PROMPT,
conditionals=[(lambda llm: isinstance (llm, LlamaCpp), DEFAULT_LLAMA_SEARCH_PROMPT)],
)
prompt = QUESTION_PROMPT_SELECTOR.get_prompt(llm)
使用 LLMChain 将提示与 llm 连接在一起。另外 LangChain 最近的更新采用了 invoke,当您看到其他文章中使用时请注意区分 run 和 invoke。
llm_chain = LLMChain(prompt=prompt, llm=llm)
question = "What is china known for?"
llm_chain.invoke({"question" : question})
步骤 5. 文本检索 + 查询 LLM 我们已经将 PDF 信息导入 DB,并启动了 LLM 服务,接下来要连接整个 RAG 步骤:
用户发送 QA
从 DB 中检索文本
将 QA 与检索的文本结合发给 LLM
LLM 基于信息进行回答
首先要创建 Retriever,它可以根据非结构化的 QA 返回相应文件。请注意,已弃用的功能是使用 RetrievalQA 结合 Retriever、QA 和 llm。现在应该使用 VectorDBQA 或新的 RetrievalQA 接口。
retriever = vectordb.as_retriever()
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff" ,
retriever=retriever,
verbose=True
)
步骤 6. 使用你的 RAG 到这里我们就串好整个 RAG 的流程,接下来我们来问问 Alison Hawk 的信息(PDF 记录的虚拟人物名称)。
query = "Tell me about Alison Hawk's career and age"
result = qa.invoke(query)
print (result["result" ])
LLM 已经获取了从数据库中取得的 Alison Hawk 上传的 PDF 文件,并且知道她是一位 28 岁的研究员。
完整代码参考 from langchain.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import LlamaCpp
from langchain.chains import RetrievalQA
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
loader = PyMuPDFLoader("Virtual_characters.pdf" )
PDF_data = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100 , chunk_overlap=5 )
all_splits = text_splitter.split_documents(PDF_data)
persist_directory = 'db'
model_name = "sentence-transformers/all-MiniLM-L6-v2"
model_kwargs = {'device' : 'cpu' }
embedding = HuggingFaceEmbeddings(model_name=model_name, model_kwargs=model_kwargs)
vectordb = Chroma.from_documents(documents=all_splits, embedding=embedding, persist_directory=persist_directory)
model_path = "llama-2_q4.gguf"
llm = LlamaCpp(
model_path=model_path,
n_gpu_layers=100 ,
n_batch=512 ,
n_ctx=2048 ,
f16_kv=True ,
callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]),
verbose=True ,
)
retriever = vectordb.as_retriever()
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff" ,
retriever=retriever,
verbose=True
)
query = "Tell me about Alison Hawk's career and age"
response = qa.invoke(query)
print (response)
常见问题与优化建议
硬件要求 本地运行 Llama2 对硬件有一定要求。如果使用 CPU 推理,速度较慢且显存占用高。建议至少配备 8GB 以上内存,若使用 GPU 加速,需 NVIDIA 显卡并安装 CUDA 驱动。量化版本(如 q4_0, q4_k_m)可显著降低资源需求。
常见错误排查
Import Error : 如果 langchain_community 报错,请确保安装了最新版本的 langchain-community 包。
Model Loading Fail : 检查 model_path 是否正确,以及 .gguf 文件格式是否损坏。
Context Overflow : 如果文档内容过长导致超出 n_ctx 限制,请增加 chunk_size 或调整 n_ctx 参数。
性能优化
Hybrid Search : 结合关键词搜索与向量搜索,提高检索准确率。
Reranking : 引入重排序模型(如 BGE-Reranker),对检索结果进行二次排序。
Caching : 对于重复查询,可缓存 Embedding 结果以减少计算开销。
总结与展望 本文详细介绍了如何使用 LangChain 框架结合 Llama2 本地大模型构建 RAG 系统。通过私有化部署,可以有效保护数据隐私,同时利用大模型的能力处理特定领域的文档问答。随着技术的演进,未来还可以探索多模态 RAG、Agent 自主规划等方向,进一步提升系统的智能化水平。开发者可根据实际需求调整组件配置,实现更高效的 AI 应用落地。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online