当前位置: 首页 > news >正文

python实战(七)——基于LangChain的RAG实践

一、任务目标

        基于之前的RAG实战,相信大家对RAG的实现已经有了一定的了解了。这篇文章将使用LangChain作为辅助,实现一个高效、便于维护的RAG程序。

二、什么是LangChain

        LangChain是一个用于构建大模型应用程序的开源框架,它内置了多个模块化组件。通过这些组件,我们能够快速且便捷地搭建一个强大的大模型应用程序。首先,我们来看一下如何通过LangChain来调用大语言模型:

from langchain_community.chat_models.openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
import osAPI_SECRET_KEY = "your api key"
BASE_URL = "your api base"
os.environ["OPENAI_API_KEY"] = API_SECRET_KEY
os.environ["OPENAI_API_BASE"] = BASE_URL# 这里的temperature参数控制答案生成的随机性,0表示按照概率最大的结果生成,也就是最稳定
# 如果温度设置为1则表示生成极富随机性的结果
# 由于我们没有指定调用哪个openai模型,默认会是gpt-3.5-turbo
chat = ChatOpenAI(temperature=0.0)# 定义template字符串,这里不需要使用f字符串来赋值
template = """请将下面的中文文本翻译为{language}。\
文本:'''{text}'''
"""# 将template字符串转换为langchain模板,这时候会自动识别prompt模板需要的参数,即{}中的内容
prompt_template = ChatPromptTemplate.from_template(template)customer_language = '英文'
customer_text = '你好,我来自中国。'# 传入相应字符串生成符合大模型输入要求的prompt
customer_messages = prompt_template.format_messages(language=customer_language, text=customer_text)
print(customer_messages[0])# 调用大语言模型
customer_response = chat.invoke(customer_messages, temperature=0.0)
print(customer_response.content)

        相比起我们直接使用openai库调用大模型的方式,LangChain库调用大语言模型要更加结构化和模块化,尤其是prompt模板的构建部分。这样做的好处是,我们可以很方便地进行程序的复用。另外,LangChain内置了多个常用场景的Prompt模板,可以拿来即用,省去了我们重新设计和构造prompt的时间。

三、RAG流程构建

1、文档加载

        LangChain支持多种文档格式的加载,比如doc、pdf、markdown甚至html等。这里,我们加载几个pdf文档(文档中的内容是调用大模型生成的关于各个西游记主角的人物特征):

from langchain_community.document_loaders.pdf import PyPDFLoader# 这里会逐页加载文档
loaders = [PyPDFLoader('孙悟空.pdf'), PyPDFLoader('猪八戒.pdf'), PyPDFLoader('沙和尚.pdf'), PyPDFLoader('唐僧.pdf')]
docs = []
for loader in loaders:pages = loader.load()# 打印pages的元素个数print(len(pages))# 打印第一个元素,也就是第一页的一部分文字看看print(pages[0].page_content[:10])# 打印第一个元素的元数据print(pages[0].metadata)docs.extend(pages)

2、文档分割

        加载后的文档可能很大,我们需要将文档分割成一个一个的小块进行存储。在进行检索的时候,也能够直接返回相关的文档块,而不需要整个文档都传给大模型。LangChain提供字符级别的文档分隔、token级别的文档分隔等多个工具。这里我们使用字符分割:

from langchain.text_splitter import RecursiveCharacterTextSplitter# chunk_size用于设置每个文本块的大小,chunk_overlap用于设置每个文本块直接的重叠部分大小
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=10)
blocks = text_splitter.split_documents(docs)

3、文本向量化存储

        为了便于后续使用,我们把文本转化成向量并存储到本地。向量化的意义在于可以很方便地比较文本相似度,从而检索出与query相关的信息返回给大模型。

from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma# 定义向量化工具
embedding_model = OpenAIEmbeddings(model='text-embedding-3-small')directory = './save_folder'
# Chroma是一个轻量级的向量存储库,langchain提供了数十个向量存储库,这里我们使用高效的Chroma
vectordb = Chroma.from_documents(documents=blocks,embedding=embedding_model,persist_directory=directory
)
# 永久化存储
vectordb.persist()# 打印文本向量数
print('文本向量数:', vectordb._collection.count())# 可以进行相似性检索,k指定返回结果数量
query = '请介绍一下唐僧的性格特点'
search_results = vectordb.similarity_search(query, k=3)
print('相似文本示例:', search_results[0])

        这里,我们使用了similarity_search方法检索相关的文本向量,这是纯相似度比较的方式,返回结果将高度相关,但也会减少多样性。如果希望在查询的相关性和多样性之间保持平衡,也可以使用max_marginal_relevance_search方法进行向量检索

