12 min read

Cortex

Índice

Cortex

[!NOTE] Este contenido aún no está completo, pero se irá actualizando con el tiempo.

Para mi proyecto de título de Ingeniería en Informática, se nos solicitó planificar y desarrollar algún proyecto de software que incorporará todo lo enseñado en la carrera, incluyendo documentación, pruebas, y todo lo necesario para un proyecto de software real.

La verdad, se nos había avisado con bastante anticipación que debíamos comenzar a pensar en algún proyecto para el título. Pero recién comencé a planificarlo dos semestres antes de entrar a la asignatura, llamada Capstone. Para la fecha en donde escribo esto, aún sigo trabajando en el proyecto. Somos un grupo de 3 personas, y cada uno tiene su rol. En mi caso, soy el líder del equipo.

Esto se divide en tres fases, siendo la primera la planificación, donde se hacen documentos como el acta de constitución, requerimientos, carta grant, etc. La segunda fase irónicamente se divide en dos, la primera siendo aún más documentación, enfocándose en la arquitectura del sistema, matriz RACI, proceso de negocio, etc.

Lo que viene después es la implementación, que es lo que estoy haciendo ahora. La idea es tener al menos la mayor cantidad de funcionalidades implementadas para la fecha de la presentación, que en mi caso es el 20 de noviembre.

Pero como soy alguien muy ansioso, meses antes hice un pequeño prototipo de lo que sería una parte esencial de la plataforma, un motor de ejecución de código en línea. ¿Pero por qué necesitaría eso? Bueno, de eso trata este post, de mi proyecto de título, Cortex.

¿Qué es Cortex?

La tecnología avanza a pasos agigantados, y la mayoría de contenido educativo se encuentra fuera de la comunidad hispanohablante. Cortex, cuyo nombre proviene de la corteza cerebral, donde se procesa la información, surge como una respuesta integral a varios desafíos críticos en el panorama actual de la educación tecnológica.

En primer lugar, aborda la problemática de la rápida evolución de la tecnología, que crea una brecha constante entre los conocimientos adquiridos y las habilidades demandadas en el mercado laboral. Esta velocidad de cambio hace que sea extremadamente difícil para los estudiantes y profesionales mantenerse actualizados con las últimas tendencias y herramientas. Además, Cortex busca superar la barrera lingüística presente en la educación tecnológica, donde la predominancia de contenido educativo en inglés limita significativamente el acceso al conocimiento para muchos estudiantes hispanohablantes. La plataforma también atiende la carencia de recursos interactivos y atractivos específicamente diseñados para captar el interés y mantener la motivación de los jóvenes en el campo de la informática, un aspecto crucial para fomentar vocaciones tecnológicas desde edades tempranas.

Por último, Cortex se posiciona como una alternativa necesaria frente a la escasez de plataformas de aprendizaje gratuitas y accesibles de alta calidad, democratizando así el acceso al conocimiento tecnológico y reduciendo las barreras económicas que a menudo impiden a muchos individuos adquirir habilidades digitales esenciales en el mundo actual.

¿Qué hace Cortex?

Se planificó que Cortex siguiera un modelo freemium, donde la mayoría de las funcionalidades serían gratuitas, pero se ofrecerían funcionalidades premium a cambio de una suscripción mensual mediante un proveedor de pagos.

Para la entrega en sí, se acordó crear un MVP (Minimum Viable Product), que puediera ser presentado y demostrado en la fecha de la presentación. Este MVP incluiría las siguientes funcionalidades:

  • Soportar al menos 5 lenguajes de programación (Java, Python, Typescript, Go y Rust)
  • Proporcionar un motor de compilación de código remoto seguro (sandboxing).
  • Presentar contenido educativo utilizando Markdown.
  • Proporcionar una experiencia de aprendizaje interactiva y accesible.
  • Ofrecer contenido educativo en español.
  • Permitir a los usuarios registrarse y guardar su progreso.
  • Ofrecer un sistema de recompensas y logros para motivar a los estudiantes.
  • Proporcionar un sistema de mentorías, donde usuarios podrían volverse mentores y ayudar a otros estudiantes mediante chat.
  • Para la versión premium, se ofrecería acceso a una aplicación de escritorio, que permitiría a los usuarios trabajar con acceso a mejores herramientas (como multiples IA para dar pistas a los estudiantes).

¿Cómo es el sistema educativo de Cortex?

