# 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

```typescript
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

```typescript
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

```typescript
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

```typescript
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)

```typescript
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

```typescript
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

```sql
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

```sql
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

```sql
-- 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

```typescript
// 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

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

### Obtener Gaps

```typescript
// 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

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

## Referencias

- [Reinforcement Learning from Human Feedback (RLHF)](https://huggingface.co/blog/rlhf)
- [Active Learning for NLP](https://www.activeloop.ai/resources/glossary/active-learning-in-nlp/)
- [Conversation Design Best Practices](https://developers.google.com/assistant/conversation-design)

## Dependencias

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