workspace/projects/gamilit/docs/01-fase-alcance-inicial/EAI-002-actividades/especificaciones/ET-EDU-005-validacion-texto-abierto.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

17 KiB

ET-EDU-005: Validación de Texto Abierto (Rueda de Inferencias)

Fecha: 2025-11-21 Versión: 1.0 Módulo: 2.5 - Rueda de Inferencias Tipo: Especificación Técnica


1. Resumen Ejecutivo

Este documento especifica cómo el sistema valida las respuestas de texto abierto (inferencias) en el ejercicio "Rueda de Inferencias". A diferencia de ejercicios con respuestas cerradas (opción múltiple, verdadero/falso), este mecanismo permite al estudiante escribir texto libre que se valida mediante coincidencia de palabras clave (keywords).


2. Formato de Respuesta Esperada

2.1 Estructura del Frontend al Backend

// Formato que envía el frontend
interface SubmitAnswerPayload {
  userId: string;        // UUID del usuario
  exerciseId: string;    // UUID del ejercicio
  answers: {
    fragments: Record<string, string>;  // { "frag-1": "texto...", "frag-2": "texto..." }
    categoryId?: string;                // Categoría seleccionada (opcional)
    timeSpent?: number;                 // Tiempo en segundos (opcional)
  }
}

2.2 Ejemplo de Respuesta Válida

{
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "exerciseId": "ex-rueda-001",
  "answers": {
    "fragments": {
      "frag-1": "Marie Curie fue una pionera en el estudio de la radiactividad y ganó dos premios Nobel.",
      "frag-2": "Su determinación la llevó a superar los obstáculos de su época y convertirse en la primera mujer catedrática.",
      "frag-3": "Los cuadernos de Marie Curie siguen siendo radiactivos después de más de cien años."
    },
    "categoryId": "cat-inferencial",
    "timeSpent": 142
  }
}

3. Proceso de Validación

3.1 Flujo General

┌─────────────────────────────────────────────────────────────────────────┐
│  1. RECEPCIÓN                                                           │
│     Backend recibe: { fragments: { "frag-1": "texto...", ... } }       │
└──────────────────────────────────┬──────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  2. OBTENCIÓN DE SOLUCIÓN                                               │
│     Base de datos retorna keywords para cada fragmento                  │
│     Ejemplo: ["pionera", "radiactividad", "nobel", "primera", "mujer"] │
└──────────────────────────────────┬──────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  3. NORMALIZACIÓN DE TEXTO                                              │
│     - Convertir a minúsculas                                            │
│     - Eliminar acentos (á→a, é→e, í→i, ó→o, ú→u, ñ→n)                 │
│     - Eliminar espacios extra                                           │
│     Ejemplo: "Marie Curie fue PIONERA" → "marie curie fue pionera"     │
└──────────────────────────────────┬──────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  4. VALIDACIÓN DE LONGITUD                                              │
│     - Mínimo: 20 caracteres                                             │
│     - Máximo: 200 caracteres                                            │
│     Si no cumple → Respuesta inválida con feedback específico          │
└──────────────────────────────────┬──────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  5. CONTEO DE KEYWORDS                                                  │
│     - Buscar cada keyword en el texto normalizado                       │
│     - Contar coincidencias (LIKE '%keyword%')                           │
│     - Mínimo requerido: 2 keywords                                      │
└──────────────────────────────────┬──────────────────────────────────────┘
                                   │
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  6. CÁLCULO DE PUNTUACIÓN                                               │
│     - Cada fragmento válido: 20 puntos                                  │
│     - Score final: (puntos_obtenidos / puntos_totales) * 100           │
│     - Ejemplo: 3/5 fragmentos válidos = 60 puntos                       │
└─────────────────────────────────────────────────────────────────────────┘

3.2 Criterios de Validación por Fragmento

Criterio Valor Default Configurable
Longitud mínima 20 caracteres
Longitud máxima 200 caracteres
Keywords mínimos 2
Puntos por fragmento 20

4. Estructura de la Solución en Base de Datos

4.1 Formato JSON de la Solución

{
  "validation": {
    "minKeywords": 2,
    "minLength": 20,
    "maxLength": 200
  },
  "fragments": [
    {
      "id": "frag-1",
      "keywords": ["pionera", "radiactividad", "nobel", "primera", "mujer", "cientifico", "premio", "campos", "unica"],
      "points": 20
    },
    {
      "id": "frag-2",
      "keywords": ["determinacion", "obstaculos", "epoca", "primera", "mujer", "catedratica", "universidad", "superacion"],
      "points": 20
    },
    {
      "id": "frag-3",
      "keywords": ["cuadernos", "radiactivos", "legado", "cien", "anos", "investigacion", "historia", "impacto"],
      "points": 20
    }
  ]
}

