Skip to content

Repository files navigation

Yachai — Copiloto pedagógico con IA para docentes rurales

En Loreto el 88 % de las familias rurales ya tiene un celular, y el Estado ya instaló internet satelital en cientos de escuelas — pero solo 12 de cada 100 niños entienden lo que leen. El problema no es el acceso a la tecnología: es que nadie diseñó la educación para funcionar entre conexión y conexión.

Yachai es un copiloto conversacional que ayuda a un docente rural a planificar clases: genera sesiones de aprendizaje y evaluaciones alineadas al Currículo Nacional (CNEB), contextualizadas a la Amazonía, al aula multigrado y a la lengua originaria — y las entrega como PDF imprimible, para que el resultado viva fuera de línea.

Proyecto para el reto ODS 4 · Educación de Calidad — Región Loreto, Perú.


El problema

Dato Fuente
6 de cada 100 estudiantes de 4.º de primaria alcanzan nivel satisfactorio en Matemática (12 en Lectura) ENLA 2025
87 % de los 3 903 colegios públicos de Loreto son rurales Minedu
Más del 70 % de las escuelas no tiene acceso básico a internet ComexPerú
88 % de los hogares rurales tiene al menos un celular INEI
1 316 localidades ya cuentan con internet satelital instalado PRONATEL / Conecta Selva
27 lenguas indígenas en la región, con pocos docentes bilingües titulados Minedu

Las escuelas unidocentes y multigrado son la norma: un maestro atiende todos los grados a la vez. Planificar una sesión alineada al currículo, diferenciada por grado y pertinente al contexto amazónico es un trabajo que hoy se hace a mano, sin apoyo y con conexión intermitente.

La tesis de diseño

La IA se usa cuando hay conexión (UGEL, hotspot de Conecta Selva, WhatsApp desde el celular). El resultado —la sesión— vive fuera de línea: exportado a PDF imprimible. La herramienta está pensada para funcionar entre conexión y conexión.


Qué lo hace distinto

  1. No inventa el currículo. Un Verificador (patrón Critic/Verifier) valida cada generación contra la base de datos: todo desempeño citado debe existir realmente en el Programa Curricular. Un código inventado invalida la sesión y fuerza a regenerar.
  2. Multigrado nativo. Una sola sesión produce actividades diferenciadas por grado; el Verificador rechaza la sesión si falta algún grado solicitado.
  3. Contextualización amazónica. Canoas y aguaje, no semáforos y manzanas.
  4. Lenguas originarias como dato, no como código. Kukama-kukamiria, shawi, awajún, kichwa amazónico, achuar, shipibo-konibo.
  5. Auditoría total. Cada generación queda registrada en PostgreSQL (generation_audit): prompt, contexto usado, modelo, tokens.
  6. Multicanal. Web (chat con voz) y WhatsApp — el canal que el docente rural ya tiene.

Cómo funciona

Docente ──► Chat web (texto o voz)  ─┐
                                     ├─► Copiloto (tool loop) ─► Verificador ─► PDF (LaTeX)
Docente ──► WhatsApp (texto o nota) ─┘         │                     │
                                               ▼                     ▼
                                      Currículo real (BD)     Rechaza citas inventadas
  1. El docente conversa con el copiloto (escribiendo o con una nota de voz).
  2. El copiloto pregunta lo que falta: área, grado(s), tema, lengua y contexto local.
  3. Usa herramientas del servidor para consultar el currículo real en la base de datos (buscar_curriculo, obtener_desempenos, buscar_recursos_contexto).
  4. Propone la sesión mediante proponer_sesion. El modelo no tiene una herramienta para guardar.
  5. El Verificador aplica tres compuertas antes de guardar:
    • Estructural — propósito, momentos inicio/desarrollo/cierre, evidencias.
    • Multigrado — cada grado solicitado tiene actividades diferenciadas.
    • Integridad de citas — cada codigo de desempeño existe en la tabla desempeno.
  6. Solo si pasa, se persiste como borrador. El docente edita y exporta a PDF.

Datos del currículo

El currículo no está en el prompt: está en la base de datos, en filas relacionales jerárquicas.

CurriculumArea → Competencia → Capacidad → Estandar (por ciclo) → Desempeno (por grado)

Contenido cargado en el MVP (primaria):

