Implementación Técnica: Chatbot de Captura de Leads (Determinístico)

Este documento describe la arquitectura, el modelo de decisión y la operación de un chatbot de captura de leads. El sistema evita depender de un LLM externo para el flujo principal de atención y captura: utiliza una máquina de estados orientada por intenciones, con validaciones explícitas y respuestas controladas por configuración.

En muchas Pymes, cuando la lógica de negocio es acotada y el objetivo es capturar demanda comercial, incorporar un LLM no siempre es un requisito técnico. En esos casos, un enfoque deterministico suele ofrecer mejor control de respuesta, costos estables y menor riesgo operativo.


1. Problema que resuelve

En preventa (consultas frecuentes, derivación a asesor y toma de contacto), la dificultad técnica está en clasificar bien la intención y cerrar datos obligatorios de contacto.

  1. Clasificar correctamente intención en mensajes breves y ambiguos.
  2. Completar datos obligatorios para handoff comercial.
  3. Evitar respuestas no autorizadas (precio, plazos, especificaciones no confirmadas).
  4. Mantener latencia y costo estables bajo picos de tráfico.

Bajo estas restricciones, el diseño determinístico ofrece una mejor relación señal/ruido que un flujo 100% generativo.


2. Arquitectura del sistema

El sistema se organiza en tres capas:

  1. Frontend (Next.js/React)
    • Renderiza historial de conversación.
    • Emite texto libre y ui_payload de quick replies.
    • Aplica UX de “typing” para humanizar respuestas instantáneas.
  2. API (FastAPI)
    • Endpoint de chat.
    • Coordinación con router de intención.
    • Persistencia de leads al completar intentos de contacto.
  3. Motor de decisión (HybridRouter)
    • Clasifica entrada con regex + keywords + umbrales de confianza.
    • Administra estado conversacional (IntentState).
    • Ejecuta estrategia de preguntas por slots hasta completar intención.

La separación evita acoplar UX, reglas de negocio y persistencia en una sola capa, y permite evolucionar cada componente con bajo impacto cruzado.


3. Modelo de decisión del HybridRouter

El router implementa un enfoque híbrido con prioridad explícita:

  1. Patrones regex (máxima precisión semántica en frases esperadas).
  2. Coincidencia por keywords (recuperación para variaciones léxicas).
  3. Fallback de baja confianza (evita respuestas incorrectas cuando no hay evidencia).

3.1. Scoring

El scoring privilegia evidencia fuerte:

def _score_entry(self, *, text: str, patterns: tuple[re.Pattern[str], ...], keywords: tuple[str, ...]) -> tuple[float, list[str]]:
    # 1) Regex: intención altamente confiable
    for pattern in patterns:
        match = pattern.search(text)
        if match:
            score = 1.0 if match.start() == 0 and match.end() == len(text) else 0.95
            return score, [f"regex:{pattern.pattern}"]

    # 2) Keywords: evidencia parcial, útil para recall
    if keywords:
        hits = sum(1 for kw in keywords if kw.lower() in text.lower())
        if hits:
            kw_score = min(0.9, 0.45 + (hits / len(keywords)) * 0.55)
            return kw_score, [f"keywords:{hits}/{len(keywords)}"]

    return 0.0, []

El resultado incorpora trazabilidad (reasons) para debugging operativo y auditoría de clasificación.

3.2. Máquina de estados por intención

Cuando una intención entra en modo “en progreso”, la conversación deja de ser un simple turno-respuesta y pasa a un flujo transaccional:

@dataclass
class IntentState:
    intent_id: str
    slots: dict[str, str]
    last_asked_slot: str | None
    updated_at: float

Esto permite:

  1. Reanudar conversación en múltiples turnos.
  2. Validar y normalizar slot por slot.
  3. Evitar pérdida de contexto entre mensajes cortos.

4. Configuración declarativa de intenciones

La lógica de negocio se define por configuración, en lugar de repartirse en múltiples condicionales dentro del código. Cada intención especifica:

  1. Señales de activación (patterns, keywords).
  2. Slots requeridos y su orden.
  3. Validadores y quick replies.
  4. Plantilla final de cierre.
