langchain 入门指南 - 实现一个多模态 chatbot

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

在前面的文章中,我们学会了如何通过 langchain 实现本地文档库的 QA,又或者通过 langchain 来实现对话式的问答系统。
在这篇文章中,我们将会学习如何通过 langchain 来实现一个多模态的 chatbot。

本文会构建一个有如下功能的 chatbot:

  • 可以生成图片
  • 可以回答用户的问题
  • 可以检索本地文档库中的信息
  • 可以从互联网进行搜索信息

什么是多模态

在前面的大部分例子中,我们跟 LLM 对话的时候都是使用了文本作为输入和输出。
但是除了文本,我们也可以让 LLM 来为我们生成图片。

多模态是指同时使用两种或两种以上的信息模式或表现形式。在人工智能和机器学习的背景下,
多模态通常指的是能够处理和融合不同类型数据的系统,这些数据可能包括文本、图像、音频、视频或其他传感器数据。

准备操作

  • 配置 OPENAI_API_KEYOPENAI_BASE_URL 环墋变量。
  • 配置 SERPER_API_KEY 环境变量,可以从 https://serper.dev 获取。

如和实现对本地文档的 QA

langchain 中,RetrievalQA 是一个结合了检索(Retrieval)和问答(QA)的组件。
它允许你构建一个系统,该系统能够根据用户的提问,从提供的文档或知识库中检索相关信息,并回答用户的问题。

RetrievalQA 的工作流程如下:

  • 检索(Retrieval):当用户提出一个问题时,RetrievalQA 会使用一个检索机制(本文会使用向量数据库做语义检索)
  • 阅读理解:一旦检索到相关的信息,RetrievalQA 会使用一个阅读理解模型来理解这些信息,并回答用户的问题。
  • 问答:最后,RetrievalQA 会使用一个问答模型(ChatModel)来生成最终的回答。

RetrievalQA 的优势在于它能够处理大量复杂的信息,并提供精确的答案。它特别适合那些需要从大量文档中检索信息的场景,例如法律文件、医学文献、技术手册等。

直接跟 LLM 对话的时候,一般都会有一个上下文大小限制的问题,太大的文档无法全部放入到上下文中。
但是可以先分片存入向量数据库中,在跟 LLM 对话之前,再从向量数据库中检索出相关的文档。最终发给 LLM 的数据只有相关的文档,这样就能够更好地回答用户的问题。

将 pdf 存入向量数据库

我们可以使用自己的 pdf 文档。

在这个例子中,我们将会使用 langchain 来将一个 pdf 文档存入向量数据库中:

from langchain_community.document_loaders import PyPDFLoader# 加载 pdf 文档
loader = PyPDFLoader("Spotmax_intro_cn_2020.pdf")
docs = loader.load()# 文档分片
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_spliter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)splits = text_spliter.split_documents(docs)
persist_directory = 'data/'from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings()
# 创建向量数据库
vectordb = Chroma.from_documents(documents=splits,embedding=embedding,collection_name="spotmax",persist_directory=persist_directory,
)
# 持久化向量数据库
vectordb.persist()

说明:

  • PyPDFLoader 是一个用于加载 pdf 文档的类。
  • RecursiveCharacterTextSplitter 是一个用于将文档分片的类。
  • Chroma 是一个向量数据库类,用于存储和检索向量化的文档。
  • vectordbChroma 的一个实例,用于存储和检索文档。
  • vectordb.persist() 用于将向量数据库持久化到磁盘。

通过上面的代码,我们将会把 Spotmax_intro_cn_2020.pdf 文档存入到向量数据库中。

使用 RetrievalQA 进行问答

在上一步将 pdf 文档存入向量数据库之后,我们就可以通过 Chroma 的实例来对其做语义检索了。

def qa(question):from langchain_community.vectorstores import Chromafrom langchain_openai import OpenAIEmbeddingsembedding = OpenAIEmbeddings()vectordb = Chroma(persist_directory='data/', embedding_function=embedding, collection_name='spotmax')from langchain.chains.retrieval_qa.base import RetrievalQAfrom langchain_openai import ChatOpenAIllm = ChatOpenAI(model_name="gpt-3.5-turbo",temperature=0,max_tokens=200,)retriever = vectordb.as_retriever(search_type="mmr",search_kwargs={"k": 3})qa0 = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever,return_source_documents=False, verbose=True)result = qa0({"query": question})return result['result']print(qa("Spotmax 是什么?"))

