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

【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

        

相关文章:

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

【LangchainStreamlit】打造一个旅游问答AI-CSDN博客 项目线上地址,无需openai秘钥可直接体验:http://101.33.225.241:8502/ github地址:GitHub - jerry1900/langchain_chatbot: langchainstreamlit打造的一个有memory的旅游聊天机器人&…...

〖大前端 - ES6篇②〗- let和const

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司…...

JAVA设计模式之代理模式详解

代理模式 1 代理模式介绍 在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式. 代理模式(Proxy Design Pattern ) 原始定义是:让你能够提供对象的替代…...

vivo发布2023 年度科技创新;阿里全新AI代理,可模拟人类操作手机

vivo 发布 2023 年度十大产品技术创新 近日,vivo 发布了「2023 年度科技创新」十大产品技术创新榜单,并将这些技术分为了 4 个板块。 「四大蓝科技」为 vivo 在去年推出的全新技术品牌,涵盖蓝晶芯片技术栈、蓝海续航系统、蓝心大模型、蓝河操…...

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏15(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言实现树倒下的效果拾取圆木砍树消耗卡路里斧头手臂穿模问题处理源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&…...

python巧用定理判断素数

目录 判断一个数n是否是素数 求一个数的素因数个数 求大于等于指定数的最小素数 在数论中有三个非常重要的关于素数的定理 1、任何数都可以表示成若干个素数的乘积 2、任意数的素因子一个大于根号n的自然数,另一个与其对应的因子则必小于根号n。 3、除了2和3以…...

2023年总结

人们总说时间会改变一切,但事实上你得自己来。 今年开始给自己的时间读书、工作、生活都加上一个2.0的release版本号,相比过去的一年还是有很多进步的。 就跟git commit一样,一步一步提交优化,年底了发个版本。用李笑来的话说&am…...

Git中为常用指令配置别名

目录 1 前言 2 具体操作 2.1 创建.bashrc文件 2.2 添加指令 2.3 使其生效 2.4 测试 1 前言 在Git中有一些常用指令比较长,当我们直接输入,不仅费时费力,还容易出错。这时候,如果能给其取个简短的别名,那么事情就…...

STM32内部Flash

目录 一、内部Flash简介 二、内部Flash构成 1. 主存储器 2. 系统存储区 3. 选项字节 三、内部Flash写入过程 1. 解锁 2. 页擦除 3. 写入数据 四、工程空间分布 某工程的ROM存储器分布映像: 1. 程序ROM的加载与执行空间 2. ROM空间分布表 一、内部Flash…...

html5 audio video

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 不可用: 可用: Google Chrome Close AutoUpdate-CSDN博客...

LoveWall v2.0Pro社区型校园表白墙源码

校园表白墙,一个接近于社区类型的表白墙,LoveWall。 源码特色; 点赞, 发评论, 发弹幕, 多校区, 分享页, 涉及违禁物等名词进行检测! 安装教程: 环境要求;…...

Flink cdc3.0动态变更表结构——源码解析

文章目录 前言源码解析1. 接收schema变更事件2. 发起schema变更请求3. schema变更请求具体处理4. 广播刷新事件并阻塞5. 处理FlushEvent6. 修改sink端schema 结尾 前言 上一篇Flink cdc3.0同步实例 介绍了最新的一些功能和问题,本篇来看下新功能之一的动态变更表结…...

WWW 2024 | 时间序列(Time Series)和时空数据(Spatial-Temporal)论文总结

WWW 2024已经放榜,本次会议共提交了2008篇文章,research tracks共录用约400多篇论文,录用率为20.2%。本次会议将于2024年5月13日-17日在新加坡举办。 本文总结了WWW 2024有关时间序列(Time Series)和时空数据&#xf…...

代码随想录算法——数组

目录 1、二分查找法 2、移除元素 3、有序数组的平方 4、长度最小的子数组 5、螺旋矩阵II 1、二分查找法 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在…...

Linux第45步_通过搭建“DNS服务器”学习图形化配置工具

学习的意义:通过搭建“DNS服务器”,来学习“图形化配置工具”。“DNS服务器”,我们用不到,但为后期移植linux系统服务,因为在移植系统时,需要用到这个“图形化配置工具”。 1、“menuconfig图形化配置工具…...

【Linux取经路】探寻shell的实现原理

文章目录 一、打印命令行提示符二、读取键盘输入的指令三、指令切割四、普通命令的执行五、内建指令执行5.1 cd指令5.2 export指令5.3 echo指令 六、结语 一、打印命令行提示符 const char* getusername() // 获取用户名 {return getenv("USER"); }const char* geth…...

【MATLAB】使用随机森林在回归预测任务中进行特征选择(深度学习的数据集处理)

1.随机森林在神经网络的应用 当使用随机森林进行特征选择时,算法能够为每个特征提供一个重要性得分,从而帮助识别对目标变量预测最具影响力的特征。这有助于简化模型并提高其泛化能力,减少过拟合的风险,并且可以加快模型训练和推理…...

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(六)NodeJS入门——http模块

047_http模块_获取请求行和请求头 hello,大家好,那第二节我们来介绍一下如何在这个服务当中来提取 HTT 请求报文的相关内容。首先先说一下关于报文的提取的方法,我在这个文档当中都已经记录好了,方便大家后续做一个快速的查阅。 …...

【数据结构与算法】(5)基础数据结构之队列 链表实现、环形数组实现详细代码示例讲解

目录 2.4 队列1) 概述2) 链表实现3) 环形数组实现 2.4 队列 1) 概述 计算机科学中,queue 是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头&#xf…...

(注解配置AOP)学习Spring的第十七天

基于注解配置的AOP 来看注解式开发 : 先把目标与通知放到Spring里管理 : Service("userService") public class UserServiceImpl implements UserService {Overridepublic void show1() {System.out.println("show1......");}Overridepublic void show2…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

windows系统MySQL安装文档

概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...