博客配套代码发布于github:13 Streamlit 快速入门

相关Agent专栏:Ai Agent教学

本系列所有博客均配套Gihub开源代码,开箱即用,仅需配置API_KEY。

如果该Agent教学系列帮到了你,欢迎给我个Star⭐

知识点:Streamlit | st.session_state | Streamlit组件 | 异步事件流 | 会话持久化


前言、

在 M12 中,我们基于 Agents SDK 实现了一套 Swarm 风格的多智能体系统:多个 Agent 能自动转接、共享上下文、协同调用工具——但它还只是“看不见的引擎”。
现在,是时候给它装上仪表盘、状态灯和操作杆,变成一个真正可交互的 AI 产品

可别误以为这个前端的架子学起来很吃力:

对 Agent 开发者而言,Streamlit 就是你的前端答案:无需 HTML/CSS/JS,只需写 Python,就能获得:

  • 🖨️ 流式打字机效果(逐字输出 LLM 回复)
  • 🔄 动态状态栏(实时显示 Agent 转接、工具调用)
  • 📊 侧边监控面板(追踪当前Agent、用户上下文)

本章承诺:哪怕你从未用过 Streamlit,30 分钟后,你将拥有一个前后端完全联通、可演示、可交付的多智能体可视化驾驶舱——后端由 Agents SDK 驱动,前端由 Streamlit 渲染


一、什么是 Streamlit?为什么它是 Agent 开发者的前端首选?

如果 Agent 全栈开发者只能掌握一个前端技术,那一定是 Streamlit。

这不是因为它“简单”,而是因为它的运行模型与 Agent 的本质高度一致

Agent 是什么?

它不是一个“输入→输出”的函数,而是一个有状态、可中断、能协作的过程

  • 它会思考 → 暂停 → 调用工具 → 转接给另一个 Agent → 等待人工确认 → 继续执行……

这种长周期、多步骤、带中间状态的行为,恰恰是传统 REST API 难以表达的——REST 天生为“无状态、一次性请求”设计。

Streamlit 呢?

它彻底跳出了“前后端分离”的思维,把 Web 应用变成了一种 “可交互的 Python 脚本”

你写的 Python 代码
    ↓
Streamlit 运行时(Runtime)
    ↓
自动渲染成网页界面

没有 HTML,没有 JS,没有 API 路由——你只需写:“当程序运行到这里时,把这句话展示给用户。”

Streamlit 的特殊性在于:你永远不需要思考“这个按钮放左边还是右边”、“气泡怎么对齐”、“侧边栏宽度多少”——你只需按逻辑顺序写下“这里要显示什么”,它就会自动生成一个美观、响应式的界面。


这种“所想即所得”的开发体验,正是它成为 Agent 快速交付首选的原因。

Agent 与 Streamlit 的对应逻辑:

Agent 特性 Streamlit 对应机制
有状态的思考过程 st.session_state 保存上下文
多步执行(暂停/继续) 用户交互触发脚本从头重跑,但状态保留
可视化中间步骤 声明式 UI 组件(如 st.status)实时更新