说明:

  • vectordb 是从现有的 Chroma 向量数据库中加载的。
  • llm 是最终回答用户问题的大模型。
  • retriever 是用于检索文档的检索器,用户的问题会先通过检索器检索到相关的文档。
  • RetrievalQA.from_chain_type 创建一个 RetrievalQA 实例,用于回答用户的问题。
  • qa0({"query": question}) 用户的问题会先通过 retriever 检索到相关的文档,然后再交给 LLM,通过 llm 来回答用户的问题。

让 LLM 生成图片

这个比较简单,使用 OpenAIdall-e-2 模型即可:

def create_image(prompt):from openai import OpenAIclient = OpenAI()response = client.images.generate(model='dall-e-2',prompt=prompt,size='256x256',quality='standard',n=1)u = response.data[0].urlmarkdown_url = f"![image]({u})"return markdown_url

这个例子中,我们会根据用户的 prompt 生成一张 256x256 像素的图片,并且返回一个 markdown 链接形式的图片地址。

从互联网搜索信息

我们可以使用 GoogleSerperAPIWrapper 来从互联网搜索信息:

def query_web(question):"""查询谷歌搜索结果"""from langchain_community.utilities import GoogleSerperAPIWrappersearch = GoogleSerperAPIWrapper()return search.run(question)

如何让 chatbot 理解不同的操作?