Cortex se basa en un sistema de aprendizaje interactivo, donde los estudiantes pueden aprender con base en ejercicios teóricos o prácticos. Nos basamos bastante en plataformas como Exercism, HyperSkill, Codecademy, entre otros.

La idea es que al usuario se le presentan Roadmaps, que son caminos de aprendizaje que el usuario decide seguir. Estos caminos están compuestos por cursos, que a su vez están compuestos por módulos y estos por lecciones. Cada lección tiene un contenido teórico y un ejercicio práctico/teórico.

Los ejercicios son la parte más importante de la plataforma, ya que son la forma en que los usuarios aprenden y practican. Estos ejercicios pueden ser de varios tipos, como:

  • Ejercicios de programación: El usuario debe escribir un programa que cumpla con ciertas condiciones.
  • Ejercicios de elección múltiple: El usuario debe seleccionar la respuesta correcta entre varias opciones
  • Ejercicios de completar el código: El usuario debe completar un código que le falta partes
  • Ejercicios de verdadero o falso: El usuario debe determinar si una afirmación es verdadera o falsa
  • Ejercicios de arrastrar y soltar: El usuario debe arrastrar elementos a su lugar correcto en la pantalla

Además, los ejercicios tienen instrucciones y pueden tener pistas, que son pequeñas ayudas que se le dan al usuario si se encuentra atascado.

¿Cómo funciona Cortex?

Cortex se compone de varios servicios, que se comunican entre sí mediante una API REST. Esta API la cree yo mismo, utilizando Spring Boot y PostgresSQL, hice que se dividiera en distintos módulos Maven para asi tener mayor organización de la aplicación. La API se encarga de manejar la autenticación, la ejecución de código, la gestión de usuarios, y la gestión de contenido educativo, entre otras cosas.

El motor de ejecución de código

Editor de código de Cortex en la aplicación de escritorio

El motor de ejecución de código fue una de las primeras cosas que hice, y fue un desafío bastante grande porque no tenía experiencia haciéndolo. Busque en internet y no encontré muchos recursos valiosos, pero sí encontré blogs sobre otras implementaciónes en distintos lenguajes (la mayoría en JavaScript).

La idea detrás de este motor es que los usuarios puedan escribir código en la plataforma y este se ejecute en un entorno seguro, tanto para el usuario como para la plataforma. Para lograr esto, investigué lo que es sandboxing, que es básicamente aislar el código que se ejecuta en un entorno controlado, para que no pueda hacer nada malicioso. Ese entorno, en nuestro caso, es un contenedor de Docker.

¿Pero como se ejecuta el código en un contenedor de Docker? Bueno, descubrí que fue era bastante simple, gracias a la librería de Java llamada docker-java, que permite interactuar con el daemon de Docker. También se usó Redis y RabbitMQ para el manejo de colas y caché.

Otra cosa que había que considerar, es como funcionaría el tema de las pruebas de los programas que creen los usuarios. Era algo complicado, puesto que dependía del lenguaje de programación.

Lo que se nos ocurrió, fue crear un repositorio de GitHub donde se almacenen los ejercicios, su configuración, sus instrucciones y pistas, además de las pruebas necesarias para verificar que el código del usuario es correcto. Esto se clonaría al contenedor de Docker gracias a la librería jgit y se usaría un scheduler para mantener actualizado dentro del contenedor, el cual buscaría actualizaciones de Git cada cierto tiempo o al iniciar el backend.

El proceso se resume en lo siguiente:

  1. El usuario escribe código en la plataforma mediante un editor en línea.
  2. El código se envía a la API, este código está cifrado en Base64 junto al ID del ejercicio y el lenguaje de programación.
  3. La API recibe el código, lo decodifica y lo envía a una cola de RabbitMQ. También se crea en una entidad llamada Submission el registro del envío del código.
  4. Un worker escucha la cola, y cuando recibe un mensaje, crea un contenedor de Docker con el código del usuario y el código de las pruebas.
  5. El worker ejecuta el código del usuario y las pruebas, y envía el resultado a la API.
  6. La API recibe el resultado, lo guarda en la entidad Solution y lo envía al frontend.
  7. El frontend recibe el resultado y lo muestra al usuario en detalle.

Para darte una idea, así se ve el JSON que se envía a la cola de RabbitMQ:

