Streamlit + AI агенты — QantCore
📱

Streamlit + AI агенты: дашборды, RAG и интерактивные приложения

Streamlit — Python-фреймворк для data-приложений, который идеально подходит для аналитических AI-агентов. Строим RAG-дашборд, табличный анализатор с естественным языком и мультиагентный оркестратор за 30 минут.

intermediate ⏱ 15 мин
Streamlit AI Agent Dashboard Architecture 📋 Sidebar st.sidebar 🔑 API Keys (secrets) 🤖 Model Selector 📁 File Upload ⚙️ Settings 📊 Session Stats 🧠 Main Dashboard Area 💬 Chat Tab 📊 Analytics Tab 🔧 Tools Tab st.chat_input / st.chat_message User: "Покажи продажи за Q1" Agent: "Вот график..." + Plotly chart 📈 st.plotly_chart Interactive viz 🗂️ st.dataframe Editable data @st.cache_resource → Agent + Vector Store (single init) 🔮 Output Pipeline Agent → SQL/Pandas DataFrame → Chart Rendered UI Легенда Sidebar Main Area Cache / Stream Viz Output

Архитектура: Streamlit-дашборд с сайдбаром, чатом, кэшированным агентом и аналитической визуализацией

# 1. Базовая структура: st.chat_input и кэширование агента

Streamlit добавляет нативные чат-компоненты: st.chat_input для ввода и st.chat_message для рендера сообщений. Ключевой момент — кэшировать инициализацию агента через @st.cache_resource, чтобы LLM и векторное хранилище не пересоздавались при каждом ререндере.

import streamlit as st
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

st.set_page_config(page_title="AI Agent Dashboard", layout="wide")
st.title("🤖 AI Agent Studio")

@st.cache_resource
def get_agent(model_name: str):
    """Кэшируем агента между ререндерами."""
    llm = ChatOpenAI(model=model_name, temperature=0)
    tools = [search_tool, calculator_tool, sql_tool]
    return create_react_agent(llm, tools)

# Инициализация истории сессии
if "messages" not in st.session_state:
    st.session_state.messages = []

# Отображаем историю
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)

    with st.chat_message("assistant"):
        agent = get_agent("gpt-4o")
        result = agent.invoke({"messages": [("user", prompt)]})
        response = result["messages"][-1].content
        st.markdown(response)
    st.session_state.messages.append({"role": "assistant", "content": response})

# 2. Стриминг токенов через st.write_stream

Streamlit 1.36+ поддерживает st.write_stream — нативный способ показывать токены по мере генерации. В комбинации с astream_events агента получаем эффект «печатной машинки» прямо в интерфейсе.

async def stream_response(prompt: str):
    agent = get_agent("gpt-4o")
    async for event in agent.astream_events(
        {"messages": [("user", prompt)]},
        version="v2"
    ):
        if event["event"] == "on_chat_model_stream":
            token = event["data"]["chunk"].content
            if token:
                yield token

# В основном потоке:
with st.chat_message("assistant"):
    response = st.write_stream(stream_response(prompt))
st.session_state.messages.append({"role": "assistant", "content": response})

# 3. Аналитический агент: SQL + Pandas через естественный язык

Самый популярный сценарий Streamlit + AI — «спроси данные на русском и получи график». Агент генерирует SQL или pandas-код, исполняет его и визуализирует результат через st.plotly_chart или st.dataframe.

import pandas as pd
import plotly.express as px
from langchain_experimental.tools import PythonAstREPLTool

df = pd.read_csv("sales_2024.csv")  # Продажи компании за год

python_repl = PythonAstREPLTool(locals={"df": df})
agent = create_react_agent(llm, [python_repl])

if prompt := st.chat_input("Спросите о данных..."):
    response = agent.invoke({"messages": [
        ("system", "Используй переменную df. После анализа построй график через Plotly."),
        ("user", prompt)
    ]})

    st.markdown(response["messages"][-1].content)

    # Визуализация результата
    if "chart_data" in response:
        fig = px.bar(response["chart_data"], x="month", y="revenue")
        st.plotly_chart(fig, use_container_width=True)

    st.dataframe(df.head(10), use_container_width=True)

# 4. RAG-дашборд: загрузка документов и семантический поиск

Классический RAG в Streamlit: пользователь загружает PDF/DOCX через st.file_uploader, документы векторизуются (через @st.cache_resource) и сохраняются в ChromaDB. Затем агент отвечает на вопросы по документам с цитированием источников.

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

with st.sidebar:
    uploaded_files = st.file_uploader(
        "📁 Загрузите документы",
        accept_multiple_files=True,
        type=["pdf", "docx", "txt"]
    )

@st.cache_resource
def build_vector_store(files):
    docs = []
    for f in files:
        if f.name.endswith(".pdf"):
            loader = PyPDFLoader(f)
            docs.extend(loader.load())
    return Chroma.from_documents(docs, OpenAIEmbeddings())

if uploaded_files:
    vector_store = build_vector_store(uploaded_files)
    retriever = vector_store.as_retriever(search_kwargs={"k": 5})

    if prompt := st.chat_input("Спросите по документам..."):
        docs = retriever.invoke(prompt)
        context = "\n\n".join(d.page_content for d in docs)
        answer = llm.invoke(f"Контекст:\n{context}\n\nВопрос: {prompt}")
        st.markdown(answer.content)

        with st.expander("📎 Источники"):
            for i, doc in enumerate(docs):
                st.caption(f"Источник {i+1}: {doc.metadata.get('source')}")
                st.text(doc.page_content[:300])

# 5. Мультиагентный оркестратор с вкладками (tabs)

Для сложных проектов создаём несколько специализированных агентов и распределяем их по вкладкам Streamlit: агент-аналитик, агент-копирайтер, агент-исследователь. Каждый — отдельный граф LangGraph со своими инструментами и памятью.

tab1, tab2, tab3 = st.tabs(["📊 Аналитик", "✍️ Копирайтер", "🔬 Исследователь"])

@st.cache_resource
def get_analyst():
    return create_react_agent(ChatOpenAI(model="gpt-4o"), [sql_tool, python_tool])

@st.cache_resource
def get_copywriter():
    return create_react_agent(ChatOpenAI(model="claude-3-opus"), [style_checker, seo_tool])

@st.cache_resource
def get_researcher():
    return create_react_agent(ChatOpenAI(model="gpt-4o"), [search_tool, summarizer])

with tab1:
    st.header("Аналитик данных")
    if q := st.chat_input("Запрос для аналитика...", key="analyst"):
        result = get_analyst().invoke({"messages": [("user", q)]})
        st.write(result["messages"][-1].content)

with tab2:
    st.header("Копирайтер")
    if brief := st.text_area("Бриф на текст", key="copywriter"):
        result = get_copywriter().invoke({"messages": [("user", brief)]})
        st.markdown(result["messages"][-1].content)

with tab3:
    st.header("Исследователь")
    if topic := st.chat_input("Тема для исследования...", key="researcher"):
        result = get_researcher().invoke({"messages": [("user", topic)]})
        st.markdown(result["messages"][-1].content)
✅ Итог

Streamlit — идеальный фреймворк для AI-дашбордов: нативные чат-компоненты, кэширование ресурсов, стриминг токенов и богатая экосистема визуализации. Мы построили RAG-дашборд с загрузкой документов, аналитического агента с SQL/Pandas и мультиагентный оркестратор на вкладках. Деплой — одна команда: streamlit deploy. Дальше — аутентификация через st.login и Stateful-сессии через st.session_state.