【橘子大模型】使用streamlit来构建自己的聊天机器人(下)

一、简介

我们之前完成了一个简易的聊天机器人,但是还留下了一些问题没有解决,比如如何开启新的会话。如何切换session_id,如何把对话做成流式的输出。这些我们就会在今天来完成。

二、关于新的会话和session_id

from dotenv import load_dotenv
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ElasticsearchChatMessageHistoryfrom streamlit import streamlit as st# load env file
load = load_dotenv("./.env")# 无密码的elasticsearch配置
es_url = "http://localhost:9200"
# 存储的索引,我们不用预先创建索引,因为说实话我也不知道字段,langchain会创建,并且自动映射字段
index_name = "chat_history"# init llm
llm = ChatOllama(base_url = "http://127.0.0.1:11434",model = "huihui_ai/deepseek-r1-abliterated:14b",temperature = 0.5,num_predict = 10000
)# 构建ElasticsearchChatMessageHistory
def get_session_history(session_id: str) :return  ElasticsearchChatMessageHistory(index=index_name,session_id=session_id,es_url=es_url,ensure_ascii=False)session_id = "levi"
st.title("你好,这里是橘子GPT,我是小橘")
st.write("请把您的问题输入,小橘会认真回答的哦。")
# 添加一个输入框,用户自己输入来替代默认的
session_id = st.text_input("请输入一个session_id,否则我们将使用默认值levi",session_id)
# 添加一个按钮,触发按钮开启新的会话,并且删除上下文记录
isClickButton:bool = st.button("点击按钮,开启新的对话")
# query input
user_prompt = st.chat_input("我是小橘,请输入你的问题吧")if isClickButton:# 清除es中存储的上下文记录,这个是物理删除delete_by_queryget_session_history(session_id).clear()# 清除当前会话的上下文st.session_state.chat_history = []# 如果没有就创建,不要每次都建立新的
if 'chat_history' not in st.session_state:st.session_state.chat_history = []
# 遍历里面的内容,取出来存进去的每一组role 和 content
for message in st.session_state.chat_history :# 通过取出来的信息,构建st.chat_message,不同的角色会有不同的ui样式,这里就是做这个的with st.chat_message(message['role']):# 把内容展示出来st.markdown(message['content'])template = ChatPromptTemplate.from_messages([('human',"{prompt}"),('placeholder',"{history}")
])
chain = template | llm | StrOutputParser()
chain_with_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="prompt",history_messages_key="history",
)if user_prompt :response = chain_with_history.invoke({"prompt": user_prompt},config={"configurable": {"session_id": session_id}})# 保存历史,上面用来遍历显示,避免后面覆盖前面的显示st.session_state.chat_history.append({'role':'user','content':user_prompt})with st.chat_message('user'):st.markdown(user_prompt)# 保存历史,上面用来遍历显示,避免后面覆盖前面的显示st.session_state.chat_history.append({'role':'assistant','content':response})with st.chat_message('assistant'):st.markdown(response)

这样就实现了需求了,可以直接跑一下看看。

三、关于流式对话

流式对话其实说白了就是回复的时候一个字一个字的显示,表现为流的样子,而不是一次性返回一大堆。