我们可以使用 Agent 来让 chatbot 理解不同的操作:

  1. 将上面提供的几种操作封装成不同的 Tool
  2. 创建一个 AgentExecutor,根据用户的输入,选择合适的 Tool 来执行。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-4",temperature=0.7,max_tokens=1000,
)
from langchain.agents import Tooltools = [Tool(name="Get current info",func=query_web,description="""only invoke it when you need to answer question about realtime info.And the input should be a search query."""),Tool(name="query spotmax info",func=qa,description="""only invoke it when you need to get the info about spotmax/maxgroup/maxarch/maxchaos.And the input should be the question."""),Tool(name="create an image",func=create_image,description="""invoke it when you need to create an image.And the input should be the description of the image.""")
]
from langchain.memory import ConversationBufferWindowMemory
from langchain.agents import ZeroShotAgent, AgentExecutor
from langchain.chains.llm import LLMChainprefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"{chat_history}
Question: {input}
{agent_scratchpad}"""prompt = ZeroShotAgent.create_prompt(tools,prefix=prefix,suffix=suffix,input_variables=["input", "chat_history", "agent_scratchpad"],
)
memory = ConversationBufferWindowMemory(k=10, memory_key="chat_history")llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools)
agent_chain = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=True

说明:

  • 将前文提到的几种能力,封装为 AgentExecutor 可以使用的 Tool
  • 使用 llm 以及 tools 作为参数创建一个 AgentExecutor

AgentExecutor

在 LangChain 中,AgentExecutor 是一个组件,它负责执行一个代理(Agent)的推理循环。Agent 是一个更高级的组件,它可以根据输入动态选择和执行工具(Tools)。

Agent 通常用于构建更复杂的应用,其中 AI 模型需要根据上下文做出决策,选择合适的行动方案,并执行这些方案以达到某个目标。例如,一个 Agent 可能需要决定何时查询数据库,何时生成文本,或者何时调用外部 API。

AgentExecutor 的作用是作为一个执行环境,它接收用户的输入,然后根据 Agent 的策略或算法来指导 Agent 如何使用可用的工具来处理这个输入。代理会生成一个或多个动作(Actions),每个动作都对应一个工具的调用。

AgentExecutor 会执行这些动作,并可能根据动作的结果更新 Agent 的状态,然后返回最终的输出给用户。

如何跟 AgentExecutor 交互

直接使用 AgentExecutorinvoke 方法即可:

agent_chain.invoke(question)

调用 invoke 之后,AgentExecutor 会根据用户的输入,选择合适的 Tool 来执行,然根据 Tool 的输出进行下一步操作(调用其他 Tool 或者生成最终答案等)。

界面展示

我们最后可以使用 gradio 来构建一个简单的 web 界面:

import gradio as grwith gr.Blocks() as demo:chatbot = gr.Chatbot(height=500) # 对话框msg = gr.Textbox(label="Prompt") # 输入框btn = gr.Button("Submit") # 按钮clear = gr.ClearButton(components=[msg, chatbot], value="Clear console") # 清除按钮btn.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])gr.close_all()
demo.launch()

这个例子中,我们添加了一个 chatbot 组件,以及为用户提供了一个输入框和一个提交按钮。

inputsoutputs 参数用于指定输入和输出的组件。inputs 会作为参数传递给 respond 函数,respond 的返回值会被传递给 outputs 组件。

最终效果如下:

在这里插入图片描述

AgentExecutor 的处理过程如下(Thought -> Action -> Observation -> Thought -> Final Answer):

> Entering new AgentExecutor chain...
Thought: The question is asking for the current weather in Guangzhou and a male outfit recommendation. I can use the 'Get current info' tool to find the weather, and the 'create an image' tool to generate the outfit image.
Action: Get current info
Action Input: Guangzhou weather today
Observation: 94°F
Thought:The weather in Guangzhou is quite hot today. Now I need to think of an outfit that would be suitable for such warm weather.
Action: create an image
Action Input: A light summer outfit for men suitable for 94°F weather
Observation: ![image](https://oaidalleapiprodscus.blob.core.windows.net/private/org-GFz12lkhEotcvDvFYzePwrtK/user-1Ci7Ci1YNFjtlIO7AIY9aNux/img-zRsrd0cFFfxYAwW1oKZV9643.png?st=2024-07-24T05%3A29%3A33Z&se=2024-07-24T07%3A29%3A33Z&sp=r&sv=2023-11-03&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-07-23T23%3A15%3A19Z&ske=2024-07-24T23%3A15%3A19Z&sks=b&skv=2023-11-03&sig=g9L0m2GHy%2BHtC48NPVDBjZWVGfrXGQzRam6XayUZvJ0%3D)
Thought:I now have the final answer.
Final Answer: 广州今天的天气很热,达到了94°F。我为你创建了一张适合这种天气的男士夏季轻便穿搭图。请参考图片中的服装搭配。![image](https://oaidalleapiprodscus.blob.core.windows.net/private/org-GFz12lkhEotcvDvFYzePwrtK/user-1Ci7Ci1YNFjtlIO7AIY9aNux/img-zRsrd0cFFfxYAwW1oKZV9643.png?st=2024-07-24T05%3A29%3A33Z&se=2024-07-24T07%3A29%3A33Z&sp=r&sv=2023-11-03&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-07-23T23%3A15%3A19Z&ske=2024-07-24T23%3A15%3A19Z&sks=b&skv=2023-11-03&sig=g9L0m2GHy%2BHtC48NPVDBjZWVGfrXGQzRam6XayUZvJ0%3D)> Finished chain.

我们可以看到在我提这个问题的时候,它做了如下操作:

  • 思考,然后发现需要获取今天广州的天气,这是 LLM 不懂的,所以使用了 Get current info 工具。
  • 获取到了天气信息之后,思考,然后发现需要生成一张图片,而我们有一个 create an image 工具,因此使用了这个工具来生成图片
  • 最终返回了今天广州的天气状况以及一张图片。

当然,我们也可以问它关于本地知识库的问题,比如 “什么是 spotmax?”(根据你自己的 pdf 提问,这里只是一个示例)

完整代码

最终完整的代码如下:

  • qa 函数用于回答用户关于本地知识库的问题
  • create_image 函数用于生成图片
  • query_web 函数用于从互联网搜索信息
  • respond 函数用于处理 chatbot 的对话响应
  • agent_chain 是一个 AgentExecutor 实例,用于执行 Agent 的推理循环
import gradio as grdef qa(question):from langchain_community.vectorstores import Chromafrom langchain_openai import OpenAIEmbeddingsembedding = OpenAIEmbeddings()vectordb = Chroma(persist_directory='data1/', embedding_function=embedding, collection_name='spotmax')from langchain.chains.retrieval_qa.base import RetrievalQAfrom langchain_openai import ChatOpenAIllm = ChatOpenAI(model_name="gpt-3.5-turbo",temperature=0,max_tokens=200,)retriever = vectordb.as_retriever(search_type="mmr",search_kwargs={"k": 3})qa0 = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever,return_source_documents=False, verbose=True)result = qa0({"query": question})return result['result']def create_image(prompt):from openai import OpenAIclient = OpenAI()response = client.images.generate(model='dall-e-2',prompt=prompt,size='256x256',quality='standard',n=1)u = response.data[0].urlmarkdown_url = f"![image]({u})"return markdown_urldef query_web(question):"""查询谷歌搜索结果"""from langchain_community.utilities import GoogleSerperAPIWrappersearch = GoogleSerperAPIWrapper()return search.run(question)def respond(message, chat_history):"""对话函数"""bot_message = get_response(message)chat_history.append((message, bot_message))return "", chat_historyfrom langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-4",temperature=0.7,max_tokens=1000,
)
from langchain.agents import Tooltools = [Tool(name="Get current info",func=query_web,description="""only invoke it when you need to answer question about realtime info.And the input should be a search query."""),Tool(name="query spotmax info",func=qa,description="""only invoke it when you need to get the info about spotmax/maxgroup/maxarch/maxchaos.And the input should be the question."""),Tool(name="create an image",func=create_image,description="""invoke it when you need to create an image.And the input should be the description of the image.""")
]
from langchain.memory import ConversationBufferWindowMemory
from langchain.agents import ZeroShotAgent, AgentExecutor
from langchain.chains.llm import LLMChainprefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"{chat_history}
Question: {input}
{agent_scratchpad}"""prompt = ZeroShotAgent.create_prompt(tools,prefix=prefix,suffix=suffix,input_variables=["input", "chat_history", "agent_scratchpad"],
)
memory = ConversationBufferWindowMemory(k=10, memory_key="chat_history")llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True, handle_parsing_errors=True)
agent_chain = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=True
)def get_response(message):res = agent_chain.invoke(message)return res['output']with gr.Blocks() as demo:chatbot = gr.Chatbot(height=500) # 对话框msg = gr.Textbox(label="Prompt") # 输入框btn = gr.Button("Submit") # 按钮clear = gr.ClearButton(components=[msg, chatbot], value="Clear console") # 清除按钮btn.click(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])gr.close_all()
demo.launch()