4.2 Descripción de Campos

Campo Tipo Descripción
validation.minKeywords integer Número mínimo de keywords que debe contener el texto
validation.minLength integer Longitud mínima del texto en caracteres
validation.maxLength integer Longitud máxima del texto en caracteres
fragments[].id string Identificador único del fragmento (debe coincidir con el enviado)
fragments[].keywords string[] Lista de palabras clave que se buscarán en el texto
fragments[].points integer Puntos otorgados si el fragmento es válido

5. Ejemplos de Validación

5.1 Respuesta VÁLIDA

Texto del usuario:

"Marie Curie fue una pionera en la radiactividad y recibió el premio Nobel dos veces"

Análisis:

  • Longitud: 89 caracteres (entre 20-200)
  • Keywords encontrados: "pionera", "radiactividad", "premio", "nobel" = 4 keywords (≥ 2)
  • Resultado: VÁLIDO - 20 puntos

5.2 Respuesta INVÁLIDA (muy corta)

Texto del usuario:

"Marie Curie ganó"

Análisis:

  • Longitud: 16 caracteres (menor que 20)
  • Resultado: INVÁLIDO - 0 puntos
  • Feedback: "El texto es demasiado corto. Mínimo 20 caracteres, tienes 16."

5.3 Respuesta INVÁLIDA (sin keywords suficientes)

Texto del usuario:

"Ella era una persona muy inteligente y trabajadora que logró muchas cosas importantes."

Análisis:

  • Longitud: 87 caracteres (entre 20-200)
  • Keywords encontrados: 0 (ninguna keyword coincide)
  • Resultado: INVÁLIDO - 0 puntos
  • Feedback: "Tu inferencia necesita más relación con el texto. Has incluido 0 conceptos clave, pero necesitas al menos 2."

5.4 Respuesta PARCIALMENTE VÁLIDA (1 keyword)

Texto del usuario:

"Marie Curie fue una mujer muy importante en la historia de la ciencia moderna."

Análisis:

  • Longitud: 78 caracteres
  • Keywords encontrados: "mujer" = 1 keyword (necesita 2)
  • Resultado: INVÁLIDO - 0 puntos
  • Feedback: "Tu inferencia necesita más relación con el texto. Has incluido 1 conceptos clave, pero necesitas al menos 2."

6. Respuesta del Backend al Frontend

6.1 Estructura de Respuesta Exitosa

{
  "attemptId": "att-123-456",
  "score": 67,
  "isPerfect": false,
  "correctAnswersCount": 2,
  "totalQuestions": 3,
  "feedback": {
    "overall": "2 de 3 inferencias válidas. Revisa los fragmentos marcados para mejorar tu respuesta."
  },
  "details": {
    "total_fragments": 3,
    "valid_fragments": 2,
    "total_points_possible": 60,
    "points_earned": 40,
    "percentage": 67,
    "validation_criteria": {
      "min_keywords": 2,
      "min_length": 20,
      "max_length": 200
    },
    "results_per_fragment": [
      {
        "fragment_id": "frag-1",
        "is_valid": true,
        "matched_keywords": ["pionera", "radiactividad", "nobel", "premio"],
        "keyword_count": 4,
        "points": 20,
        "feedback": "¡Excelente! Has incluido 4 conceptos clave del fragmento."
      },
      {
        "fragment_id": "frag-2",
        "is_valid": true,
        "matched_keywords": ["determinacion", "obstaculos"],
        "keyword_count": 2,
        "points": 20,
        "feedback": "¡Excelente! Has incluido 2 conceptos clave del fragmento."
      },
      {
        "fragment_id": "frag-3",
        "is_valid": false,
        "matched_keywords": ["cuadernos"],
        "keyword_count": 1,
        "points": 0,
        "feedback": "Tu inferencia necesita más relación con el texto. Has incluido 1 conceptos clave, pero necesitas al menos 2."
      }
    ]
  },
  "rewards": {
    "xpEarned": 67,
    "mlCoinsEarned": 15
  }
}

7. Función SQL de Validación

7.1 Ubicación

apps/database/ddl/schemas/educational_content/functions/14-validate_rueda_inferencias.sql

7.2 Funciones Disponibles

Función Propósito
_validate_single_fragment() Función auxiliar que valida un fragmento individual
validate_rueda_inferencias_text() Valida un fragmento específico dado exercise_id y fragment_id
validate_rueda_inferencias() Wrapper estándar que valida todos los fragmentos

7.3 Normalización de Texto

La función gamilit.normalize_text() realiza:

  1. Conversión a minúsculas (LOWER())
  2. Eliminación de acentos: á→a, é→e, í→i, ó→o, ú→u, ñ→n
  3. Trim de espacios
