如何使用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数据生态系统无缝协作
安装与设置
要验证安装成功,可以运行Streamlit的示例应用:
这会启动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提供了丰富的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应用非常简单:
这会在本地启动一个Web服务器,并自动打开浏览器访问应用。
云端部署
Streamlit提供了一个名为Streamlit Cloud的托管服务,让你可以免费部署应用:
- 将代码推送到GitHub仓库
- 在Streamlit Cloud中注册并连接你的GitHub账户
- 选择仓库和主文件(如app.py)
- 点击部署
几分钟内,你的应用就会有一个公开可访问的URL!
你也可以使用其他服务部署Streamlit应用,如Heroku、AWS或Azure。
最佳实践
- 模块化代码:将复杂逻辑分割成函数和类,提高可维护性
- 使用缓存:对计算密集型函数使用
@st.cache_data
装饰器 - 响应式设计:考虑不同屏幕尺寸,使用columns和容器布局
- 错误处理:添加try-except块,优雅地处理API错误
- 会话管理:合理使用st.session_state,管理用户会话
- 性能优化:减少不必要的重新计算,延迟加载大型资源
- 用户体验:添加进度条和反馈信息,提升用户体验
下一步
现在你已经掌握了Streamlit的基础知识,接下来可以:
- 创建更复杂的多页面应用
- 集成数据可视化库如Plotly和Altair
- 添加用户认证和访问控制
- 将你的Agent与Streamlit界面结合
- 学习通过Streamlit Components扩展功能
通过Streamlit,你可以让你的AI Agent拥有一个用户友好的界面,真正发挥其潜力!🚀