总结

虽然 OpenAI 提供了 function calling 的特性,但是直接使用起来还是比较麻烦,通过 AgentExecutor 结合 tools 的方式,可以更好地组织和管理 chatbot 的能力。

在这篇文章中,我们学习了如何通过 langchain 来实现一个多模态的 chatbot,它可以生成图片、回答用户的问题、检索本地文档库中的信息、从互联网搜索信息等。

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

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

相关文章

解决Centos不支持docker命令行tab提示问题!!!

一、CentOS不支持Docker Tab提示 在使用CentOS操作系统时,有些用户可能会遇到不能自动补全Docker命令的问题。这是因为CentOS默认不支持Docker Tab提示功能,需要手动配置才能实现。在这篇科普文章中,我们将介绍如何解决这个问题,…

GCA检查设计约束流程

文章目录 一、什么是GCA二、GCA流程1 .启动GCA2 .设置lib3 .读design并链接design4 .读SDC5 .analyze_design6 .报告或图形化界面分析结果 一、什么是GCA Galaxy Constraint Analyzer,简称GCA,是一个专门检查设计约束的工具,现在已经集成在P…

MySQL使用教程 最最最实用的零基础教程 直接从安装开始教!!!!

数据构成了我们日益数字化的社会基础。想象一下,从移动应用和银行系统到搜索引擎,再到如 ChatGPT 这样的先进人工智能聊天机器人,这些工具若没有数据支撑,将寸步难行。你有没有好奇过这些海量数据都存放在哪里呢?答案正…

ArchLinux部署waydroid

在Arch Linux系统上部署Waydroid运行Android APP 文章目录 在Arch Linux系统上部署Waydroid运行Android APP1. 安装要求2. 本机环境3. 安装 Waydroid4. 网络配置5.注册Google设备6. 运行效果图 Waydroid是Anbox配合Haliun技术开发的LXC Android容器,可在GUN/Linux系…

华三云课堂CAS5.0忘记admin密码?

当忘记修改后的管理平台 admin 账户密码,无法登录管理平台。 处理步骤: (1) SSH 连接服务器。 (2) 执行/var/lib/h3class/bin/reset-admin-pwd.sh 命令。 (3) 重置后的密码为 Cloud1234

src漏洞挖掘--验证码篇

图片验证码 验证码前端检测 验证码由客户端JS生成并且仅仅在客户端用JS验证,通过抓包看数据传输是否有验证码字段或者是关闭JS看能否通过验证。 测试方法:当我们开始抓包,输入任意验证码,页面提示验证码错误,且没有抓…

如何远程调用运维人员电脑桌面

要远程调用运维人员的电脑桌面,可以通过以下几种方法和工具: 一、使用第三方远程控制软件: 要远程访问操作和维护人员的桌面,您可以使用专业的远程桌面软件按照以下步骤操作。以下是使用 Splashtop 的一般指南,但对于…

ME11-创建采购信息记录

