Streamlit — Python-фреймворк для data-приложений, который идеально подходит для аналитических AI-агентов. Строим RAG-дашборд, табличный анализатор с естественным языком и мультиагентный оркестратор за 30 минут.
Архитектура: Streamlit-дашборд с сайдбаром, чатом, кэшированным агентом и аналитической визуализацией
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})
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})
Самый популярный сценарий 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)
Классический 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])
Для сложных проектов создаём несколько специализированных агентов и распределяем их по вкладкам 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.