En Eurmes al principio teníamos pocos tipos de ejercicio, por ejemplo un texto con hueco donde rellenábamos lo que faltaba, o un vídeo en el idioma que estábamos aprendiendo y luego se nos ponía una serie de preguntas sobre ese vídeo para ver que comprendíamos lo que decía. Más adelante se fueron añadiendo muchos más tipos de ejercicio: etiquetar imágenes, correctores de pronunciación, conversación en tiempo real, etcétera. Entonces lo que vemos aquí es que en un ecosistema, a lo largo del tiempo, se van añadiendo muchos tipos nuevos de ejercicios diferentes, y lo que buscamos es que esto sea un poco más automático gracias a la inteligencia artificial. Es decir, el objetivo es que yo, a través de un prompt, diga: oye, hay que añadir este nuevo tipo de ejercicio que consiste en agrupar conceptos en un grupo o en otro grupo. Entonces, al final, a la IA tenemos que orientarla con guías y controlarla con sensores, que al final es lo que hace el harness engineering, para que produzca esos nuevos tipos de ejercicio y enganchen bien en nuestro ecosistema. Y de eso trata este vídeo concretamente: vamos a utilizar técnicas avanzadas de TypeScript para controlar y orientar que la IA añada nuevos tipos de ejercicio, de manera que mantenga la calidad, la arquitectura software, y que escale, sea extensible y no afecte al rendimiento. Vamos a empezar viendo directamente un ejemplo para enseñarle a ella a crear nuevos tipos de ejercicio. Primero tenemos que estudiar cómo es nuestro ecosistema y entenderlo para saber qué tienen en común todos los ejercicios. Entonces, ¿qué tienen en común todos estos ejercicios? Primero, todos los ejercicios tienen una vista simple para los estudiantes, que es esta de aquí, una vista interactiva donde vamos respondiendo cada uno de los ejercicios, por ejemplo así, vamos viendo poca respuesta. Y también un componente hermano de esta vista que no es tan ligero, sino que es todo lo contrario, es mucho más complejo y sirve para crear o modificar este ejercicio, por ejemplo añadir nuevos textos, añadir más preguntas por texto aquí debajo, bien con IA o nosotros manualmente, y marcar la respuesta correcta. Entonces vemos aquí que esto es mucho más complejo y tiene mucha más lógica, pero es para el profesor, para que el profesor pueda crear contenido. Entonces el primer requisito que tiene todo ejercicio es que tenga una vista simple y una vista compleja para crearlo. Además de eso, cada ejercicio tiene su propia definición del dato. Esto es un texto con un array de preguntas, esto es un audio con un array de posibles hermanos que emparejar, vale, y aquí lo mismo, y esto es escuchar un audio de la BBC y escribir aquí después lo que oyes, estás practicando ABC en el A1, por eso es tan fácil. Entonces, al final, lo que vemos es que cada ejercicio tiene su estructura de datos. Segundo requisito. Entonces, el tercer requisito es que cada ejercicio tiene su propia configuración. Es decir, este ejercicio, si le doy a crear con IA, tiene una serie de configuraciones: qué tipo de contenido es sobre el que voy a realizar preguntas, un audio, una imagen, un aviso; cuántos textos hay, cuántas preguntas hay por texto, cuántas respuestas hay por pregunta, e instrucciones para la IA, y así le guiamos mucho a la IA cómo crear un ejercicio. Y además de eso, si vamos a otro ejercicio diferente y lo creamos con IA, aquí vemos que las opciones son totalmente diferentes: qué tipo de hueco es, cómo es el formato del texto, si hay un audio de apoyo o no, cuántos huecos tiene el texto. Entonces vemos que también cada ejercicio tiene su propia configuración. Adicionalmente, también en los ejercicios tenemos que tener un mecanismo de control de que, si yo hago el ejercicio este como alumno el uno de enero de dos mil veintiséis, y el tres de diciembre de dos mil veintiséis un profesor dice: oye, que aquí hay una errata, esto no debería ser en plural, debería ser en singular, lo corrijo, pues hay que establecer un mecanismo en cada ejercicio, donde en cada ejercicio ese mecanismo es diferente, para controlar versionado. Hay muchísimas más cosas que hay que tener en cuenta; yo, por motivos de simplificación en el vídeo, simplemente he explicado estas cuatro. Entonces vamos a ver cómo podemos utilizar TypeScript como guía que oriente a la IA a generar nuevos tipos de ejercicios sin nosotros intervenir mucho. Entonces, lo primero que hay que hacer cuando vamos a añadir un nuevo tipo de ejercicio es poner aquí el nombre, vale, que en este caso voy a utilizar speaking in real time, vale. Entonces, una vez añadimos este nuevo tipo de ejercicio, vemos que saltan errores por todos lados, que es el objetivo, ¿no? Y estos errores guían, orientan a la IA hacia dónde tiene que ir. Entonces, si nosotros vamos aquí, vemos que aquí hay un error, está diciéndote: oye, esto está mal. Entonces la IA va a venir por aquí y va a decir: esto está mal, hay que mirarlo. Y nosotros con TypeScript estamos diciendo: tienen que estar en este diccionario todas las llaves que declares aquí, es decir, que speaking en real time debería estar aquí. Y una vez hacemos eso, también estamos obligados a definir a la derecha un ExerciseTypeDescriptor, que lo que viene a decirnos es, nos obliga a que por cada ejercicio implementemos una serie de funciones; si no lo hacemos, no va a encajar y el compilador va a decir fuera. Esas funciones son las etiquetas traducibles, es decir, cada ejercicio tiene sus nombres, sus etiquetas, que tienen que estar traducidas en todos los idiomas; también el hash que controla el versionado del ejercicio; también cómo se calcula el estado de completitud y cómo se calcula el estado del ejercicio, si está a medias, si hay fallos, si está todo correcto. Eso es en cuanto a un ejercicio, pero no nos quedamos aquí. Si nosotros vamos y aquí también con TypeScript estamos controlando que aquí están todas las llaves, es decir, aquí qué faltaría, pues el que acabamos de crear, el speaking en real time, y al añadirlo en este diccionario obligamos a que tenga esto y esto: qué es un componente con una serie de características, tiene que tener un título, un subtítulo, un componente asociado, unos inputs, vale, que recibe ese ejercicio bien definido. Entonces lo que vemos es que con TypeScript estamos forzando, orientando a la IA a que tiene que crear un componente de tipo vista de estudiante, y lo mismo en el caso del constructor, un componente de tipo constructor de ejercicio, vale. Entonces aquí lo que estamos haciendo es eso: con TypeScript orientando y obligando a que la IA siga un patrón para encajar en el ecosistema, vale. Otra cosa que hay que tener en cuenta es que cuando definimos un nuevo tipo de ejercicio también estamos obligados a definir los tipos en el modo construcción. Es decir, aquí yo voy a añadir el real time, y esto me obliga a poner en la derecha un objeto de un tipo específico, que es este de aquí, y este objeto me obliga a implementar unas funciones: cómo se genera un ejercicio vacío, qué valores por defecto, cómo calculo si está válido la creación de ejercicio, y cómo calculo si está vacío para no pedirte lo de deshacer los cambios, porque si está vacío no tiene sentido pedírtelo, entre otras cosas. Y también, muy importante, la configuración del ejercicio: cuántos números de huecos tiene, cuántas preguntas, cuántos audios, qué voz tiene el audio. Cada ejercicio tiene su propia configuración; este tendrá una, el de rellenar huecos tendrá una configuración específica, tipo de hueco, tipo de diseño, y el de múltiple choice tendrá otra, es decir, qué tipo de estímulo, si es una imagen, un vídeo, cuántas preguntas hay por vídeo, por imagen o por texto. Entonces cada uno tiene su configuración, y vemos que con TypeScript estoy obligando a que, si hay un nuevo tipo de ejercicio, definamos todo esto. Bueno, continuamos con el tema de los datos, y antes quiero ver una cosa. Normalmente, cuando nosotros interactuamos con LLM, queremos que nos devuelvan la información con una determinada estructura que podamos predecir para poder tratarla a nivel software, y aquí es donde viene output_config. output_config lo que hace es que le mandamos un esquema específico que es el resultado que esperamos de la IA, y al ser determinista, es decir, siempre me vas a enviar un JSON que tenga nombre, email y estos campos de aquí. Entonces, como yo puedo predecir que cualquier LLM, esto es extrapolable para Gemini, para Mistral, para OpenAI, nosotros lo que estamos haciendo aquí es obligar a que el formato de la salida sea este; si no es este, va a haber un error. Entonces podemos controlar ese error. En el caso de TypeScript usamos Zod, que es exactamente lo que nosotros usamos; aprovechamos Zod para definir cómo es la estructura de datos de nuestros ejercicios. El multiple choice tiene preguntas, tiene opciones, que es un array de tipo cadena de texto donde tenemos diferentes opciones. Y aparte de eso, también obligamos a definir cómo crear un esquema a partir de una configuración que ha definido el usuario, vale. Es decir, nosotros al final vamos a recibir una configuración por parte del usuario y vamos a mezclarlo con el esquema original para generar un nuevo esquema que le diga a la IA exactamente cómo queremos el ejercicio cuando lo generamos. Y aquí viene algo muy importante. Bueno, nos podemos ver tentados a: bueno, pues yo tengo que obligar a crear un esquema Zod, a crear un esquema a partir de la configuración del usuario, a crear el modelo de datos, bueno, lo creo todo en front con TypeScript, lo controlo, y que la IA, pues cuando cree nuevos ejercicios, se basa en todo el TypeScript de front para generar todos estos componentes. ¿Cuál es el problema de esto? Que desde el lado del cliente un usuario malintencionado puede cambiar el prompt, cambiar el esquema, cambiar cosas, y no podemos controlarlo. Entonces, para conseguir ese beneficio de que desde front, cuando creamos un componente nuevo, simplemente todo un esquema, y a la vez conseguir el beneficio de que, si lo movemos a back, el prompt y el esquema, pues ya es más seguro todo, porque comprobamos que lo que se envía a la IA cumple tanto el esquema como el prompt, cumplen con todo, y tanto front como back tiran del mismo archivo, de un contrato que tienen los dos, y nuevamente con TypeScript obligamos a implementarlo. Bueno, tal y como vimos aquí, la manera de conseguirlo es que yo aquí obligo, fijaros que esto se llama contratos y está fuera del front y del back, es un hermano de ellos, y es un contrato entre front y back. Y lo bueno de esto es que si yo cambio algo aquí, se entera front al momento y también back, vale. Entonces, al final, si yo añado un nuevo campo o añado algo nuevo, pues ya los dos lugares se enteran a la vez y es más fácil de mantener, porque uno de los grandes problemas en los equipos de software es que back escribe las cosas a su manera, front escribe las cosas a su manera, y después cuando se comunican se lía. Entonces para eso están los contratos. Y al final, en estos contratos nosotros obligamos a implementar esos esquemas y esas funciones, y de esta manera comprobamos tanto en front como en back que se cumplen los esquemas: cuando enviamos a la IA, y cuando guardamos en base de datos comprobamos que el esquema es el adecuado, y en front también, cuando configuramos el ejercicio, antes de enviarlo a back comprobamos también que el esquema está bien. La manera en que controlamos que, cuando recibimos un nuevo tipo de ejercicio, cumple con un determinado formato, es con esto de aquí, vale. Nosotros aquí, en esta función, lo que hacemos es que comprobamos, basándonos en los contratos que importamos de contratos, que se cumple el esquema; si no, no dejamos pasar ni siquiera la primera barrera para entrar al back, es como que ni siquiera entra el controlador. Bueno, finalmente, otra cosa que tenemos que ver es cómo definir TypeScript como sensor, el compilador de TypeScript. Para ello vamos al archivo settings dentro de .claude, aunque es verdad que hay un patrón también para agentes para Cursor, Codex, etcétera, pero vamos a centrarnos en Claude, porque si lo entendemos en un sitio, lo entendemos en todo. Y en el archivo settings tenemos hooks, y hay distintos tipos de gancho. Uno de ellos es Stop, es decir, cuando para de generar código nuevo que le hemos pedido; aquí en el chat le hemos pedido: oye, desarróllame esto, hazme esto; cuando ella termina de generar código, ejecuta lo que le especificamos aquí. Y lo que le estamos especificando es este archivo shell, que lo que hace es comprobar que el compilador de TypeScript, el TypeScript compiler, funciona bien tanto para los contratos como para el lado front, y solo se considera la tarea terminada, pues eso es lo que lo convierte en sensor. Cuando esto pasa, cuando esto dice okay, todo bien; si no lo dice, la tarea no se considera terminada y el chat o el agente vuelve a intentarlo, modificando el código hasta que lo consigue. A modo de recapitulación, en este vídeo hemos visto cómo utilizamos técnicas avanzadas de TypeScript para guiar a la IA en la construcción de nuevo software, y también hemos utilizado TypeScript para actuar como sensor, es decir, una especie de alarma que salta cuando no cumplimos con nuestra arquitectura o nuestras normas. Es decir, que TypeScript actúa tanto como guía como sensor en el harness engineering. Además, hemos aprovechado este vídeo para dejar claro que siempre que podamos no enviamos desde cliente el prompt o los esquemas que esperamos recibir, porque pueden ser alterados desde el lado cliente y que utilicen nuestras inteligencias artificiales para propósitos no legítimos. Espero que te haya gustado este vídeo y que todo lo que hayas aprendido en él lo puedas utilizar en tus propios proyectos. Si tienes cualquier duda, envíame un mensaje, o cualquier sugerencia, y nada, nos vemos en otros vídeos.