from dotenv import load_dotenv
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ElasticsearchChatMessageHistoryfrom streamlit import streamlit as st# load env file
load = load_dotenv("./.env")# 无密码的elasticsearch配置
es_url = "http://localhost:9200"
# 存储的索引,我们不用预先创建索引,因为说实话我也不知道字段,langchain会创建,并且自动映射字段
index_name = "chat_history"
# streamlit code
session_id = "levi"# init llm
llm = ChatOllama(base_url = "http://127.0.0.1:11434",model = "huihui_ai/deepseek-r1-abliterated:14b",temperature = 0.5,num_predict = 10000
)
# 构建ElasticsearchChatMessageHistory
def get_session_history(session_id: str) :return ElasticsearchChatMessageHistory(index=index_name,session_id=session_id,es_url=es_url,ensure_ascii=False)st.title("你好,这里是橘子GPT,我是小橘")
st.write("请把您的问题输入,小橘会认真回答的哦。")
# 设置一个输入框,用户输入的内容来替代默认的session_id。
session_id = st.text_input("请输入一个session_id,否则我们将使用默认值levi",session_id)
# 添加一个按钮,点击按钮的时候清空历史会话
isClickButton:bool = st.button("点击按钮,开启新的对话")
if isClickButton:# 注意这一行会去调用es客户端delete_by_query物理删除es中的上下文数据,最好自己定制,不要直接用get_session_history(session_id).clear()# 清除当前窗口的上下文记录st.session_state.chat_history = []# query input
user_prompt = st.chat_input("我是小橘,请输入你的问题吧")# 构建langchain执行
def invoke_history_by_stream(chain,prompt,session_id):chain_with_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key = prompt,history_messages_key="history",)# 以stream流的方式执行responseStream = chain_with_history.stream({"prompt": prompt},config={"configurable": {"session_id": session_id}})# 遍历返回,逐个回复for response in responseStream:yield response# 如果没有就创建,不要每次都建立新的
if 'chat_history' not in st.session_state:st.session_state.chat_history = []
# 遍历里面的内容,取出来存进去的每一组role 和 content
for message in st.session_state.chat_history :# 通过取出来的信息,构建st.chat_message,不同的角色会有不同的ui样式,这里就是做这个的with st.chat_message(message['role']):# 把内容展示出来st.markdown(message['content'])template = ChatPromptTemplate.from_messages([('human',"{prompt}"),('placeholder',"{history}")
])
chain = template | llm | StrOutputParser()# 如果感知到输入
if user_prompt :# 保存历史,上面用来遍历显示,避免后面覆盖前面的显示st.session_state.chat_history.append({'role':'user','content':user_prompt})with st.chat_message('user'):st.markdown(user_prompt)with st.chat_message('assistant'):# 以流式的方式渲染答案streamResp = st.write_stream(invoke_history_by_stream(chain,user_prompt,session_id))# 保存历史,上面用来遍历显示,避免后面覆盖前面的显示st.session_state.chat_history.append({'role': 'assistant', 'content': streamResp})

这就是流的代码,可以执行一下。

四、优化一版

我们再来优化一下样式。这个可以有可以不做,最好交给前端来做,streamlit的ui太生硬了。加了几个模板占位的替换。

from dotenv import load_dotenv
from langchain_community.chat_message_histories import ElasticsearchChatMessageHistory
from langchain_ollama import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import MessagesPlaceholder, ChatPromptTemplatefrom streamlit import streamlit as stload = load_dotenv("./.env")
es_url = "http://localhost:9200"
index_name = "chat_history"
def get_session_history(session_id: str) :return ElasticsearchChatMessageHistory(index=index_name,session_id=session_id,es_url=es_url,ensure_ascii=False)
# init llm
llm = ChatOllama(base_url = "http://127.0.0.1:11434",model = "huihui_ai/deepseek-r1-abliterated:14b",temperature = 0.5,num_predict = 10000
)user_id = "levi"with st.sidebar:st.image("./orange.png", width=150)user_id = st.text_input("输入你的id", user_id)role = st.radio("你想获得什么级别的回答呢?", ["初学者", "专家", "大佬"], index=0)if st.button("清空历史上下文,开启新的对话"):st.session_state.chat_history = []get_session_history(user_id).clear()st.markdown("""<div style='display: flex; height: 70vh; justify-content: center; align-items: center;'><h2>请问你需要什么帮助呢?</h2></div>""",unsafe_allow_html=True
)if 'chat_history' not in st.session_state:st.session_state.chat_history = []for message in st.session_state.chat_history:with st.chat_message(message['role']):st.markdown(message['content'])template = ChatPromptTemplate.from_messages([MessagesPlaceholder(variable_name="history"),('system', f"你作为一个 {role} 级别的人来回答这个问题"),('human', "{prompt}")
])chain = template | llm | StrOutputParser()def invoke_history(chain, session_id, prompt):history = RunnableWithMessageHistory(chain, get_session_history,input_messages_key="prompt",history_messages_key="history")for response in history.stream({"prompt": prompt},config={"configurable": {"session_id": session_id}}):yield responseprompt = st.chat_input("输入你的问题,小橘会为你回答。")if prompt:st.session_state.chat_history.append({'role': 'user', "content": prompt})with st.chat_message('user'):st.markdown(prompt)with st.chat_message('assistant'):streamResponse = st.write_stream(invoke_history(chain, user_id, prompt))st.session_state.chat_history.append({'role': 'assistant', "content": streamResponse})

