跳转至

如何使用Streamlit

你是否有疑惑,当你构建好一个AI Agent,却不知道如何让你的同学朋友使用?是做一个手机APP,还是写一个电脑软件,或者说做一个微信小程序?这些办法都太复杂了,有没有简单通用的办法呢,有的兄弟有的,最简单的办法就是做一个网页,这样不管是手机,平板,mac电脑,windows电脑都可以很轻松的访问。那么有没有不用写前端的办法呢,也有的,python社区已经有了很多不用写代码就可以搭建前端的库,最出名的便是streamlit,gradio。

欢迎来到AI应用界面构建的神器——Streamlit!🎨💻

Streamlit是什么?

Streamlit是一个开源Python库,专为数据科学家和机器学习工程师设计,让你能够快速为模型和数据分析创建美观、交互式的Web应用。它将复杂的Web开发简化成简单的Python脚本,让你专注于应用逻辑而非前端技术。

如果把AI模型比作汽车引擎,那么Streamlit就是让这个引擎拥有漂亮仪表盘和方向盘的工具!🚗

为什么在Agent开发中使用Streamlit?

想象一下你刚刚构建了一个强大的AI Agent,但只能在命令行中与它交互,这就像有了一台超级计算机却只能通过DOS界面使用一样遗憾!Streamlit解决了这个问题:

  • 零前端知识:不需要学习HTML、CSS、JavaScript
  • 实时反馈:代码更改后自动刷新应用
  • 简单部署:几分钟内将应用分享给他人
  • 丰富组件:内置各种UI元素,从文本框到复杂图表
  • 完美集成:与Python数据生态系统无缝协作

安装与设置

pip install streamlit

要验证安装成功,可以运行Streamlit的示例应用:

streamlit hello

这会启动Streamlit的示例应用,并在浏览器中打开。

Streamlit基础知识

创建第一个应用

创建一个名为app.py的文件,内容如下:

import streamlit as st

st.title("我的第一个Streamlit应用")
st.write("欢迎来到Streamlit的世界!")

name = st.text_input("请输入你的名字")
if name:
    st.write(f"你好,{name}!")

然后在命令行运行:

streamlit run app.py

浏览器会自动打开应用,看到一个简单的欢迎界面和输入框。

常用组件

Streamlit提供了丰富的UI组件:

# 展示标题和文本
st.title("应用标题")
st.header("这是一个标题")
st.subheader("这是一个子标题")
st.text("这是普通文本")
st.markdown("**这是Markdown格式**的文本")

# 显示数据
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["张三", "李四", "王五"],
    "年龄": [25, 30, 35],
    "城市": ["北京", "上海", "广州"]
})

st.dataframe(df)  # 交互式数据表格
st.table(df)      # 静态表格
st.json({"name": "John", "age": 30})  # JSON格式

# 输入组件
name = st.text_input("姓名")
age = st.slider("年龄", 0, 100, 25)  # 最小值,最大值,默认值
happy = st.checkbox("我很开心")
option = st.selectbox("最喜欢的食物", ["披萨", "汉堡", "寿司"])
color = st.color_picker("选择一个颜色")

# 媒体元素
st.image("https://i.imgur.com/4YdLKG9.png", caption="一张图片")
st.audio("sample.mp3")
st.video("sample.mp4")

# 状态和进度
st.spinner("加载中...")
progress_bar = st.progress(0)
for i in range(100):
    progress_bar.progress(i + 1)
    time.sleep(0.1)
st.success("完成!")
st.error("出错了!")
st.warning("警告信息")
st.info("提示信息")

# 侧边栏
with st.sidebar:
    st.header("侧边栏标题")
    option = st.radio("选择一个选项", ["选项1", "选项2", "选项3"])

🌟 小案例:简单聊天机器人

让我们创建一个简单的聊天界面,与OpenAI API集成:

import streamlit as st
import openai
from openai import OpenAI
import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

# 设置页面标题
st.set_page_config(page_title="AI助手", page_icon=":robot_face:")

