你好,我是黄佳,欢迎来到LangChain实战课!
上节课,咱们的聊天机器人已经基本完成,这节课,我们要看一看如何把它部署到网络上。
“聊天机器人”项目说明 简单回顾一下这个项目的设计。
**第一步:**通过LangChain的ConversationChain,实现一个最基本的聊天对话工具。
**第二步:**通过LangChain中的记忆功能,让这个聊天机器人能够记住用户之前所说的话。
**第三步:**通过LangChain中的检索功能,整合易速鲜花的内部文档资料,让聊天机器人不仅能够基于自己的知识,还可以基于易速鲜花的业务流程,给出专业的回答。
**第四步(可选):**通过LangChain中的数据库查询功能,用户可以输入订单号来查询订单状态,或者看看有没有存货等等。
**第五步:**在网络上部署及发布这个聊天机器人,供企业内部员工和易速鲜花用户使用。
在上一个项目中,我们是通过 Flask 部署的人脉工具。Flask是一个通用的、微型的Web应用框架,非常适合创建各种Web应用程序,不仅仅局限于机器学习或数据科学项目。Flask为开发者提供了很高的灵活性,你可以自定义路由、模板、前端和后端的交互等等。对于初学者,Flask可能需要更长时间来学习,尤其是需要结合其他前端技术或数据库技术时。
不过,对于机器学习项目来说,我们还有其他部署方案。比如 Streamlit 和 Gradio,就为机器学习和数据科学应用提供了快速、专门化的解决方案。如果你的项目目标是快速展示和验证模型效果,那么 Streamlit 和 Gradio 是优秀的选择。这些框架提供了简单易用的 API 和丰富的可视化组件,让你可以用少量代码快速构建交互式应用程序,提高你的开发效率,也可以更好地展示工作成果。
下面,我就带着你用这两种机器学习部署框架来展示我们的聊天机器人。
方案 1 :通过 Streamlit 部署聊天机器人 首先来看看Streamlit。这是一个挺有名的专门为数据科学家和机器学习工程师设计的开源Python库,它可以迅速地将Python脚本转化为交互式Web应用。
Streamlit 的一些主要特点和亮点包括:
简易性:Streamlit 的真正魅力在于它的简单性,只需几行代码,你就可以为其数据或模型创建交互式应用。 无需前端经验:与传统的Web开发框架相比,使用 Streamlit,你不需要深入了解HTML、CSS或JavaScript,所有交互都是通过Python代码来管理的。 实时交互:当你更改代码或数据时,Streamlit 应用会实时更新,这为迭代和实验提供了极大的便利。 内置组件:Streamlit 附带了许多内置的可视化和交互组件,如滑块、按钮、表格等,可以无缝集成到你的应用中。 数据集可视化:除了基本的图形和图表,Streamlit 还支持其他数据可视化库,如 Plotly、Matplotlib 和 Altair,使你能够轻松地展示数据。 设计简洁:Streamlit 的界面设计简洁而优雅,使得应用程序看起来既专业又时尚。 部署和共享:尽管 Streamlit 专注于创建应用,但它也有与部署和分享相关的工具和整合,如 Streamlit Sharing,允许你免费托管其应用。 社区与生态系统:Streamlit 拥有一个积极的开源社区,定期提供新的功能更新、组件和扩展。
我们用下面的语句,安装Streamlit。
然后,简单的几行代码,就可以做出一个网页版的小程序。
1 2 3 4 5 6 7 8 9 10 import streamlit as st # 设置标题 st.title('平方计算器') # 创建一个滑块 number = st.slider("Select a number:", min_value=0, max_value=100) # 显示选中数字的平方 st.write(f"Square of {number} is {number ** 2}")
用 streamlit run <your_script_name>.py(注意,必须是streamlit run命令,而不是通过python命令来跑程序)来运行程序,就可以在浏览器中看到它。
1 streamlit run 01_SimpleStreamlit.py
此时,在 localhost 的 8501 端口,程序开始启动。
下面就通过 Streamlit 来重构聊天机器人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 # 导入所需的库 import os import streamlit as st from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Qdrant from langchain.memory import ConversationSummaryMemory from langchain.chat_models import ChatOpenAI from langchain.chains import ConversationalRetrievalChain from langchain.document_loaders import PyPDFLoader from langchain.document_loaders import Docx2txtLoader from langchain.document_loaders import TextLoader # 设置OpenAI API密钥 os.environ["OPENAI_API_KEY"] = 'Your OpenAI Key' # ChatBot类的实现 class ChatbotWithRetrieval: def __init__(self, dir): # 加载Documents base_dir = dir # 文档的存放目录 documents = [] for file in os.listdir(base_dir): file_path = os.path.join(base_dir, file) if file.endswith('.pdf'): loader = PyPDFLoader(file_path) documents.extend(loader.load()) elif file.endswith('.docx') or file.endswith('.doc'): loader = Docx2txtLoader(file_path) documents.extend(loader.load()) elif file.endswith('.txt'): loader = TextLoader(file_path) documents.extend(loader.load()) # 文本的分割 text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0) all_splits = text_splitter.split_documents(documents) # 向量数据库 self.vectorstore = Qdrant.from_documents( documents=all_splits, # 以分块的文档 embedding=OpenAIEmbeddings(), # 用OpenAI的Embedding Model做嵌入 location=":memory:", # in-memory 存储 collection_name="my_documents",) # 指定collection_name # 初始化LLM self.llm = ChatOpenAI() # 初始化Memory self.memory = ConversationSummaryMemory( llm=self.llm, memory_key="chat_history", return_messages=True ) # 设置Retrieval Chain retriever = self.vectorstore.as_retriever() self.qa = ConversationalRetrievalChain.from_llm( self.llm, retriever=retriever, memory=self.memory ) def chat_loop(self): print("Chatbot 已启动! 输入'exit'来退出程序。") while True: user_input = input("你: ") if user_input.lower() == 'exit': print("再见!") break # 调用 Retrieval Chain response = self.qa(user_input) print(f"Chatbot: {response['answer']}") # Streamlit界面的创建 def main(): st.title("易速鲜花聊天客服") # Check if the 'bot' attribute exists in the session state if "bot" not in st.session_state: st.session_state.bot = ChatbotWithRetrieval("OneFlower") user_input = st.text_input("请输入你的问题:") if user_input: response = st.session_state.bot.qa(user_input) st.write(f"Chatbot: {response['answer']}") if __name__ == "__main__": main()
以下是使用 Streamlit 进行的更改和添加功能的简要说明。
界面创建: a. st.title("易速鲜花聊天客服"):设置 Web 应用程序的标题为“易速鲜花聊天客服”。 会话状态: a. 使用 st.session_state 来存储用户会话状态。这是 Streamlit 的一个特性,允许你在用户与应用程序交互时保存变量。 b. if "bot" not in st.session_state:检查是否已经有一个 bot 实例存在于 session state 中。如果没有,就创建一个新的 ChatbotWithRetrieval 实例,并将其保存到 session state。这样做的好处是可以避免在每次用户与应用程序交互时重新初始化机器人。 用户交互: a. user_input = st.text_input("请输入你的问题:"):创建一个文本输入框供用户输入问题。当用户输入内容并提交后,代码会获取用户的输入,并使用聊天机器人的 qa 方法来获取响应。 b. st.write(f"Chatbot: {response['answer']}"):在应用程序界面上显示机器人的响应。 主函数中,当脚本被执行时,它将启动 Streamlit 服务器,并显示创建的 Web 应用程序。 用 streamlit run 运行程序,就可以开始聊天了!
方案2 :通过 Gradio 部署聊天机器人 与 Streamlit 不同,Gradio 界面更侧重于模型的交互,据说上手也更简单。这使得 Gradio 非常适合展示和测试机器学习模型。我在GitHub上看到很多新的开源LLM都是提供一个Gradio UI界面来进行测试的。相比之下, Streamlit 则提供了更丰富的 Web 应用开发功能。
到底有多简单,等会你看到 Gradio UI的界面,你就明白我的意思了。
下面,我们先安装这个包。
通过 Gradio 框架重构聊天机器人的程序代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 # 导入所需的库 import os import gradio as gr from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Qdrant from langchain.memory import ConversationSummaryMemory from langchain.chat_models import ChatOpenAI from langchain.chains import ConversationalRetrievalChain from langchain.document_loaders import PyPDFLoader from langchain.document_loaders import Docx2txtLoader from langchain.document_loaders import TextLoader # 设置OpenAI API密钥 os.environ["OPENAI_API_KEY"] = 'Your OpenAI Key' class ChatbotWithRetrieval: def __init__(self, dir): # 加载Documents base_dir = dir # 文档的存放目录 documents = [] for file in os.listdir(base_dir): file_path = os.path.join(base_dir, file) if file.endswith('.pdf'): loader = PyPDFLoader(file_path) documents.extend(loader.load()) elif file.endswith('.docx') or file.endswith('.doc'): loader = Docx2txtLoader(file_path) documents.extend(loader.load()) elif file.endswith('.txt'): loader = TextLoader(file_path) documents.extend(loader.load()) # 文本的分割 text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0) all_splits = text_splitter.split_documents(documents) # 向量数据库 self.vectorstore = Qdrant.from_documents( documents=all_splits, # 以分块的文档 embedding=OpenAIEmbeddings(), # 用OpenAI的Embedding Model做嵌入 location=":memory:", # in-memory 存储 collection_name="my_documents",) # 指定collection_name # 初始化LLM self.llm = ChatOpenAI() # 初始化Memory self.memory = ConversationSummaryMemory( llm=self.llm, memory_key="chat_history", return_messages=True ) # 初始化对话历史 self.conversation_history = "" # 设置Retrieval Chain retriever = self.vectorstore.as_retriever() self.qa = ConversationalRetrievalChain.from_llm( self.llm, retriever=retriever, memory=self.memory ) def get_response(self, user_input): # 这是为 Gradio 创建的新函数 response = self.qa(user_input) # 更新对话历史 self.conversation_history += f"你: {user_input}\nChatbot: {response['answer']}\n" return self.conversation_history if __name__ == "__main__": folder = "OneFlower" bot = ChatbotWithRetrieval(folder) # 定义 Gradio 界面 interface = gr.Interface( fn=bot.get_response, # 使用我们刚刚创建的函数 inputs="text", # 输入是文本 outputs="text", # 输出也是文本 live=False, # 实时更新,这样用户可以连续与模型交互 title="易速鲜花智能客服", # 界面标题 description="请输入问题,然后点击提交。" # 描述 ) interface.launch() # 启动 Gradio 界面
以下是 Gradio 部分代码的详细解释。
get_response(self, user_input):这个新函数是为 Gradio 创建的,它接收用户输入作为参数,并返回机器人的响应。为了保持聊天历史连续性,此函数将每次的用户输入和机器人的响应添加到 conversation_history,并返回整个聊天历史。使用 gr.Interface() 来定义 Gradio 界面。fn=bot.get_response:设置界面的主函数为刚刚创建的 get_response 函数。live=False:确保实时更新是关闭的,这意味着用户需要点击提交按钮来发送他们的问题,而不是一边打字,一边生成回答的流模式(比较适合展示生成式模型)。 启动 Gradio 界面。interface.launch():调用这个方法会启动 Gradio 的 Web 服务器,并在默认的 Web 浏览器中打开一个新窗口,显示刚刚定义的界面,用户可以通过这个界面与机器人交互。 运行程序,聊天机器人在本地端口 7860 上启动。
这里,输入输出窗口的配置更加清晰,而且相对于原来的只记录一轮对话的机器人,这里我们增加了历史对话信息记录功能。
总结时刻 Streamlit 和 Gradio 都是让数据科学家和开发者能够快速为机器学习模型创建 Web UI 的框架。
Streamlit 是为数据应用、仪表板和可视化设计的。它提供了多种小部件,使得用户可以与数据和模型进行交互。它非常 Pythonic,意味着它的使用方式非常自然,对于熟悉Python的人来说非常直观。 Gradio 更多是为了展示和演示机器学习模型。它提供了一种快速的方法,使非技术用户也能与机器学习模型进行交互,无需编写复杂的代码。 以下是对它们特点进行的对比总结。
无论选择哪个框架,你都可以在非常短的时间内为你的应用创建一个Web UI。至于选择哪个,更多取决于你的具体需求和个人喜好。
如果你用这些框架,在你熟悉的业务场景中,利用LangChain和LLM的能力开发出了更为酷炫的LangChain应用,不要忘记在留言区中和大家分享!说说思路就行,有具体代码实现更好。
思考题 我的易速鲜花Chatbot有很多不完美的地方,比如,检索功能的设计不够细致,UI不够美观,等等。请你在这个Repo的基础上,大刀阔斧地进行改进。 请你用Flask框架设计自己的Chatbot UI,重构聊天机器人,实现更多、更完善的功能。 请你回过头去看看[第01讲]我给你留的3道思考题。那时候,你不了解LangChain,现在你已经基本掌握了它的精髓,能否把第01讲的思考题重新回答一遍呢?应该很有趣吧! 期待在留言区看到你的成果分享,如果觉得内容对你有帮助,也欢迎分享给有需要的朋友!