【Langchain+Streamlit】旅游聊天机器人

【Langchain+Streamlit】打造一个旅游问答AI-CSDN博客

项目线上地址,无需openai秘钥可直接体验:http://101.33.225.241:8502/

github地址:GitHub - jerry1900/langchain_chatbot: langchain+streamlit打造的一个有memory的旅游聊天机器人,可以和你聊旅游相关的事儿

        上节课,我们介绍了一个用streamlit和langchain打造的问答机器人,但是这个机器人有一个问题就是它只能一问一答,而且这个机器人是没有记忆的,你问他连续的问题他就傻帽了,所以,今天我们介绍一下如何打造一个类似chatGPT界面,有上下文感知、可以和你连续聊天的机器人,当然,我们也限定这个机器人只能聊旅游相关的问题。

1. 工程结构介绍

        之后我们做的项目越来越大,不可能所有代码都堆到一个主文件里,所以要设计良好的工程代码结构,这个是本项目的工程结构:

        .streamlit里面的secrets.toml是存放key和其他一些常用配置参数的文件。

        .venv是虚拟环境。

        chain.py里有两个函数,一个build_chain,一个generate_response。

        chat.py是主体程序。

        template.py是存放prompt模板的地方。

        其他两个文件不用管。

        完整的代码,去github上去拉。

2. 引入必要的包

        我们先用streamlit打造一个聊天界面,之前我们已经有了一些streamlit的基础,这里我们就讲的稍微稍微快一点:

import io
import streamlit as st
from PIL import Imagefrom langchain.memory import ConversationBufferMemoryfrom chain import generate_response,build_chainst.title('🤖AI小万的旅游聊天机器人😜')with st.sidebar:# 设置一个可点击打开的展开区域with st.expander("🤓国内可访问的openai账号"):st.write("""1. 如果使用默认地址,可以使用openai官网账号(需科学上网🥵).2. 如果你没有openai官网账号,可以联系博主免费试用国内openai节点账号🥳.""")# 本地图片无法直接加载,需先将图片读取加载为bytes流,然后才能正常在streamlit中显示image_path = r"C:\Users\Administrator\langchain_chatbot\wechat.jpg"image = Image.open(image_path)image_bytes = io.BytesIO()image.save(image_bytes, format='JPEG')st.image(image_bytes, caption='AI小万老师的微信', use_column_width=True)

  3. 进行聊天记录和memory的初始化,打印聊天记录

        先引入必要的包,注意要引入ConversationBufferMemory,因为我们要在初始化的时候先初始化一个memory,同时我们要初始化一个messages,这些都要放到session里。

# 初始化聊天记录
if "messages" not in st.session_state:st.session_state.messages = []st.session_state.memory = ConversationBufferMemory(memory_key='chat_history')

       streamlit有一个特性:用户只能使用互动组件(interactive widgets)触发回调,并且每次操作互动组件时,都会触发重新运行(rerun)。rerun(重新运行)是streamlit的一个特色,指的是将应用代码从头到尾重新运行一遍。

# 展示聊天记录
for message in st.session_state.messages:if message["role"] == "user":with st.chat_message(message["role"], avatar='☺️'):st.markdown(message["content"])else:with st.chat_message(message["role"], avatar='🤖'):st.markdown(message["content"])

        因此,我们要把session里的messages里的消息遍历一遍打印出来,这样确保你刚才添加的最新的消息 也在页面上显示出来,我们继续。

4. 进行用户输入、回答生成和回答展示

if prompt := st.chat_input('我们来聊一点旅游相关的事儿吧'):with st.chat_message('user', avatar='☺️'):st.markdown(prompt)st.session_state.messages.append({'role': 'user', 'content': prompt})chain = build_chain(st.session_state.memory)answer = generate_response(chain, prompt)response = answer['text']with st.chat_message('assistant', avatar='🤖'):st.markdown(response)st.session_state.messages.append({'role': 'assistant', 'content': response})

        下面这行代码,先检查prompt是否为空,如果为空则给他一个st的输入:

if prompt := st.chat_input('聊点和旅游相关的事儿吧,么么哒'):

        然后设置用户的输入和头像,然后把用户的输出用markdown语法显示出来(我没有试用write可不可以,你可以自己试一下):

with st.chat_message('user', avatar='☺️'):st.markdown(prompt)st.session_state.messages.append({'role': 'user', 'content': prompt})

       然后这里用了两个我们自己构造的函数,一个是build_chain,这里记住要把session里的memory传入进来,这个动作帮助我们构造的模型保持上下文的记忆:

chain = build_chain(st.session_state.memory)

       然后我们调用generate_response方法来获得模型的回答:

answer = generate_response(chain, prompt)

  5. build_chain方法

        这个方法的入参是一个在前文中构造好的memory,其他的方法我们在之前的课中都介绍过,大家可以自己去翻看:

def build_chain(memory):llm = OpenAI(temperature=0,# openai_api_key=os.getenv("OPENAI_API_KEY"),openai_api_key=st.secrets['api']['key'],# base_url=os.getenv("OPENAI_BASE_URL")base_url=st.secrets['api']['base_url'])prompt = PromptTemplate.from_template(BASIC_TEMPLATE)conversation = LLMChain(llm=llm,prompt=prompt,verbose=True,memory=memory,)return conversation

6.generate_response方法

      这个更简单,不多说了:

def generate_response(chain,input_text):response = chain.invoke(input_text)return  response

7. template单独设置

        我看别的项目,都是把template单独建一个文件,然后引入,这样代码结构比较清晰,我也有样学样了:

from template import BASIC_TEMPLATE

        这里是template里BASIC_TEMPLATE的具体内容:

BASIC_TEMPLATE="""
你是一个万贺创造的旅游问答机器人,你只回答用户关于旅游和地理方面的问题。
如果用户的问题中没有出现地名或者没有出现如下词语则可以判定为与旅游无关:‘玩、旅游、好看、有趣、风景’案例:
1. 用户问题:今天天气如何? 你的回答:抱歉,我只负责回答和旅游、地理相关的问题。
2. 用户问题:你是谁?你的回答:我是万贺创造的旅游问答机器人,我只负责回答和旅游、地理相关的问题。
3. 用户问题:今天股市表现如何?你的回答:抱歉我只负责回答和旅游、地理相关的问题注意:
1. 价格也是旅游相关的问题,如果你不清楚的话直接回答不知道过去的聊天记录:
{chat_history}用户的问题: 
{question}你的回答:
"""

8. 完整代码

        chat.py

import io
import streamlit as st
from PIL import Imagefrom langchain.memory import ConversationBufferMemoryfrom chain import generate_response,build_chainst.title('🤖AI小万的旅游聊天机器人😜')with st.sidebar:# 设置一个可点击打开的展开区域with st.expander("🤓国内可访问的openai账号"):st.write("""1. 如果使用默认地址,可以使用openai官网账号(需科学上网🥵).2. 如果你没有openai官网账号,可以联系博主免费试用国内openai节点账号🥳.""")# 本地图片无法直接加载,需先将图片读取加载为bytes流,然后才能正常在streamlit中显示image_path = r"C:\Users\Administrator\langchain_chatbot\wechat.jpg"image = Image.open(image_path)image_bytes = io.BytesIO()image.save(image_bytes, format='JPEG')st.image(image_bytes, caption='AI小万老师的微信', use_column_width=True)# 初始化聊天记录
if "messages" not in st.session_state:st.session_state.messages = []st.session_state.memory = ConversationBufferMemory(memory_key='chat_history')# 展示聊天记录
for message in st.session_state.messages:if message["role"] == "user":with st.chat_message(message["role"], avatar='☺️'):st.markdown(message["content"])else:with st.chat_message(message["role"], avatar='🤖'):st.markdown(message["content"])# 用于用户输入
if prompt := st.chat_input('我们来聊一点旅游相关的事儿吧'):with st.chat_message('user', avatar='☺️'):st.markdown(prompt)st.session_state.messages.append({'role': 'user', 'content': prompt})chain = build_chain(st.session_state.memory)answer = generate_response(chain, prompt)response = answer['text']with st.chat_message('assistant', avatar='🤖'):st.markdown(response)st.session_state.messages.append({'role': 'assistant', 'content': response})

        chain.py

  

import osimport streamlit as stfrom langchain_openai import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChainfrom template import BASIC_TEMPLATEdef build_chain(memory):llm = OpenAI(temperature=0,# openai_api_key=os.getenv("OPENAI_API_KEY"),openai_api_key=st.secrets['api']['key'],# base_url=os.getenv("OPENAI_BASE_URL")base_url=st.secrets['api']['base_url'])prompt = PromptTemplate.from_template(BASIC_TEMPLATE)conversation = LLMChain(llm=llm,prompt=prompt,verbose=True,memory=memory,)return conversationdef generate_response(chain,input_text):response = chain.invoke(input_text)return  response

        

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

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

相关文章

js逆向-某东验证码逆向分析

声明 本文仅供学习参考,如有侵权可私信本人删除,请勿用于其他途径,违者后果自负! 如果觉得文章对你有所帮助,可以给博主点击关注和收藏哦! 插句个人内容:本人最近正在找工作,工作城…

基于SpringBoot的美妆管理系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式 🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 &…

CS50x 2024 - Lecture 1 - C

本周学习C语言,重点是函数、变量、条件语句和循环。 05:11介绍了编程语言的转换过程,从源代码到机器码,以及编译器的作用。 编译器是将一种语言翻译成另一种语言的程序 09:18使用CS50.dev进行编程,介绍了VS Code和命令行界面的…

LeetCode Python - 1.两数之和

文章目录 题目答案运行结果 题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能…

数据结构——算法的时间复杂度和空间复杂度

1、算法效率 1.1如何衡量一个算法的好坏&#xff1f; 比如我们最熟悉的斐波那契数列 long long Fib(int N) {if(N < 3)return 1;return Fib(N-1) Fib(N-2); } 上面的斐波那契数列使用递归实现&#xff0c;看起来非常的简洁&#xff0c;那么代码一定是越简洁越好么&…

Python CSV文件读取和写入

本文主要为Python 实现CSV文件读取和写入操作。 CSV文件写入和读取 因为没有现成的csv文件&#xff0c;所以csv的顺序为先写入后读取。 写入 创建csv文件并把数据写入&#xff0c;有两种实现方式&#xff1a;直接插入所有行和插入单行。 示例如下&#xff1a; import csv i…

pycharm 配置 conda 新环境

1. conda 创建新环境 本章利用pycharm将conda新建的环境载入进去 关于conda的下载参考上一章博文&#xff1a;深度学习环境配置&#xff1a;Anaconda 安装和 pip 源 首先利用conda 新建虚拟环境 这里按 y 确定 安装好如下&#xff1a;这里两行命令代表怎么激活和关闭新建的虚…

顺序表、链表相关OJ题(2)

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; 一、旋转数组&#xff08;力扣&#xff09; 经典算法OJ题&#xff1a;旋转数组 思路1&#xff1a;每次挪动1位&#xff0c;右旋k次 时间复杂度&#xff1a;o(N^2) 右旋最好情况&#xff1a;k是n的倍数…

Verilog刷题笔记21

题目&#xff1a; A priority encoder is a combinational circuit that, when given an input bit vector, outputs the position of the first 1 bit in the vector. For example, a 8-bit priority encoder given the input 8’b10010000 would output 3’d4, because bit[4…

幻方(Magic Square)

幻方&#xff08;Magic Square&#xff09; 幻方概述 什么是幻方呢&#xff1f;幻方&#xff08;Magic Square&#xff09;就是指在nn&#xff08;n行n列&#xff09;的方格里填上一些连续的数字&#xff0c;使任意一行、任意一列和对角线上的数字的和都相等。例如有33的3行3…

【PyQt】06-.ui文件转.py文件

文章目录 前言方法一、基本脚本查看自己的uic安装目录 方法二、添加到扩展工具里面&#xff08;失败了&#xff09;方法二的成功步骤总结 前言 方法一、基本脚本 将Qt Designer&#xff08;一种图形用户界面设计工具&#xff09;生成的.ui文件转换为Python代码的脚本。 pytho…

【大模型上下文长度扩展】LongLoRA:长序列大模型微调新方式

LongLoRA&#xff1a;长序列大模型微调新方式 核心问题子问题1: 上下文窗口限制子问题2: 计算资源限制子问题3: 高效微调方法的缺乏低秩权重更新&#xff08;LoRA&#xff09;S2-Attn&#xff08;Shifted Sparse Attention&#xff09; 分析不足 扩展大模型处理长上下文能力不同…

Netty核心原理与基础实战(二)——详解Bootstrap

接上篇&#xff1a;Netty核心原理与基础实战&#xff08;一&#xff09; 1 Bootstrap基础概念 Bootstrap类是Netty提供的一个便利的工厂类&#xff0c;可以通过它来完成Netty的客户端或服务端的Netty组件的组装&#xff0c;以及Netty程序的初始化和启动执行。Netty的官方解释是…

【数据结构与算法】二叉树(Binary Tree)

相关推荐&#xff1a;堆&#xff08;Heap&#xff09; / 堆排序&#xff08;HeapSort&#xff09; / TopK 文章目录 1.树1.1 树相关概念1.2 举例树的应用 2. 二叉树2.1 二叉树分类2.2 特殊的二叉树2.3 二叉树的存储结构 3. 二叉树实现与热门问题 1.树 树是一种非线性的数据结构…

力扣:42. 接雨水 84.柱状图中最大的矩形(单调栈,双指针)

这两道题解题思路类似&#xff0c;一个是单调递增栈&#xff0c;一个是单调递减栈。本篇博客给出暴力&#xff0c;双指针和单调栈解法。 42. 接雨水 题目&#xff1a; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后…

AMD64 linux 环境中,如何将main.go打包成不带 .exe 的可执行文件?

在终端中先进入main.go所在的文件夹&#xff0c;然后运行这三条命令即可 $env:GOOS"linux" $env:GOARCH"amd64" go build main.go 最终结果&#xff0c;成功出现不带 .exe 结尾的可执行包&#xff1a;

日本失去的三十年:去杠杆用了14年

去年以来&#xff0c;日股在日本央行转鹰预期、基本面改善和一系列监管新规的催化下高歌猛进&#xff0c;日经指数已经逼近90年代资产泡沫时期的高位。今年迄今累计上涨8.51%&#xff0c;领跑全球&#xff0c;“失落的三十年”似乎已经远去。 日本因何走向衰退&#xff1f;“失…

【C语言】位与移位操作符详解

目录 1.⼆进制和进制转换 ①十进制&#xff1a;生活中最常用 ②二进制&#xff1a;计算机中使用的&#xff0c;每个数字称为一个比特 ③八进制、十六进制也如上 ④二进制转十进制 ⑤十进制转二进制 ⑥二进制转八进制 ⑦二进制转十六进制 2.原码、反码、补码 3.移位操…

【已更新】2024美赛C题代码教学思路数据处理数学建模分析Momentum in Tennis

问题一完整的代码已给出&#xff0c;预计2号晚上或者3号凌晨全部给出。 代码逻辑如下&#xff1a; C题第一问要求我们开发一个模型&#xff0c;捕捉得分时的比赛流程&#xff0c;并将其应用于一场或多场比赛。你的模型应该确定哪名球员在比赛的特定时间表现得更好&#xff0c;…

AI-数学-高中-24-三角函数一般形式的各参数含义

原作者视频&#xff1a;三角函数】12三角函数一般形式的各参数含义&#xff08;易&#xff09;_哔哩哔哩_bilibili 1.函数中的A标识符&#xff1a;表示曲线中间平衡位置的振幅&#xff0c;值域为正负A&#xff1a;[-A,A]。 2.函数中的B标识符&#xff1a;决定曲线纵向上下平移…