Kuuli Technologies
English
Backend/API26 de mayo de 2026 · 6 min de lectura

Streamlit vs HTML+Flask: por qué los labs de IA usan Python puro para la UI

Cuando se enseña a construir chatbots con LangChain, hay una decisión clave: ¿Flask + HTML + JavaScript, o Streamlit? Desde un punto de vista pedagógico, la diferencia importa mucho. Aquí la comparativa y cuándo usar cada uno.

El problema pedagógico

En un lab de construcción de chatbots con LangChain y Python, la decisión sobre qué framework de UI usar no es trivial. Si el objetivo es enseñar a integrar un LLM con memoria, RAG y herramientas — ¿cuánto tiempo tiene sentido dedicar a HTML, CSS, JavaScript y Flask antes de llegar a lo que importa?

Un notebook que usa Flask con HTML embebido puede tardar tres celdas y 120 líneas de código solo para tener una caja de texto y un botón funcionando. El alumno que llega a esa celda sin background de frontend queda bloqueado en sintaxis HTTP antes de ver la parte interesante: cómo el LLM responde, cómo persiste la memoria, cómo se recuperan documentos desde el vector store.

La premisa: cuando enseñas IA/ML aplicada a desarrolladores, la UI es el vehículo, no el destino. Streamlit elimina la fricción de frontend para que el aprendizaje se quede donde tiene que estar: en la lógica de la cadena LLM.

El mismo chatbot: 120 líneas vs 12

Un chatbot básico sin memoria, con Flask, requiere tres bloques de código separados: el HTML+CSS+JS de la interfaz, el servidor Flask con sus rutas y el threading para arrancar dentro de un notebook. Con Streamlit, el equivalente exacto cabe en 12 líneas:

Flask + HTML (notebook)
# Celda 1: ~60 líneas de HTML + CSS + jQuery
html_template = """
<html><body>
  <div id="chat">...</div>
  <script>
    function sendMessage() {
      fetch('/chat', {...})
      .then(r => r.json())
      .then(d => appendMessage(d))
    }
  </script>
</body></html>
"""

# Celda 2: servidor Flask
from flask import Flask, request, jsonify
import threading

app = Flask(__name__)

@app.route('/chat', methods=['POST'])
def chat():
    msg = request.json['message']
    response = llm.invoke(msg)
    return jsonify({'reply': response.content})

t = threading.Thread(target=app.run)
t.start()

# Celda 3: iframe
serve_kernel_port_as_iframe(5000)
Streamlit (app.py)
import streamlit as st
from langchain_openai import ChatOpenAI

st.title("Chatbot v0")
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.3
)

user_msg = st.chat_input("Escribe...")
if user_msg:
    st.chat_message("user").write(user_msg)
    response = llm.invoke(user_msg)
    st.chat_message("assistant").write(
        response.content
    )

Mismo comportamiento observable. La diferencia es que el alumno que llega al bloque de Streamlit puede entender cada línea sin saber HTML ni HTTP.

Comparativa completa

AspectoHTML + FlaskStreamlit
LenguajeHTML + CSS + JS + PythonSolo Python
Servidor webFlask manual + threadingIncluido en el framework
Estado / memoria del chatGestión manual (session, variables globales)st.session_state automático
Arrancar la appthreading + Flask.run() en notebookstreamlit run app.py
Reactividad UIjQuery + fetch manual a FlaskAutomática al cambiar widgets
Curva de aprendizajeAlta — requiere dominar 3 lenguajesMuy baja — solo Python
Personalización UITotal — CSS libre, cualquier layoutLimitada — componentes predefinidos
Para producciónSí, con trabajo de DevOpsSí para datos/IA; no para apps enterprise

El concepto que más confunde: session_state

Streamlit re-ejecuta el script completo cada vez que el usuario interactúa con un widget. Esto es contraintuitivo para quien viene de Flask o de React, pero es el modelo de programación del framework. session_state es la solución: un diccionario que sobrevive a cada re-ejecución y permite conservar el historial de mensajes entre turnos.

✗ Sin session_state
# Cada vez que el usuario escribe, Streamlit re-ejecuta el script
# → el historial de mensajes desaparece
# → el chat parece "reiniciarse" en cada mensaje
✓ Con session_state
if "messages" not in st.session_state:
    st.session_state.messages = []

# Los mensajes sobreviven a cada re-ejecución
st.session_state.messages.append({"role": "user", "content": prompt})

Analogía para clase: "Es como si cada vez que presionas Enter, Python cierra y vuelve a abrir el script desde cero. session_state es la caja donde guardas cosas antes de que eso pase — es la única memoria que sobrevive al reinicio."

Cuándo usar cada uno

Usa HTML + Flask cuando…

Necesitas control total sobre la UI (layout, branding, animaciones)
El proyecto es una app web que otros usuarios visitarán con su navegador
El equipo tiene perfil frontend y quiere separar responsabilidades
Necesitas integrarte con un frontend React/Vue ya existente

Usa Streamlit cuando…

El objetivo es enseñar o demostrar la lógica de un LLM / pipeline RAG
Quieres un prototipo funcional en una sesión de lab de 2 horas
El equipo es todo Python y no hay background de frontend
Builds un dashboard de datos o un explorador de documentos interno
En contextos pedagógicos de IA/ML, Streamlit es casi siempre la elección correcta: elimina la fricción de frontend y pone el foco donde importa.
Flask + HTML es valioso para entender qué pasa "por debajo" — úsalo primero como base conceptual, luego muestra Streamlit como la herramienta que abstrae esa complejidad.
session_state es el concepto clave que hay que fijar: Streamlit re-ejecuta el script entero en cada interacción, y session_state es la única memoria que persiste entre re-ejecuciones.

¿Quieres aplicar esto en tu equipo?

En Kuuli Technologies llevamos estas metodologías a equipos reales. Escríbenos si quieres una sesión de trabajo o una consultoría sobre agentes IA y SDD.