-- Ejemplo interno
v_normalized_user_text := LOWER(gamilit.normalize_text(TRIM(p_user_text)));

8. Configuración del Ejercicio en Seeds

8.1 Ejemplo de Seed Completo

INSERT INTO educational_content.exercises (
    id,
    mission_id,
    type,
    difficulty_level,
    title,
    description,
    instructions,
    config,
    content,
    solution,
    rubric,
    time_limit_seconds,
    xp_reward,
    ml_coins_reward,
    order_index,
    is_active
) VALUES (
    'ex-rueda-001'::uuid,
    'mission-002'::uuid,
    'rueda_inferencias'::educational_content.exercise_type,
    'intermediate'::educational_content.difficulty_level,
    'Rueda de Inferencias: Marie Curie',
    'Gira la ruleta y escribe inferencias sobre los fragmentos de texto.',
    'Lee cada fragmento y escribe una inferencia que demuestre comprensión profunda.',
    -- CONFIG: Configuración visual y de comportamiento
    '{
        "wheelAnimation": true,
        "showTimer": true,
        "timerSeconds": 30,
        "allowSkip": false,
        "minCharacters": 20,
        "maxCharacters": 200
    }'::jsonb,
    -- CONTENT: Lo que ve el estudiante
    '{
        "categories": [
            { "id": "cat-literal", "name": "Literal", "color": "#4ECDC4", "icon": "📖" },
            { "id": "cat-inferencial", "name": "Inferencial", "color": "#FF6B6B", "icon": "🔍" },
            { "id": "cat-critico", "name": "Crítico", "color": "#95E1D3", "icon": "💡" },
            { "id": "cat-creativo", "name": "Creativo", "color": "#F38181", "icon": "🎨" }
        ],
        "fragments": [
            { "id": "frag-1", "text": "Marie Curie fue la primera mujer en ganar un Premio Nobel..." },
            { "id": "frag-2", "text": "A pesar de los obstáculos de su época..." },
            { "id": "frag-3", "text": "Sus cuadernos de laboratorio siguen siendo radiactivos..." }
        ],
        "settings": {
            "timerSeconds": 30,
            "minLength": 20,
            "maxLength": 200
        }
    }'::jsonb,
    -- SOLUTION: Keywords para validación
    '{
        "validation": {
            "minKeywords": 2,
            "minLength": 20,
            "maxLength": 200
        },
        "fragments": [
            {
                "id": "frag-1",
                "keywords": ["pionera", "radiactividad", "nobel", "primera", "mujer", "cientifico", "premio"],
                "points": 20
            },
            {
                "id": "frag-2",
                "keywords": ["determinacion", "obstaculos", "epoca", "primera", "catedratica", "superacion"],
                "points": 20
            },
            {
                "id": "frag-3",
                "keywords": ["cuadernos", "radiactivos", "legado", "cien", "anos", "historia", "impacto"],
                "points": 20
            }
        ]
    }'::jsonb,
    -- RUBRIC
    '{
        "criteria": [
            { "name": "Relevancia", "weight": 0.5, "description": "Uso de conceptos clave del texto" },
            { "name": "Longitud", "weight": 0.3, "description": "Cumplimiento de extensión requerida" },
            { "name": "Coherencia", "weight": 0.2, "description": "Texto comprensible y bien estructurado" }
        ]
    }'::jsonb,
    180,  -- time_limit_seconds (3 minutos total)
    50,   -- xp_reward
    20,   -- ml_coins_reward
    5,    -- order_index
    true  -- is_active
);

9. Guía para Crear Keywords Efectivos

9.1 Principios

  1. Conceptos clave del texto: Incluir sustantivos y verbos importantes
  2. Sin acentos: Las keywords deben estar normalizadas (sin acentos)
  3. Variedad: Incluir sinónimos para dar flexibilidad
  4. Específicos al tema: Evitar palabras muy genéricas

9.2 Ejemplo de Buenos Keywords

Para un fragmento sobre Marie Curie:

  • "pionera", "radiactividad", "nobel", "polonio", "radio", "descubrimiento"
  • "importante", "famosa", "científica" (muy genéricos)

9.3 Cantidad Recomendada

  • Mínimo: 5 keywords por fragmento
  • Recomendado: 7-10 keywords por fragmento
  • Máximo: 15 keywords por fragmento

10. Trazabilidad

Documento Relación
DB-071 Función SQL de validación
BE-FE-071 DTO de backend
FE-071 Componente de frontend
US-ACT-005 Historia de usuario

11. Historial de Versiones

Versión Fecha Cambios
1.0 2025-11-21 Versión inicial

Autor: Frontend Agent (NEXUS-FRONTEND) Revisado por: - Estado: APROBADO