今天做一个超有趣的项目:用 Python + 智谱 AI 大模型,配合 Streamlit 快速搭建一个可以自定义昵称和性格的虚拟伴侣聊天室。
运行成功后,你会看到这样的界面:左侧边栏 可以随时修改伴侣的昵称和性格,还能管理历史聊天记录;右侧主界面就像微信一样,你发一句,AI 伴侣秒回一句,而且会根据你设定的性格给出完全不同的回复风格!

📝 说明 :本项目代码逻辑参考自 黑马程序员 在 B 站的公开教程,代码经过实测可稳定运行。
相关资料:Download Python | Python.org、Install Streamlit - Streamlit Docs、快速开始 - 智谱AI开放文档
一、环境准备(前置条件)
在开始之前,请确保你的电脑上装了 Python 3.9 及以上版本。
打开你的命令行(终端),复制下面这行命令,一键安装我们需要的两个核心依赖包:
bash
pip install streamlit zai-sdk
非常重要的一步 :你需要去 智谱 AI 开放平台 注册一个账号,并在控制台获取你的 API Key。这是大模型识别你身份的钥匙,没有它代码是无法运行的哦!
二、跟着界面写代码
我们按照用户看到界面的顺序,一步步把功能加上去!新建一个名为 app.py 的文件,跟着我动手吧!
第一步:先跑起一个空白页面
我们要先在浏览器里看到东西。注意,st.set_page_config 必须是整个 Streamlit 脚本的第一个命令。
python
import streamlit as st
st.set_page_config(
page_title="AI Partner",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded",
menu_items={},
)
st.title("AI Partner")
st.write(
"This is the AI Partner page. It will be used to showcase the AI capabilities of the company."
)
代码解释:
这是 Streamlit 的页面全局配置函数,必须写在所有 Streamlit 代码的最前面(最好是脚本的第一行)。
page_title和page_icon:决定了浏览器标签页上显示的标题和图标。layout="wide":让页面内容默认宽屏展示,而不是挤在中间一条窄带上。initial_sidebar_state="expanded":让左侧边栏默认展开,因为我们后面要在里面放设置项。
现在运行 streamlit run app.py,你已经能看到一个空白的网页主体了!
第二步:加上主界面的聊天框
网页有了,我们要让它像微信一样可以聊天。但是,因为 Streamlit 每次交互都会重新运行整个脚本,为了不让刚聊的记录刷新没,我们必须引入 st.session_state 来存储聊天历史。
python
import streamlit as st
# 初始化状态,保证刷新不丢失数据
if "message" not in st.session_state:
st.session_state.message = []
st.set_page_config(
page_title="AI Partner",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded",
menu_items={},
)
st.title("AI Partner")
st.write(
"This is the AI Partner page. It will be used to showcase the AI capabilities of the company."
)
# 展示聊天信息
for message in st.session_state.message:
st.chat_message(message["role"]).write(f"{message['content']}")
# 输入框
prompt = st.chat_input("你最近有什么烦恼吗?")
if prompt:
# 发送消息
st.chat_message("user").write(f"{prompt}")
print(f"【你】: {prompt}")
# 追加用户消息到会话消息历史中
st.session_state.message.append({"role": "user", "content": prompt})
代码解释:
st.session_state:这是 Streamlit 的灵魂 !因为 Streamlit 每次用户操作(比如输入文字)都会从头到尾重新运行一遍脚本。如果没有它,你刚发的消息刷新一下就没了。我们用if "message" not in st.session_state来判断,如果是第一次运行,就初始化一个空列表,后续把聊天记录都存在这个列表里,实现"记忆"功能。st.chat_message:用来渲染聊天气泡。传入的role参数决定了气泡的位置:"user"靠右显示,"assistant"靠左显示。st.chat_input:在页面底部生成一个固定的输入框,返回值就是用户敲进去的文字。
现在你可以输入文字了,但是界面只会显示你说的话,因为还没有接入 AI 大脑。
第三步:让 AI 开口说话(接入大模型)
要跟智谱 AI 对话,我们需要初始化客户端,并定义一个系统提示词来设定伴侣的灵魂。这里我们预设了默认的名字和性格,并填入你刚才申请的 API Key。
python
import streamlit as st
from zai import ZhipuAiClient
# 初始化状态
if "message" not in st.session_state:
st.session_state.message = []
# 初始化名字
if "name" not in st.session_state:
st.session_state.name = "小甜甜"
# 初始化性格
if "desc" not in st.session_state:
st.session_state.desc = (
"活泼开朗的小可爱,喜欢分享生活的点滴,善于发现生活中的美好。"
)
# 初始化session_id
if "session_id" not in st.session_state:
st.session_state.session_id = ""
# 系统提示词模板,将昵称和性格注入大模型
system_prompt = """
你叫 %s ,现在是用户的真实伴侣,请完全带入伴侣角色。
规则:
1、每次只回1条消息
2、禁止在任何场景和状态描述性文字
3、匹配用户的语言
4、回复简短,像微信聊天一样
5、有需要的话可以用emoji表情
6、用符合伴侣性格的方式对话
7、回复的内容,要充分体现伴侣的性格特征
伴侣性格:
%s
你必须严格遵守上述规则来回复用户。
"""
# 初始化客户端(记得替换为你自己的 API Key!)
client = ZhipuAiClient(api_key="你的API_Key")
# ... 前面的 set_page_config 及界面代码 ...
# 输入框
prompt = st.chat_input("你最近有什么烦恼吗?")
if prompt:
st.chat_message("user").write(f"{prompt}")
print(f"【你】: {prompt}")
st.session_state.message.append({"role": "user", "content": prompt})
# ---- 核心交互:流式请求大模型 ----
# 创建一个空的响应消息容器,用于实时显示AI助手的回复
response_message = st.empty()
# 初始化一个空字符串,用于累积完整的回复内容
full_respone = ""
# 打印分隔线,用于调试输出,显示当前会话消息
print("------------------------------------------------------------------------------------------------")
print(st.session_state.message)
print("------------------------------------------------------------------------------------------------")
# 创建聊天完成请求,使用GLM-4.7-flash模型
response = client.chat.completions.create(
model="glm-4.7-flash", # 指定使用的模型
messages=[
# 系统提示,包含用户名称和描述信息
{
"role": "system",
"content": system_prompt
% (st.session_state.name, st.session_state.desc),
},
# 将当前会话中的所有消息作为对话历史
*st.session_state.message,
],
temperature=0.6, # 设置温度参数,控制回复的随机性
stream=True, # 启用流式输出,实现实时显示回复
)
# 获取回复
for chunk in response:
if chunk.choices[0].delta.content is not None:
full_respone += chunk.choices[0].delta.content
# 实时显示AI助手的回复
response_message.chat_message("assistent").write(full_respone)
# 追加完整的回复到会话消息历史中
st.session_state.message.append({"role": "assistant", "content": full_respone})
代码解释:
system_prompt % (...):这是 Python 的字符串格式化语法。把用户在侧边栏输入的name和desc填入占位符%s中,作为系统提示词,强制大模型扮演这个角色。messages列表 :大模型接口要求传入对话历史。第一项固定是system角色设定;*st.session_state.message使用了 Python 的解包语法,把咱们存的历史聊天记录一条条展开追加进去,这样 AI 才知道前文聊了啥。stream=True:开启流式输出。如果不用它,AI 会憋半天把一大段话一起吐出来;用了它,AI 就会一个字一个字地往外蹦,体验更丝滑。for chunk in response:遍历流式返回的数据块,每次拿到一点点内容就拼接到full_respone上,并实时用write()刷新界面上的气泡。
现在再运行,AI 已经能根据默认性格跟你聊天了!为了代码更整洁,我们把上面获取回复的逻辑封装成函数 fetchZhiPuStream(),在 if prompt: 里直接调用它即可。
第四步:加上左侧边栏,让伴侣性格可配置
现在性格是写死的,我们要在左侧加个控制面板。输入框绑定了 key="name" 和 key="desc",Streamlit 会自动把用户输入的内容同步给 st.session_state.name 和 st.session_state.desc。
python
# ... 前面的初始化和函数代码 ...
st.set_page_config(...)
# 左侧边栏设计
with st.sidebar:
st.subheader("控制面板")
st.divider()
st.subheader("设置")
st.text_input("请输入伴侣的名字", key="name", placeholder="请输入伴侣的名字")
st.text_area("请输入伴侣的性格", key="desc", placeholder="请输入您的伴侣的性格")
# ... 主界面代码 ...
代码解释:
with st.sidebar::在这个代码块内写的一切组件,都会被自动放到网页左侧的侧边栏中,和主聊天区隔离开。key="name"/key="desc":这是 Streamlit 的一个神仙特性 !只要你在组件里指定了key,当用户在输入框打字时,Streamlit 会自动 把输入的值同步给st.session_state["name"]和st.session_state["desc"]。不需要你写任何回调函数,也不需要手动存值,非常优雅。
去侧边栏改个名字和性格,再聊天,你会发现 AI 的人设瞬间就变了!
第五步:加上历史会话管理(按需引入 datetime/json/os)
现在每次关闭网页聊天就没了,我们要加上"新建、保存、加载、删除"会话的功能。
为什么要引入新的包?
- 保存文件需要起个唯一名字:引入
datetime,用当前时间作为会话标识。 - 聊天记录要存成本地文件:引入
json处理数据格式,引入os创建文件夹。
python
from datetime import datetime
import json
import os
# ... 前面的代码 ...
# 生成会话名称,格式为年-月-日_时-分-秒
def generate_session_name():
return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# 保存会话
def saveSession():
# 如果session_id为空,则生成新的
if not st.session_state.session_id:
st.session_state.session_id = generate_session_name()
# 准备要保存的数据字典,包含名称、描述、消息和会话ID
data = {
"name": st.session_state.name,
"desc": st.session_state.desc,
"message": st.session_state.message,
"session_id": st.session_state.session_id,
}
# 如果不存在session文件夹,则创建
if not os.path.exists("./session"):
os.mkdir("./session")
# 保存到session文件夹中
with open(
f"./session/{st.session_state.session_id}.json", "w", encoding="utf-8"
) as f:
# 写入json文件
# f.write(json.dumps(data, ensure_ascii=False, indent=2))
json.dump(data, f, ensure_ascii=False, indent=2)
st.rerun() # 重新加载页面
# 加载历史会话
def loadSessionList():
session_list = []
if os.path.exists("./session"):
for session in os.listdir("./session"):
if session.endswith(".json"):
session_list.append(session.split(".")[0])
session_list.sort(reverse=True) # 按时间排序
return session_list
# 加载指定会话
def loadSession(session_id):
try:
with open(f"./session/{session_id}.json", "r", encoding="utf-8") as f:
data = json.load(f)
st.session_state.name = data["name"]
st.session_state.desc = data["desc"]
st.session_state.message = data["message"]
st.session_state.session_id = data["session_id"]
except Exception:
st.error("加载会话失败!")
# 删除指定会话
def deleteSession(session_id):
os.remove(f"./session/{session_id}.json")
# 如果session_id为当前会话,则清空
if st.session_state.session_id == session_id:
st.session_state.session_id = ""
st.session_state.message = []
代码解释:
generate_session_name:用当前精确到秒的时间(如2023-10-27_14-30-00)作为会话 ID,保证每次新建的聊天记录文件名绝对不会重复。json.dump(..., ensure_ascii=False):把 Python 的字典转成 JSON 存入文件。ensure_ascii=False非常关键,如果不加它,中文字符会被存成类似\u5c0f的乱码,加上后就能正常保存和显示中文了。st.rerun():保存完文件后,强制让 Streamlit 重新运行一遍脚本。这样新建的会话就会立刻出现在左侧边栏的"历史会话"列表中,界面瞬间刷新。loadSession/deleteSession:分别使用json.load()读取数据还原状态,使用os.remove()删除本地文件。如果删除的是当前正在聊的会话,还要顺手把session_state里的记录清空,防止界面上出现幽灵数据。
最后,我们在左侧边栏加上对应的按钮来调用这些函数,并在主界面发送消息后调用 saveSession() 保存记录。
python
with st.sidebar:
st.subheader("控制面板")
# 新建会话按钮
if st.button("新建会话", icon="✏️", width="stretch"):
st.session_state.message = []
st.session_state.session_id = ""
st.rerun() # 重新加载页面
st.text("历史会话")
# 历史会话列表按钮
session_list = loadSessionList()
for session in session_list:
col1, col2 = st.columns([4, 1])
with col1:
if st.button(
session,
width="stretch",
type=(
"primary" if session == st.session_state.session_id else "secondary"
),
icon="📄",
key=f"session_{session}",
):
loadSession(session)
st.rerun() # 重新加载页面
with col2:
if st.button(
"", width="stretch", icon="❌", key=f"delete_session_{session}"
):
deleteSession(session)
st.rerun() # 重新加载页面
st.divider()
st.subheader("设置")
st.text_input("请输入伴侣的名字", key="name", placeholder="请输入伴侣的名字")
st.text_area("请输入伴侣的性格", key="desc", placeholder="请输入您的伴侣的性格")
# ... 主界面代码 ...
if prompt:
# 发送消息
st.chat_message("user").write(f"{prompt}")
print(f"【你】: {prompt}")
# 追加用户消息到会话消息历史中
st.session_state.message.append({"role": "user", "content": prompt})
# fetchZhiPu()
fetchZhiPuStream()
# 保存会话
saveSession()
三、如何运行(3秒上手)
- 打开命令行,进入到
app.py所在的目录。 - 输入下面这行神奇的命令,敲回车:
bash
streamlit run app.py
- 浏览器会自动弹出一个网页。第一步 先在代码顶部把你的 智谱 API Key 替换进去;第二步在左侧边栏设置一个你喜欢的性格,就可以开始和你的虚拟伴侣畅聊啦!
四、常见报错与排查(防呆指南)
-
常见报错 1 :
ModuleNotFoundError: No module named 'zai'解决办法 :这说明依赖没装好。请检查你是不是在正确的虚拟环境下,重新执行一遍pip install zai-sdk。 -
常见报错 2 :API Key 报错或
Authentication error解决办法 :99% 的原因是 Key 没复制完整,或者多复制了空格。去智谱后台重新复制一次,确保完整替换代码中的你的API_Key。 -
常见报错 3 :网页打开是一片空白或一直转圈卡住 解决办法 :可能是网络波动导致 Streamlit 没启动成功。在命令行里按
Ctrl + C强制停止,然后重新运行streamlit run app.py。