在这里插入图片描述

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

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

相关文章

php-cgi参数注入攻击经历浅谈

起因&#xff1a; 阿里云服务器再次警告出现挖矿程序。上一次服务器被攻击后&#xff0c;怕有恶意程序残留&#xff0c;第一时间重装了系统&#xff0c;也没有详查攻击入口。不过事后还是做了一些防范&#xff0c;这台留作公网访问的服务器上并未保留业务数据&#xff0c;只作…

自动驾驶中的实时挑战:如何优化车辆动力学模型

自动驾驶中的实时优化:自行车模型与双轨模型的计算复杂度权衡 在自动驾驶领域,车辆动力学建模是实现精准控制和路径规划的关键。自行车模型和双轨模型作为两种主流的建模方法,在实时性需求下如何平衡计算复杂度与精确度,是工程师们必须面对的挑战。本文将深入探讨这两种模…

Hybrid 架构的概念,以及如何优化Hybrid 通信方案,提升页面加载速度和渲染性能

1. 什么是 Hybrid 架构&#xff1f; Hybrid&#xff08;混合&#xff09;架构是指 结合 Web 技术和 Native&#xff08;原生&#xff09;技术 的移动应用开发模式&#xff0c;通常由以下部分组成&#xff1a; Web 部分&#xff1a;使用 HTML、CSS、JavaScript&#xff08;或前…

关于类模板STL中vector容器的运用和智能指针的实现

代码题&#xff1a;使用vector实现一个简单的本地注册登录系统 注册&#xff1a;将账号密码存入vector里面&#xff0c;注意防重复判断 登录&#xff1a;判断登录的账号密码是否正确 #include <iostream> #include <cstring> #include <cstdlib> #in…

OpenCV 从入门到精通(day_04)

1. 绘制图像轮廓 1.1 什么是轮廓 轮廓是一系列相连的点组成的曲线&#xff0c;代表了物体的基本外形。相对于边缘&#xff0c;轮廓是连续的&#xff0c;边缘不一定连续&#xff0c;如下图所示。其实边缘主要是作为图像的特征使用&#xff0c;比如可以用边缘特征可以区分脸和手…

Python错误分析与调试

在Python编程的过程中&#xff0c;我们难免会遇到各种各样的错误&#xff0c;而有效地分析和调试这些错误&#xff0c;能让我们的代码快速恢复正常运行&#xff0c;今天就来和大家聊聊Python中错误分析与调试的相关内容。 错误分析 Python中的错误大致可以分为语法错误和逻…

Browser-use:基于 Python 的智能浏览器自动化 AI 工具调研与实战

Browser-use&#xff1a;基于 Python 的智能浏览器自动化 AI 工具调研与实战 一、概述 Browser-use 是一个旨在将 AI “智能体”&#xff08;Agents&#xff09;与真实浏览器进行交互的 Python 库&#xff0c;可以轻松实现浏览器自动化。在配合 LLM&#xff08;如 GPT 系列&a…

网络空间安全(51)邮件函数漏洞

前言 邮件函数漏洞&#xff0c;特别是在PHP环境中使用mail()函数时&#xff0c;是一个重要的安全问题。 一、概述 在PHP中&#xff0c;mail()函数是一个用于发送电子邮件的内置函数。其函数原型为&#xff1a; bool mail ( string $to , string $subject , string $message [, …

LLaMA-Factory 数据集成从入门到精通

一、框架概述 LLaMA-Factory 框架通过Alpaca/Sharegpt双格式体系实现多任务适配&#xff0c;其中Alpaca专注结构化指令微调&#xff08;含SFT/DPO/预训练&#xff09;&#xff0c;Sharegpt支持多角色对话及多模态数据集成。核心配置依托 dataset_info.json 实现数据源映射、格…

如何根据设计稿进行移动端适配:全面详解

