Arquitectura del asistente de chat

Stack por capas y flujos de petición del widget RAG del portfolio, handoff humano e ingesta del operador.

Capas del sistema

Cada capa tiene una sola responsabilidad. Los datos fluyen de arriba abajo; PubSub y webhooks conectan rutas asíncronas.

Cliente

Widget del navegador

ChatWidgetLive · WebSocket · en/es

LiveView

Telegram

Bot del visitante + consola del operador

Webhook
Borde

TLS / CDN

Cloudflare → misaelp.dev

HTTPS

Nginx

Proxy → 127.0.0.1:4001

Contabo VPS
Phoenix

Router

/:locale/* · /webhook/telegram

Bandit

Controladores y LiveViews

ChatWidgetLive · TelegramController

Phoenix 1.8
Dominio

Orquestación del chat

Persistir mensajes · modo ai|human

MisaelParedes.Chat

Pipeline RAG

Embedding · recuperar · rerank · generar

MisaelParedes.AI.RAG

Handoff y Telegram

Palabras clave · operador /reply · /ingest

Handoff · Operator
Datos

PostgreSQL

conversations · messages

Ecto

pgvector

document_chunks · búsqueda coseno

768-dim

ETS + PubSub

Límites · push en vivo al widget

OTP
Externo

API de embeddings

Consulta + vectores de chunks

Gemini

Completado de chat

Respuesta fundamentada en el contexto

Gemini / OpenAI

Rerank opcional

Refinamiento top-k de chunks

Cohere
Flechas sólidas en los flujos = petición síncrona Líneas punteadas = PubSub / webhook de Telegram

Iteraciones de la petición

Tres bucles principales. La mayoría del tráfico sigue el Flujo A hasta que una palabra clave activa el Flujo B.

Flujo A

Respuesta RAG

Modo IA por defecto · según idioma

1

El visitante envía la pregunta

ChatWidgetLive → Chat.handle_visitor_message/3

2

Comprobación de límite

Bucket ETS por sesión · límite por hora

3

Embedding de la pregunta

AI.Provider.embed/1 → vector de 768 dimensiones

4

Recuperar chunks

Knowledge.search_similar/2 en pgvector

5

Rerank opcional

Rerank de Cohere si está configurado · si no, truncar top-k

6

Aumentar prompt

Idioma + contexto del portfolio + guardrails

7

Completado LLM

Provider.chat/2 → respuesta en primera persona + fuentes

8

Persistir y enviar

Conversations.add_message · PubSub → stream del widget

Flujo B

Handoff humano

Operador vía Telegram

1

Palabra clave de handoff detectada

p. ej. «quiero hablar contigo» · Handoff.activate/2

2

Cambiar a modo humano

conversation.mode = human · mensaje puente al visitante

3

Notificar al operador

Telegram: alerta + transcripción completa · chat marcado activo

4

Respuestas del operador

Texto libre o /reply ID · Telegram → store_operator_reply

5

Entrega en tiempo real

PubSub conversation:ID → ChatWidgetLive stream_insert

6

Devolver a la IA

/release en Telegram · el modo vuelve a ai

Flujo C

Ingesta de conocimiento

El operador actualiza pgvector

1

El operador envía /ingest

Idioma + slug de fuente · borrador o texto de una sola vez

2

Dividir en chunks

Párrafos · ~1200 caracteres máx. por chunk

3

Embedding e inserción

Indexer.index_chunks/1 → document_chunks

4

Disponible en la próxima consulta

La recuperación del Flujo A usa los nuevos vectores al instante

5

Estadísticas y reconstrucción

/kb cuenta por fuente · /reindex desde Content

Fuentes indexadas

  • Chunks del portfolio integrados (Content.knowledge_chunks/0)
  • Telegram /ingest — notas en vivo por idioma y fuente
  • /reindex reconstruye desde código; /kb muestra conteos de chunks

Barreras de seguridad

  • Límite horario de RAG por sesión de visitante (ETS)
  • Límite de herramientas solo en eventos LiveView
  • Prompts en primera persona · citar contexto · sin inventar hechos
Ver repo demo

Pregunta por mi experiencia

Asistente del portfolio — arquitectura, proyectos y consultoría.

Cómo funciona la recuperación RAG