基于langchain RAG问答应用实战
来源:AiGC面试宝典 作者:宁静致远 日期:2024年02月08日
一、前言
1.1 介绍
本次选用百度百科——藜麦数据(https://baike.baidu.com/item/藜麦/5843874)模拟个人或企业私域数据,并基于langchain开发框架,实现一种简单的RAG问答应用示例。
📝通俗解释:RAG(Retrieval-Augmented Generation,检索增强生成)是一种让AI回答问题更准确的技术。简单来说,就是先从你的资料库里找到相关内容,然后把找到的内容和问题一起交给AI,让AI根据这些真实资料来回答,而不是凭空编造。这就像考试时允许翻书查资料一样。
1.2 软件资源
- CUDA 11.7
- Python 3.10
- pytorch 1.13.1+cu117
- langchain
二、环境搭建
2.1 下载代码
$ git clone https://github.com/xxx/rag-demo.git
$ cd rag-demo📝通俗解释:这里的代码是演示项目,实际使用时需要从GitHub等平台下载对应的RAG示例代码。
2.2 构建环境
$ conda create -n py310_chat python=3.10 # 创建新环境
$ source activate py310_chat # 激活环境📝通俗解释:使用conda创建独立的Python环境,避免和其他项目冲突。类似于给这个项目分配一个独立的"工作房间"。
2.3 安装依赖
$ pip install datasets langchain sentence_transformers tqdm chromadb langchain_wenxin📝通俗解释:这些是开发RAG应用需要的核心库:
- langchain:AI应用开发框架
- sentence_transformers:用于把文字转换成向量
- chromadb:向量数据库,用来存储和搜索文本的"数字指纹"
- langchain_wenxin:百度文心一言的接口
三、RAG问答应用实战
3.1 数据准备
将藜麦数据(https://baike.baidu.com/item/藜麦/5843874)保存到 藜麦.txt 文件中。
📝通俗解释:这就是你的"知识库",RAG系统会从这个文件里查找答案。实际应用中可以是PDF、Word、网页等各种格式的文档。
3.2 本地数据加载
from langchain.document_loaders import TextLoader
loader = TextLoader("./藜麦.txt")
documents = loader.load()
documents输出示例:
[Document(page_content='藜(读音lí)麦(Chenopodium quinoa Willd.)是藜科藜属植物。穗部可呈红、紫、黄,植株形状类似灰灰菜,成熟后穗部类似高粱穗。植株大小受环境及遗传因素影响较大,从0.3-3米不等...', metadata={'source': './藜麦.txt'})]📝通俗解释:TextLoader负责读取文本文件,把文件内容转换成Document对象。每个Document包含page_content(实际内容)和metadata(来源信息)。
3.3 文档分割
文档分割,借助langchain的字符分割器,这里采用固定字符长度分割chunk_size=128。
# 文档分割
from langchain.text_splitter import CharacterTextSplitter
# 创建拆分器,chunk_size=128表示每128个字符分成一段
text_splitter = CharacterTextSplitter(chunk_size=128, chunk_overlap=0)
# 拆分文档
documents = text_splitter.split_documents(documents)
documents📝通俗解释:为什么要分割文档?因为AI每次只能处理有限长度的内容。把长文档切成小块后,可以更精准地找到相关内容。就像把一本书拆成一页页的,这样找答案时只翻相关的那几页,而不是整本书都翻一遍。
3.4 向量化&数据入库
接下来对分割后的数据进行embedding(向量化),并写入数据库。这里选用m3e-base作为embedding模型,向量数据库选用Chroma。
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.vectorstores import Chroma
# embedding model: m3e-base
model_name = "moka-ai/m3e-base"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': True}
embedding = HuggingFaceBgeEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs,
query_instruction="为文本生成向量表示用于文本检索"
)
# 将数据加载到Chroma向量数据库
db = Chroma.from_documents(documents, embedding)
# 测试相似度搜索
db.similarity_search("藜麦一般在几月播种?")📝通俗解释:
- 向量化(Embedding):把文字转换成一串数字(向量)。相似的文字会有相似的向量,这样就可以用数学方法找到"相似的内容"。
- 向量数据库:专门用来存储和搜索向量的数据库,查询速度非常快。
- m3e-base:一个开源的中文Embedding模型,负责把中文文本转换成向量。
3.5 Prompt设计
Prompt设计,这里只是一个prompt的简单示意,在实际业务场景中需要针对场景特点针对性调优。
template = '''
【任务描述】
请根据用户输入的上下文回答问题,并遵守回答要求。
【背景知识】
{{context}}
【回答要求】
- 你需要严格根据背景知识的内容回答,禁止根据常识和已知信息回答问题。
- 对于不知道的信息,直接回答"未找到相关答案"
----------
{question}
'''📝通俗解释:Prompt就是给AI的"指令模板"。这里的会被替换成从知识库检索到的相关内容,{question}是用户的问题。这样AI就能"看着资料"回答问题了。
3.6 RetrievalQA Chain构建
这里采用ConversationalRetrievalChain,支持多轮对话。
from langchain import LLMChain
from langchain_wenxin.llms import Wenxin
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
# LLM选型:使用百度文心一言
llm = Wenxin(model="ernie-bot", baidu_api_key="your_api_key", baidu_secret_key="your_secret_key")
# 获取检索器
retriever = db.as_retriever()
# 创建对话记忆,保存聊天历史
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 构建ConversationalRetrievalChain
qa = ConversationalRetrievalChain.from_llm(llm, retriever, memory=memory)
# 测试问答
qa({"question": "藜麦怎么防治虫害?"})输出示例:
{'question': '藜麦怎么防治虫害?',
'chat_history': [HumanMessage(content='藜麦怎么防治虫害?'),
AIMessage(content='藜麦常见虫害有象甲虫、金针虫、蝼蛄、黄条跳甲、横纹菜蝽、蒿蓄齿胫叶甲、潜叶蝇、蚜虫、夜蛾等。防治方法:可每亩用3%的辛硫磷颗粒剂2-2.5千克于耕地前均匀撒施,随耕地翻入土中。也可以每亩用40%的辛硫磷乳油250毫升,加水1-2千克,拌细土20-25千克配成毒土,撒施地面翻入土中,防治地下害虫。')],
'answer': '藜麦常见虫害有象甲虫、金针虫、蝼蛄、黄条跳甲、横纹菜蝽、蒿蓄齿胫叶甲、潜叶蝇、蚜虫、夜蛾等。防治方法:可每亩用3%的辛硫磷颗粒剂2-2.5千克于耕地前均匀撒施,随耕地翻入土中。也可以每亩用40%的辛硫磷乳油250毫升,加水1-2千克,拌细土20-25千克配成毒土,撒施地面翻入土中,防治地下害虫。'}📝通俗解释:
- ConversationalRetrievalChain:RAG的核心链条,它的工作流程是:接收用户问题 → 从知识库检索相关内容 → 把内容和问题一起给AI → AI生成答案
- ConversationBufferMemory:负责记住聊天历史,这样AI能理解"它刚才说的"、"继续刚才的话题"这类上下文
- retriever:检索器,负责从向量数据库找到最相关的内容
3.7 高级用法
针对多轮对话场景,增加 question_generator 对历史对话记录进行压缩生成新的question,增加 combine_docs_chain 对检索得到的文本进一步融合。
from langchain import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain, StuffDocumentsChain
from langchain.chains.qa_with_sources import load_qa_with_sources_chain
from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
# 构建初始 messages 列表
messages = [
SystemMessagePromptTemplate.from_template(qa_template),
HumanMessagePromptTemplate.from_template('{question}')
]
# 初始化 prompt 对象
prompt = ChatPromptTemplate.from_messages(messages)
llm_chain = LLMChain(llm=llm, prompt=prompt)
# 文档合并链
combine_docs_chain = StuffDocumentsChain(
llm_chain=llm_chain,
document_separator="\n\n",
document_variable_name="context",
)
# 问题生成链(用于压缩历史问题)
q_gen_chain = LLMChain(llm=llm, prompt=PromptTemplate.from_template(qa_condense_template))
# 构建高级问答链
qa = ConversationalRetrievalChain(
combine_docs_chain=combine_docs_chain,
question_generator=q_gen_chain,
return_source_documents=True,
return_generated_question=True,
retriever=retriever
)
# 测试问答
print(qa({'question': "藜麦怎么防治虫害?", "chat_history": []}))输出示例:
{
'question': '藜麦怎么防治虫害?',
'chat_history': [],
'answer': '根据背景知识,藜麦常见虫害有象甲虫、金针虫、蝼蛄、黄条跳甲、横纹菜蝽、蒿蓄齿胫叶甲、潜叶蝇、蚜虫、夜蛾等。防治方法如下:\n\n1. 可每亩用3%的辛硫磷颗粒剂2-2.5千克于耕地前均匀撒施,随耕地翻入土中。\n2. 也可以每亩用40%的辛硫磷乳油250毫升,加水1-2千克,拌细土20-25千克配成毒土,撒施地面翻入土中,防治地下害虫。\n\n以上内容仅供参考,如果需要更多信息,可以阅读农业相关书籍或请教农业专家。',
'source_documents': [
Document(page_content='病害:主要防治叶斑病,使用12.5%的烯唑醇可湿性粉剂3000-4000倍液喷雾防治...', metadata={'source': './藜麦.txt'}),
Document(page_content='中期管理:在藜麦8叶龄时,将行中杂草、病株及残株拔掉,提高整齐度...', metadata={'source': './藜麦.txt'})
],
'generated_question': '藜麦怎么防治虫害?'
}📝通俗解释:
- question_generator:当有多轮对话时,把历史问题和当前问题合并,生成一个"完整的问题"去检索。这样即使问"它怎么防治",AI也能理解是问藜麦的病虫害防治
- combine_docs_chain:如果一次检索到多段相关内容,这个链会把它们合并成一段完整的背景知识
- return_source_documents=True:返回用到了哪些资料来源,方便核实答案的准确性
总结
本文展示了基于LangChain构建RAG问答应用的完整流程:
- 数据准备:加载私有知识库文档
- 文档分割:将长文档拆分成小块
- 向量化:使用Embedding模型将文本转换为向量
- 向量存储:存入Chroma向量数据库
- 问答构建:使用ConversationalRetrievalChain实现问答
📝通俗解释:整个RAG系统就像一个"智能图书管理员"。用户问问题时,管理员不是凭记忆回答,而是先去书架找到相关的书(检索),然后根据书上的内容回答(生成)。这样保证了答案的准确性,不会AI胡编乱造。