加载本地文本文件并分割处理
使用嵌入模型将文本转换为向量表示
构建向量数据库(FAISS)实现高效相似性检索
结合检索结果和大语言模型生成回答
特点 | 说明 | 优势体现 |
---|---|---|
模块化封装 | LangChain提供标准化组件 | 快速搭建原型系统 |
自动化流程 | 内置文档处理、检索、生成流水线 | 减少开发工作量 |
中文优化 | 使用bge-zh嵌入模型 | 中文语义理解更准确 |
本地化部署 | 模型和API均在本地运行 | 数据隐私有保障 |
导入依赖模块
from langchain_community.vectorstores import FAISS # 导入FAISS向量存储库 - 用于高效存储和检索向量化文档
from langchain_huggingface import HuggingFaceEmbeddings # 导入Hugging Face嵌入模型 - 用于将文本转换为向量表示
from langchain_community.document_loaders import TextLoader # 导入文本加载器 - 用于加载本地文本文件
from langchain.text_splitter import RecursiveCharacterTextSplitter # 导入递归字符文本分割器 - 用于将长文本分割为适合处理的片段
from langchain_openai import ChatOpenAI # 导入ChatOpenAI模型 - 提供语言模型能力
# from langchain.chains import RetrievalQA # 导入RetrievalQA链
from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
初始化语言模型
# 使用 OpenAI API 的 ChatOpenAI 模型
chat_model = ChatOpenAI(
api_key='EMPTY', # 设置为'EMPTY'因为使用本地部署的API
base_url='http://127.0.0.1:10222/v1', # 本地API服务的地址和端口
model='Qwen2.5-7B-Instruct' # 使用的具体模型名称(通义千问2.5的7B指令微调版)
)
加载和预处理文本数据
# 加载文本文件 "黑悟空.txt",编码格式为 'utf-8'
loader = TextLoader("黑悟空.txt", encoding='utf-8')
docs = loader.load() # 将文件内容加载到变量 docs 中
# 把文本分割成 200 字一组的切片,每组之间有 20 字重叠
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200, # 每个文本块的最大长度(字符数)
chunk_overlap=20 # 相邻文本块之间的重叠字符数(避免信息割裂)
)
chunks = text_splitter.split_documents(docs) # 将文档分割成多个小块
初始化嵌入模型
# 初始化嵌入模型,使用预训练的语言模型 'bge-large-zh-v1___5'
embedding = HuggingFaceEmbeddings(
model_name='/home/AI_big_model/models/AI-ModelScope/bge-large-zh-v1___5' # 本地模型路径
)
构建向量数据库
# 构建 FAISS 向量存储和对应的 retriever
vs = FAISS.from_documents(chunks, embedding) # 将文本块转换为向量并存储在FAISS中
retriever = vs.as_retriever() # 创建一个检索器用于从向量存储中获取相关信息
配置问答系统提示模板
# 创建一个系统消息,用于定义机器人的角色
system_message = SystemMessagePromptTemplate.from_template(
"根据以下已知信息回答用户问题。\n 已知信息{context}" # 系统提示词,指导模型基于给定上下文回答
)
# 创建一个人类消息,用于接收用户的输入
human_message = HumanMessagePromptTemplate.from_template(
"用户问题:{question}" # 用户问题模板
)
# 将这些模板结合成一个完整的聊天提示
chat_prompt = ChatPromptTemplate.from_messages([
system_message,
human_message,
])
# 定义链的类型参数,包括使用的提示模板
chain_type_kwargs = {"prompt": chat_prompt}
构建问答链
# 创建一个问答链,将语言模型、检索器和提示模板结合起来
qa = RetrievalQA.from_chain_type(
llm=chat_model, # 使用的语言模型(之前初始化的Qwen2.5)
chain_type="stuff", # 处理检索结果的方式("stuff"表示简单拼接所有相关文档)
retriever=retriever, # 之前创建的检索器
chain_type_kwargs=chain_type_kwargs # 包含提示模板的配置
)
执行问答
# 用户的问题
user_question = "黑熊精自称为?"
# 使用检索器获取与问题相关的文档
related_docs = retriever.invoke(user_question)
print(related_docs) # 打印检索到的相关文档
# 使用问答链来回答问题 "黑熊精自称为?" 并打印结果
print(qa.invoke(user_question))
完整代码
from langchain_community.vectorstores import FAISS # 导入FAISS向量存储库
from langchain_huggingface import HuggingFaceEmbeddings # 导入Hugging Face嵌入模型
from langchain_community.document_loaders import TextLoader # 导入文本加载器
from langchain.text_splitter import RecursiveCharacterTextSplitter # 导入递归字符文本分割器
from langchain_openai import ChatOpenAI # 导入ChatOpenAI模型
# 使用 OpenAI API 的 ChatOpenAI 模型
chat_model = ChatOpenAI(
api_key='EMPTY',
base_url='http://127.0.0.1:10222/v1',
model='Qwen2.5-7B-Instruct'
)
# 加载文本文件 "黑悟空.txt",编码格式为 'utf-8'
loader = TextLoader("黑悟空.txt", encoding='utf-8')
docs = loader.load() # 将文件内容加载到变量 docs 中
# 把文本分割成 200 字一组的切片,每组之间有 20 字重叠
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)
chunks = text_splitter.split_documents(docs) # 将文档分割成多个小块
# 初始化嵌入模型,使用预训练的语言模型 'bge-large-zh-v1___5'
embedding = HuggingFaceEmbeddings(model_name='/home/AI_big_model/models/AI-ModelScope/bge-large-zh-v1___5')
# 构建 FAISS 向量存储和对应的 retriever
vs = FAISS.from_documents(chunks, embedding) # 将文本块转换为向量并存储在FAISS中
retriever = vs.as_retriever() # 创建一个检索器用于从向量存储中获取相关信息
# from langchain.chains import RetrievalQA # 导入RetrievalQA链
from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
# 创建一个系统消息,用于定义机器人的角色
system_message = SystemMessagePromptTemplate.from_template(
"根据以下已知信息回答用户问题。\n 已知信息{context}"
)
# 创建一个人类消息,用于接收用户的输入
human_message = HumanMessagePromptTemplate.from_template(
"用户问题:{question}"
)
# 将这些模板结合成一个完整的聊天提示
chat_prompt = ChatPromptTemplate.from_messages([
system_message,
human_message,
])
# 定义链的类型参数,包括使用的提示模板
chain_type_kwargs = {"prompt": chat_prompt}
# 创建一个问答链,将语言模型、检索器和提示模板结合起来
# chat_model:生成回答的语言模型, stuff:所有检索到的文档内容合并成一个大文本块,然后传递给语言模型。
# retriever: 之前创建的一个 FAISS 检索器实例。它的作用是从 FAISS 向量存储中找到与用户问题最相关的文档或文本块。这些相关的文档会被传递给语言模型以生成回答。
# chain_type_kwargs 是一个字典,包含了用于配置问答链的一些关键参数。
qa = RetrievalQA.from_chain_type(llm=chat_model, chain_type="stuff", retriever=retriever, chain_type_kwargs=chain_type_kwargs)
# 用户的问题
user_question = "黑熊精自称为?"
# 使用检索器获取与问题相关的文档
related_docs = retriever.invoke(user_question)
print(related_docs)
# 使用问答链来回答问题 "黑熊精自称为?" 并打印结果
print(qa.invoke(user_question))
加载本地文本文件并进行分块处理
使用嵌入模型将文本块转换为向量表示
构建向量数据库(FAISS)实现高效检索
结合检索结果和大语言模型生成回答
组件 | 作用 | 重要参数/说明 |
---|---|---|
TextLoader | 加载本地文本文件 | encoding='utf-8' 确保中文正确读取 |
RecursiveCharacterTextSplitter | 智能文本分割 | chunk_size=200 控制文本块大小 |
HuggingFaceEmbeddings | 文本向量化 | bge-large-zh-v1 是优秀的中文嵌入模型 |
FAISS | 向量相似性搜索 | 高效的近似最近邻搜索库 |
ChatPromptTemplate | 提示工程 | 定义系统角色和用户交互格式 |
RunnablePassthrough | 数据传递 | 保持原始输入不变传递给下一步 |
StrOutputParser | 输出标准化 | 确保输出格式统一 |
# 导入必要的库和模块
from langchain_openai import ChatOpenAI # 导入OpenAI聊天模型接口
from langchain_community.document_loaders import TextLoader # 导入文本加载器
from langchain_text_splitters import RecursiveCharacterTextSplitter # 导入递归文本分割器
from langchain_huggingface import HuggingFaceEmbeddings # 导入HuggingFace嵌入模型
from langchain_community.vectorstores import FAISS # 导入FAISS向量数据库
from langchain.prompts import ( # 导入提示模板相关类
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate
)
from langchain_core.output_parsers import StrOutputParser # 导入字符串输出解析器
from langchain_core.runnables import RunnablePassthrough # 导入可运行组件
# 初始化语言模型
chat_model = ChatOpenAI(
api_key='EMPTY', # 使用本地部署的API,不需要真实API key
base_url='http://127.0.0.1:10222/v1', # 本地API服务地址
model='Qwen2.5-7B-Instruct' # 使用的模型名称(通义千问2.5的7B指令微调版)
)
# 加载文本文件
loader = TextLoader("黑悟空.txt", encoding='utf-8') # 指定文件路径和编码
docs = loader.load() # 加载文档内容,返回Document对象列表
# 文本分块处理
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200, # 每个文本块的最大长度(字符数)
chunk_overlap=20 # 相邻文本块之间的重叠字符数(保持上下文连贯)
)
chunks = text_splitter.split_documents(docs) # 执行分割,返回分割后的文档块列表
# 初始化嵌入模型
embedding = HuggingFaceEmbeddings(
model_name='/home/AI_big_model/models/AI-ModelScope/bge-large-zh-v1___5' # 本地嵌入模型路径
)
# 构建向量数据库
vs = FAISS.from_documents(
chunks, # 分割后的文档块
embedding=embedding # 使用的嵌入模型
)
# 创建检索器
retriever = vs.as_retriever() # 将向量存储转换为检索器接口
# 构建系统消息模板(定义AI角色)
system_message = SystemMessagePromptTemplate.from_template(
"根据以下已知信息回答用户问题。\n 已知信息{context}" # 系统提示词模板
)
# 构建用户消息模板(接收用户问题)
human_message = HumanMessagePromptTemplate.from_template(
"用户问题:{question}" # 用户问题模板
)
# 组合完整提示模板
chat_prompt = ChatPromptTemplate.from_messages([
system_message, # 系统消息
human_message # 用户消息
])
# 初始化输出解析器
output_parser = StrOutputParser() # 将模型输出解析为字符串
# 定义文档格式化函数
def format_docs(docs):
"""将检索到的文档列表合并为单个字符串"""
return "\n\n".join(doc.page_content for doc in docs) # 用两个换行符连接各文档内容
# 构建RAG处理链
rag_chain = (
# 第一步:构建包含context和question的字典
# context通过检索器获取文档后经format_docs格式化
# question直接传递用户输入(RunnablePassthrough)
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| chat_prompt # 第二步:应用提示模板
| chat_model # 第三步:调用语言模型生成回答
| output_parser # 第四步:解析模型输出
)
# 执行问答
print(rag_chain.invoke("黑熊精自称为?")) # 传入问题并打印回答
对比维度 | RetrievalQA 链 | 管道式实现 |
流程控制 | 固定三步流程:检索 → 拼接 → 生成。 | 高度可定制:可拆分为5+个可定制步骤。 |
调试能力 | 受限:仅能获取最终输出,难以检查中间节点。 | 强大:可检查每个中间节点的输出,便于精确定位问题。 |
定制化能力 | 有限:仅能修改prompt模板。 | 极高:支持自定义检索策略、结果后处理、多模型协作等。 |
扩展性 | 受限:通过 chain_type 选择有限的处理方式。 |
极高:可插入缓存层、路由逻辑、业务校验规则等。 |
维护成本 | 低:由 LangChain 框架维护兼容性。 | 中:需自行维护整个流程逻辑。 |
适用场景 | • POC验证 • 标准问答场景 • 有限计算资源 |
• 生产环境 • 复杂逻辑需求 • 需要监控各环节的场景 |
性能优化空间 | 有限:仅能调整 chunk_size 、top_k 等参数。 |
广阔:可优化异步处理、混合检索、动态分块策略等。 |
错误处理 | 统一:通常为统一的错误输出。 | 分级:可自定义检索失败降级、生成重试机制等。 |
维度 | RetrievalQA链 | 管道式实现 |
---|---|---|
开发速度 | ★★★★★ | ★★★ |
流程透明度 | ★★ | ★★★★★ |
定制灵活性 | ★★ | ★★★★★ |
调试便利性 | ★★ | ★★★★★ |
复杂场景适应性 | ★★ | ★★★★★ |
维护成本 | ★★★★★ | ★★★ |
使用 RetrievalQA 链:
适合快速原型开发或简单的问答系统。
如果标准的检索和生成流程足够满足需求,这是更简单的选择。
使用管道式实现:
适合需要高度定制或对流程有更复杂需求的场景。