使用python快速开发与PDF文档对话的Gemini聊天机器人

检索增强生成(Retrieval-augmented generation,RAG)使得我们可以让大型语言模型(LLMs)访问外部知识库数据(如pdf,word、text等),从而让人们可以更加方便的通过LLM来学习外部数据的知识。今天我们将利用之前学习到的RAG方法,谷歌Gemini模型和langchain框架来快速开发一个能够和pdf文件对话的机器人,之所以要选择Gemini模型是因为它的API目前是免费调用的,而OpenAI的API则是要收费的,而我没有那么多银子,所以只能选择免费的。

一、什么是检索增强生成 (Retrieval-augmented generation,RAG)?

检索增强生成 (RAG) 是一种使用来自私有或专有数据源的信息来辅助文本生成的技术。 它将检索模型(设计用于搜索大型数据集或知识库)和生成模型(例如 大型语言模型 (LLM) ,此类模型会使用检索到的信息生成可供阅读的文本回复)结合在一起。一个基本的RAG检索过程主要包含以下这些步骤:

一个典型的RAG系统一般包含两个主要的部件:

  • 检索器组件:根据用户问题从外部数据源(如pdf,word,text等)检索相关信息并提供给LLM 以便回答用户问题。
  • 生成器组件:LLM根据检索到的相关信息,生成正确的、完整的对用户友好的答案。

我之前写过一系列的关于使用langchain与文档对话的博客,如果想详细了解RAG的基本过程可以先看一下我写的这些博客。

在本次的与PDF文档对话的系统中我们使用的检索器组件是基于Langchain框架的父文档检索器,如果对它还不熟悉朋友可以先看一下我之前写的父文档检索器这篇博客,至于生成器组件,我们使用的是基于谷歌的Gemini大模型。对于如何开发一个基于web页面的聊天机器人程序还不熟悉的朋友可以查看我之前写的使用python快速开发各种聊天机器人应用这篇博客,以便可以快速上手开发机器人应用程序。对谷歌gemini模型还不熟悉的朋友可以查看我之前写的谷歌Gemini API 应用(一):基础应用这篇博客,以便可以快速了解Gemini API的使用方法。

在本文的最后我会给大家分享完整的代码,大家可以在此基础上不断的完善代码开发出符合你们自己需求的机器人。

一,环境配置

这里我们主要使用的是panel和langchain这两个python包:

pip install google-generativeai
pip install panel
pip install langchain
pip install chroma

如果在安装过程中报错,请根据报错信息安装其他必要的python包。

二、组件介绍

这里我们会使用谷歌Gemini模型组件,以及基于langchain的检索器组件,其中包括:文档分割器组件、父文档检索器组件、向量数据库组件、另外我们还需要使用python的web框架组件panel等。下面我们导入这些组件的python包:

import tempfile
import panel as pn
import param
from langchain.document_loaders import PyPDFLoader
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.vectorstores import Chroma
from panel_chat_examples import EnvironmentWidgetBase
import google.generativeai as genai

这里需要说明的是我们使用的LLM是谷歌原生的Gemini模型组件,而非由langchain非封装的gemini组件,之所以使用谷歌原生组件是因为我发现langchain的gemini组件存在一些bug, 它不能设置谷歌模型的安全策略,导致机器人在回答问题的时候经常会报用户问题存在安全性问题的异常,这可能是由于目前这个langchain的gemini组件还是早期版本,估计后面会完善这些问题。

三、功能模块介绍

31. Embedding模型的选择

如果对Embedding模型还不太熟悉的朋友可以查看我之前写的Embedding模型的选择这篇博客,因为本次实验使用的是中文内容的文档,所以我还是选择了BAAI的"bge-small-zh-v1.5", 如果大家使用的是英文的pdf文档则可以将Embedding模型切换为了BAAI的"bge-small-en-v1.5":

#支持中文的BAAI embedding模型
bge_embeddings = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-small-zh-v1.5",cache_folder="D:\\models")

