GLM-4V-9B Streamlit UI定制指南:添加清空历史、图片缩略图、下载日志功能

1. 项目概述

GLM-4V-9B是一个强大的多模态大模型,能够同时处理图像和文本输入。本项目基于Streamlit框架构建了一个用户友好的界面,让用户可以通过简单的操作与模型进行交互。

经过深度优化,这个版本解决了官方示例在特定环境下的兼容性问题,特别是PyTorch和CUDA版本的匹配问题。通过4-bit量化技术,模型现在可以在消费级显卡上流畅运行,大大降低了硬件门槛。

核心优化亮点

  • 自动检测视觉层参数类型,避免数据类型冲突导致的错误
  • 修正了Prompt拼接顺序,确保模型正确理解图像和文本的关系
  • 优化了显存使用,让8GB显存的显卡也能运行这个大型模型

2. 环境准备与快速部署

2.1 系统要求

在开始定制之前,确保你的系统满足以下要求:

  • Python 3.8或更高版本
  • 支持CUDA的NVIDIA显卡(建议8GB以上显存)
  • 至少20GB的可用磁盘空间
  • 稳定的网络连接(用于下载模型权重)

2.2 安装依赖

创建并激活Python虚拟环境后,安装所需依赖:

# 创建虚拟环境
python -m venv glm4v-env
source glm4v-env/bin/activate  # Linux/Mac
# 或
glm4v-env\Scripts\activate      # Windows

# 安装核心依赖
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install streamlit transformers accelerate bitsandbytes
pip install Pillow python-multipart

2.3 快速启动

下载项目代码后,通过以下命令启动基础版本:

streamlit run app.py --server.port 8080

启动后,在浏览器中访问 http://localhost:8080 即可看到基础界面。

3. 清空历史功能实现

3.1 为什么需要清空历史功能

在多轮对话过程中,历史记录会不断累积,可能导致以下问题:

  • 占用过多内存,影响性能
  • 新旧对话混杂,影响模型理解
  • 隐私考虑,用户可能希望清除敏感对话内容

3.2 实现清空历史功能

在Streamlit应用中添加清空按钮非常简单。在app.py中找到会话状态管理部分,添加以下代码:

import streamlit as st

# 在侧边栏添加清空按钮
with st.sidebar:
    if st.button("🗑️ 清空历史", help="清除所有对话记录"):
        # 重置会话状态中的消息历史
        st.session_state.messages = []
        st.session_state.uploaded_images = []
        st.rerun()

# 初始化消息历史
if "messages" not in st.session_state:
    st.session_state.messages = []

if "uploaded_images" not in st.session_state:
    st.session_state.uploaded_images = []

3.3 增强清空功能

为了更好的用户体验,我们可以添加确认对话框防止误操作:

# 添加确认对话框的清空功能
if st.sidebar.button("🗑️ 清空历史"):
    # 使用对话框确认
    if st.session_state.messages:  # 只有有历史时才确认
        confirm = st.sidebar.checkbox("确认清空所有对话历史?")
        if confirm:
            st.session_state.messages = []
            st.session_state.uploaded_images = []
            st.sidebar.success("历史记录已清空!")
            st.rerun()
    else:
        st.sidebar.info("当前没有对话历史")

4. 图片缩略图功能实现

4.1 图片缩略图的价值

在多轮对话中,用户可能会上传多张图片。缩略图功能可以帮助:

  • 快速识别已上传的图片
  • 方便在不同图片间切换
  • 提供视觉反馈,确认图片已正确上传

4.2 实现图片缩略图展示

在侧边栏中显示已上传图片的缩略图:

from PIL import Image
import io

# 在侧边栏显示已上传图片的缩略图
if st.session_state.uploaded_images:
    st.sidebar.markdown("### 📷 已上传图片")
    
    for i, img_data in enumerate(st.session_state.uploaded_images):
        # 创建缩略图
        img = Image.open(io.BytesIO(img_data))
        img.thumbnail((100, 100))  # 缩放到100x100像素
        
        # 显示缩略图和删除按钮
        col1, col2 = st.sidebar.columns([3, 1])
        col1.image(img, use_column_width=True)
        
        # 添加删除单个图片的按钮
        if col2.button("❌", key=f"delete_img_{i}"):
            st.session_state.uploaded_images.pop(i)
            st.rerun()