4、问答

        这里,我们构造一个检索式问答链,并让程序返回它所用到的检索结果:

from langchain_community.chat_models import ChatOpenAI
from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain.prompts import PromptTemplatellm = ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.0)
template = '请根据上下文回答以下问题。上下文:{context},问题:{question}'
qa_prompt = PromptTemplate.from_template(template)qa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={'prompt':qa_prompt}
)question = '请介绍一下唐僧的性格特点'
response = qa_chain({'query':question})
# 打印模型响应
print(response['result'])
# 打印第一个相关的检索结果
print(response['source_documents'][0])

        上面的代码中,{context}是检索结果,{question}是指我们的提问,这两个变量不可缺少,变量命名和相对位置也应当与上面一致,至于放置的绝对位置则无特别要求

四、完整代码

        代码运行过程中如果存在报错是因为一些依赖库没有安装,pip install就可以了。如果是无报错退出且状态码是00005结尾的,那么应该是Chroma读取内存数据的时候报错了,persist存储到本地之后,使用Chroma(persist_directory='你的存储地址', embedding_function='初始化之后的embedding模型')即可正常运行。

from langchain_community.document_loaders.pdf import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.chat_models import ChatOpenAI
from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain.prompts import PromptTemplate
import osAPI_SECRET_KEY = "your api key"
BASE_URL = "your api base"
os.environ["OPENAI_API_KEY"] = API_SECRET_KEY
os.environ["OPENAI_API_BASE"] = BASE_URL# 这里会逐页加载文档
loaders = [PyPDFLoader('孙悟空.pdf'), PyPDFLoader('猪八戒.pdf'), PyPDFLoader('沙和尚.pdf'), PyPDFLoader('唐僧.pdf')]
docs = []
for loader in loaders:pages = loader.load()docs.extend(pages)# chunk_size用于设置每个文本块的大小,chunk_overlap用于设置每个文本块直接的重叠部分大小
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=10)
blocks = text_splitter.split_documents(docs)# 定义向量化工具
embedding_model = OpenAIEmbeddings(model='text-embedding-3-small')
directory = 'save_folder'
# Chroma是一个轻量级的向量存储库,langchain提供了数十个向量存储库,这里我们使用高效的Chroma
vectordb = Chroma.from_documents(documents=blocks,embedding=embedding_model,persist_directory=directory
)
# 永久化存储
vectordb.persist()# 指定模型
llm = ChatOpenAI(model_name='gpt-3.5-turbo', temperature=0.0)
# 构建模板
template = '请根据上下文回答以下问题。上下文:{context},问题:{question}'
qa_prompt = PromptTemplate.from_template(template)# 构建检索式问答链
qa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={'prompt':qa_prompt}
)quest = '请介绍一下唐僧的性格特点'
response = qa_chain({'query':quest})
# 打印模型响应
print('模型响应:', response['result'])
# 打印第一个相关的检索结果
print('第一个相关的检索结果:', response['source_documents'][0])

五、总结

        本文实现了基于LangChain的RAG问答助手程序,但是其中仍然有许多值得打磨的细节,例如目前的问答助手只能支持一问一答的互动形式,如果需要基于历史聊天记录进行问答,则需要使用LangChain中其他的模块化工具进行聊天记录的存储和调用,这些内容将在后续的博文中讨论。


http://www.mrgr.cn/news/67212.html

相关文章:

  • c语言安全分析(一)——字符串(1)
  • 流程与模式
  • 【数据结构】算法的时间复杂度和空间复杂度
  • RabbitMQ 不公平分发介绍
  • 介绍一下strcpy函数(c基础)
  • Power Pivot综合业务分析系统与高级分析功能
  • ABAP开发:数据库表更新时机介绍
  • 数组和指针的复杂关系
  • STL整理
  • 终端文件管理神器 !!!
  • 电商行业企业员工培训的在线知识库构建
  • 宏转录组组装:rnaSPAdes
  • 论文速读:动态再训练-更新用于无源目标检测的Mean Teacher(ECCV2024)
  • 代码随想录第二十二天
  • Golang--面向对象
  • 【c++丨STL】vector的使用
  • 问题排查:C++ exception with description “getrandom“ thrown in the test body
  • 揭秘2024年最火的5个科技趋势,你准备好迎接了吗?
  • 机器学习(四)——神经网络(神经元、感知机、BP神经网络、梯度下降、多层神经网络、Python源码)
  • ELF加载,进程地址空间与可执行程序的关系
  • C++转义序列
  • 【ETL:概念、流程与应用】
  • 基本开关电源(DCDC)电路分析
  • 4.1 WINDOWS XP,ReactOS对象与对象目录----1
  • Java的Object类常用的方法(详述版本)
  • 11.6学习日志