IntentConfig(
    id="contacto_humano",
    patterns=(r"\b(hablar|contactar)\s+con\s+asesor\b", ...),
    slots=(
        SlotConfig(
            name="medio_contacto",
            question="¿Preferís por Mail o WhatsApp?",
            quick_replies=(
                QuickReplyConfig("wa", "WhatsApp", "slot:medio_contacto:whatsapp"),
                QuickReplyConfig("ml", "Mail", "slot:medio_contacto:mail"),
            ),
        ),
        SlotConfig(name="dato_contacto", question="Por favor escribí tu número o mail:"),
    ),
    final_template="¡Listo! Un asesor te contactará por {medio_contacto} al {dato_contacto}.",
)

4.1. Normalización de entrada

El sistema consume dos canales:

  1. Texto libre del usuario.
  2. ui_payload emitido por botones.

Los payloads reducen entropía en la extracción de slots críticos y mejoran la tasa de completitud en embudos comerciales.


5. Integración API y persistencia de leads

Cuando una intención se completa, el backend dispara side-effects de negocio de forma explícita.

if decision.intent_completed and decision.matched_id == "contacto_humano":
    slots = decision.intent_slots or {}
    repo.store_contact_lead(
        source="chatbot",
        email=slots.get("dato_contacto") if "@" in slots.get("dato_contacto") else None,
        phone=slots.get("dato_contacto") if "@" not in slots.get("dato_contacto") else None,
        metadata={"slots": slots},
    )

La condición de disparo evita persistir leads incompletos y hace idempotente la integración a nivel de intención finalizada.


6. Seguridad y superficie de riesgo

6.1. Riesgos reducidos frente a LLM remoto

Al no depender de generación libre para respuestas de negocio, se minimizan:

  1. Prompt injection para desviar políticas de respuesta.
  2. Exposición accidental de prompts del sistema.
  3. Variabilidad no controlada en salida ante inputs adversariales.
  4. Dependencia de costos variables por token.

6.2. Riesgos que sí siguen presentes

Un diseño determinístico no elimina riesgos generales de aplicación:

  1. Spam o abuso volumétrico en endpoints.
  2. Inyección en capas de persistencia si faltan validaciones.
  3. PII mal gestionada en logs.
  4. Configuraciones de regex ambiguas que produzcan falsos positivos.

Por eso se recomienda complementar con rate limiting, sanitización, políticas de retención y monitoreo por intención.


7. Operación, observabilidad y SLOs

Para operar este enfoque en producción, conviene medir al menos:

  1. intent_match_rate: porcentaje de mensajes con intención clasificada por encima del umbral.
  2. slot_completion_rate: porcentaje de conversaciones que completan todos los slots requeridos.
  3. lead_conversion_rate: conversaciones iniciadas que terminan en lead persistido.
  4. fallback_rate: frecuencia de rutas de baja confianza.
  5. p95_latency_ms: latencia del endpoint de chat.

SLO típico para PyME en etapa inicial:

  1. p95_latency_ms < 400 para ruta determinística.
  2. slot_completion_rate > 70% en intención de contacto.
  3. fallback_rate < 15% en tráfico orgánico estabilizado.

Los umbrales exactos deben calibrarse por vertical y calidad de tráfico.


8. Trade-offs y límites del enfoque

Este modelo es superior cuando el dominio es acotado y altamente operacional (captura de lead, FAQ estable, triage inicial). No es la mejor opción para:

  1. Soporte abierto con alta variabilidad semántica.
  2. Tareas de redacción o reasoning multi-documento.
  3. Conversaciones largas donde el usuario espera respuestas creativas o explicativas complejas.

Una arquitectura híbrida realista es: determinístico para embudo comercial y derivación; generativo solo en zonas de bajo riesgo y con guardrails explícitos.


9. Conclusión técnica

Para muchas Pymes, el costo/beneficio de un chatbot de leads no mejora por agregar un LLM externo en el flujo principal. Un motor determinístico bien diseñado entrega:

  1. Comportamiento auditable.
  2. Costo predecible.
  3. Menor superficie de ataque asociada a prompts.
  4. Mejor control de calidad comercial en cada respuesta.

Más que discutir “IA sí o no”, conviene ajustar la complejidad técnica al objetivo de negocio y al nivel de confiabilidad que necesita la operación.