Cómo están montados hoy el RAG
y la lógica de negocio
Descripción del sistema tal como funciona en producción a día de hoy — no del roadmap ni de la propuesta comercial. Sirve como referencia técnica independiente de que se contrate o no el piloto.
1. Arquitectura de un turno de conversación
Pipeline "cascaded": transcripción → recuperación de conocimiento (RAG) → modelo de lenguaje en streaming → síntesis de voz en streaming, encadenados para que el audio empiece a sonar antes de que el modelo termine de generar el texto completo.
| STT (voz → texto) | gpt-realtime-whisper |
| LLM | gpt-4o-mini |
| TTS (texto → voz) | gpt-4o-mini-tts |
| Embeddings | text-embedding-3-small · 1536 dim |
Fencing por epoch
Cada turno lleva un número de epoch monotónico. Si el usuario interrumpe (barge-in), el epoch sube y cualquier evento tardío del turno cancelado se descarta comparando epochs — así una respuesta ya cortada no puede "colarse" después de la interrupción.
Máquina de estados
La sesión transita entre listening / thinking / tool_executing / synthesizing / assistant_speaking / interrupted. Gobierna qué eventos son válidos en cada momento y se emite al cliente para reflejar el estado en la interfaz.
Streaming encadenado
El texto del LLM se trocea por frases (SpeechChunker) y cada fragmento se envía a síntesis en cuanto está listo, con hasta 2 síntesis en vuelo a la vez — el audio de la primera frase puede sonar mientras el modelo aún genera la segunda.
Filler / backchannel
Existe un mecanismo para reproducir una coletilla pregrabada ("déjame ver…") si el audio real tarda más de 450 ms, pensado para enmascarar latencia. Implementado y probado, pero desactivado en la configuración actual.
2. Cómo se recupera el conocimiento (RAG)
Modelo de datos
Jerarquía en PostgreSQL (esquema knowledge, con pgvector): organización → marca → producto. El conocimiento en sí recorre otra cadena independiente: fuente (con nivel de confianza) → documento → versión de documento (con hash de contenido, para detectar cambios) → chunk.
| Campo del chunk | Valores |
|---|---|
| content_kind | FACT · MARKETING_CLAIM · COMMERCIAL_GUIDANCE · INFERENCE |
| confidence | HIGH · MEDIUM · LOW |
| visibility | PUBLIC · INTERNAL · RESTRICTED · ACCOUNT_SCOPED |
| valid_from / valid_until | ventana temporal de validez del dato (opcional) |
Publicación versionada
Los chunks no se sirven directamente: se agrupan en releases versionadas por marca (estados DRAFT → EVALUATING → APPROVED → PUBLISHED → ARCHIVED/REVOKED), y una restricción de base de datos garantiza que solo puede haber un release PUBLISHED activo por marca a la vez. El asistente conversa siempre contra un release publicado y fijado por configuración — como una release de software, no una edición libre en caliente sobre lo que se está sirviendo.
Búsqueda híbrida en cada turno
Por cada pregunta se lanzan dos búsquedas en paralelo sobre el release publicado y se fusionan sus resultados:
Léxica
Texto completo en español (tsvector, con más peso en título que en sección y contenido), con una consulta estricta y otra relajada término a término como red de seguridad ante errores de transcripción.
Semántica
Similitud de embeddings de la pregunta contra los del chunk (distancia coseno, índice HNSW) — encuentra lo relevante aunque no comparta las palabras exactas.
Ambas listas se combinan por Reciprocal Rank Fusion (no por la puntuación bruta de cada motor, sino por la posición de cada resultado en su propio ranking) y se seleccionan los 8 fragmentos mejor situados como contexto del turno.
Aislamiento multi-tenant
Cada consulta fija app.organization_id en la sesión de base de datos, y Row-Level Security de PostgreSQL fuerza esa condición en cada tabla del esquema — el aislamiento entre organizaciones está garantizado por la propia base de datos, no solo por la lógica de la aplicación.
Grounding: cómo se evita que invente
Los fragmentos recuperados se inyectan como bloques de evidencia marcados explícitamente como datos no confiables, nunca instrucciones. El prompt exige responder solo con lo respaldado por esa evidencia, prohíbe completar datos de producto con conocimiento general del modelo, y obliga a reconocer con naturalidad cuando falta un dato en vez de rellenarlo. Si la búsqueda falla técnicamente o no devuelve resultados, hay mensajes de repliegue específicos que impiden que el sistema alucine en su lugar.
3. La capa de negocio
Lo que diferencia al sistema de un chatbot genérico envuelto en voz: un prompt de sistema con un rol y reglas de negocio concretas, no un asistente de propósito general.
Rol definido
"Copiloto comercial conversacional para profesionales de hostelería" — no un asistente genérico. Español de España, tono de compañero comercial, respuestas breves para no alargar la latencia percibida.
Reglas para salida hablada
Prohibido markdown, comodines tipo "[tu nombre]" y plantillas a rellenar. Si se pide un guion o frase, se entrega ya utilizable con datos reales, nunca como hueco a completar.
Guion de objeciones
Ante una objeción del cliente final, prioriza dar al comercial una frase que decir y una única pregunta de descubrimiento — no una lista genérica de argumentos.
Resumen tipo CRM
Bajo demanda, devuelve cliente/contexto, interés, objeciones, acuerdos y próximo paso a partir del propio historial de la conversación — ya funciona hoy, sin integración externa todavía.
Regla de cierre y filtro determinista
El prompt prohíbe explícitamente coletillas de IA ("¿te puedo ayudar en algo más?", "aquí estoy"), pero un modelo de lenguaje puede emitirlas igualmente pese a la instrucción. Por eso hay, además, un filtro determinista por código: retiene la última frase de cada respuesta y la descarta si coincide con un patrón de coletilla conocido — nunca vacía la respuesta entera, y el mismo filtro se usa tanto en producción como en la evaluación automática de calidad, para medir lo que realmente se dice y no solo lo que el prompt pide.
Memoria de conversación
Ventana acotada a los últimos ~20 turnos o 8.000 caracteres (lo que se alcance antes). Se reconstruye en cada turno junto con el prompt de sistema y el contexto RAG recuperado — no hay resumen ni persistencia entre sesiones todavía.
4. Observabilidad y coste
Cada turno se liquida individualmente en el momento: tokens de entrada/salida del LLM, milisegundos de audio de STT y TTS, coste en USD por proveedor, y latencias derivadas (tiempo al primer token, tiempo al primer audio, tiempo de boca a oído). Este es el registro de actividad real que ya alimenta hoy los números de coste de la propuesta — no una promesa de instrumentación futura.
5. Estado actual de la ingesta de conocimiento
El esquema de base de datos ya está preparado para múltiples marcas y para un flujo de aprobación completo por release (borrador → evaluación → aprobado → publicado → archivado/revocado). En el estado operativo actual, sin embargo, el alta y actualización de contenido se hace mediante scripts de ingesta ejecutados manualmente, no a través de una interfaz de administración — y el sistema en marcha está fijado a una única organización, marca y release publicados por configuración.
En otras palabras: la arquitectura ya soporta varias marcas y un ciclo de revisión editorial; lo que falta hoy es la capa de administración encima para operarlo sin tocar código.