本教程将手把手带你使用Python和Streamlit框架,结合阿里云千问大模型,开发一个具备流式输出、对话保存和历史记录管理功能的桌面级AI应用。你将学会如何申请API密钥、配置开发环境,并编写核心代码实现一个完整的AI助理。


1. 准备工作:申请千问API Key

在开始编码前,我们需要获取调用千问大模型的“钥匙”。

  1. 访问阿里云官网并登录/注册账号。
  2. 进入“百炼”(Model Studio)平台。
  3. 在“我的模型”或“API密钥”管理页面,创建一个新的API Key。
  4. 注意:代码中 api_key='' 目前是空的,运行前必须填入你的 Key,否则会报错。
2. 环境搭建与依赖安装

本项目主要依赖以下库:

  • streamlit: 用于快速构建Web应用界面。
  • dashscope: 阿里云通义千问的Python SDK。
  • json & os: 用于文件操作和系统交互。

你可以使用pip命令安装所需依赖:

pip install streamlit dashscope
3. 项目结构

界面非常直观,主要分为 侧边栏(控制区) 和 主区域(对话区)

1. 侧边栏操作
  • 新建对话 (➕️):点击后会清空当前聊天记录,并生成一个新的会话(以当前时间命名)。
  • 历史会话:程序会自动在本地创建 SessionData 文件夹来保存聊天记录。
    • 点击会话名:切换不同的历史对话。
    • 点击 X:删除对应的会话记录。
  • AI 设置
    • 昵称:修改 AI 的名字(默认是“小卓子”)。
    • 性格:修改 AI 的回复风格(默认是“可爱”)。
2. 主对话区
  • 输入问题:在底部的输入框中输入你的问题。
  • 流式输出:AI 的回复是逐字显示的(类似打字机效果),非常有交互感。

4. 核心代码实现详解

1. 初始化与配置 应用启动时,会检查session_state中是否存在必要的变量(如messages, nickName等),若不存在则初始化默认值。同时,配置了页面标题、布局等信息。

2. 侧边栏功能:历史对话与设置 侧边栏提供了“新建对话”、“历史会话列表”以及“AI设置”功能。

  • 历史会话:通过os.listdir读取SessionData目录下的所有.json文件,并动态生成按钮。点击按钮即可调用change_session函数加载对应的历史记录。
  • AI设置:允许用户自定义AI的昵称和性格(System Prompt),并实时更新到session_state中。

3. 流式对话与渲染 这是应用最核心的交互部分。当用户输入问题并提交后,代码会执行以下步骤:

  1. 展示用户输入:使用st.chat_message('user').write(prompt)在界面上展示用户的提问。
  2. 构建消息列表:将系统设定(角色、性格)与历史对话记录合并,作为上下文传给模型。
  3. 调用API:使用dashscope.Generation.call发起请求,并设置stream=True以启用流式输出。
  4. 实时渲染:通过for循环遍历response的每一个chunk,将内容实时拼接并写入一个空的st.empty()占位符中,从而实现文字逐字出现的“打字机”效果。
  5. 保存记录:将完整的AI回复追加到session_state.messages中,以便后续对话使用。

完整代码如下

import json
import os
import streamlit as st
import dashscope
import Module.AI实例Module.SaveSession as sessionOp
import datetime

# 保存会话
def save_session():
    if st.session_state["messages"] != []:
        session_data={
            "nickName":st.session_state["nickName"],
            "nature":st.session_state["nature"],
            "messages":st.session_state["messages"],
            "sessionName":st.session_state["sessionName"]
        }
        sessionOp.SaveSessionToFile(session_data,f"{session_data['sessionName']}.json")

# 重置会话
def rest_session():
        st.session_state.messages = []
        st.session_state.nickName = "小卓子"
        st.session_state.nature = "可爱"
        st.session_state.sessionName = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        st.session_state.messages=[]

# 加载会话
def change_session(filepath):
    save_session()
    with open(f"SessionData/{filepath}","r",encoding="utf-8") as f:
        session_data=json.load(f)
        st.session_state.nickName = session_data["nickName"]
        st.session_state.nature = session_data["nature"]
        st.session_state.sessionName = session_data["sessionName"]
        st.session_state.messages = session_data["messages"]

# 删除会话
def remove_session(filepath):
    os.remove(f"SessionData/{filepath}")

# 设置页面配置
st.set_page_config("AI对话","👨🏽‍💻","wide","expanded")
# 创建一个标题
st.title("AI对话")


# 初始化聊天缓存
if "messages" not in st.session_state:
    st.session_state.messages = []

if "nickName" not in st.session_state:
    st.session_state.nickName = "小卓子"

if "nature" not in st.session_state:
    st.session_state.nature="可爱"

if "sessionName" not in st.session_state:
    st.session_state.sessionName=datetime.datetime.now().strftime("%Y%m%d%H%M%S")

# 侧边栏
st.sidebar.subheader("新建对话")
with st.sidebar:
    if(st.button("新建对话",width="stretch",icon="➕️")):
        save_session()
        rest_session()

    st.text("历史会话")

    if os.path.exists("SessionData"):
        for fileName in os.listdir("SessionData"):
            col1, col2 = st.columns([0.8, 0.2], width="stretch")
            if fileName.endswith(".json"):
                session_key = fileName.replace(".json", "")
                with col1:
                    if st.button(fileName.replace(".json",""),width="stretch",type="primary" if st.session_state.sessionName+".json"==fileName else "secondary"):
                        change_session(fileName)
                with col2:
                     if st.button("X",key=f"delete_{session_key}"):
                        remove_session(fileName)
                        st.rerun()


st.sidebar.subheader("AI设置")
with st.sidebar:
    nickName=st.text_input("昵称",placeholder="请输入昵称",value=st.session_state.nickName)
    if nickName:
        st.session_state.nickName=nickName
    nature=st.text_area("性格",placeholder="请输入性格",value=st.session_state.nature)
    if nature:
        st.session_state.nature=nature

# 展示聊天记录
for message in st.session_state.messages:
    st.chat_message(message["role"]).write(message["content"])

# 创建一个文本输入框
prompt=st.chat_input("请输入您的问题")
if prompt:
    # 内容
    st.chat_message('user').write(prompt)
    # 保存用户的问题
    st.session_state.messages.append({'role': 'user', 'content': prompt})
    # 调用AI
    response = dashscope.Generation.call(
        api_key='',
        model="qwen-plus",
        messages=[
            {'role': 'system','content': f'你是一名{st.session_state.nature}的AI助理,你的名字叫{st.session_state.nickName},请你使用温柔可爱的语气回答用户的问题'}
        ]+st.session_state.messages,
        result_format='message',
        stream=True
    )
    # 创建一个空组件 流式输出
    response_messages=st.empty()
    full_content=""
    for chunk in response:
        if chunk.output.choices[0].message.content is not None:
            full_content=chunk.output.choices[0].message.content
            response_messages.chat_message("ai").write(full_content)

    # 非流式输出
    # st.chat_message('ai').write(response.output.choices[0].message.content)
    # 保存AI的回答
    st.session_state.messages.append({'role': 'assistant', 'content': full_content})
5. 运行与测试
  1. 将上述代码保存为app.py
  2. 在终端执行streamlit run app.py启动应用。
  3. 浏览器将自动打开,你就可以开始与你的专属AI助理聊天了。
  4. 尝试在侧边栏修改AI的性格和昵称,体验不同的对话风格。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