用 Streamlit + 智谱 AI 打造虚拟伴侣聊天机器人

今天做一个超有趣的项目:用 Python + 智谱 AI 大模型,配合 Streamlit 快速搭建一个可以自定义昵称和性格的虚拟伴侣聊天室。

运行成功后,你会看到这样的界面:左侧边栏 可以随时修改伴侣的昵称和性格,还能管理历史聊天记录;右侧主界面就像微信一样,你发一句,AI 伴侣秒回一句,而且会根据你设定的性格给出完全不同的回复风格!

📝 说明 :本项目代码逻辑参考自 黑马程序员 在 B 站的公开教程,代码经过实测可稳定运行。

相关资料:Download Python | Python.orgInstall 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_titlepage_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 的字符串格式化语法。把用户在侧边栏输入的 namedesc 填入占位符 %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.namest.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秒上手)

  1. 打开命令行,进入到 app.py 所在的目录。
  2. 输入下面这行神奇的命令,敲回车:
bash 复制代码
streamlit run app.py
  1. 浏览器会自动弹出一个网页。第一步 先在代码顶部把你的 智谱 API Key 替换进去;第二步在左侧边栏设置一个你喜欢的性格,就可以开始和你的虚拟伴侣畅聊啦!

四、常见报错与排查(防呆指南)

  • 常见报错 1ModuleNotFoundError: 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

相关推荐
Cubar2 小时前
[一]开源ERP:odoo19二次开发问题-原库存模块问题
python·开源·odoo·开源erp
滴滴滴嘟嘟嘟.2 小时前
强化学习-PPO 奖励塑形实验:Pendulum-v1 中角速度惩罚权重的影响
开发语言·python
Sylvia33.3 小时前
足球数据接口开发实战:如何用火星数据API盘活赛事应用
java·服务器·开发语言·数据库·python
lupai3 小时前
身份证二要素实名认证 API 落地应用指南
python
cxr8283 小时前
大语言模型上下文缓存命中率测试全场景清单
人工智能·python·算法·缓存·语言模型·自然语言处理·llm
某林2123 小时前
大模型边缘部署到底层硬件闭环
python·架构·机器人·硬件架构·ros2
LadenKiller4 小时前
最新量化软件选择,先按能力判断表达生成和执行
人工智能·python
iuu_star4 小时前
Python大模型智能学习平台——设计与实现(AI教学系统)
大数据·人工智能·python·学习
天才测试猿4 小时前
如何封装自动化测试框架?
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例