实战-AI智能伴侣-基本交互

实战-AI智能伴侣-基本交互

2026年9月18日·#编程学习/python学习笔记Python/Streamlit/AI·1673 字 8 分钟
浏览量加载中...
AI 摘要

用 Streamlit + openai 库搭出能与大模型对话的网页:页面配置、会话状态初始化、消息渲染与发送

项目目标#

做一个”AI 智能伴侣”网页:左边是控制面板(会话列表、伴侣昵称、性格),右边是聊天窗口,输入问题后大模型以伴侣的口吻流式回复。

做完整个章节后,页面长这样:

AI 智能伴侣最终效果:左侧为会话管理面板,右侧为聊天窗口

本篇是第一步:把基本交互跑通(对应课程代码 03. ai_partner_1.py)。后续依次叠加流式输出、会话记忆、会话管理。

整体骨架#

1. 设置页面配置 st.set_page_config
2. 展示标题和 Logo st.title / st.logo
3. 初始化会话状态 st.session_state
4. 渲染历史消息 for + st.chat_message
5. 创建大模型客户端 OpenAI(...)
6. 等待用户输入 st.chat_input
7. 调用大模型并展示 client.chat.completions.create
8. 把这一轮对话存入 session_state

完整代码(基本交互版)#

import streamlit as st
import os
from openai import OpenAI
print("----------> 重新执行此文件 , 渲染展示页面")
# 设置页面的配置项
st.set_page_config(
page_title="AI智能伴侣",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded",
menu_items={}
)
# 大标题
st.title("AI智能伴侣")
# Logo
st.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 才创建空列表)
以后每次重跑条件不成立,直接沿用已有的列表,历史消息不会丢
Important

不能写成 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 显示在左侧,样式自动区分。

Note

这个 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 还记不住上一轮(模型是无状态的)。下一步就要改造成”带上历史消息”,见 会话记忆

相关#

练习题#

一、回忆填空(写完再展开对答案)#

  1. 页面配置、大标题、Logo 分别是:st.set_page_config / st.____ / st.____
  2. 初始化聊天记录:if "messages" not in st.____: st.session_state.messages = ____
  3. 渲染历史消息:for message in st.session_state.messages: st.____(message["____"]).write(message["content"])
  4. 底部聊天输入框:prompt = st.____("请输入您要问的问题")
  5. if prompt: 能判断”用户这次真的发了消息”,是因为非空字符串转布尔值是 ____
  6. 存下用户的问题:st.session_state.messages.____({"role": "user", "content": prompt})
  7. 取模型回复:response.____[0].____.content
  8. 为什么必须用 st.session_state?因为 Streamlit 每次交互都会 ____ 整个脚本,普通变量会丢
填空答案(做完再点开)
  1. 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 os
    import streamlit as st
    from openai import OpenAI
    st.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.content
    st.chat_message("assistant").write(reply)
    st.session_state.messages.append({"role": "assistant", "content": reply})

评论区

[ 标签 ]
# AI37# AI 编程2# AI工具1# Ajax2# Apifox1# AstrBot3# Astro2# CC Switch1# CDN2# Claude Code1# claudecode2# ClaudeCode1# Cloudflare2# CloudFlare2# CloudFlare-ImgBed3# coc3# CSS6# DeepSeek6# deepseek2# DELETE1# Docker1# EdgeOne3# Gist1# git1# GitHub1# hexo-circle-of-friends1# HTML6# HTTP5# ImageManager1# Java23# java13# JavaScript5# JDBC3# JSON2# JUnit1# LangChain25# Logback1# Maven6# Muse Spark1# Mybatis1# MyBatis4# MySQL28# MySql1# NapCat1# Node.js1# obsidian2# Obsidian5# OpenCode4# ORM1# PathVariable1# PicGo1# PyCharm1# Python65# RequestBody1# RequestMapping1# RESTful风格1# skills1# Slf4j1# SpringBoot11# SQL2# Streamlit5# Svelte2# TailwindCSS1# Telegram3# Tlias2# Vercel1# vscode2# Vue7# Waline3# WebDAV1# Web基础6# Web开发6# WinSCP1# YAML1# 三层架构1# 中二宣言1# 书籍1# 使用文档10# 写作1# 函数2# 刷步数1# 前端32# 动态1# 动漫1# 包1# 单词2# 博客7# 博客工作流1# 博客开发2# 参数接收1# 友链1# 反思2# 图床6# 地图1# 备份2# 大模型1# 奇思妙想1# 存储1# 学习方法6# 学校1# 宝塔面板3# 宝宝10# 对象1# 导航栏1# 工具2# 开发1# 开发工具1# 开发规范1# 开心1# 异常处理1# 影视2# 微信1# 性能优化2# 总结1# 想法15# 感受1# 感悟11# 指南1# 提示词工程1# 插件5# 故障排除1# 效率工具2# 教程10# 数据分析9# 数据库27# 数据结构1# 文件操作2# 斩神1# 日常92# 日志框架1# 朋友圈1# 朱元璋1# 模块1# 模板1# 正则表达式2# 测试1# 游戏2# 爬虫7# 生活迁移1# 电影2# 电脑1# 碎碎念1# 视觉识别1# 类1# 类型注解1# 网络基础2# 网络教室1# 羊毛2# 脚本2# 脚本工具1# 自动化2# 蓝奏云1# 订阅推荐2# 记录2# 评论系统1# 词根1# 词缀1# 说说1# 足迹1# 跑步2# 路径参数1# 转载2# 运动1# 部落冲突1# 配置1# 随机图1# 面向对象5# 音乐3# 音标1# 饮食1# 驼峰命名1# 高德地图1
[ 公告 ]

如果你喜欢,那么欢迎来到我的世界!

了解更多
[ 音乐 ]
封面

音乐

暂未播放

0:000:00
暂无歌词
找不到相关结果。
[ contents ]
[ 全部文章 ]