🍽️ 打个比方

  • REST API 像快餐店:下单 → 等待 → 取餐(每次都是全新会话
  • Streamlit 像包厢餐厅:点菜 → 上菜 → 加菜 → 结账,服务员全程记得你的需求(状态连续
那它只是“Demo 工具”吗?

很多人说 Streamlit 不适合生产,这是对场景的误解

  • 如果你要做百万并发的 ToC 产品?确实不合适。
  • 但如果你要做:
    • 内部 Agent 工具
    • 客户定制化演示
    • 快速交付 MVP
    • 多智能体系统的可视化调试面板

Streamlit 就是最高效、最低摩擦的交付方式

我们学它,不是因为“它好学”(虽然确实好学),
而是因为——它天生为 Agent 这类过程型系统而生

我们先掌握Streamlit的几个组件,用以搭建最基础的框架。


二、快速上手:掌握Streamlit的七种武器

Streamlit 的代码极其简洁——你不需要设计页面结构,只需按执行顺序声明“这里要显示什么”

而要打造一个具备流式输出、状态追踪、上下文记忆的现代 Agent 界面,仅靠基础组件是不够的。
Streamlit 提供了一套精巧的“七种武器”,分别对应 Agent 交互中的核心能力:

能力维度 组件 作用
交互输入 st.chat_input 用户输入入口
对话展示 st.chat_message 渲染聊天气泡
状态反馈 st.status + st.empty 展示思考过程 & 打字机效果
记忆中枢 st.session_state 跨轮次保存对话与 Agent 状态
全局控制 st.sidebar + st.rerun 配置面板 & 主动刷新

下面进行逐一详解。


环境搭建

pip install streamlit

在 Python 文件开头引入:

import streamlit as st

1. 交互输入:st.chat_input (输入框)

专为对话场景设计的输入框,回车后自动触发逻辑。

prompt = st.chat_input("请输入问题")
if prompt:
    # 处理用户输入
    st.write(f"你输入了: {prompt}")

⚠️ 注意:用户输入后,整个脚本会从头重新运行(这是 Streamlit 的核心机制)。


2. 对话展示:st.chat_message

自动生成类似 ChatGPT 的对话气泡,自动区分用户与机器人,支持头像定制。

# 用户消息
with st.chat_message("user", avatar="👤"):
    st.write("你是人吗")

# 助手回复
with st.chat_message("assistant", avatar="🤖"):
    st.write("似乎不太像是人")


3. 状态反馈:保存或展示当前状态

st.status (折叠状态栏)

将复杂的中间步骤收纳起来,避免界面杂乱。

with st.status("Agent 正在思考...",expanded=True): # expanded:默认是否打开折叠栏
    st.write("正在查询数据库...")
    st.write("正在转接专员...")
    # 最终更新状态
    st.success("处理完成") 

✅ 小技巧:后续可通过 status.update(label="新标题", state="complete") 动态更新状态。

st.empty(动态占位符)

先占一个位置,后续可反复更新内容,实现逐字输出(流式效果)。

import time
response = st.empty()
for msg in ["处理中...", "完成!"]:
    response.markdown(msg)
    time.sleep(1)

4. 记忆中枢:st.session_state

解决“页面刷新就失忆”的问题,本质是一个持久化字典

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

# 读写
st.session_state.messages.append({"role":"user","content":prompt})

✅ 所有跨轮次的状态(对话历史、当前 Agent、用户画像)都应存于此。

5. 全局控制:

st.sidebar(侧边监控面板)

用于放置配置项、用户信息或系统状态,类似 VS Code 左侧栏。

with st.sidebar:
    st.write("我是侧边栏")
    st.json({"用户": "张三"}) # 自动把字典渲染成漂亮的 JSON 树
st.rerun(手动触发刷新)

用于在状态更新后立即反映到界面,但应该谨慎使用。

st.rerun() 会立即终止当前脚本执行,并从头重新运行整个应用。

它适用于状态已变更但 UI 尚未反映的场景,但在 Agent 聊天应用中通常不需要

✅ 唯一推荐用法:清空对话
if st.button("🗑️ 清空对话"):
    st.session_state.messages = []
    st.rerun()  # 立即刷新界面,清除所有气泡
❌ 为什么聊天流程中不用?
  • Streamlit 是同步执行模型:代码从上到下跑完后才渲染 UI,AI 回复自然会显示;
  • 用户输入后,自动触发整页重跑,历史消息通过 st.session_state 正确还原;
  • 额外调用 st.rerun() 反而会导致页面闪烁或重复输出。

💡 记住:除“清空对话”等极少数操作外,Agent 应用无需手动刷新。让 Streamlit 的自然执行流接管一切。

下一步:用七种武器,搭建静态骨架

现在,你已掌握构建生产级 Agent 界面所需的全部能力单元。
接下来,我们将先用其中 4 个(sidebar、chat_message、chat_input、status)搭出一个静态 UI 骨架,再逐步注入其余武器,让它真正活起来。

三、实战一:搭建静态的UI骨架

有了前面的装备,我们只需 20 行代码,就能快速搭出一个完整的 Agent 界面骨架——先不管逻辑,只关注布局

import streamlit as st

st.set_page_config(page_title="智能客服驾驶舱",layout="wide") # 标签页的命名
st.title("✈️ 智能航天客服 Swarm") # 标题

# 侧边栏
with st.sidebar:
    st.header("📦 驾驶舱监控") # 侧边栏标题
    st.info("当前坐席:前台 TriageAgent") # 侧边栏高亮信息
    st.subheader("用户画像") # 侧边栏副标题
    st.json({"name":"张三","vip":True})

# 画聊天历史(模拟)
with st.chat_message("user",avatar="👤"): # 用一个小表情代表发言人头像
    st.write("我要退票")

with st.chat_message("assistant",avatar="🤖"):
    st.write("好的,为您转接退票专员...")

# 画输入框
prompt = st.chat_input("请输入您的问题")
if prompt:
    # 当用户输入后,页面会刷新,显示下面的内容
    with st.chat_message("user",avatar="👤"):
        st.write(prompt)

▶ 运行与验证

在终端执行:

streamlit run app.py

Streamlit 会启动一个本地 Web 服务,并自动打开浏览器。

你会看到:

  • 顶部标题 ✈️ 智能航空客服 Swarm
  • 左侧 驾驶舱监控面板 显示坐席状态与用户画像
  • 中间是模拟的对话历史
  • 底部是可输入的聊天框

🔁 重要机制提醒
Streamlit 会在每次用户交互(如输入、点击)后,从头重新运行整个脚本,并根据

st.session_state 和 当前输入 重建 UI。
因此,修改代码后只需刷新网页,即可看到最新效果(无需重启服务)。

下一步:注入“灵魂”

这个界面目前是静态的——它不会调用 Agent,也不会真正处理请求。
但它已经具备了完整的产品形态。
接下来,我们将把 M12 中基于 Agents SDK 构建的 Swarm 引擎接入进来,让这个骨架真正活起来

四、实战二:接入Agents SDK —— 构建首个真正联通的 Agent 产品

现在,我们把前面搭好的 UI 骨架,真正连上后端智能体。

目标很明确:用最少的代码,验证 Streamlit 能驱动 Agents SDK —— 不搞流式、不搞异步,先跑通再说。

思路

用户输入 → 追加到历史 → 同步调用 Agent(会卡几秒)→ 拿到结果 → 更新界面。

虽然简单,但这一步意味着:你的 Agent 不再只是终端里的脚本,而是一个可交互的产品原型

完整代码:

import streamlit as st
from agents import Runner, set_tracing_disabled
from agents.agent import Agent
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
from openai import AsyncOpenAI
from config import OPENAI_API_KEY

set_tracing_disabled(True) # 去掉tracing

# 初始化模型
client = AsyncOpenAI(api_key=OPENAI_API_KEY,base_url="https://api.deepseek.com")
model = OpenAIChatCompletionsModel(model="deepseek-chat",openai_client=client)

# 定义一个通用Agent
smart_agent = Agent(
    name="SmartAssistant",
    instructions="""
        你是航空公司智能客服助手,客户姓名:张三(白金会员),航班号:CA1234。

        你具备以下能力:
        - 用户说“退票”“退款”“取消” → 回复:✅ 退款申请已提交,预计3个工作日内原路返回。
        - 用户说“改签”“换航班” → 回复:✅ 明日同航线航班尚有余座,已为您预留,可随时确认改签。
        - 其他任何问题 → 礼貌、专业地直接回答

        回复要简洁、自然、带表情符号,让用户感到温暖。
        """,
    model=model
)

# Streamlit UI组件
st.set_page_config(page_title="智能客服驾驶舱",layout="wide") # 标签页命名
st.title("✈️ 智能航天客服 Swarm") # 页面标题

# 侧边栏:驾驶舱监控(先固定不变动)
with st.sidebar:
    st.header("🖥️ 驾驶舱监控")
    st.success("当前坐席: 智能助理 SmartAssistant 🤖") # success用作高亮块显示
    st.subheader("用户画像")
    st.json({"user_name": "张三(白金会员)", "flight_no": "CA1234"})

# 会话状态与历史消息
if "messages" not in st.session_state:# 首次运行时,初始化空列表,用于存储聊天消息。
    st.session_state["messages"] = []

for msg in st.session_state["messages"]:# 重新渲染历史消息,确保聊天上下文在页面刷新后依然可见
    avatar = "👤" if msg['role'] == 'user' else "🤖"
    with st.chat_message(msg['role'],avatar=avatar):
        st.write(msg["content"])

# 用户输入与核心交互
prompt = st.chat_input("请输入您的问题(试试:我要退票 / 想改签 / 你好)")
if prompt:
    # 显示用户信息
    st.session_state.messages.append({"role":"user","content":prompt})
    with st.chat_message("user",avatar="👤"):
        st.write(prompt)

    # 调用Agents SDK
    with st.spinner("思考中..."): # 执行耗时操作时,显示旋转的加载动画
        result = Runner.run_sync( # 同步运行智能体
            smart_agent, # 参数:agent
            st.session_state.messages # 参数:对话历史
        )

    # 显示AI回复
    reply = result.final_output # 最终AI回复内容
    st.session_state.messages.append({"role":"assistant","content":reply})
    with st.chat_message("assistant",avatar="🤖"):
        st.write(reply)

💡 注意:

  • 使用 Runner.run_sync() 做同步调用,便于调试;
  • 通过 st.session_state.messages 保持对话上下文;

运行测试:

下一步:扩充能力

这个版本是“能跑就行”的最小闭环。
但它有几个明显短板:

  1. Agent 是固定的,无法像 M12 那样动态转接;
  2. 全程阻塞,用户体验卡顿。
  3. 无法调用任何工具。

下一章,我们就把 M12 的完整 Swarm 引擎(含多 Agent + 工具调用)接入进来,并升级为流式输出 + 动态状态反馈,让这个驾驶舱真正“活”起来。


五、实战三:工程化的Agent产品 —— 打造可交付的Swarm驾驶舱

目标:在上一章同步 MVP 基础上,升级为 支持多 Agent 动态转接 + 工具调用 + 流式输出 的生产级界面。

📚 新增四大能力
  1. 动态坐席显示:侧边栏随 Agent 切换实时更新
  2. 流式打字机效果:逐字输出,提升响应感
  3. 工具调用可视化:显示“正在查座位”“正在退款”等过程
  4. 会话持久化:对话历史存入数据库,支持多用户隔离

🔧 关键挑战与解法

挑战 1:Streamlit 是同步的,如何跑异步 Agent?

问题:Streamlit 底层基于异步,但用户代码默认是同步执行。若直接调用 async 函数会报错,其本质是异步无法嵌套异步。

解法:用 nest_asyncio.apply() 允许嵌套异步

import nest_asyncio
nest_asyncio.apply()  # ← 加这一行即可

💡 这是 Streamlit + 异步 Agent 的标准搭配,不可省略。


挑战 2:如何让侧边栏“动态变色”?

问题:Streamlit 的 UI 是按执行顺序生成的。一旦某行 st.write("A") 执行完 ,它就“固化”在页面上,无法后续修改。后面再写也只会追加而不是更新原文本。

正确做法:用 `st.empty()` 创建一个**可重复更新的占位符**,并封装渲染逻辑。

解法占位符 + 渲染函数

# 1. 创建占位符(只创建一次)
agent_status_placeholder = st.empty()

# 2. 定义渲染函数(统一控制样式和逻辑)
def render_agent_status(placeholder, agent):
    if agent.name == "RefundAgent":
        placeholder.success("当前坐席: 退票专员 🚨")
    # ...其他类型

# 3. 在 Agent 切换时调用
render_agent_status(agent_status_placeholder, new_agent)

为什么必须用函数:
因为 Agent 切换可能发生在多个地方(如流式事件、按钮点击),用函数可以确保所有场景下显示规则一致,避免重复代码。

原则静态 UI 直接写,动态 UI 用占位符


挑战 3:如何监听 Agent 的流式事件?

问题:我们需要同时处理:文本流、Agent 切换、工具调用。

解法:复用 M12模块 的事件循环逻辑(s03_main.py),但对接 Streamlit UI

async for event in stream.stream_events():
    if event.type == "raw_response_event":
        # 实时更新打字机效果
        reply += delta
        message_placeholder.markdown(reply + "▌")
    
    elif event.type == "agent_updated_stream_event":
        # 更新侧边栏 + 记录日志
        st.session_state.current_agent = new_agent
        render_agent_status(agent_status_placeholder, new_agent)
    
    elif event.type == "tool_called":
        # 显示工具调用
        status.write(f"🔧 调用: {tool_name}")

为什么必须用事件循环:

因为 Agents SDK 的 stream_events() 是一个异步生成器,它逐个发出事件。只有通过 async for 才能逐条处理,实现“边思考边输出”。(这点在12模块已经有所提及)

核心思想:把 M12 的终端打印,换成 Streamlit 的 UI 更新。


挑战 4:如何持久化多用户会话?

问题:st.session_state 只在单次浏览器会话有效。用户刷新页面、关闭标签或换设备,历史记录就会丢失。

解法:引入 SQLiteSession ,将对话自动存入本地数据库。

# 每个用户分配唯一 session_id(例如基于时间戳或用户 ID)
if "session_id" not in st.session_state:
    st.session_state.session_id = str(uuid.uuid4())

# 初始化持久化会话(首次创建,后续自动加载历史)
if "session" not in st.session_state:
    st.session_state.session = SQLiteSession(
        session_id=st.session_state.session_id,
        db_path="./conversations.db"
    )

💡 关键机制:

  • SQLiteSession 在初始化时自动从数据库加载该 session_id 的历史消息;
  • 每次调用 stream.run(..., session=...) 时,新消息会自动追加保存,无需手动调用 .save()。
  • 不同 session_id 对应不同用户,天然实现多用户数据隔离。

注:为保持章节聚焦,本章未展示完整代码。
所有功能均基于 M12 模块的 s01_tools.pys02_agent.py 实现,前端仅负责 UI 集成与事件响应。

完整可运行代码已开源:https://github.com/Annyfee/agent-craft

👍 最终效果:

Agent 能动态切换坐席调用工具查询酒店/MCP服务,并实时展示调试日志——一个真正可交付的 Swarm 驾驶舱。


⚠️ 已知限制:页面闪烁

由于 Streamlit 的重跑机制,复杂 UI 在异步更新时可能出现轻微闪烁。
这是框架当前限制,不影响实际功能,优先保证逻辑正确性。


✅ 总结:本章交付了什么?

能力 实现方式
多 Agent 动态转接 agent_updated_stream_event + 占位符更新
工具调用可视化 tool_called 事件 + st.status
流式输出 st.empty() + 逐字更新
会话持久化 SQLiteSession + session_id

现在,你拥有了一个接近真实产品的 Swarm 驾驶舱——它不仅能思考,还能协作、调用工具、记住历史。

总结:前端篇收官,全栈能力已就绪:

至此,M13 的三个实战已完整覆盖 Streamlit 与 Agents SDK 的集成方法
我们不仅能快速搭建可视化界面,更能构建出具备 多 Agent 协作、工具调用、流式交互 能力的智能体产品原型。

更重要的是——
从 LangChain 到 LangGraph,从 RAG 到 MCP,从后端逻辑到前端交互,Agent 全栈技术栈已全部打通

预告:14篇 综合实战项目

🔜 下一篇将是我们的「综合实战」毕业项目
我们将融合所有已学技术,打造一个贴近真实场景的产品级 Agent 系统。

  • 它支持多用户、可持久化、能调用外部服务
  • 笔者将部署在线版本,供你体验
  • 同时提供一键部署脚本,你可轻松复现并二次开发

这不仅是一次总结,更是一次交付——
属于你的第一个全栈 Agent 产品,即将上线。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