这里的cache_folder指的是存放Embedding模型的文件夹,当首次执行机器人程序的时候系统会自动从HuggingFace的网站下载Embedding模型并将它存放到默认的cache_folder文件夹下(C盘),所以这里我们可以指定cache_folder文件夹的路径把Embedding模型存储在C盘以外的地方。

3.2 Gemini模型的安全策略

Gemini模型在回答用户问题的时候有一套严格的关于内容的安全审查机制,以防止出现违反道德伦理,色情暴力等内容。由于本次实验我使用的PDF文档是:"阿凡提故事大全.pdf"它是一个中文文档,当机器人在回答问题的时候不知为何经常出现内容违规的提示,这可能是由于gemini模型的默认安全审查策略的阈值设置的太高引起的,因此当我把安全审查策略的阈值调到"无"以后就不再出现违规提示了:

generation_config = {"temperature": 0.0,"top_p": 1,"top_k": 1,"max_output_tokens": 2048,
}safety_settings = [{"category": "HARM_CATEGORY_HARASSMENT","threshold": "BLOCK_NONE"},{"category": "HARM_CATEGORY_HATE_SPEECH","threshold": "BLOCK_NONE"},{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","threshold": "BLOCK_NONE"},{"category": "HARM_CATEGORY_DANGEROUS_CONTENT","threshold": "BLOCK_NONE"}]

这里我们设置了模型参数generation_config 和安全策略参数safety_settings ,在模型参数中我们设置了temperature为0,该参数的取值范围为0-1,参数值越低,llm给出的答案越精准,参数值越高给出的答案变化性越大,因此为了让llm不要产生多样化的结果,我们应尽量调低temperature的值,设置为0或0.1都可以。另外我们将所有安全策略的阈值都设置为"BLOCK_NONE"即不做安全性审查。

3.3父文档检索器

如果对langchain的父文档检索器策略还不熟悉的朋友可以先看一下我之前写的父文档检索器这篇博客,下面是我们在panel中定义父文档检索器的一些组件:

#加载文档
@pn.cache(ttl=TTL)
def _get_docs(pdf):# load documentswith tempfile.NamedTemporaryFile("wb", delete=False) as f:f.write(pdf)file_name = f.nameloader = PyPDFLoader(file_name)docs = loader.load()return docs#创建父文档切割器
@pn.cache(ttl=TTL)
def _get_parent_splitter():return RecursiveCharacterTextSplitter(chunk_size=1000)
#创建子文档切割器
@pn.cache(ttl=TTL)
def _get_child_splitter():return RecursiveCharacterTextSplitter(chunk_size=400)
#创建内存存储组件
@pn.cache(ttl=TTL)
def _get_MemoryStore():return InMemoryStore()
#创建向量数据库   
@pn.cache(ttl=TTL)
def _get_vector_db():        vectorstore = Chroma(collection_name="split_parents", embedding_function = bge_embeddings)return vectorstore
#创建父文档检索器
@pn.cache(ttl=TTL)
def _get_retriever(pdf, number_of_chunks: int):docs=_get_docs(pdf)vectorstore= _get_vector_db()store=_get_MemoryStore()child_splitter=_get_child_splitter()parent_splitter=_get_parent_splitter()#创建父文档检索器retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter,parent_splitter=parent_splitter,search_kwargs={"k": number_of_chunks})#添加文档集retriever.add_documents(docs)return retriever

3.4创建gemini模型对象

下面是在panel中定义gemini模型对象和模型生成内容的方法:

#创建gemini模型对象
@pn.cache(ttl=TTL)
def _get_model():#创建gemini modelmodel = genai.GenerativeModel(model_name="gemini-pro",generation_config=generation_config,safety_settings=safety_settings)return modeldef _get_response(contents):retriever=_get_retriever(state.pdf, state.number_of_chunks)model=_get_model()relevant_docs=retriever.get_relevant_documents(contents)contexts='\n\n'.join([w.page_content for w in relevant_docs])#prompt模板template = f"""请根据下面给出的上下文来回答下面的问题,并给出完整的答案:
上下文:{contexts}问题: {contents}
"""response = model.generate_content(template)chunks = []for chunk in relevant_docs:name = f"Chunk {chunk.metadata['page']}"content = chunk.page_contentchunks.insert(0, (name, content))return response.text, chunks

3.5创建panel页面组件

这里我们还需要创建panel的页面组件和设置环境变量等全局变量以便设置谷歌的api_key和callback函数,这里我们我们仍然使用的是panel的chat_interface作为我们的页面聊天组件,对应panel还不熟悉的朋友可以查看我之前写的使用python快速开发各种聊天机器人应用以及panel的官方文档:

# 定义应用程序全局变量
class EnvironmentWidget(EnvironmentWidgetBase):GOOGLE_API_KEY: str = param.String()class State(param.Parameterized):pdf: bytes = param.Bytes()number_of_chunks: int = param.Integer(default=2, bounds=(1, 5), step=1)environ = EnvironmentWidget()
state = State()# 定义页面组件
pdf_input = pn.widgets.FileInput.from_param(state.param.pdf, accept=".pdf", height=50)
text_input = pn.widgets.TextInput(placeholder="First, upload a PDF!")# 定义和配置 panel ChatInterface
def _get_validation_message():pdf = state.pdfgoogle_api_key = environ.GOOGLE_API_KEYif not pdf and not google_api_key:return "请在左侧侧边栏中输入谷歌api key 然后上传PDF文件!"if not pdf:return "请先上传pdf 文件"if not google_api_key:return "请先输入谷歌api key"genai.configure(api_key=google_api_key,transport='rest')return ""def _send_not_ready_message(chat_interface) -> bool:message = _get_validation_message()if message:chat_interface.send({"user": "System", "object": message}, respond=False)return bool(message)async def respond(contents, user, chat_interface):if _send_not_ready_message(chat_interface):returnif chat_interface.active == 0:chat_interface.active = 1chat_interface.active_widget.placeholder = "在这里输入您的问题!"yield {"user": "Gemini", "object": "现在可以开始和pdf对话了!"}returnresponse, documents = _get_response(contents)pages_layout = pn.Accordion(*documents, sizing_mode="stretch_width", max_width=800)answers = pn.Column(response, pages_layout)yield {"user": "Gemini", "object": answers}chat_interface = pn.chat.ChatInterface(callback=respond,sizing_mode="stretch_width",widgets=[pdf_input, text_input],disabled=True,
)@pn.depends(state.param.pdf, environ.param.GOOGLE_API_KEY, watch=True)
def _enable_chat_interface(pdf, google_api_key):if pdf and google_api_key:chat_interface.disabled = Falseelse:chat_interface.disabled = True_send_not_ready_message(chat_interface)## Wrap the app in a nice templatetemplate = pn.template.BootstrapTemplate(title="PDF文档对话机器人",sidebar=[environ,state.param.number_of_chunks],main=[chat_interface],
)
template.servable()

四、机器人使用方法介绍

我们需要在命令行窗口中执行机器人的源代码程序:

panel serve gemini_pdf_bot.py

在浏览器中打开访问机器人的链接:

完成输入谷歌api key和上传pdf文件以后,就可以开始和机器人聊天了:

五、总结

今天我们主要介绍了如何开发一个简单的RAG系统:基于pdf文档问答的机器人应用,其中我们应用了langchain的父文档检索策略,panel的页面聊天组件chat_interface以及谷歌的Gemini大模型。希望今天的内容对大家学习RAG和聊天机器人程序有所帮助。

六、完整代码

链接:https://pan.baidu.com/s/1w6MzGDflLF7N3-NlENKj8w 提取码:tllt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/317399.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

管程-第三十三天

目录 为什么要引入管程 管程的定义和基本特征 用管程解决生产者消费者问题 结论 本节思维导图 为什么要引入管程 原因:在解决进程的同步与互斥问题时,信号量机制存在编写困难和易出错的问题 能不能设计一种机制,让程序员写程序时不再需…

MySQL常见面试题总结

1.MySQL基础 1.1什么是关系型数据库? 顾名思义,关系型数据库(RDB,Relational Database)就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多…

.NET国产化改造探索(一)、VMware安装银河麒麟

随着时代的发展以及近年来信创工作和…废话就不多说了,这个系列就是为.NET遇到国产化需求的一个闭坑系列。接下来,看操作。 安装银河麒麟 麒麟系统分银河麒麟和中标麒麟,我选择的是银河麒麟服务器版的,关于如何下载,…

php ext-sodium 拓展安装 linux+windows

php编译安装(linux),可以参考:php编译安装 一、windows soduim源码包自带,直接修改php.ini,取消extensionsodium注释即可 二、linux 1.安装依赖 apt-get install libsodium-dev2.进入源码目录 这里写自己的源码目录 cd /us…

YOLOv8改进 更换轻量化模型MobileNetV3

一、MobileNetV3论文 论文地址:1905.02244.pdf (arxiv.org) 二、 MobileNetV3网络结构 MobileNetV3引入了一种新的操作单元,称为"Mobile Inverted Residual Bottleneck",它由一个1x1卷积层和一个3x3深度可分离卷积层组成。这个操…

Java基础-----集合类(二)

文章目录 1. 泛型简介2. 使用泛型的好处3.使用泛型3.1 泛型类3.2 泛型接口3.3 泛型方法 4 泛型的通配符4.1 <?>&#xff1a;无边界的通配符4.2 <? extends E>&#xff1a;固定上边界的通配符4.3 <? super E>&#xff1a;固定下边界的通配符 5.总结 今天主…

【React】class组件生命周期函数的梳理和总结(第一篇)

1. 前言 本篇梳理和总结一下React的生命周期函数&#xff0c;方便使用class组件的同学查阅&#xff0c;先上生命周期图谱。 2. 生命周期函数 生命周期函数说明constructor(props) 功能&#xff1a;如果不需要初始化state或不进行方法绑定&#xff0c;class组件可以不用实现构造…

【华为机试】2023年真题B卷(python)-考古问题

一、题目 题目描述&#xff1a; 考古问题&#xff0c;假设以前的石碑被打碎成了很多块&#xff0c;每块上面都有一个或若干个字符&#xff0c;请你写个程序来把之前石碑上文字可能的组合全部写出来&#xff0c;按升序进行排列。 二、输入输出 三、示例 示例1: 输入输出示例仅供…

BLE协议—协议栈基础

BLE协议—协议栈基础 BLE协议栈基础通用访问配置文件层&#xff08;Generic Access Profile&#xff0c;GAP&#xff09;GAP角色设备配置模式和规程安全模式广播和扫描 BLE协议栈基础 蓝牙BLE协议栈包含三部分&#xff1a;主机、主机接口层和控制器。 主机&#xff1a;逻辑链路…

resetlogs失败故障恢复-ORA-01555---惜分飞

客户数据库resetlogs报错 Tue Dec 19 15:21:23 2023 ALTER DATABASE MOUNT Successful mount of redo thread 1, with mount id 1683789043 Database mounted in Exclusive Mode Lost write protection disabled Completed: ALTER DATABASE MOUNT Tue Dec 19 15:22:01 2023…

视频云存储/视频智能分析平台EasyCVR在麒麟系统中无法启动该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

软考证书学下来,对找工作有哪些帮助?

“软考&#xff1f;真的别考”。 “考了半天&#xff0c;到过期了还能没用上&#xff0c;hr根本不看”。 我在豆瓣上看着网友们的评论&#xff0c;心中五味杂陈。 每年有超过100万人参加的考试&#xff0c;却被人吐槽说一无是处&#xff1f; 这种国家级认证的考试&#xff0…