erp-core/docs/04-modelado/requerimientos-funcionales/mgn-018/RF-MGN-018-005-entrenamiento-feedback.md

20 KiB

RF-MGN-018-005: Entrenamiento y Feedback

Módulo: MGN-018 - AI Agents & Chatbots Prioridad: P2 Story Points: 8 Estado: Definido Fecha: 2025-12-05

Descripción

El sistema debe permitir mejorar continuamente el rendimiento de los agentes de IA mediante:

  1. Feedback de usuarios finales (satisfacción)
  2. Feedback de supervisores (correcciones)
  3. Análisis de conversaciones para identificar gaps
  4. Ajustes al prompt y configuración basados en datos

Actores

  • Actor Principal: Supervisor/QA
  • Actores Secundarios:
    • Cliente final (feedback de satisfacción)
    • AI Agent (sujeto del entrenamiento)
    • Tenant Admin (configuración)

Precondiciones

  1. Agente activo con conversaciones procesadas
  2. Feature ai_feedback_enabled
  3. Permiso de supervisión sobre el agente

Flujo Principal - Feedback de Usuario

  1. Conversación con agente termina
  2. Sistema envía encuesta de satisfacción
  3. Usuario califica experiencia (1-5 estrellas)
  4. Usuario opcionalmente deja comentario
  5. Sistema almacena feedback vinculado a conversación
  6. Sistema actualiza métricas del agente

Flujo Alternativo - Corrección de Supervisor

  1. Supervisor revisa conversación en dashboard
  2. Supervisor identifica respuesta incorrecta
  3. Supervisor selecciona mensaje a corregir
  4. Supervisor proporciona respuesta correcta
  5. Sistema almacena corrección como ejemplo
  6. Sistema puede incluir en fine-tuning o RAG

Flujo Alternativo - Análisis de Gaps

  1. Sistema analiza conversaciones con bajo rating
  2. Sistema identifica patrones de fallo:
    • Temas sin cobertura en KB
    • Intents no reconocidos
    • Escalaciones frecuentes
  3. Sistema genera reporte de gaps
  4. Admin/Supervisor revisa recomendaciones
  5. Admin actualiza KB o prompt según necesidad

Tipos de Feedback

1. Feedback de Usuario Final

interface UserFeedback {
  conversation_id: string;
  rating: 1 | 2 | 3 | 4 | 5;
  comment?: string;
  helpful: boolean;
  would_recommend: boolean;
  submitted_at: Date;
}

2. Corrección de Supervisor

interface SupervisorCorrection {
  message_id: string;
  supervisor_id: string;

  // Respuesta original del agente
  original_response: string;

  // Corrección propuesta
  corrected_response: string;

  // Categoría del error
  error_type: 'factual' | 'tone' | 'incomplete' | 'inappropriate' | 'off_topic';

  // Notas explicativas
  notes?: string;

  // ¿Usar para entrenamiento?
  use_for_training: boolean;
}

3. Gap Identificado

interface IdentifiedGap {
  type: 'missing_kb_content' | 'unhandled_intent' | 'unclear_response' | 'tool_limitation';

  // Evidencia
  sample_messages: string[];
  conversation_ids: string[];
  frequency: number;

  // Sugerencia
  suggested_action: string;
  priority: 'low' | 'medium' | 'high';

  // Estado
  status: 'new' | 'acknowledged' | 'resolved';
}

Encuesta de Satisfacción

Configuración

interface SatisfactionSurveyConfig {
  enabled: boolean;

  // Cuándo mostrar
  trigger: 'end_of_conversation' | 'after_resolution' | 'after_x_messages';
  trigger_value?: number;

  // Formato
  format: 'stars' | 'thumbs' | 'nps';

  // Preguntas adicionales
  include_comment: boolean;
  include_recommendation: boolean;

  // Canal
  send_via: 'same_channel' | 'email';

  // Mensaje
  survey_message: string;
}

Mensaje de Encuesta (WhatsApp)

