import streamlit as st
import os#操作系统模块
from openai import OpenAI
import json#用json数据包保存会话记录
from datetime import datetime#引入时间库 作为每一个会话记录的名称
#配置页面
st.set_page_config(
page_title="雷霆AI",
page_icon="🎵",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'Get Help': 'https://github.com/lyrumu',
'About': "一个基于streamlit的AI糖糖伴侣!"
}
)
#保存会话记录函数
def save_session():
if st.session_state.cur_session:
session_data = {
"lover": st.session_state.lover,
"character": st.session_state.character,
"cur_session": st.session_state.cur_session,
"messages": st.session_state.messages
}
# 创建文件夹
if not os.path.exists("sessions"):
os.mkdir("sessions")
with open(f"sessions/{st.session_state.cur_session}.json", "w", encoding="utf-8") as f:
json.dump(session_data,f, ensure_ascii=False, indent=2)
#生成文件名函数
def generate_session_id():
return datetime.now().strftime("%Y-%m-%d_%H_%M_%S")#注意不要用冒号 文件名不能包含冒号
#加载所有会话列表信息
def load_sessions():
session_list=[]
if os.path.exists("sessions"):
file_list=os.listdir("sessions")#列出目录所有文件
for file_name in file_list:
if file_name.endswith(".json"):
session_list.append(file_name[:-5])#“.json”不需要 所以用一个切片
session_list.sort(reverse=True)#将最新会话记录显示在会话列表最上方
return session_list
#加载指定会话信息
def load_session(session_name):
try:
if os.path.exists(f"sessions/{session_name}.json"):
with open(f"sessions/{session_name}.json","r",encoding="utf-8") as f:
session_data = json.load(f)
st.session_state.lover = session_data["lover"]
st.session_state.character = session_data["character"]
st.session_state.messages = session_data["messages"]
st.session_state.cur_session = session_name
except Exception as e:
st.error("加载失败了哦",e)
#删除指定会话
def delete_session(session_name):
try:
if os.path.exists(f"sessions/{session_name}.json"):
os.remove(f"sessions/{session_name}.json")
if session_name==st.session_state.cur_session:
st.session_state.cur_session=generate_session_id()
st.session_state.messages=[]
except Exception as e:
st.error("删除失败了哦",e)
#保存当前会话记录名称
if "cur_session" not in st.session_state:
cur_time = generate_session_id()#获取当前系统准确时间
st.session_state.cur_session= cur_time
#保存侧边栏历史信息
#昵称
if "lover" not in st.session_state:
st.session_state.lover = "你的伴侣"
#性格
if "character" not in st.session_state:
st.session_state.character = "温柔"
#侧边栏
#想让元素出现在侧边栏 要么每个都加上.sidebar 要么用with
with (st.sidebar):
st.header("伴侣控制面板")
#新建会话按钮
if st.button("新建聊天",width="stretch",icon="🥚"):
#保存当前会话
save_session()
#创建新的会话
if st.session_state.messages:#只有当前已经进行了会话 点击按钮才会创建新的空会话 更合理
st.session_state.messages=[]#每次会话都从空文件开始 昵称和性格不管也没事
st.session_state.cur_session=generate_session_id()
save_session()
#显示会话列表
st.text("聊天记录")
session_list=load_sessions()
for log in session_list:
col1,col2=st.columns([4,1])#将会话记录按钮和删除按钮按一定比例展示在同一行
with col1:
if st.button(log,width="stretch",icon="📖",type="primary" if log==st.session_state.cur_session else "secondary"):#进入所要加载的会话
load_session(log)
st.rerun()
with col2:
if st.button("",width="stretch",icon="❌️",key=f"{log}"):#删除所选的会话
#如果不加key 每个删除会话按钮都是同一个label 会报错 所以加key确定唯一性是必要的
delete_session(log)
st.rerun()
#分割线
st.divider()
#伴侣信息
nickname = st.text_input("昵称", placeholder="写上你希望的伴侣名字")
if nickname:
st.session_state.lover = nickname
like=st.text_area("性格",placeholder="写上你期望ta的性格")
if like:
st.session_state.character = like
#页面标题
st.title("雷霆AI")
st.logo("resources/logo.png")
#系统提示词 待输入
system_prompt = f"""
你叫{st.session_state.lover},现在是用户的真实伴侣,请完全代入伴侣角色。
规则:
1. 每次只回1条消息
2. 禁止任何场景或状态描述性文字
3. 匹配用户的语言
4. 回复简短,像微信聊天一样
5. 有需要的话可以用❤️💓等emoji表情
6. 用符合伴侣性格的方式对话
7. 回复的内容,要充分体现伴侣的性格特征
伴侣性格:
{st.session_state.character}
你必须严格遵守上述规则来回复用户。
"""
#用于定义提示词消息输入框
prompt=st.chat_input("问些雷霆问题吧~")
#初始化历史聊天信息的保存
if 'messages' not in st.session_state:#messages存储当前会话的所有聊天记录
st.session_state.messages=[]
st.text(f"当前会话:{st.session_state.cur_session}")
#展示历史聊天信息
#每次重新输入提示词 整个代码都会重头跑一遍 所以每次遍历更新后的列表打印出来即可
for message in st.session_state.messages:
st.chat_message(message["role"]).write(message["content"])
# if message["role"]=="user":
# st.chat_message("user",avatar="🫦").write(message["content"])
# elif message["role"]=="assistant":
# st.chat_message("assistant",avatar="💩").write(message["content"])
if prompt:
#chat_message()用于显示输入输出的信息
st.chat_message("user",avatar="🫦").write(prompt)
#这里只用一个图标 多个会无法渲染而报错的
print("---->调用大模型,提示词为:",prompt)#这个不会显示在网页 而是显示在终端 提供日志来调试
#保存用户提示词
st.session_state.messages.append({"role": "user", "content":prompt})
#接下来调用AI大模型
client = OpenAI(
api_key=os.environ.get('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com")
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
#历史聊天记录此时已经包含最新输入的提示词
#历史聊天记录是一个列表 每一个元素是字典 因此解包即可
#就解决了会话记忆的问题
*st.session_state.messages,
],
stream=True#流式输出
)
#以下为非流式输出的输出代码
# print("<-----大模型返回结果:",response.choices[0].message.content)#这里也是为了调试 提供日志
# st.chat_message("assistant",avatar="💩").write(response.choices[0].message.content)
# #只写到这里的话 每次对话都会覆盖上一次的
# st.session_state.messages.append({"role": "assistant", "content":response.choices[0].message.content})
response_message=st.empty()#每次创建一个空对象 用来保存大模型返回的完整结果 以便流式输出
full_response = ""
for chunk in response:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
response_message.chat_message("assistant",avatar="💩").write(full_response)#完成流式输出
st.session_state.messages.append({"role": "assistant", "content": full_response})
#每次大模型回复 也需要保存会话
save_session()