Construir interfaces de usuario para aplicaciones de Modelos de Lenguaje Grande (LLM) puede ser desafiante, especialmente cuando quieres enfocarte en la lógica de IA en lugar del desarrollo frontend. Streamlit resuelve este problema proporcionando un framework simple basado en Python que transforma scripts de datos en aplicaciones web interactivas en minutos.
Esta guía te llevará a través del proceso de crear interfaces LLM con Streamlit, comparándolo con alternativas y demostrando patrones de implementación práctica usando ejemplos del mundo real.
Guía Paso a Paso para Integración con Streamlit
Paso 1: Instalación y Configuración
# Instalar Streamlit
pip install streamlit
# Crear tu primera app
streamlit hello
Paso 2: Estructura Básica de Interfaz LLM
import streamlit as st
import openai
from datetime import datetime
# Configuración de página
st.set_page_config(
page_title="LLM Assistant",
page_icon="🤖",
layout="wide"
)
# Inicializar estado de sesión
if "conversation" not in st.session_state:
st.session_state.conversation = []
# Barra lateral para configuración
with st.sidebar:
st.header("Configuration")
api_key = st.text_input("OpenAI API Key", type="password")
model = st.selectbox("Model", ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"])
temperature = st.slider("Temperature", 0.0, 2.0, 0.7)
Paso 3: Implementación de Interfaz de Chat
# Interfaz principal de chat
st.title("🤖 AI Legal Assistant")
# Mostrar historial de conversación
for message in st.session_state.conversation:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Entrada de chat
if prompt := st.chat_input("Ask a legal question..."):
# Agregar mensaje de usuario
st.session_state.conversation.append({
"role": "user",
"content": prompt,
"timestamp": datetime.now()
})
# Mostrar mensaje de usuario
with st.chat_message("user"):
st.markdown(prompt)
# Generar respuesta de IA
with st.chat_message("assistant"):
with st.spinner("Analyzing your question..."):
response = generate_legal_response(prompt, api_key, model, temperature)
st.markdown(response)
# Agregar respuesta de IA a la conversación
st.session_state.conversation.append({
"role": "assistant",
"content": response,
"timestamp": datetime.now()
})
Comparación con Alternativas
Streamlit vs Gradio
| Característica | Streamlit | Gradio |
|---|---|---|
| Curva de Aprendizaje | Suave, enfocado en Python | Empinada, requiere conocimiento ML |
| Personalización | Alta flexibilidad | Limitada a componentes predefinidos |
| Despliegue | Múltiples opciones | Principalmente Hugging Face |
| Caso de Uso | Apps web generales | Demos y prototipos ML |
| Complejidad de Código | Python simple | Basado en componentes |
Streamlit vs Flask/FastAPI
| Característica | Streamlit | Flask/FastAPI |
|---|---|---|
| Velocidad de Desarrollo | Muy rápida | Moderada a lenta |
| Conocimiento Frontend | No requerido | HTML/CSS/JS necesario |
| Desarrollo de API | Limitado | Excelente |
| Escalabilidad | Buena para prototipos | Excelente para producción |
| Curva de Aprendizaje | Mínima | Empinada |
Caso de Estudio: Proyecto lus-laboris-py
El proyecto lus-laboris-py es un excelente ejemplo de cómo Streamlit puede complementar una aplicación existente. Actualmente, este proyecto cuenta con una API REST robusta implementada con FastAPI, pero le falta una interfaz de usuario accesible. Aquí es donde Streamlit puede agregar un valor significativo.
Estado Actual del Proyecto
lus-laboris-py actualmente tiene:
- ✅ API REST completa con FastAPI
- ✅ Sistema RAG funcional para investigación legal
- ✅ Base de datos con documentos legales
- ✅ Endpoints bien definidos para consultas
- ❌ Interfaz de usuario (ausente)
Recomendación: Agregar Streamlit como Frontend
¿Por qué Streamlit es la solución perfecta para lus-laboris-py?
- Complementa FastAPI: Streamlit puede consumir la API existente sin modificar el backend
- Desarrollo rápido: Se puede crear una UI completa en horas, no días
- Sin conocimientos frontend: El equipo puede enfocarse en la lógica legal, no en HTML/CSS
- Integración perfecta: Python nativo se integra naturalmente con FastAPI
Implementación Propuesta
# app_streamlit.py - Frontend para lus-laboris-py
import streamlit as st
import requests
import json
# Configuración
API_BASE_URL = "http://localhost:8000" # URL de la API FastAPI
st.set_page_config(
page_title="Lus Laboris - Asistente Legal",
page_icon="⚖️",
layout="wide"
)
st.title("⚖️ Lus Laboris - Asistente Legal")
# Sidebar para configuración
with st.sidebar:
st.header("Configuración")
api_url = st.text_input("URL de la API", value=API_BASE_URL)
# Interfaz principal de consulta legal
st.subheader("Consulta Legal")
query = st.text_area(
"Describe tu consulta legal:",
placeholder="Ejemplo: ¿Cuáles son los requisitos para un contrato de trabajo?",
height=100
)
if st.button("Consultar", type="primary"):
if query.strip():
with st.spinner("Procesando consulta legal..."):
try:
# Llamada a la API FastAPI existente
response = requests.post(
f"{api_url}/query",
json={"query": query},
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
result = response.json()
# Mostrar respuesta
st.success("Consulta procesada exitosamente")
# Respuesta principal
st.subheader("Respuesta:")
st.markdown(result.get("answer", "No se encontró respuesta"))
# Documentos fuente
if "sources" in result:
st.subheader("Documentos Fuente:")
for i, source in enumerate(result["sources"], 1):
with st.expander(f"Documento {i}: {source.get('title', 'Sin título')}"):
st.write(source.get("content", ""))
st.caption(f"Relevancia: {source.get('score', 'N/A')}")
# Métricas de rendimiento
if "metrics" in result:
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Tiempo de Respuesta", f"{result['metrics'].get('response_time', 0):.2f}s")
with col2:
st.metric("Documentos Recuperados", result['metrics'].get('documents_found', 0))
with col3:
st.metric("Confianza", f"{result['metrics'].get('confidence', 0):.2%}")
else:
st.error(f"Error en la API: {response.status_code}")
except requests.exceptions.RequestException as e:
st.error(f"Error de conexión: {str(e)}")
else:
st.warning("Por favor, ingresa una consulta legal")
# Sección de gestión de documentos
st.subheader("Gestión de Documentos")
tab1, tab2, tab3 = st.tabs(["Subir Documentos", "Buscar Documentos", "Estadísticas"])
with tab1:
st.write("Cargar nuevos documentos legales al sistema")
uploaded_file = st.file_uploader(
"Seleccionar archivo legal",
type=['pdf', 'docx', 'txt'],
help="Formatos soportados: PDF, DOCX, TXT"
)
if uploaded_file:
if st.button("Procesar Documento"):
with st.spinner("Procesando documento..."):
files = {"file": uploaded_file.getvalue()}
response = requests.post(f"{api_url}/upload", files=files)
if response.status_code == 200:
st.success("Documento procesado exitosamente")
else:
st.error("Error al procesar el documento")
with tab2:
st.write("Buscar documentos en la base de datos")
search_query = st.text_input("Término de búsqueda")
if st.button("Buscar"):
if search_query:
with st.spinner("Buscando..."):
response = requests.get(f"{api_url}/search", params={"q": search_query})
if response.status_code == 200:
results = response.json()
for doc in results.get("documents", []):
with st.expander(f"{doc.get('title', 'Sin título')}"):
st.write(doc.get("summary", ""))
st.caption(f"Fecha: {doc.get('date', 'N/A')}")
with tab3:
st.write("Estadísticas del sistema")
# Obtener estadísticas de la API
try:
stats_response = requests.get(f"{api_url}/stats")
if stats_response.status_code == 200:
stats = stats_response.json()
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Documentos", stats.get("total_documents", 0))
with col2:
st.metric("Consultas Hoy", stats.get("queries_today", 0))
with col3:
st.metric("Tiempo Promedio", f"{stats.get('avg_response_time', 0):.2f}s")
with col4:
st.metric("Tasa de Éxito", f"{stats.get('success_rate', 0):.1%}")
# Gráfico de consultas por día
if "daily_queries" in stats:
st.subheader("Consultas por Día")
st.line_chart(stats["daily_queries"])
except requests.exceptions.RequestException:
st.error("No se pudieron cargar las estadísticas")
# Footer
st.markdown("---")
st.markdown("**Lus Laboris** - Sistema de Investigación Legal Asistida por IA")
st.caption("Powered by FastAPI + Streamlit + RAG")
Beneficios de esta Implementación
- Reutilización del Backend: No se modifica la API FastAPI existente
- UI Completa: Interfaz intuitiva para usuarios no técnicos
- Desarrollo Rápido: Implementación en horas, no semanas
- Mantenimiento Simple: Un solo archivo Python para toda la UI
- Escalabilidad: Fácil despliegue con Streamlit Cloud
Arquitectura Propuesta
lus-laboris-py/
├── api/ # Backend FastAPI existente
│ ├── main.py
│ ├── models/
│ └── routes/
├── frontend/ # Nuevo frontend Streamlit
│ ├── app_streamlit.py # Aplicación principal
│ ├── components/ # Componentes reutilizables
│ └── utils/ # Utilidades
├── data/ # Documentos legales
└── requirements.txt # Dependencias actualizadas
Patrones de Diseño Comunes
Patrón 1: Aplicaciones Multi-Página
# Navegación de páginas
pages = {
"Chat": chat_page,
"Documents": documents_page,
"Analytics": analytics_page,
"Settings": settings_page
}
selected_page = st.sidebar.selectbox("Navigate", list(pages.keys()))
pages[selected_page]()
Patrón 2: Actualizaciones en Tiempo Real
# Auto-refresh para datos en tiempo real
if st.button("Enable Real-time Updates"):
placeholder = st.empty()
while True:
latest_data = fetch_latest_data()
placeholder.json(latest_data)
time.sleep(5) # Actualizar cada 5 segundos
Patrón 3: Pipeline de Procesamiento de Archivos
# Procesamiento de archivos con progreso
def process_files(uploaded_files):
progress_bar = st.progress(0)
status_text = st.empty()
for i, file in enumerate(uploaded_files):
status_text.text(f"Processing {file.name}...")
process_single_file(file)
progress_bar.progress((i + 1) / len(uploaded_files))
st.success("All files processed!")
Tips de Despliegue
1. Streamlit Community Cloud
# .streamlit/config.toml
[server]
port = 8501
headless = true
[browser]
gatherUsageStats = false
2. Despliegue con Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "app_streamlit.py", "--server.port=8501", "--server.address=0.0.0.0"]
3. Configuración de Ambiente
# Variables de ambiente
import os
from dotenv import load_dotenv
load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
DATABASE_URL = os.getenv("DATABASE_URL")
API_BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8000")
DEBUG_MODE = os.getenv("DEBUG_MODE", "False").lower() == "true"
Optimización de Rendimiento
1. Caché
@st.cache_data
def load_legal_documents():
return load_documents_from_database()
@st.cache_resource
def initialize_llm_model():
return load_llm_model()
2. Operaciones Asíncronas
import asyncio
import aiohttp
async def fetch_legal_data_async(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
return await asyncio.gather(*tasks)
3. Connection Pooling de Base de Datos
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
engine = create_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=10,
max_overflow=20
)
Mejores Prácticas
-
Gestión de Estado de Sesión: Usa
st.session_statepara mantener datos de usuario - Manejo de Errores: Implementa manejo comprensivo de errores para llamadas API
- Estados de Carga: Siempre muestra indicadores de carga para operaciones largas
- Diseño Responsivo: Usa columnas y contenedores para mejor layout
- Seguridad: Nunca expongas API keys en código del lado del cliente
- Testing: Escribe pruebas unitarias para tus funciones de lógica central
Recursos:
Top comments (0)