第 46 课 | Streamlit Web 界面:让 Agent 系统拥有「脸面」

用 Streamlit 构建对话界面、配置面板、实时日志和成本看板——让非技术人员也能使用 Agent 系统,从命令行工具变成 Web 产品。


一、业务痛点:Agent 系统谁来用

到目前为止,我们的 Agent 系统都是这样运行的:

cd code/lesson-40-market-entry
uv run market_entry.py
# 输出一大段文本到终端

问题是:谁会这样用?

  • 老板不会打开终端
  • 市场部同事不知道什么是 uv run
  • 客户只看过 Web 界面

Agent 系统如果不能被目标用户使用,技术再好也是白费。Web 界面是产品化的第一步。

Streamlit 的优势

维度传统 Web 开发Streamlit
技术栈HTML + CSS + JS + 后端框架纯 Python
开发时间数天到数周10 分钟到数小时
前端知识必须零
部署复杂(Nginx + WSGI + …)一行命令
适合场景复杂交互数据应用、Agent 对话

Streamlit 是 Python 生态中最快构建 Web 界面的方式。


二、界面架构

Agent 后端

Streamlit Web 界面

💬 对话面板
聊天式交互

⚙️ 配置面板
模型/温度/Token

📋 实时日志
Agent 执行过程

📈 成本看板
Token 消耗统计

🤖 Agent 核心
LLM + Tools + Memory

🗄️ 日志数据库
SQLite


三、完整实现

3.1 基础对话界面

import streamlit as st
from agent_kit.llm_client import LLMClient
from agent_kit.config import Config

# 页面配置
st.set_page_config(
    page_title="AI 商业情报官",
    page_icon="🤖",
    layout="wide",
)

# 标题
st.title("🤖 AI 商业情报官")
st.caption("多 Agent 协作的商业决策支持系统")

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

# 显示历史消息
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.markdown(msg["content"])

# 用户输入
if prompt := st.chat_input("请输入你的问题..."):
    # 显示用户消息
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)
    
    # 调用 Agent
    with st.chat_message("assistant"):
        with st.spinner("Agent 思考中..."):
            response = agent.run(prompt)
            st.markdown(response["answer"])
            
            # 显示执行细节
            with st.expander("查看执行细节"):
                st.json({
                    "steps": response.get("steps", 0),
                    "tokens_used": response.get("tokens", 0),
                    "tools_called": response.get("tools", []),
                    "latency": response.get("latency", 0),
                })
    
    st.session_state.messages.append({"role": "assistant", "content": response["answer"]})
    st.session_state.total_tokens += response.get("tokens", 0)

3.2 配置面板(侧边栏)

# 侧边栏配置
with st.sidebar:
    st.header("⚙️ 配置")
    
    # 模型选择
    model = st.selectbox(
        "模型",
        options=["qwen2:14b", "qwen2:7b", "llama3:8b", "gpt-4o-mini"],
        index=0,
    )
    
    # 后端选择
    backend = st.selectbox(
        "后端",
        options=["ollama", "vllm", "openai", "deepseek"],
        index=0,
    )
    
    # 温度
    temperature = st.slider(
        "温度(创造性)",
        min_value=0.0,
        max_value=2.0,
        value=0.7,
        step=0.1,
        help="越高越有创造性,越低越保守",
    )
    
    # Max Token
    max_tokens = st.slider(
        "最大输出 Token",
        min_value=256,
        max_value=4096,
        value=2048,
        step=256,
    )
    
    st.divider()
    
    # 系统信息
    st.header("📊 系统状态")
    st.metric("总 Token 消耗", f"{st.session_state.total_tokens:,}")
    st.metric("消息数", len(st.session_state.messages))
    
    if st.button("🗑️ 清空对话"):
        st.session_state.messages = []
        st.session_state.total_tokens = 0
        st.rerun()

3.3 实时日志面板

# 右侧日志面板
with st.expander("📋 实时日志", expanded=False):
    log_container = st.container()
    
    def add_log(message: str, level: str = "info"):
        """添加日志条目"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        color_map = {
            "info": "blue",
            "success": "green",
            "warning": "orange",
            "error": "red",
        }
        color = color_map.get(level, "blue")
        
        with log_container:
            st.markdown(f"`[{timestamp}]` :{color}[{message}]")

3.4 成本看板

# 成本计算
PRICING = {
    "qwen2:1.5b": 0.0001,
    "qwen2:7b": 0.0003,
    "qwen2:14b": 0.0008,
    "llama3:8b": 0.0005,
    "gpt-4o-mini": 0.0015,
}

def calculate_cost(model: str, tokens: int) -> float:
    """计算成本"""
    price_per_1k = PRICING.get(model, 0.0005)
    return tokens / 1000 * price_per_1k

# 在侧边栏显示成本
with st.sidebar:
    st.divider()
    st.header("💰 成本估算")
    current_cost = calculate_cost(model, st.session_state.total_tokens)
    st.metric("当前会话成本", f"¥{current_cost:.4f}")

四、完整界面布局

┌──────────────────────────────────────────────────────┐
│  🤖 AI 商业情报官                                      │
│  多 Agent 协作的商业决策支持系统                         │
├────────────────────┬─────────────────────────────────┤
│ ⚙️ 配置(侧边栏)    │  💬 对话区                       │
│                    │                                  │
│ 模型: [qwen2:14b]  │  👤 用户: 对比华东和华南的销售     │
│ 后端: [ollama]     │  🤖 Agent: 正在分析...            │
│ 温度: [0.7 ┄──]   │                                  │
│ MaxToken: [2048]   │  📋 实时日志 [展开]               │
│                    │  [10:00:01] 开始处理查询           │
│ 📊 系统状态         │  [10:00:02] 调用 query_database   │
│ Token: 12,450      │  [10:00:03] 查询完成,3条记录      │
│ 消息数: 8          │                                  │
│                    │  📈 成本统计                      │
│ 💰 成本             │  Token: 2,450 | 成本: ¥0.002     │
│ ¥0.012             │                                  │
└────────────────────┴─────────────────────────────────┘

五、运行

cd code/lesson-46-streamlit-web
uv sync
uv run streamlit run streamlit_app.py

浏览器打开 http://localhost:8501


六、本课小结

  1. Web 界面是 Agent 系统产品化的第一步——让非技术人员也能使用
  2. Streamlit 纯 Python 开发,10 分钟构建可用界面
  3. 核心组件:对话面板 + 侧边栏配置 + 实时日志 + 成本看板
  4. 会话状态管理(session_state)是 Streamlit 的核心机制

配套代码:code/lesson-46-streamlit-web/streamlit_app.py

Logo

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

更多推荐