ME11 创建采购信息记录 创建Info Record时,工厂不是必输项。因为采购组织可以对应多个工厂,所以可以针对一个工厂设定Info Record,也可以不固定。 计划交货时间维护供应商收到采购订单后,把货送到工厂的时间。 标准数量一般维护为…

MacOS查看端口占用

在macOS系统中,可以使用lsof和netstat命令来查看端口占用情况。 使用lsof命令 lsof -i :端口号替换端口号为你想要检查的端口。例如,要查看端口8080的占用情况,可以使用: lsof -i :8080使用netstat命令 首先,你需要…

工作中经常听到的云、边、端到底是什么意思?

工作中经常听到的云、边、端到底是什么意思? 在数字化和智能化飞速发展的今天,“云”、“边”、“端”这三个词频频出现在我们的生活和工作中。它们代表着不同的数据处理和计算模式,极大地改变了我们的生活方式。那么,它们分别是…

《Linux运维总结:基于x86_64架构CPU使用docker-compose一键离线部署zookeeper 3.8.4容器版分布式集群》

总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:《Linux运维篇:Linux系统运维指南》 一、部署背景 由于业务系统的特殊性,我们需要面对不同的客户部署业务系统&#xff0…

邮件攻击案例系列四:某金融企业遭遇撒网式钓鱼邮件攻击

案例描述 2023 年 3 月末,某知名投资公司业务经理李先生先后收到两封看似是来自邮件服务商和公司网络安全部门发出的邮件,标题是“紧急:邮箱安全备案更新通知”。邮件内容称,由于最近公司内部系统升级,所有员工必须重…

【Python机器学习】朴素贝叶斯——使用朴素贝叶斯过滤垃圾邮件

使用朴素贝叶斯解决一些现实生活中的问题时,需要先从文本内容中得到字符串列表,然后生成词向量。 使用朴素贝叶斯对电子邮件进行分类的过程: 1、收集数据:提供文本文件 2、准备数据:将文本文件解析成词条向量 3、分析…

【Windows】Mountain Duck(FTP服务器管理工具)软件介绍

软件介绍 Mountain Duck是一款基于Cyberduck开发的应用程序,它允许用户通过FTP、SFTP、WebDAV、S3和OpenStack Swift等协议连接到云存储和远程服务器,并在本地文件浏览器中以熟悉的方式访问和管理这些文件。 功能特点 支持多种协议: Mountain Duck支持…

面向对象程序设计(C++)模版初阶

1. 函数模版 1.1 函数模版概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本,可以类比函数参数,函数模版就是将函数参数替换为特定类型版本 1.2 函数模版格…

BUG 太多?苹果罕见“重新推送”ios18 beta 4测试版

在刚刚过去的周末,苹果公司面向开发者,重新发布了 iOS / iPadOS 18 Beta 4 更新,内部版本号从 22A5316j 变为 22A5316k,目前尚不清楚两个 Beta 4 版本更新之间的区别。 此次更新包大小仅为251M左右,是 更新。 对于已经…

精品PPT | 微信云原生大数据平台构建及落地实践.pptx

一、大数据上云概述 1.为什么大数据要上云 2.微信大数据平台架构演进 二、大数据上云基础建设 1.统一编排 2.Pod 设计及大数据配套能力 3.计算组件云环境适配 三、稳定性及效率提升 1.K8S 集群稳定性与弹性配额 2.可观测性与智能运维

基于OpenCV C++的网络实时视频流传输——Windows下使用TCP/IP编程原理

1.TCP/IP编程 1.1 概念 IP 是英文 Internet Protocol (网络之间互连的协议)的缩写,也就是为计算机网络相互连接进行通信而设计的协议。任一系统,只要遵守 IP协议就可以与因特网互连互通。 所谓IP地址就是给每个遵循tcp/ip协议连…

[QT开发_音乐播放器项目笔记01]

目录 一:常用类 26 QByteArray 是 Qt 框架中的一个类,用于处理字节数组。它提供了动态大小的字节数组,可以用于存储和操作二进制数据,比如文件内容、网络数据等。 QT项目记录: 一:常用类 26 QByteArray…

【JavaScript】详解JavaScript语法

文章目录 一、变量和数据类型二、运算符三、条件语句四、循环语句五、函数六、对象和数组七、ES6新特性八、实际应用案例 JavaScript是一门广泛应用于Web开发的编程语言。掌握JavaScript语法是成为前端开发者的第一步。本文将详细介绍JavaScript的基本语法,包括变量…