实战-AI智能伴侣-基本交互
实战-AI智能伴侣-基本交互
用 Streamlit + openai 库搭出能与大模型对话的网页:页面配置、会话状态初始化、消息渲染与发送
项目目标
做一个”AI 智能伴侣”网页:左边是控制面板(会话列表、伴侣昵称、性格),右边是聊天窗口,输入问题后大模型以伴侣的口吻流式回复。
做完整个章节后,页面长这样:

本篇是第一步:把基本交互跑通(对应课程代码 03. ai_partner_1.py)。后续依次叠加流式输出、会话记忆、会话管理。
整体骨架
1. 设置页面配置 st.set_page_config2. 展示标题和 Logo st.title / st.logo3. 初始化会话状态 st.session_state4. 渲染历史消息 for + st.chat_message5. 创建大模型客户端 OpenAI(...)6. 等待用户输入 st.chat_input7. 调用大模型并展示 client.chat.completions.create8. 把这一轮对话存入 session_state完整代码(基本交互版)
import streamlit as stimport osfrom openai import OpenAI
print("----------> 重新执行此文件 , 渲染展示页面")
# 设置页面的配置项st.set_page_config( page_title="AI智能伴侣", page_icon="🤖", layout="wide", initial_sidebar_state="expanded", menu_items={})
# 大标题st.title("AI智能伴侣")
# Logost.logo("resources/logo.png")
# 系统提示词system_prompt = "你是一名非常可爱的AI助理, 你的名字叫小甜甜, 请你使用温柔可爱的语气回答用户的问题"
# 初始化聊天信息if "messages" not in st.session_state: st.session_state.messages = []
# 展示聊天信息for message in st.session_state.messages: # {"role": "user", "content": prompt} st.chat_message(message["role"]).write(message["content"])
# 创建与AI大模型交互的客户端对象 (DEEPSEEK_API_KEY 环境变量的名字, 值就是DeepSeek的API_KEY的)client = OpenAI(api_key=os.environ.get('DEEPSEEK_API_KEY'), base_url="https://api.deepseek.com")
# 消息输入框prompt = st.chat_input("请输入您要问的问题")if prompt: # 字符串会自动转换为布尔值, 如果字符串非空, 则为True; ""否则为False st.chat_message("user").write(prompt) print("----------> 调用AI大模型, 提示词: ", prompt)
# 保存用户输入的提示词 st.session_state.messages.append({"role": "user", "content": prompt})
# 调用AI大模型 response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], stream=False )
# 输出大模型返回的结果 print("<---------- 大模型返回的结果: ", response.choices[0].message.content) st.chat_message("assistant").write(response.choices[0].message.content)
# 保存大模型返回的结果 st.session_state.messages.append({"role": "assistant", "content": response.choices[0].message.content})关键点拆解
1. 为什么第一句是 print 调试
print("----------> 重新执行此文件 , 渲染展示页面")因为 Streamlit 每次交互都会重新执行整个脚本(见 Streamlit入门)。这行 print 打在终端里,用来亲眼确认”脚本又跑了一遍”——理解这一点,才能理解后面为什么必须用 st.session_state。
2. 用 session_state 保存聊天记录
if "messages" not in st.session_state: st.session_state.messages = []| 写法 | 含义 |
|---|---|
if "messages" not in st.session_state | 只在第一次运行时初始化(字典里没有这个 key 才创建空列表) |
| 以后每次重跑 | 条件不成立,直接沿用已有的列表,历史消息不会丢 |
不能写成 st.session_state.messages = [](不加 if 判断)。那样每次重跑都会把聊天记录清空,界面会一直只剩当前这一句。
3. 渲染历史消息
for message in st.session_state.messages: st.chat_message(message["role"]).write(message["content"])messages 列表里的每个元素都带 role,直接把它传给 st.chat_message(),user 显示在右侧、assistant 显示在左侧,样式自动区分。
这个 messages 列表的格式({"role": ..., "content": ...})就是发给大模型 API 的格式,界面展示和接口调用用的是同一份数据,不需要单独维护两套。
4. 输入的判断
if prompt:st.chat_input() 在用户还没输入内容时返回 None,输入并回车后返回字符串。字符串在 if 中会自动转成布尔值:非空为 True,"" 为 False。所以 if prompt: 等价于”用户这次真的发了消息”。
5. 这一轮调用为什么只发了两个 message
messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt},]这一版只把当前这一句发给模型,所以此时 AI 还记不住上一轮(模型是无状态的)。下一步就要改造成”带上历史消息”,见 会话记忆。
相关
练习题
一、回忆填空(写完再展开对答案)
- 页面配置、大标题、Logo 分别是:
st.set_page_config/st.____/st.____ - 初始化聊天记录:
if "messages" not in st.____: st.session_state.messages = ____ - 渲染历史消息:
for message in st.session_state.messages: st.____(message["____"]).write(message["content"]) - 底部聊天输入框:
prompt = st.____("请输入您要问的问题") if prompt:能判断”用户这次真的发了消息”,是因为非空字符串转布尔值是 ____- 存下用户的问题:
st.session_state.messages.____({"role": "user", "content": prompt}) - 取模型回复:
response.____[0].____.content - 为什么必须用
st.session_state?因为 Streamlit 每次交互都会 ____ 整个脚本,普通变量会丢
填空答案(做完再点开)
- title、logo 2. session_state、
[]3. chat_message、role 4. chat_input 5. True 6. append 7. choices、message 8. 重新执行
二、裸写题
-
2-1 先把”记忆”跑起来 不接大模型:用两条假消息(一条 user、一条 assistant)初始化
st.session_state.messages,把它们渲染成聊天气泡;再放一个输入框,用户输入后追加到列表并回显。提示(先自己想,实在想不出再点开)一级 · 思路:页面每次重跑都要能”看到”历史 → 用 session_state;渲染就是遍历列表 二级 · 方法:
st.session_state/st.chat_message/st.chat_input三级 · 骨架:for message in ...: st.chat_message(message["role"]).write(message["content"])参考答案(做完再点开)import streamlit as st# 先塞两条假消息,检验"渲染 + 追加"这条链路if "messages" not in st.session_state:st.session_state.messages = [{"role": "user", "content": "你好"},{"role": "assistant", "content": "你好呀,我是小甜甜~"},]st.title("AI智能伴侣(暂不接模型)")# 每次重跑都按列表渲染一遍历史for message in st.session_state.messages:st.chat_message(message["role"]).write(message["content"])prompt = st.chat_input("请输入您要问的问题")if prompt:st.session_state.messages.append({"role": "user", "content": prompt})st.chat_message("user").write(prompt)# 刷新页面后消息还在 —— 因为列表存在 session_state 里,不是普通变量 -
2-2 接上大模型 在 2-1 的基础上接上 deepseek-chat:用户发消息 → 调模型 → 把回复也渲染出来并存进 messages。
提示一级 · 思路:存 → 调 → 显示 → 再存,四步都要在
if prompt:里面 二级 · 方法:client.chat.completions.create/st.session_state.messages.append三级 · 骨架:messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}](这一版只发当前这一句,看看 AI 记不记得上一轮)参考答案(做完再点开)import osimport streamlit as stfrom openai import OpenAIst.set_page_config(page_title="AI智能伴侣", page_icon="🤖", layout="wide")st.title("AI智能伴侣")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=False,)reply = response.choices[0].message.contentst.chat_message("assistant").write(reply)st.session_state.messages.append({"role": "assistant", "content": reply})
评论区
如果你喜欢,那么欢迎来到我的世界!
了解更多