# 使用自定义CSS美化界面
st.markdown("""
<style>
.chat-message {
    padding: 1.5rem; border-radius: 0.5rem; margin-bottom: 1rem; display: flex
}
.chat-message.user {
    background-color: #2b313e;
}
.chat-message.assistant {
    background-color: #475063;
}
.chat-message .avatar {
  width: 20%;
}
.chat-message .avatar img {
  max-width: 78px;
  max-height: 78px;
  border-radius: 50%;
  object-fit: cover;
}
.chat-message .content {
  width: 80%;
  padding-left: 10px;
}
</style>
""", unsafe_allow_html=True)

# 创建标题
st.title("AI聊天助手 🤖")

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

# 显示聊天历史
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# 获取用户输入
prompt = st.chat_input("请输入您的问题...")

# 处理用户输入
if prompt:
    # 添加用户消息到历史
    st.session_state.messages.append({"role": "user", "content": prompt})

    # 显示用户消息
    with st.chat_message("user"):
        st.markdown(prompt)

    # 显示助手回复(正在处理)
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        message_placeholder.markdown("思考中...")

        try:
            # 调用OpenAI API
            client = OpenAI()
            response = client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=[
                    {"role": m["role"], "content": m["content"]}
                    for m in st.session_state.messages
                ],
                stream=True
            )

            # 处理流式响应
            full_response = ""
            for chunk in response:
                if chunk.choices[0].delta.content is not None:
                    full_response += chunk.choices[0].delta.content
                    message_placeholder.markdown(full_response + "▌")

            # 更新最终响应
            message_placeholder.markdown(full_response)

            # 将助手回复添加到历史
            st.session_state.messages.append({"role": "assistant", "content": full_response})

        except Exception as e:
            message_placeholder.markdown(f"出错了: {str(e)}")

高级功能

1. 页面布局

Streamlit提供灵活的布局选项:

# 列布局
col1, col2, col3 = st.columns(3)
with col1:
    st.header("列1")
    st.image("img1.jpg")
with col2:
    st.header("列2")
    st.image("img2.jpg")
with col3:
    st.header("列3")
    st.image("img3.jpg")

# 标签页
tab1, tab2, tab3 = st.tabs(["猫", "狗", "猪"])
with tab1:
    st.header("喵星人")
    st.image("cat.jpg")
with tab2:
    st.header("汪星人")
    st.image("dog.jpg")
with tab3:
    st.header("佩奇")
    st.image("pig.jpg")

# 可展开内容
with st.expander("点击查看更多信息"):
    st.write("这里是隐藏的详细信息...")
    st.image("details.jpg")

2. 缓存和会话状态

优化性能并保持状态:

# 缓存计算密集型函数
@st.cache_data
def load_large_dataset():
    # 假设这是一个耗时的数据加载操作
    import time
    time.sleep(3)  # 模拟耗时操作
    return pd.DataFrame({"data": range(100)})

data = load_large_dataset()  # 第一次调用会执行,后续调用会使用缓存

# 使用会话状态
if "counter" not in st.session_state:
    st.session_state.counter = 0

if st.button("点击增加计数"):
    st.session_state.counter += 1

st.write(f"计数: {st.session_state.counter}")

3. 文件上传和下载

处理文件上传和生成下载:

# 文件上传
uploaded_file = st.file_uploader("选择一个CSV文件", type="csv")
if uploaded_file is not None:
    data = pd.read_csv(uploaded_file)
    st.dataframe(data)

    # 数据处理
    processed_data = data.describe()

    # 提供下载
    csv = processed_data.to_csv()
    st.download_button(
        label="下载处理后的数据",
        data=csv,
        file_name="processed_data.csv",
        mime="text/csv"
    )

实际案例:Agent控制面板

现在,让我们构建一个完整的Agent控制面板,将我们之前学到的内容整合在一起:

import streamlit as st
import pandas as pd
import time
import random
from langchain_openai import ChatOpenAI
from langchain.agents import AgentType, initialize_agent
from langchain.tools import Tool
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from dotenv import load_dotenv
import os

# 加载环境变量
load_dotenv()