如何根据设计稿进行移动端适配&#xff1a;全面详解 文章目录 如何根据设计稿进行移动端适配&#xff1a;全面详解1. **理解设计稿**1.1 设计稿的尺寸1.2 设计稿的单位 2. **移动端适配的核心技术**2.1 使用 viewport 元标签2.1.1 代码示例2.1.2 参数说明 2.2 使用相对单位2.2.…

07-Spring Boot 自动配置原理全解析

Spring Boot 自动配置原理全解析&#xff08;EnableAutoConfiguration 源码追踪&#xff09; Spring Boot 之所以能大幅简化配置&#xff0c;核心就在于它的 自动配置机制&#xff0c;而这一机制背后主要依赖于 EnableAutoConfiguration 注解。本文将从使用、源码、常见问题及…

前端如何检测项目中新版本的发布?

前言 你是否也曾遇到过这种情况&#xff0c;每次发完版之后都还会有用户反馈问题没有被修复&#xff0c;一顿排查之后发现他用的还是旧的版本。 用户&#xff1a;在 XX 页面 XX 字段还是不展示 我&#xff1a;刷新下页面 用户&#xff1a;刷新了啊 我&#xff1a;强刷一下&…

Vue 项目使用 pdf.js 及 Elasticpdf 教程

摘要&#xff1a;本文章介绍如何在 Vue 中使用 pdf.js 及基于 pdf.js 的批注开发包 Elasticpdf。简单 5 步可完成集成部署&#xff0c;包括数据的云端同步&#xff0c;示例代码完善且简单&#xff0c;文末有集成代码分享。 1. 工具库介绍与 Demo 1.1 代码包结构 ElasticPDF基…

聊聊Spring AI的ChromaVectorStore

序 本文主要研究一下Spring AI的ChromaVectorStore 示例 pom.xml <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-chroma</artifactId></dependency>配置 spring:ai:vectorstore:…

整数编码 - 华为OD统一考试(A卷、Java)

题目描述 实现一种整数编码方法,使得待编码的数字越小,编码后所占用的字节数越小。 编码规则如下: 编码时7位一组,每个字节的低7位用于存储待编码数字的补码。字节的最高位表示后续是否还有字节,置1表示后面还有更多的字节,置0表示当前字节为最后一个字节。采用小端序编…

Linux 递归查找并删除目录下的文件

在 Linux 中&#xff0c;可以使用 find 命令递归查找并删除目录下的文件 1、示例命令 find /path/to/directory -type f -name "filename_pattern" -exec rm -f {} 2、参数说明 /path/to/directory&#xff1a;要查找的目标目录type f&#xff1a;表示查找文件&am…

【笔记】VS中C#类库项目引用另一个类库项目的方法

VS中C#类库项目引用另一个类库项目的方法 在 C# 开发中&#xff0c;有时我们需要在一个类库项目中引用另一个类库项目&#xff0c;但另一个项目可能尚未编译成 DLL。在这种情况下&#xff0c;我们仍然可以通过 Visual Studio 提供的项目引用功能进行依赖管理。 &#x1f3af; …

第五讲(下)| string类的模拟实现

string类的模拟实现 一、Member constants&#xff08;成员常数&#xff09;npos 二、Member functions&#xff08;成员函数&#xff09;constructor&#xff08;构造&#xff09;、destructor&#xff08;析构&#xff09;、c_str遍历1 &#xff1a;Iterators遍历2&#xff1…

洛谷题单3-P4956 [COCI 2017 2018 #6] Davor-python-流程图重构

题目描述 在征服南极之后&#xff0c;Davor 开始了一项新的挑战。下一步是在西伯利亚、格林兰、挪威的北极圈远征。 他将在 2018 年 12 月 31 日开始出发&#xff0c;在这之前需要一共筹集 n 元钱。 他打算在每个星期一筹集 x 元&#xff0c;星期二筹集 xk 元&#xff0c;……

【正点原子】如何设置 ATK-DLMP135 开发板 eth0 的开机默认 IP 地址

开机就想让 eth0 乖乖用静态 IP&#xff1f;别再被 DHCP 抢走地址了&#xff01; 三步教你彻底掌控 ATK-DLMP135 的网络启动配置&#xff0c;简单粗暴&#xff0c;实测有效&#xff01; 正点原子STM32MP135开发板Linux核心板嵌入式ARM双千兆以太网CAN 1. 删除 dhcpcd 自动获取…