Describe tu negocio en texto libre y pruébalo como si fueras un cliente — el chatbot responde usando esta descripción como contexto, en tiempo real.
Backend en capas: cada carpeta tiene una única responsabilidad, nada se mezcla.
api/routersRecibe cada petición HTTP, valida los datos y delega al service. Sin lógica de negocio.servicesLa lógica real: crear un negocio, decidir la siguiente pregunta, armar el contexto y pedirle una respuesta a la IA.schemasContratos de entrada/salida (Pydantic): qué puede mandar el cliente y qué le devolvemos.modelsLas tablas de la base de datos: negocios, hechos del cuestionario, mensajes y solicitudes.repositoriesÚnicas clases que hablan directamente con la base de datos — solo consultas.integrationsClientes del proveedor de IA: un modelo local gratuito por defecto, con una IA externa como opción.coreConfiguración, límite de peticiones por minuto y cabeceras de seguridad.static/Todo el frontend: HTML, CSS y JavaScript, sin ningún framework.tests/Pruebas automáticas — tanto de que el chatbot funcione bien como de seguridad.Una sola lista, dos trabajos: el onboarding y el chat comparten la misma lista de temas — nunca hay dos copias que desincronizar. El onboarding la usa para saber qué preguntar; el chat la reutiliza como contexto, para no tratar esos temas como huecos totalmente desconocidos.
ONBOARDING_TOPICS = [ "precio", "entrega a domicilio", "métodos de pago", "pedidos por encargo", "descuentos por cantidad", "horario", ]
Cómo se conecta con el modelo de IA local: corre en su propio contenedor dentro de la red interna del proyecto — no se "busca" en la red, la dirección queda fija por configuración interna. Sin SDK oficial compatible, así que se le habla por HTTP plano contra su API REST, igual que cualquier cliente.
Al arrancar, un "precalentamiento" en segundo plano carga el modelo en RAM antes de que llegue el primer cliente (si no, la primera respuesta tarda casi un minuto en vez de segundos). Cada respuesta se entrega después en streaming, fragmento por fragmento. Si el modelo falla o no responde, el error se captura y el chat degrada a un mensaje legible — nunca corta a medias. Y como "hablar con la IA" sigue siempre el mismo contrato, cambiar de proveedor es solo una variable de configuración, no tocar código.
Un modelo de lenguaje genera texto palabra por palabra, por probabilidad — no tiene una "memoria" real de lo que decidió antes, en cada turno vuelve a leer todo el contexto y responde de nuevo. Seguir una instrucción abierta como "no repitas un tema que ya usaste" exige comparar lo nuevo contra todo lo anterior — un razonamiento que un modelo grande hace bien, y uno chico, no siempre.
Con Claude esto funcionaría razonablemente bien — es un modelo mucho más grande y consistente siguiendo instrucciones abiertas. Con el modelo que usamos aquí (Ollama, local), probándolo en vivo, ese mismo pedido terminó preguntando "¿hacen envíos a domicilio?" reformulado 4 veces seguidas en una sola sesión.
¿Por qué usamos igual el modelo chico? Es gratis. Corre en nuestro propio contenedor, sin depender de ninguna API externa — así cualquiera prueba este demo sin que le cueste créditos a nadie. Claude sigue disponible como opción para quien traiga su propia clave y quiera mejor calidad.
La solución: en vez de pedirle al modelo que decida y redacte a la vez, separamos las dos tareas. Qué tema preguntar lo decide el código — un conteo simple, pregunta 1 → tema 1, pregunta 2 → tema 2 — nunca el modelo, así es imposible que se repita. Al LLM solo le queda la parte en la que sí es bueno: redactar la pregunta para el tema que ya se eligió.
La teoría de la información (Claude Shannon, 1948) dice algo simple: si hay que distinguir entre N posibilidades, cada pregunta de sí/no vale por lo que elimina, no por lo que confirma. Si cada pregunta corta el espacio de posibilidades a la mitad, 20 preguntas bien diseñadas alcanzan para distinguir más de un millón de casos (2²⁰).
"Cada pregunta, si está bien diseñada, elimina la mitad de las posibilidades restantes." — J. Denker, sobre la entropía y el juego de las 20 preguntas
El juego "20 preguntas" es mucho más viejo que esta matemática: hay registros desde la década de 1780, y en EE. UU. fue un programa de radio masivo desde 1946. La explicación formal llegó después — Shannon formalizó la entropía en 1948, y este juego terminó siendo la forma más clara de explicarla.
Nuestro problema es el inverso del juego original: no hay que adivinar qué hace el negocio — ya lo sabemos por su descripción. Hay que validar qué NO hace, antes de que un cliente lo descubra con la pregunta incorrecta.
Un modelo de lenguaje no "busca" una respuesta en una base de datos — genera texto palabra por palabra, encadenando la más probable según todo lo que aprendió. Esa misma amplitud de conocimiento general es lo que lo vuelve genérico: si falta contexto específico, el modelo no se detiene a decir "no sé" — completa la frase igual, con la respuesta más probable, no la correcta.
En la práctica, quien describe su negocio suele decir "vendemos esto y aquello, abrimos tal día" y ahí se detiene. Preguntas simples y muy específicas — qué métodos de pago acepta, si se puede pedir el mismo día, si hace falta reservar — casi nunca se mencionan solas. Son justo esos huecos, no los temas grandes, los que hacen que un chatbot genérico responda mal o falle.
Nuestra solución: encerrar a la IA en el contexto real del negocio para responder, sin cortarle el conocimiento general que sigue siendo útil (que una panadería "hace pan" es obvio, y sigue sirviendo). Lo específico la hace confiable en casos puntuales — pero falla justo en lo más obvio, porque ese contexto que es obvio para una persona no existe para un programa. Ahí entra el cuestionario: no para quitarle imaginación a la IA, sino para darle el contexto específico que, por diseño, nunca tuvo.
Técnicamente la respuesta es rápida: se combina el contexto específico del negocio con un modelo de IA que lo procesa en segundos. Pero para quien espera, "rápida" no siempre se siente como un logro — hoy hasta un par de segundos de espera puede sentirse eterno, acostumbrados como estamos a redes sociales diseñadas para no soltar la atención ni un instante. Y, paradójicamente, una respuesta que aparece demasiado rápido también puede sentirse como que "no se esforzó" — con poco valor percibido.
Nuestra solución no toca la velocidad real — toca la percepción de la espera. El texto que aparece mientras se genera la respuesta no es un resumen real de lo que la IA está pensando (la IA no "piensa" en frases) — es una narración diseñada para transmitir esfuerzo: casi dice esto, casi dice aquello, un poco más... Cuando la respuesta final llega, el usuario la valora más porque sintió que algo trabajó para dársela.
La mascota (Inspector Cuy) que aparece y se mueve durante la espera cumple el mismo propósito, más allá de darle identidad al demo: mantiene la atención ocupada esos pocos segundos, en vez de dejar al usuario mirando un espacio vacío — y hace que la experiencia se recuerde.