- 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>
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 | ✅ Sí |
| Longitud máxima | 200 caracteres | ✅ Sí |
| Keywords mínimos | 2 | ✅ Sí |
| Puntos por fragmento | 20 | ✅ Sí |
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:
- Conversión a minúsculas (
LOWER()) - Eliminación de acentos: á→a, é→e, í→i, ó→o, ú→u, ñ→n
- 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
- Conceptos clave del texto: Incluir sustantivos y verbos importantes
- Sin acentos: Las keywords deben estar normalizadas (sin acentos)
- Variedad: Incluir sinónimos para dar flexibilidad
- 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