const surveyTemplate = {
  type: 'interactive',
  interactive: {
    type: 'button',
    body: {
      text: '¿Qué tan útil fue nuestra conversación?'
    },
    action: {
      buttons: [
        { type: 'reply', reply: { id: 'rating_5', title: '⭐⭐⭐⭐⭐' }},
        { type: 'reply', reply: { id: 'rating_3', title: '⭐⭐⭐' }},
        { type: 'reply', reply: { id: 'rating_1', title: '⭐' }}
      ]
    }
  }
};

Dashboard de Supervisión

Vista de Conversaciones para Revisión

┌─────────────────────────────────────────────────────────────────┐
│  📋 Revisión de Conversaciones - Asistente de Ventas            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Filtros: [Bajo rating ▼] [Escaladas ▼] [Últimos 7 días ▼]     │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ #1234 | ⭐ 2/5 | Escalada | 12 mensajes | Hace 2h           ││
│  │ Cliente preguntó sobre devoluciones, agente no tenía info  ││
│  │ [Ver conversación]  [Marcar revisada]                       ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ #1235 | ⭐ 1/5 | Cerrada | 8 mensajes | Hace 5h             ││
│  │ "El bot no entendió nada de lo que le decía"               ││
│  │ [Ver conversación]  [Marcar revisada]                       ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
│  Mostrando 2 de 15 conversaciones por revisar                   │
└─────────────────────────────────────────────────────────────────┘

Vista de Conversación con Corrección

┌─────────────────────────────────────────────────────────────────┐
│  Conversación #1234                                    [Cerrar] │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  👤 Cliente (14:23)                                             │
│  "Hola, quiero devolver un producto que compré hace 2 semanas" │
│                                                                  │
│  🤖 Agente (14:23)                                              │
│  "Hola, gracias por contactarnos. Puedo ayudarte con eso.      │
│   ¿Cuál es el número de tu pedido?"                            │
│                                                                  │
│  👤 Cliente (14:24)                                             │
│  "Es el pedido #ORD-2025-789"                                   │
│                                                                  │
│  🤖 Agente (14:24)  ⚠️ Respuesta incorrecta                    │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ "Lo siento, no tengo información sobre políticas de         ││
│  │  devolución. ¿Puedo ayudarte con algo más?"                ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
│  [✏️ Corregir respuesta]                                        │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ Respuesta correcta:                                         ││
│  │ "He verificado tu pedido #ORD-2025-789. Tienes 30 días     ││
│  │ para devolver el producto. Puedes hacerlo en cualquiera    ││
│  │ de nuestras tiendas o solicitar recolección a domicilio.   ││
│  │ ¿Qué opción prefieres?"                                    ││
│  │                                                             ││
│  │ Tipo de error: [Información faltante ▼]                    ││
│  │ ☑️ Usar para entrenamiento                                  ││
│  │                                            [Guardar]        ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Análisis de Gaps

Algoritmo de Detección

interface GapDetectionConfig {
  // Umbrales
  low_rating_threshold: number;      // Default: 3
  escalation_rate_threshold: number; // Default: 0.3 (30%)
  frequency_threshold: number;       // Mínimo de ocurrencias

  // Análisis de contenido
  analyze_unhandled_questions: boolean;
  analyze_repeated_clarifications: boolean;
  analyze_topic_clusters: boolean;

  // Período
  analysis_period_days: number;      // Default: 30
}

async function detectGaps(agentId: string, config: GapDetectionConfig): Promise<IdentifiedGap[]> {
  const gaps: IdentifiedGap[] = [];

  // 1. Analizar conversaciones con bajo rating
  const lowRatedConversations = await getLowRatedConversations(agentId, config);

  // 2. Extraer preguntas sin respuesta satisfactoria
  const unansweredQuestions = extractUnansweredQuestions(lowRatedConversations);

  // 3. Clusterizar por tema
  const topicClusters = await clusterByTopic(unansweredQuestions);

  // 4. Identificar gaps más frecuentes
  for (const cluster of topicClusters) {
    if (cluster.frequency >= config.frequency_threshold) {
      gaps.push({
        type: 'missing_kb_content',
        sample_messages: cluster.samples,
        conversation_ids: cluster.conversation_ids,
        frequency: cluster.frequency,
        suggested_action: `Agregar contenido sobre: ${cluster.topic}`,
        priority: cluster.frequency > 10 ? 'high' : 'medium',
        status: 'new'
      });
    }
  }

  // 5. Analizar intents no manejados
  const unhandledIntents = await findUnhandledIntents(agentId, config.analysis_period_days);
  // ... agregar a gaps

  return gaps;
}

