1.功能扩充(ai_partner_2):实现了一个流式输出(打字机效果)的 AI 对话回复,并将最终的完整回复保存到会话历史中。
代码:
python
import streamlit as st
import os
from openai import OpenAI
st.set_page_config(
page_title="AI智能伴侣",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded",
menu_items={}
)
st.title("AI智能伴侣")
st.logo("py_project02/resources/logo.png")
system_prompt = "你是一个非常可爱的AI助理,你的名字叫做小甜甜,请你用温柔可爱的语气回答用户的问题"
if "messages" not in st.session_state:
st.session_state.messages = []
for message in st.session_state.messages:
st.chat_message(message["role"]).write(message["content"])
client = OpenAI(api_key=os.environ.get("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")
prompt = st.chat_input("请输入您要问的问题")
if prompt:
st.chat_message("user").write(prompt)
st.session_state.messages.append({"role":"user","content":prompt})
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role":"system","content":system_prompt},
{"role":"user","content":prompt},
],
stream=True
)
# st.chat_message("assistant").write(response.choices[0].message.content)
response_message = st.empty()
full_response = ""
for chunk in response:
if chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
full_response += content
response_message.chat_message("assistant").write(full_response)
st.session_state.messages.append({"role":"assistant","content":full_response})
(1).流式输出:stream=True
(2).*st.session_state.messages
- 历史记录(上下文) :这是 Python 的解包语法 (
*)。 st.session_state.messages是一个列表,里面存着之前的对话(比如[{"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好!"}])。- 加上
*号,相当于把这个列表里的每一条对话都摊开 ,塞进messages这个大列表里。 - 作用 :这让 AI 知道刚才聊了什么,从而实现多轮对话(比如你问"它多少钱?",AI 知道"它"指的是上一句提到的商品)。
(3).response_message = st.empty()
这行代码的作用是创建一个"空容器"或"占位符",并将其赋值给变量response_message。你可以把它想象成在页面上预留了一个空的"坑位" 。这个坑位当前是空的,但你可以随时往里面填东西(比如文字、图表),而且更新这个坑位里的内容时,页面其他部分不会闪烁或重绘response_message = st.emty()通常用于实现流式输出(打字机效果)。 response_message = st.empty() 的意思就是:"我在页面上准备了一个叫 response_message 的空白黑板,稍后我要在这个黑板上不停地擦除和重写,而不需要把整个教室(页面)都拆了重装。"
Streamlit 的默认机制是:只要用户有交互,整个页面就会重新运行并刷新。但 st.empty() 允许你打破这个规则,只更新页面的某一小块区域。
st.empty(): 在页面上挖一个坑,返回一个占位符对象。response_message: 给这个占位符起个名字,方便后续操作。
(4).full_response = ""
- 这就像是一个空的"笔记本"(变量)。
- 用来在后台把 AI 吐出来的每一个字拼凑起来,存成一句完整的话。
(5).for chunk in response:
- DeepSeek 或 OpenAI 的流式接口(
stream=True)不会一次性给你整段话,而是像挤牙膏一样,一段一段(chunk)地发给你。 - 这个循环就是**"一直听着 AI 说话,直到它说完"**。
(6).if chunk.choices[0].delta.content is not None:
- 这是一个安全检查。有时候 AI 发过来的数据包里可能没有文字(比如只是连接信号),这行代码确保只有当真的有文字内容时才处理。
(7).full_response += content
- 拼积木 :把刚收到的这一小段字,粘到后台那个"笔记本"(
full_response)的后面。
(8).response_message.chat_message("assistant").write(full_response)
- 刷黑板 :把拼好的完整句子(
full_response)写到刚才那个"黑板"(response_message)上。 - 关键点 :每次写入都会覆盖掉黑板上旧的内容。因为速度很快,用户看起来就像是文字在原地不断变长。
(9).st.session_state_message.append({"role": "assistant", "content": fill_response}) 存档记忆
- 当循环结束(AI 说完了),此时
full_response里已经是完整的回答了。 - 这行代码把这段完整的回答打包成
{"role": "assistant", "content": "..."}的格式,存进st.session_state这个"长期记忆库"里。 - 为什么这么做? 如果不存进去,等你下一次跟 AI 说话时,它就不记得自己刚才说过什么了(因为它本质上是无状态的)。
2.功能扩充(ai_partner_3):实现了动态的角色扮演设定。它不再是使用固定的系统提示词,而是根据用户在界面上选择的"昵称"和"性格",实时生成一段专属的指令发给 AI。
python
import streamlit as st
import os
from openai import OpenAI
st.set_page_config(
page_title="AI智能伴侣",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded",
menu_items={}
)
st.title("AI智能伴侣")
st.logo("py_project02/resources/logo.png")
system_prompt = """
你叫 %s,现在是用户的真实伴侣,请完全代入伴侣角色。
规则:
1. 每次只回1条消息
2. 禁止任何场景或状态描述性文字
3. 匹配用户的语言
4. 回复简短,像微信聊天一样
5. 有需要的话可以用❤️🌸等emoji表情
6. 用符合伴侣性格的方式对话
7. 回复的内容, 要充分体现伴侣的性格特征
伴侣性格:
- %s
你必须严格遵守上述规则来回复用户。
"""
if "messages" not in st.session_state:
st.session_state.messages = []
if "nick_name" not in st.session_state:
st.session_state.nick_name = "小甜甜"
if "nature" not in st.session_state:
st.session_state.nature = "活泼开朗的东北姑娘"
for message in st.session_state.messages:
st.chat_message(message["role"]).write(message["content"])
client = OpenAI(api_key=os.environ.get("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")
with st.sidebar:
st.subheader("伴侣信息")
# 昵称输入框
nick_name = st.text_input("昵称", placeholder="请输入昵称", value=st.session_state.nick_name)
if nick_name:
st.session_state.nick_name = nick_name
# 性格输入框
nature = st.text_area("性格",placeholder="请输入性格", value=st.session_state.nature)
if nature:
st.session_state.nature = nature
prompt = st.chat_input("请输入您要问的问题")
if prompt:
st.chat_message("user").write(prompt)
st.session_state.messages.append({"role":"user","content":prompt})
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt % (st.session_state.nick_name, st.session_state.nature)},
*st.session_state.messages
],
stream=True
)
# st.chat_message("assistant").write(response.choices[0].message.content)
response_message = st.empty()
full_response = ""
for chunk in response:
if chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
full_response += content
response_message.chat_message("assistant").write(full_response)
st.session_state.messages.append({"role":"assistant","content":full_response})
(1)system_prompt % (st.session_state,nick_name, st.session_state,nature)
- 动态系统指令 :这是对话的**"剧本"**。
system_prompt:这通常是一个包含占位符的字符串模板,比如"你叫%s,性格是%s,请保持这个角色..."。% (...):这是 Python 的字符串格式化 写法。它会把后面括号里的变量填入前面的%s位置。st.session_state.nick_name和st.session_state.nature:这是从 Streamlit 的会话状态中读取的用户设定。- 作用 :这意味着 AI 的人设不是写死的,而是活的。比如用户在侧边栏选了昵称"小美"和性格"傲娇",代码就会自动组装成:"你叫小美,性格是傲娇...",然后发给 AI。