20 KiB
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:
- Feedback de usuarios finales (satisfacción)
- Feedback de supervisores (correcciones)
- Análisis de conversaciones para identificar gaps
- 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
- Agente activo con conversaciones procesadas
- Feature
ai_feedback_enabled - Permiso de supervisión sobre el agente
Flujo Principal - Feedback de Usuario
- Conversación con agente termina
- Sistema envía encuesta de satisfacción
- Usuario califica experiencia (1-5 estrellas)
- Usuario opcionalmente deja comentario
- Sistema almacena feedback vinculado a conversación
- Sistema actualiza métricas del agente
Flujo Alternativo - Corrección de Supervisor
- Supervisor revisa conversación en dashboard
- Supervisor identifica respuesta incorrecta
- Supervisor selecciona mensaje a corregir
- Supervisor proporciona respuesta correcta
- Sistema almacena corrección como ejemplo
- Sistema puede incluir en fine-tuning o RAG
Flujo Alternativo - Análisis de Gaps
- Sistema analiza conversaciones con bajo rating
- Sistema identifica patrones de fallo:
- Temas sin cobertura en KB
- Intents no reconocidos
- Escalaciones frecuentes
- Sistema genera reporte de gaps
- Admin/Supervisor revisa recomendaciones
- 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
- Reinforcement Learning from Human Feedback (RLHF)
- Active Learning for NLP
- Conversation Design Best Practices
Dependencias
- RF Requeridos: RF-018-003 (Procesamiento), RF-018-004 (Herramientas)
- Bloqueante para: Ninguno