Reporte de Gaps

┌─────────────────────────────────────────────────────────────────┐
│  📊 Análisis de Gaps - Asistente de Ventas (Últimos 30 días)    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Gaps Identificados: 5                                          │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ 🔴 Alta Prioridad                                          ││
│  │                                                             ││
│  │ 1. Políticas de devolución                                 ││
│  │    Frecuencia: 23 menciones | 15 conversaciones afectadas  ││
│  │    Acción: Agregar documento de políticas a Knowledge Base ││
│  │    [Ver ejemplos]  [Marcar resuelto]                       ││
│  │                                                             ││
│  │ 2. Disponibilidad de sucursales                            ││
│  │    Frecuencia: 18 menciones | 12 conversaciones afectadas  ││
│  │    Acción: Crear herramienta de consulta de horarios       ││
│  │    [Ver ejemplos]  [Marcar resuelto]                       ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ 🟡 Media Prioridad                                         ││
│  │                                                             ││
│  │ 3. Métodos de pago aceptados                               ││
│  │    Frecuencia: 8 menciones | 6 conversaciones afectadas    ││
│  │    [Ver ejemplos]  [Marcar resuelto]                       ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                  │
│                    [Exportar reporte]  [Programar análisis]     │
└─────────────────────────────────────────────────────────────────┘

Reglas de Negocio

  • RN-1: Encuesta de satisfacción solo se envía una vez por conversación
  • RN-2: Correcciones de supervisor requieren permiso de "ai_supervisor"
  • RN-3: Análisis de gaps se ejecuta semanalmente automáticamente
  • RN-4: Datos de feedback se retienen por 1 año
  • RN-5: Feedback anónimo disponible si tenant lo configura
  • RN-6: Mínimo 50 conversaciones para análisis estadístico confiable

Criterios de Aceptación

  • Encuesta de satisfacción configurable y funcional
  • Usuarios pueden calificar y comentar
  • Supervisores pueden corregir respuestas
  • Correcciones se almacenan para referencia
  • Análisis de gaps identifica patrones
  • Reporte de gaps con prioridades
  • Métricas de satisfacción en dashboard
  • Exportación de datos de feedback
  • Notificaciones de gaps críticos

Entidades Involucradas

ai_agents.feedback

CREATE TABLE ai_agents.feedback (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    agent_id UUID NOT NULL,
    conversation_id UUID NOT NULL REFERENCES ai_agents.conversations(id),

    -- Tipo de feedback
    feedback_type VARCHAR(50) NOT NULL, -- user_rating, supervisor_correction, system_analysis

    -- Rating de usuario
    rating INT, -- 1-5
    comment TEXT,
    helpful BOOLEAN,
    would_recommend BOOLEAN,

    -- Corrección de supervisor
    message_id UUID,
    original_response TEXT,
    corrected_response TEXT,
    error_type VARCHAR(50),
    correction_notes TEXT,
    use_for_training BOOLEAN DEFAULT false,
    corrected_by UUID,

    -- Metadata
    channel VARCHAR(50),
    metadata JSONB DEFAULT '{}',

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT chk_feedback_type CHECK (feedback_type IN ('user_rating', 'supervisor_correction', 'system_analysis'))
);

CREATE INDEX idx_feedback_tenant ON ai_agents.feedback(tenant_id);
CREATE INDEX idx_feedback_agent ON ai_agents.feedback(agent_id);
CREATE INDEX idx_feedback_conversation ON ai_agents.feedback(conversation_id);
CREATE INDEX idx_feedback_type ON ai_agents.feedback(feedback_type);
CREATE INDEX idx_feedback_rating ON ai_agents.feedback(rating) WHERE rating IS NOT NULL;

ai_agents.identified_gaps

CREATE TABLE ai_agents.identified_gaps (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    agent_id UUID NOT NULL,

    -- Clasificación
    gap_type VARCHAR(50) NOT NULL,
    -- missing_kb_content, unhandled_intent, unclear_response, tool_limitation

    -- Descripción
    title VARCHAR(200) NOT NULL,
    description TEXT,
    topic_keywords TEXT[],

    -- Evidencia
    sample_messages TEXT[],
    conversation_ids UUID[],
    frequency INT NOT NULL,

    -- Recomendación
    suggested_action TEXT,
    priority VARCHAR(20) NOT NULL, -- low, medium, high

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'new',
    -- new, acknowledged, in_progress, resolved, wont_fix

    resolved_at TIMESTAMPTZ,
    resolved_by UUID,
    resolution_notes TEXT,

    -- Análisis
    analysis_period_start DATE NOT NULL,
    analysis_period_end DATE NOT NULL,
    detected_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT chk_gap_status CHECK (status IN ('new', 'acknowledged', 'in_progress', 'resolved', 'wont_fix'))
);

CREATE INDEX idx_gaps_tenant ON ai_agents.identified_gaps(tenant_id);
CREATE INDEX idx_gaps_agent ON ai_agents.identified_gaps(agent_id);
CREATE INDEX idx_gaps_status ON ai_agents.identified_gaps(status);
CREATE INDEX idx_gaps_priority ON ai_agents.identified_gaps(priority);

ai_agents.training_examples

-- Ejemplos curados para potencial fine-tuning o few-shot learning
CREATE TABLE ai_agents.training_examples (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    agent_id UUID NOT NULL,

    -- Origen
    source_type VARCHAR(50) NOT NULL, -- supervisor_correction, curated, synthetic
    source_feedback_id UUID REFERENCES ai_agents.feedback(id),

    -- Ejemplo
    user_message TEXT NOT NULL,
    ideal_response TEXT NOT NULL,
    context TEXT, -- Contexto adicional si necesario

    -- Clasificación
    category VARCHAR(100),
    tags TEXT[],

    -- Estado
    is_approved BOOLEAN DEFAULT false,
    approved_by UUID,
    approved_at TIMESTAMPTZ,

    -- Uso
    used_in_training BOOLEAN DEFAULT false,
    training_run_id VARCHAR(100),

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID
);

CREATE INDEX idx_training_tenant ON ai_agents.training_examples(tenant_id);
CREATE INDEX idx_training_agent ON ai_agents.training_examples(agent_id);
CREATE INDEX idx_training_approved ON ai_agents.training_examples(is_approved);

API Endpoints

Enviar Feedback de Usuario

// POST /api/v1/ai-agents/conversations/{id}/feedback
interface SubmitFeedbackRequest {
  rating: 1 | 2 | 3 | 4 | 5;
  comment?: string;
  helpful?: boolean;
  would_recommend?: boolean;
}

Crear Corrección de Supervisor

// POST /api/v1/ai-agents/messages/{id}/correction
interface CreateCorrectionRequest {
  corrected_response: string;
  error_type: string;
  notes?: string;
  use_for_training?: boolean;
}

Obtener Gaps

// GET /api/v1/ai-agents/{id}/gaps?status=new&priority=high
interface GetGapsResponse {
  gaps: IdentifiedGap[];
  summary: {
    total: number;
    by_priority: Record<string, number>;
    by_type: Record<string, number>;
  };
}

Ejecutar Análisis de Gaps

// POST /api/v1/ai-agents/{id}/analyze-gaps
interface AnalyzeGapsRequest {
  period_days?: number;
  force_refresh?: boolean;
}

Referencias

Dependencias

  • RF Requeridos: RF-018-003 (Procesamiento), RF-018-004 (Herramientas)
  • Bloqueante para: Ninguno