# 页面配置
st.set_page_config(
    page_title="Agent控制中心",
    page_icon="🤖",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 自定义CSS
st.markdown("""
<style>
.big-font {
    font-size:24px !important;
}
.medium-font {
    font-size:18px !important;
}
.highlight {
    background-color: #f0f2f6;
    padding: 10px;
    border-radius: 10px;
}
.sidebar-content {
    padding: 1rem;
}
</style>
""", unsafe_allow_html=True)

# 侧边栏设置
with st.sidebar:
    st.image("https://i.imgur.com/m6YGrXo.png", width=100)
    st.markdown("# Agent控制中心")
    st.markdown("---")

    # 模型选择
    model_name = st.selectbox(
        "选择LLM模型",
        ["gpt-3.5-turbo", "gpt-4"]
    )

    # 温度设置
    temperature = st.slider("温度", 0.0, 1.0, 0.7, 0.1)

    # 工具选择
    st.markdown("### 启用工具")
    use_search = st.checkbox("网络搜索", True)
    use_calculator = st.checkbox("计算器", True)
    use_weather = st.checkbox("天气查询", True)

    # Agent类型
    agent_type = st.radio(
        "Agent类型",
        ["ReAct", "自定义", "结构化"]
    )

    st.markdown("---")
    st.markdown("### 会话记录")
    if "total_tokens" not in st.session_state:
        st.session_state.total_tokens = 0
    if "total_cost" not in st.session_state:
        st.session_state.total_cost = 0

    st.markdown(f"总Token数: {st.session_state.total_tokens}")
    st.markdown(f"估计成本: ${st.session_state.total_cost:.4f}")

    if st.button("清除对话历史"):
        st.session_state.messages = []
        st.session_state.agent_thoughts = []
        st.session_state.total_tokens = 0
        st.session_state.total_cost = 0

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

# 主界面
st.title("AI Agent交互界面 🤖")

# 分栏布局
chat_col, debug_col = st.columns([3, 2])

# 聊天区域
with chat_col:
    st.markdown("### 对话区")

    # 显示聊天历史
    chat_container = st.container()
    with chat_container:
        for message in st.session_state.messages:
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    # 用户输入
    user_input = st.chat_input("请输入您的问题...")

    if user_input:
        # 添加用户消息到历史
        st.session_state.messages.append({"role": "user", "content": user_input})

        # 重新显示所有消息(包括新的用户消息)
        with chat_container:
            with st.chat_message("user"):
                st.markdown(user_input)

        # 模拟Agent思考和工具调用
        with debug_col:
            st.markdown("### Agent思考过程")
            thinking_container = st.empty()

            # 模拟Agent的思考过程
            thoughts = []
            for i in range(3):  # 模拟3个思考步骤
                if agent_type == "ReAct":
                    thought = f"**思考 {i+1}:** 我需要理解用户的问题 '{user_input[:20]}...',并决定下一步行动。"
                    thoughts.append(thought)
                    thinking_container.markdown("\n\n".join(thoughts))
                    time.sleep(1)

                    # 模拟工具使用
                    if i == 1 and (use_search or use_calculator or use_weather):
                        tool_name = random.choice(
                            [t for t, enabled in [("搜索", use_search), ("计算器", use_calculator), ("天气", use_weather)] if enabled]
                        )
                        tool_thought = f"**工具调用:** 我将使用{tool_name}工具来获取必要信息。"
                        thoughts.append(tool_thought)
                        thinking_container.markdown("\n\n".join(thoughts))
                        time.sleep(1)

                        tool_result = f"**工具结果:** 已获取关于'{user_input[:15]}...'的相关信息。"
                        thoughts.append(tool_result)
                        thinking_container.markdown("\n\n".join(thoughts))
                        time.sleep(1)

            final_thought = "**最终决策:** 根据收集的信息,我现在可以为用户生成一个有帮助的回复。"
            thoughts.append(final_thought)
            thinking_container.markdown("\n\n".join(thoughts))

            # 保存思考过程
            st.session_state.agent_thoughts.append({"query": user_input, "thoughts": thoughts})

        # 显示助手回复(正在处理)
        with chat_container:
            with st.chat_message("assistant"):
                message_placeholder = st.empty()
                message_placeholder.markdown("思考中...")

                # 模拟处理延迟
                time.sleep(2)

                # 生成回复(实际应用中应该调用真实的LLM和Agent)
                if model_name == "gpt-3.5-turbo":
                    try:
                        # 创建并配置LLM
                        llm = ChatOpenAI(model=model_name, temperature=temperature)

                        # 假设工具设置
                        tools = []
                        if use_calculator:
                            tools.append(Tool(
                                name="计算器",
                                func=lambda x: eval(x),
                                description="用于执行数学计算"
                            ))

                        # 根据agent_type设置不同的Agent
                        if agent_type == "ReAct":
                            agent_chain = initialize_agent(
                                tools=tools,
                                llm=llm,
                                agent=AgentType.REACT_DESCRIPTION,
                                verbose=True
                            )
                        else:
                            # 简单的LLM链作为后备
                            template = """
                            作为一个有帮助的AI助手,请回答用户的问题:

                            用户问题: {input}

                            回答:
                            """
                            prompt = PromptTemplate(template=template, input_variables=["input"])
                            agent_chain = LLMChain(llm=llm, prompt=prompt)

                        # 执行Agent
                        response = agent_chain.invoke({"input": user_input})
                        response_text = response.get("output", response.get("text", "我不确定如何回答这个问题。"))

                        # 更新Token使用量(这里使用模拟数据)
                        tokens_used = len(user_input.split()) + len(response_text.split()) 
                        st.session_state.total_tokens += tokens_used
                        # 粗略估计成本
                        st.session_state.total_cost += tokens_used * 0.000002  # 每token约0.002美分

                        # 显示回复
                        full_response = response_text
                    except Exception as e:
                        full_response = f"处理请求时出错: {str(e)}"
                else:
                    # 模拟GPT-4回复
                    full_response = f"这是对'{user_input}'的模拟GPT-4回复。在实际应用中,这将调用真实的GPT-4 API。"

                # 更新显示
                message_placeholder.markdown(full_response)

                # 添加助手回复到历史
                st.session_state.messages.append({"role": "assistant", "content": full_response})

# 调试和历史区域
with debug_col:
    if len(st.session_state.agent_thoughts) > 0:
        st.markdown("### 历史思考过程")

        # 显示所有历史思考过程
        for i, thought_record in enumerate(st.session_state.agent_thoughts):
            with st.expander(f"问题 {i+1}: {thought_record['query'][:30]}..."):
                for thought in thought_record["thoughts"]:
                    st.markdown(thought)
                    st.markdown("---")

    # 系统状态
    st.markdown("### 系统状态")
    stats_container = st.container()
    with stats_container:
        col1, col2 = st.columns(2)
        with col1:
            st.metric("API调用次数", len(st.session_state.messages) // 2)
            st.metric("平均响应时间", "1.2秒")
        with col2:
            st.metric("成功率", "98%")
            st.metric("活跃会话", "1")

部署Streamlit应用

本地部署

在本地运行Streamlit应用非常简单:

streamlit run app.py

这会在本地启动一个Web服务器,并自动打开浏览器访问应用。

云端部署

Streamlit提供了一个名为Streamlit Cloud的托管服务,让你可以免费部署应用:

  1. 将代码推送到GitHub仓库
  2. 在Streamlit Cloud中注册并连接你的GitHub账户
  3. 选择仓库和主文件(如app.py)
  4. 点击部署

几分钟内,你的应用就会有一个公开可访问的URL!

你也可以使用其他服务部署Streamlit应用,如Heroku、AWS或Azure。

最佳实践

  1. 模块化代码:将复杂逻辑分割成函数和类,提高可维护性
  2. 使用缓存:对计算密集型函数使用@st.cache_data装饰器
  3. 响应式设计:考虑不同屏幕尺寸,使用columns和容器布局
  4. 错误处理:添加try-except块,优雅地处理API错误
  5. 会话管理:合理使用st.session_state,管理用户会话
  6. 性能优化:减少不必要的重新计算,延迟加载大型资源
  7. 用户体验:添加进度条和反馈信息,提升用户体验

下一步

现在你已经掌握了Streamlit的基础知识,接下来可以:

  • 创建更复杂的多页面应用
  • 集成数据可视化库如Plotly和Altair
  • 添加用户认证和访问控制
  • 将你的Agent与Streamlit界面结合
  • 学习通过Streamlit Components扩展功能

通过Streamlit,你可以让你的AI Agent拥有一个用户友好的界面,真正发挥其潜力!🚀