{
  "code": "cHViIGZuIGhlbGxvKCkgLT4gU3RyaW5nIHsKICAgICJIZWxsbywgV29ybGQhIi50b19zdHJpbmcoKQp9Cg==",
  "language": "rust",
  "exercise_id": 19
}

En este caso, el código es pub fn hello() -> String { "Hello, World!".to_string() }, el lenguaje es Rust y el ID del ejercicio es 19.

La respuesta que envía el worker es algo como esto:

{
  "status": "SUBMITTED",
  "message": "Code execution task submitted successfully",
  "task_id": "280fa11c-56d2-4676-b373-10f1bdf85655",
  "submission_time": "2024-11-14T02:28:33.954607445"
}

Por debajo, el worker está ejecutando el código del usuario y las pruebas, y si todo sale bien, se puede acceder mediante él task_id a la solución del ejercicio.

El backend

Como mencioné antes, el backend está hecho en Spring Boot, y se divide en varios módulos Maven. La mayoría de los módulos son bastante simples, como el de autenticación, que se encarga de manejar el registro y login de usuarios tanto con correo como con Google o GitHub, o el de usuarios, que se encarga de manejar la información de los usuarios, el de educación, que maneja el contenido educativo, etc.

Una de las cosas interesantes que tuve que desarrollar fue el módulo de pagos, y este tiene una historia curiosa. Al principio, se nos ocurrió que podríamos hacer los pagos con MercadoPago, pero encontramos dificultades haciéndolo funcionar como queríamos, probamos PayPal, pero tampoco nos convenció, así que decidimos hacerlo mediante Lemon Squeezy, una plataforma de pagos que fue comprada por Stripe. Uno de los problemas es ue no tiene SDK para Java, así que tuve que ensuciarme las manos y crear una librería para ello. Siendo honesto, fue la primera librería que tuve que hacer, intente hacer que fuera agnóstica al framework. Esta se encuentra en su propio módulo. Utilicé okhttp3 para hacer las peticiones HTTP e intenté usar características modernas de Java, dado que usábamos la JDK 22. La librería se encarga de manejar varias, pero no todas, de las funcionalidades de la API de Lemon Squeezy, como manejar suscripciones, pagos, licencias, etc.

El frontend

El frontend está hecho en Nuxt.js. La verdad, no tengo mucho que decir sobre él, ya que no hice todo yo, sino que fue un trabajo en conjunto con mis compañeros. Lo que sí puedo decir, es que usamos una layer de Nuxt para reutilizar componentes, y que usamos TailwindCSS (junto a Shadcn-Vue) para el diseño.

La App de escritorio

La app de escritorio es un proyecto aparte, y se hizo con Tauri. La idea detrás de ella es que solo los usuarios con una licencia pueden acceder a ella. Dentro de la app, los usuarios pueden acceder a distintas IA que les ayudan a resolver los ejercicios, como Claude, Gemini y Ollama.

La app en sí, como toda app de Tauri, tiene un backend en Rust y un frontend presentado mediante una WebView del sistema. Es multiplataforma, y se puede instalar en Windows, Linux y macOS (no tenemos forma de probarlo en macOS, pero debería funcionar). Trabaje en la parte de Rust y la mayoría de funcionalidades complejas, como la comunicación con la API de Cortex, la comunicación con las IA, y todo eso lo dividí en distintos crates para tener una mejor organización.

Para esta app, tuve que ensuciarme las manos y contribuir al plugin de OAuth de Tauri, que no estaba completo y solo tenía la parte de Rust hecha. La parte de TypeScript era inexistente, asi que contribuí al crear los bindings y mejorar la documentación del plugin. Pueden ver el plugin en GitHub.

Una de las cualidades que más me gusta es que el editor de código está en la layer de Nuxt, por ende, cuando usas el editor en la app de escritorio, ¡es el mismo que en la web! Para el editor, se usó CodeMirror.

Otras de las funcionalidades que tiene la app de escritorio es que también es para uso administrativo, desde ahí, el equipo de Cortex puede crear Roadmaps, cursos, módulos, lecciones, mediante un editor de texto enriquecido, y también pueden ver las estadísticas de los usuarios.

El editor de texto enriquecido viene gracias a TipTap, también las imágenes de los contenidos se pueden subir mediante drag and drop, y se guardan en Cloudinary.

Tanto el frontend como la app de escritorio se comunican con la API de Cortex. Y todo se almacena en un monorepo de GitHub.