4.3 增强缩略图功能

添加图片预览和选择功能:

# 增强版图片缩略图组件
def image_thumbnail_gallery():
    if not st.session_state.uploaded_images:
        return None
    
    st.sidebar.markdown("### 📷 图片库")
    
    # 让用户选择当前活动的图片
    image_titles = [f"图片 {i+1}" for i in range(len(st.session_state.uploaded_images))]
    selected_image = st.sidebar.selectbox("选择图片", image_titles)
    
    # 显示选中图片的预览
    selected_index = image_titles.index(selected_image)
    img_data = st.session_state.uploaded_images[selected_index]
    img = Image.open(io.BytesIO(img_data))
    
    st.sidebar.image(img, caption=selected_image, use_column_width=True)
    
    return selected_index

# 在适当位置调用
current_image_index = image_thumbnail_gallery()

5. 下载日志功能实现

5.1 日志功能的重要性

下载对话日志对于以下场景非常有用:

  • 保存重要的对话内容供后续参考
  • 分享有趣的对话结果
  • 收集用户反馈和改进模型
  • 学术研究或案例分析

5.2 实现对话日志下载

添加导出对话历史的功能:

import json
from datetime import datetime

def export_conversation():
    """将对话历史导出为JSON文件"""
    if not st.session_state.messages:
        return None
    
    # 构建导出数据
    export_data = {
        "export_date": datetime.now().isoformat(),
        "model": "GLM-4V-9B",
        "conversation": []
    }
    
    for msg in st.session_state.messages:
        export_data["conversation"].append({
            "role": msg["role"],
            "content": msg["content"],
            "timestamp": datetime.now().isoformat()
        })
    
    # 转换为JSON字符串
    return json.dumps(export_data, ensure_ascii=False, indent=2)

# 在侧边栏添加导出按钮
if st.session_state.messages:
    export_json = export_conversation()
    st.sidebar.download_button(
        label="💾 导出对话",
        data=export_json,
        file_name=f"glm4v_conversation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
        mime="application/json",
        help="将当前对话导出为JSON文件"
    )

5.3 多种格式导出支持

提供不同格式的导出选项:

# 支持多种导出格式
def export_conversation_multiformat(format_type="json"):
    """支持多种格式的对话导出"""
    if not st.session_state.messages:
        return None, ""
    
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    
    if format_type == "json":
        data = json.dumps({
            "export_date": datetime.now().isoformat(),
            "conversation": st.session_state.messages
        }, ensure_ascii=False, indent=2)
        filename = f"glm4v_conversation_{timestamp}.json"
        mime_type = "application/json"
    
    elif format_type == "txt":
        # 纯文本格式
        text_content = f"GLM-4V-9B 对话导出 - {datetime.now()}\n\n"
        for msg in st.session_state.messages:
            role = "用户" if msg["role"] == "user" else "助手"
            text_content += f"{role}: {msg['content']}\n\n"
        
        data = text_content
        filename = f"glm4v_conversation_{timestamp}.txt"
        mime_type = "text/plain"
    
    elif format_type == "md":
        # Markdown格式
        md_content = f"# GLM-4V-9B 对话导出\n\n*导出时间: {datetime.now()}*\n\n"
        for msg in st.session_state.messages:
            role = "**用户**" if msg["role"] == "user" else "**助手**"
            md_content += f"{role}: {msg['content']}\n\n---\n\n"
        
        data = md_content
        filename = f"glm4v_conversation_{timestamp}.md"
        mime_type = "text/markdown"
    
    return data, filename, mime_type

# 在UI中添加格式选择
if st.session_state.messages:
    st.sidebar.markdown("### 💾 导出选项")
    export_format = st.sidebar.selectbox("导出格式", ["JSON", "TXT", "Markdown"])
    
    data, filename, mime_type = export_conversation_multiformat(export_format.lower())
    
    st.sidebar.download_button(
        label=f"下载{export_format}格式",
        data=data,
        file_name=filename,
        mime=mime_type
    )

6. 完整功能集成

6.1 整合所有定制功能

将上述功能整合到主应用中,创建一个完整的定制版UI:

import streamlit as st
import torch
from PIL import Image
import io
import json
from datetime import datetime

# 页面配置
st.set_page_config(
    page_title="GLM-4V-9B 增强版",
    page_icon="🦅",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 初始化会话状态
if "messages" not in st.session_state:
    st.session_state.messages = []
if "uploaded_images" not in st.session_state:
    st.session_state.uploaded_images = []
if "current_image_index" not in st.session_state:
    st.session_state.current_image_index = 0

# 侧边栏 - 功能区域
with st.sidebar:
    st.title("🦅 GLM-4V-9B 控制面板")
    
    # 图片上传
    st.markdown("### 📤 上传图片")
    uploaded_file = st.file_uploader("选择图片文件", type=["jpg", "jpeg", "png"])
    
    if uploaded_file is not None:
        # 保存上传的图片
        img_data = uploaded_file.read()
        if img_data not in st.session_state.uploaded_images:
            st.session_state.uploaded_images.append(img_data)
            st.success("图片上传成功!")
    
    # 图片缩略图库
    if st.session_state.uploaded_images:
        st.markdown("### 📷 图片库")
        image_titles = [f"图片 {i+1}" for i in range(len(st.session_state.uploaded_images))]
        selected_title = st.selectbox("选择图片", image_titles)
        st.session_state.current_image_index = image_titles.index(selected_title)
        
        # 显示选中图片的预览
        img_data = st.session_state.uploaded_images[st.session_state.current_image_index]
        img = Image.open(io.BytesIO(img_data))
        st.image(img, use_column_width=True)
    
    # 工具功能区
    st.markdown("### 🛠️ 工具")
    
    # 清空历史按钮
    if st.button("🗑️ 清空对话历史", use_container_width=True):
        if st.session_state.messages:
            if st.checkbox("确认清空所有对话历史?"):
                st.session_state.messages = []
                st.success("历史记录已清空!")
                st.rerun()
        else:
            st.info("当前没有对话历史")
    
    # 导出功能
    if st.session_state.messages:
        st.markdown("### 💾 导出对话")
        export_format = st.selectbox("格式选择", ["JSON", "TXT", "Markdown"])
        
        # 导出逻辑(略,使用前面实现的函数)

6.2 优化用户体验

添加一些用户体验优化功能:

# 添加一些用户体验增强功能

# 1. 输入框置底并固定
def bottom_input():
    st.markdown("---")
    input_col, button_col = st.columns([5, 1])
    
    with input_col:
        user_input = st.text_input(
            "输入你的问题...", 
            key="user_input",
            label_visibility="collapsed"
        )
    
    with button_col:
        send_button = st.button("发送", use_container_width=True)
    
    return user_input, send_button

# 2. 对话历史自动滚动到底部
def scroll_bottom():
    # 使用JavaScript实现自动滚动
    js = """
    <script>
        function scrollToBottom() {
            window.parent.document.querySelector('section.main').scrollTo(0, window.parent.document.querySelector('section.main').scrollHeight);
        }
        setTimeout(scrollToBottom, 100);
    </script>
    """
    st.components.v1.html(js, height=0)

# 3. 在适当位置调用滚动函数
if st.session_state.messages:
    # 显示对话历史
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])
    
    # 自动滚动到底部
    scroll_bottom()

# 获取用户输入
user_input, send_button = bottom_input()

7. 总结

通过本指南,我们为GLM-4V-9B的Streamlit界面添加了三个实用功能:

清空历史功能让用户可以随时重置对话,保护隐私并释放内存。我们实现了简单的清空按钮和带确认对话框的安全清空功能,防止误操作。

图片缩略图功能大大提升了多图片对话的体验。用户可以直观地看到所有已上传图片,方便在不同图片间切换和管理。

下载日志功能提供了对话导出能力,支持JSON、纯文本和Markdown多种格式,满足不同场景下的需求。

这些定制功能不仅提升了用户体验,也展示了Streamlit框架的强大灵活性。你可以基于这些示例,继续扩展更多实用功能,如对话主题管理、个性化设置保存、或者集成其他AI服务。

记住,好的UI设计应该始终以用户需求为中心,不断迭代优化。希望本指南能为你的项目开发提供有价值的参考。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