Área Competencias Capacidades Estándares Desempeños
Comunicación 3 13 9 115
Matemática 4 16 12 156
Total 7 29 21 271

Los 271 desempeños son citables: todos tienen su código oficial y fueron verificados contra el PDF fuente. Un desempeño sin código se marca needsReview = true y el Verificador no lo acepta como cita.

Fuentes oficiales

La extracción se hizo con pdfplumber sobre las tablas por grado y se validó página por página. Los datos viven en apps/api/src/database/seeds/cneb/*.json y se cargan con un loader idempotente.


Stack

Capa Tecnología
Monorepo pnpm 11 · Node ≥ 22
Backend NestJS 11 · TypeORM · PostgreSQL 16
Frontend React 19 · Vite 7 · Tailwind v4 · shadcn/ui · TanStack Query
Contratos Zod compartido (@app/contracts, build dual CJS + ESM)
IA OpenAI directo · AI SDK v6 (ai, @ai-sdk/openai) · gpt-4o · gpt-4o-transcribe
PDF LaTeX vía Tectonic (embebido en la API)
WhatsApp Evolution API
Deploy Docker · Coolify

Regla de oro: synchronize: false siempre. El esquema cambia solo con migraciones.

Estructura

apps/
  api/                 NestJS
    src/
      agent/            Tool loop + Verificador + herramientas de currículo
      copiloto/         Chat conversacional (streaming) + transcripción de voz
      curriculum/       Área → Competencia → Capacidad → Estándar → Desempeño
      sesiones/         Sesiones de aprendizaje (generar, leer, editar, PDF)
      evaluaciones/     Exámenes alineados al currículo (+ PDF)
      pdf/              Servicio Tectonic (LaTeX → PDF) y plantillas
      whatsapp/         Webhook de Evolution + enrutado al copiloto
      generation-audit/ Auditoría de cada generación
      auth/ users/ escuelas/ materiales/ common/ config/ database/
  web/                 React (chat, sesiones, panel)
packages/
  contracts/           Esquemas Zod compartidos
  tsconfig/            Configuración TypeScript base

Puesta en marcha (local)

Requisitos: Node ≥ 22, pnpm (vía Corepack), Docker.

# 1. Dependencias
corepack enable
pnpm install

# 2. Entorno
cp .env.example .env      # completa OPENAI_API_KEY y JWT_SECRET

# 3. Base de datos
pnpm db:up                # PostgreSQL 16 (imagen pgvector) en Docker
pnpm migration:run        # migraciones
pnpm seed                 # usuario administrador inicial
pnpm --filter @app/api seed:cneb   # carga el currículo (idempotente)

# 4. Desarrollo
pnpm dev                  # API (3000) + Web (5173)

Ingreso: el usuario administrador que definas en ADMIN_EMAIL / ADMIN_PASSWORD. El inicio de sesión valida formato de correo electrónico.

Scripts útiles

Comando Qué hace
pnpm dev API + Web en paralelo
pnpm build Compila todos los paquetes
pnpm lint / pnpm typecheck Calidad estática
pnpm test Tests unitarios (sin base de datos ni clave de IA)
pnpm --filter @app/api test:db Tests de integración contra PostgreSQL
pnpm db:up / pnpm db:down Levanta / baja PostgreSQL
pnpm migration:run / pnpm migration:revert Migraciones
pnpm --filter @app/api seed:cneb Carga el currículo desde los JSON

Variables de entorno

Un único .env en la raíz (Vite lee las VITE_* desde ahí).

Base de datos y aplicación

Variable Descripción
DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME PostgreSQL
API_PORT Puerto de la API (por defecto 3000)
NODE_ENV development / production
CORS_ORIGIN Orígenes exactos separados por coma (nunca *: se usan cookies)
FRONTEND_URL URL del frontend

Autenticación

Variable Descripción
AUTH_LOCAL_ENABLED Habilita el ingreso con correo y contraseña
JWT_SECRET JWT_EXPIRES_IN Sesión JWT en cookie httpOnly
COOKIE_SECURE COOKIE_SAMESITE COOKIE_DOMAIN Cookie de sesión
BCRYPT_ROUNDS Costo de hash
ADMIN_EMAIL ADMIN_PASSWORD ADMIN_NAME Administrador inicial (semilla idempotente)
GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET GOOGLE_CALLBACK_URL OAuth opcional (vacío = desactivado)

Inteligencia artificial

Variable Descripción
OPENAI_API_KEY Requerida para generar. Sin ella el chat responde 503 y el resto funciona igual
OPENAI_MODEL Por defecto gpt-4o
OPENAI_TRANSCRIBE_MODEL Por defecto gpt-4o-transcribe

WhatsApp (opcional)

Variable Descripción
EVOLUTION_URL URL de la instancia de Evolution API
EVOLUTION_API_KEY Clave de Evolution
EVOLUTION_INSTANCE Nombre de la instancia
WA_WEBHOOK_TOKEN Token compartido que protege el webhook

Frontend

Variable Descripción
VITE_API_URL URL absoluta de la API, sin /api al final. Se hornea en tiempo de build

API

Todas las rutas usan el prefijo global /api (excepto /health). La autenticación es por cookie httpOnly; los clientes deben enviar credenciales. Las rutas de negocio requieren rol DOCENTE (el rol admin es superusuario y pasa todas las compuertas).

Copiloto conversacional

Método Ruta Descripción
POST /api/copiloto/chat Chat en streaming (UI Message Stream de AI SDK v6). Cuerpo: { conversacionId?, messages }
POST /api/copiloto/transcribe Voz → texto. multipart/form-data, campo audio{ text }
POST /api/copiloto/conversaciones Crea un hilo de conversación
GET /api/copiloto/conversaciones Hilos del docente
GET /api/copiloto/conversaciones/:id/mensajes Historial (para retomar)

Currículo

Método Ruta Descripción
GET /api/curriculo/areas Áreas curriculares
GET /api/curriculo/competencias?area= Competencias y capacidades del área

Sesiones de aprendizaje

Método Ruta Descripción
POST /api/sesiones/generar Genera una sesión (pasa por el Verificador)
GET /api/sesiones/:id Detalle con contenidoJson
PATCH /api/sesiones/:id Edita contenido y/o estado (borradorfinal)
GET /api/sesiones/:id/pdf PDF de la sesión (LaTeX → Tectonic)

Evaluaciones

Método Ruta Descripción
POST /api/evaluaciones/generar Genera un examen alineado al currículo
GET /api/evaluaciones/:id Detalle
GET /api/evaluaciones/:id/pdf PDF del examen

Otros

Método Ruta Descripción
POST /api/pdf/compile Compila LaTeX a PDF
POST /api/whatsapp/webhook Webhook público de Evolution (protegido por token)
GET /health Health check (sin prefijo)
/api/auth/* register, login, logout, me, config

Canal de WhatsApp

Un docente escribe o envía una nota de voz por WhatsApp y recibe la sesión, con los códigos oficiales de los desempeños citados. Es el mismo copiloto y el mismo Verificador: no hay una segunda ruta de IA.

Nota de voz ─► Evolution ─► webhook ─► transcripción ─► copiloto ─► Verificador ─► respuesta

Configuración

  1. Define en la API: EVOLUTION_URL, EVOLUTION_API_KEY, EVOLUTION_INSTANCE y WA_WEBHOOK_TOKEN. Reinicia el contenedor para que tome las variables.

  2. En Evolution, apunta el webhook a la API incluyendo el token en la URL:

    https://TU-API/api/whatsapp/webhook?token=EL_MISMO_WA_WEBHOOK_TOKEN
    

    También se acepta el encabezado x-webhook-token con ese valor.

  3. Suscribe el evento MESSAGES_UPSERT (mensajes entrantes).

Verificación

curl -i -X POST "https://TU-API/api/whatsapp/webhook?token=TOKEN"   # 200 {"ok":true}
curl -i -X POST "https://TU-API/api/whatsapp/webhook"               # 401

Detalles

  • Idempotencia: cada mensaje entrante se registra por su identificador de Evolution (wa_inbound_log); los reintentos no se procesan dos veces.
  • Identidad: el número de teléfono se asocia a un usuario docente, creado en el primer mensaje. Simplificación de demostración: no hay verificación por OTP.
  • Si las variables de Evolution no están definidas, el resto de Yachai arranca con normalidad y el canal queda inactivo.

Generación de PDF

Las sesiones y evaluaciones se renderizan a LaTeX con plantillas deterministas y se compilan con Tectonic, embebido en la imagen de la API.

  • El Dockerfile descarga el binario de Tectonic y precalienta la caché de paquetes durante el build, para que la primera compilación en producción no dependa de la red.
  • Todo el texto proveniente del usuario o del modelo se escapa antes de entrar al documento.
  • Si el binario no está disponible en tiempo de ejecución, los endpoints de PDF responden 503 y la aplicación sigue funcionando.

Despliegue (Coolify)

Dos servicios a partir del mismo repositorio:

Servicio Dockerfile Notas
API apps/api/Dockerfile Ejecuta migraciones y semillas al arrancar. Incluye Tectonic
Web apps/web/Dockerfile VITE_API_URL es un argumento de build, no de runtime

Cookies entre subdominios. La sesión viaja por cookie httpOnly. Publica ambos servicios bajo el mismo dominio (por ejemplo app.midominio.com y api.midominio.com) y configura COOKIE_DOMAIN=.midominio.com, COOKIE_SAMESITE=lax, COOKIE_SECURE=true y CORS_ORIGIN con el origen exacto del frontend.

pgvector. El esquema incluye una columna de embeddings para búsqueda semántica. Si el PostgreSQL de destino no tiene la extensión vector, la migración lo detecta, omite la columna y continúa: el MVP funciona igual. Para habilitar la búsqueda semántica, usa una imagen pgvector/pgvector:pg16 y vuelve a ejecutar la migración.


Modelo de datos

users (roles: admin | docente | director | user)
escuela            { nombre, ugel, esUnidocente, esMultigrado, lenguas[] }

curriculum_area → competencia → capacidad
                              → estandar   (por ciclo: III, IV, V)
                              → desempeno  (por grado 1-6, codigo, needsReview, embedding?)

sesion_aprendizaje { docenteId, escuelaId, grados[], areaId, competenciaIds[],
                     lengua, contexto, contenidoJson, estado, generationAuditId }
evaluacion         { ..., contenidoJson (ítems con desempenoCodigo) }
material           { sesionId, tipo, lengua, contenido }
generation_audit   { prompt, contextoUsado, versionCurriculo, modelo, tokens }
conversacion / mensaje    Historial del copiloto
wa_inbound_log            Idempotencia de WhatsApp

Migraciones en apps/api/src/database/migrations/ (7 a la fecha).


Pruebas

pnpm test                          # unitarios: no requieren base de datos ni clave de IA
pnpm --filter @app/api test:db     # integración: requiere PostgreSQL

La cobertura se concentra donde importa: integridad de citas del Verificador (un código inventado se rechaza), diferenciación multigrado, idempotencia del loader del currículo, y seguridad e idempotencia del webhook de WhatsApp.


Estado y límites conocidos

Funcionando

  • Copiloto conversacional con voz (web) y WhatsApp
  • Currículo real de Comunicación y Matemática (primaria) con 271 desempeños citables
  • Verificador con las tres compuertas
  • Generación de sesiones y evaluaciones
  • Exportación a PDF vía LaTeX
  • Auditoría de generaciones

Simplificaciones de esta versión

  • La identidad por WhatsApp no verifica el número (sin OTP).
  • El contenido curricular cubre Comunicación y Matemática de primaria; el resto de áreas y niveles es trabajo posterior.
  • La búsqueda semántica (pgvector) está preparada en el esquema pero no es parte del camino de la demostración.
  • Las lenguas originarias se tratan como dato de contextualización; no hay traducción automática certificada.

Sostenibilidad

El modelo es B2G / B2I — UGEL, DRE, gobiernos regionales, PRONATEL, FITEL, ONG y FORMABIAP — nunca cobro directo a las familias. Se apoya en infraestructura que el Estado ya pagó: los hotspots satelitales de Conecta Selva y el celular que el 88 % de los hogares rurales ya tiene.

Metas ODS 4 atendidas: 4.1 (educación primaria de calidad), 4.5 (equidad y grupos vulnerables) y 4.c (docentes calificados).


Créditos

Construido sobre TemplateFullStack. La arquitectura de agente con herramientas auditadas y el patrón Critic/Verifier se adaptaron de MayordomoAI.

Datos curriculares: Ministerio de Educación del Perú (Minedu) — Currículo Nacional de la Educación Básica y Programa Curricular de Educación Primaria, 2016.

About

Yachai — Copiloto pedagógico con IA para docentes rurales. Accede con las credenciales: admin@admin.com : admin

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages