rukh · lab

// M0 · lección 00

Taller: repo, entorno, datos y tablero

Qué vas a construir en trece módulos, cómo se estudia este curso, y el taller del módulo 0: uv con CUDA, Stockfish verificado, MLflow local, el recorte de datos de Lichess con sus filtros explicados y un tablero que solo acepta jugadas legales.

  • 180 min
  • nivel base
  • vigente
  • actualizado el18 de septiembre de 2026

Qué vas a construir

Vas a construir un modelo de lenguajeModelo de lenguajeRed neuronal entrenada para predecir el siguiente token dada la secuencia anterior. Aplicado a partidas de ajedrez, predice la siguiente jugada dadas las jugadas previas; de esa única tarea emergen las reglas y el juego. que juega al ajedrez. No un programa que calcula jugadas, como Stockfish, sino una red neuronal que ha leído millones de partidas y que, dada la secuencia de jugadas de una partida, propone la siguiente. La misma idea que hay detrás de un LLM de texto, con una diferencia que lo cambia todo: aquí cada tokenTokenUnidad mínima que el modelo lee y escribe. En Rukh, por defecto, un token es una jugada completa en notación UCI (`e2e4`); en otras tokenizaciones puede ser un carácter o un trozo de texto aprendido por BPE. El modelo nunca ve letras ni tableros: ve identificadores enteros de tokens. es una jugada, y un motor de ajedrez puede decir en milisegundos si esa jugada es legal, buena o mala.

Eso es lo que hace del ajedrez un dominio perfecto para aprender IA generativa de verdad, no de oídas:

  • Es verificable. La legalidad y la calidad de cada jugada las decide StockfishStockfishMotor de ajedrez libre (GPL-3) y el más fuerte disponible. En Rukh no juega por el modelo: es el juez. Con `UCI_LimitStrength` y `UCI_Elo` (1320-3190) se convierte en un rival calibrado para estimar Elo, y a poca profundidad da recompensas baratas para el alineamiento., no una persona con un formulario. Cuando lleguemos al alineamiento (reward model, DPO, GRPO), las recompensas serán reales, baratas y reproducibles.
  • Los datos son abiertos. Lichess publica miles de millones de partidas, puzles y evaluaciones bajo CC0CC0Dedicación al dominio público de Creative Commons: el autor renuncia a todos los derechos. Lichess publica sus partidas, puzles y evaluaciones bajo CC0, así que se pueden recortar, redistribuir y usar para entrenar sin pedir permiso (se cita por cortesía).. No hay que negociar licencias ni esconder de dónde salen.
  • Los modelos son pequeños. Decenas de millones de parámetros bastan para jugar por encima de 1200 EloEloEscala de fuerza de juego: una diferencia de 200 puntos implica que el mejor gana unas tres de cada cuatro veces. Lichess usa Glicko-2, compatible en la práctica. Rukh estima el Elo de cada modelo jugando contra Stockfish limitado y publica el intervalo de confianza.. Se entrenan en horas en una sola GPU y caben en el navegador, así que puedes medirlo todo y publicarlo todo.
  • La demo se entiende en cinco segundos. Un tablero, y juegas contra lo que has entrenado.

El curso tiene dos fases y trece módulos. En la primera (M0-M6) construyes el modelo de principio a fin; en la segunda (A1-A6) lo conviertes en un entrenador con herramientas, memoria y trazas.

Módulo Qué entra Qué funciona al terminar
M0 · Taller Repos, entorno, datos, tablero rukh info, datos recortados, tablero con jugadas legales
M1 · Datos y tokenización Tokens, vocabularios, dataloaders, fugas entre splits Lotes de las tres tokenizaciones y una comparativa
M2 · El decoderDecoderArquitectura Transformer con atención causal: cada posición solo mira hacia atrás, así que sirve para generar de izquierda a derecha. GPT es un decoder; `MoveDecoder`, el modelo de Rukh, también. Atención causal, receta de entrenamiento, muestreo, ONNX rukh-small a ≥ 1200 Elo, jugable en la web
M3 · El encoderEncoderTransformer con atención bidireccional: cada posición ve toda la secuencia. No genera; representa. En Rukh el encoder lee una partida entera y produce un vector por posición del que salen el valor y la detección de errores. Bidireccional, masked move modeling, pooling, cabezas Valor y alerta de error en vivo
M4 · Fine-tuning e instrucción SFT, tokens de control, LoRA, QLoRA, ecosistema HF Selector “juega como 1500/2000/2400”
M5 · Alineamiento Reward model, DPO, GRPO, reward hacking Modelos DPO y GRPO medibles
M6 · Evaluar, exportar, publicar Métricas, intervalos de confianza, cuantización, model cards Tabla única y demo final
A1 · Embeddings y RAG Vector DB, recuperación, RAGAS Comentarios con citas de Capablanca
A2 · Agente con herramientas LangGraph, LangSmith Un entrenador que analiza partidas
A3 · Evaluación y observabilidad Suites de evaluación, regresión Suite en verde por commit
A4 · Multiagente Roles, orquestación Jugador, entrenador y redactor
A5 · MCP Servidores y clientes MCP Herramientas de ajedrez desde cualquier cliente
A6 · Capstone Backend, web del entrenador Juegas, te comenta en vivo, informe final

Y esta es la tabla que convierte una lista de módulos en un programa de estudio: cada concepto de IA generativa y agéntica entra cuando un lab lo necesita para tomar una decisión.

Concepto Dónde se aprende con el proyecto
Tokenización (vocabulario fijo, BPE, tokens especiales) M1: tres tokenizaciones de partidas comparadas
Datasets, dataloaders, padding, máscaras, streaming M1: de parquet a lotes
Embeddings y posiciones (aprendidas, RoPE) M2
Atención, multi-cabeza, máscara causal, decoder GPT M2: decoder a mano
Receta de entrenamiento (AdamW, warmup, bf16, clipping) M2
Muestreo (temperatura, top-k, enmascarado) M2
Encoder bidireccional, MLM, pooling, clasificación M3: masked move modeling y cabezas
Fine-tuning completo frente a parcial, curvas por etiquetas M3-M4
Instruction tuning (condicionar el comportamiento) M4: token de Elo objetivo
LoRA, QLoRA, adaptadores intercambiables M4
Ecosistema HF (transformers, peft, trl, datasets) M4: QLoRA de Qwen sobre PGN
Reward model, Bradley-Terry M5
DPO (β, referencia implícita) M5
GRPO / RL con recompensas verificables, reward hacking M5
Evaluación, harness reproducible, model cards M6
Cuantización, ONNX, inferencia en navegador M2 y M6
Embeddings para recuperación, vector DB, RAG, RAGAS A1
Agentes con herramientas, LangGraph, LangSmith A2-A3
Multiagente, MCP, capstone A4-A6

Cómo estudiar este curso

Cada módulo sigue el mismo patrón, y conviene saberlo antes de empezar:

  1. Teoría justa. Solo la que el lab necesita para tomar una decisión: qué tokenización, qué pooling, qué β en DPO. Ningún capítulo de teoría suelto. Si un concepto no cambia una decisión del proyecto, no está.
  2. Labs guiados. Notebooks finos en rukh/labs/ con huecos que rellenas y una solución de referencia. Casi todo el código pesado vive en el paquete rukh, con tests, para que el notebook sea el sitio donde piensas y no donde copias.
  3. Artefactos públicos. Cada módulo termina con algo publicado: un dataset, un modelo con su card en Hugging Face, una versión nueva de la demo, un post. Si no está publicado, no está hecho.
  4. Tabla única. Toda etapa se mide igual (ver abajo) y se compara con la anterior y con modelos públicos. Sin número no hay model card.
  5. Demo siempre verde. Cada semana acaba con algo jugable en rukh.borjaglez.com.

Cómo se mide es lo primero que hay que fijar, porque decide todo lo demás. Las métricas de la tabla única son cuatro:

  • Tasa de jugadas legalesTasa de legalidadPorcentaje de jugadas propuestas por el modelo que son legales cuando no se le enmascaran las ilegales. Es la primera métrica de la tabla única: mide si el modelo ha aprendido las reglas solo viendo partidas. En la demo, la legalidad la decide chess.js; el modelo solo propone. sin máscara: qué porcentaje de las jugadas que propone el modelo, sin ayuda, son legales. Mide si ha aprendido las reglas.
  • Exactitud de siguiente jugada: top-1 y top-3 frente a la jugada humana en partidas que no ha visto, por tramo de Elo.
  • Puzles resueltos por dificultad (1000-1500, 1500-2000, 2000+).
  • Elo estimado contra Stockfish con fuerza limitada (UCI_Elo), cien partidas por nivel, con intervalo de confianza.

El ritmo objetivo son 8-10 horas por semana: M0 y M1 en la primera, M2 en la segunda, y así hasta cerrar la fase 1 en cinco semanas. Los entrenamientos largos se lanzan al final de una sesión y se recogen en la siguiente; mientras corren, se escribe la lección del módulo. El progreso de las lecciones lo guarda tu navegador (botón “Marcar completada” al final), sin cuentas ni servidores.

El entorno

El proyecto vive en tres repositorios independientes: rukh (Python: datos, modelo, entrenamiento, evaluación, agentes), rukh-lab (este sitio) y rukh-web (la demo para jugar). No comparten paquetes: lo que necesitan en común (tokens CSS, tokenizador en TypeScript, JSON precomputado) se copia con un script y un test de hash vigila que no diverja.

Python 3.12 con uv

uv gestiona la versión de Python, el entorno virtual y el fichero de bloqueo. El proyecto fija Python 3.12 en .python-version y dos extras excluyentes para PyTorch: cpu (para la CI) y cu128 (CUDA 12.8, la que necesita una RTX 5090).

Terminal
git clone https://github.com/borja-glez/rukh
cd rukh
uv sync --extra cu128 --group dev
uv run rukh info

Salida real en la máquina de referencia (RTX 5090, 2026-09-18); <repo> es la carpeta donde hayas clonado rukh:

$ uv run rukh info
python 3.12.11
platform Windows-11-AMD64
torch 2.11.0+cu128
cuda True
gpu NVIDIA GeForce RTX 5090
stockfish <repo>\tools\stockfish\stockfish.exe
mlflow_uri sqlite:///<repo>/mlruns/mlflow.db
data_dir <repo>\data
mlruns_dir <repo>\mlruns

rukh info --json devuelve lo mismo como JSON, que es lo que usan los tests. Si cuda sale False, lo primero que hay que mirar es que el extra instalado sea cu128 y no cpu: el fichero uv.lock fija torch==2.11.0 para los dos índices, y un test (test_lock_guard.py) falla si alguien cambia la restricción en uno solo.

Los tests unitarios no necesitan GPU ni Stockfish y son la comprobación rápida de que el entorno está bien:

Terminal
uv run pytest -m unit -q

Salida real en la máquina de referencia (RTX 5090, 2026-09-18):

$ uv run pytest -m unit -q
......................................................... [100%]
57 passed, 2 deselected in 4.78s

Los dos tests deseleccionados son los que no llevan la marca unit; el filtro -m unit los deja fuera a propósito.

Stockfish

Stockfish es el juez del curso: no juega por el modelo, lo evalúa. El script scripts/get_stockfish.py descarga la versión 19 oficial a tools/stockfish/ (carpeta ignorada por git) y rukh engine check abre el motor por el protocolo UCIUCIDos cosas con el mismo nombre. La notación UCI escribe una jugada como casilla de origen, casilla de destino y promoción opcional (`e7e8q`): no depende del contexto, por eso es la tokenización por defecto. El protocolo UCI es la forma en que hablamos con Stockfish desde `python-chess`., lee el rango de UCI_Elo, lo limita a un Elo concreto y juega unas cuantas medias jugadas contra un jugador aleatorio para comprobar que todo responde.

Terminal
uv run python scripts/get_stockfish.py
uv run rukh engine check --elo 1400 --plies 40

Salida real en la máquina de referencia (RTX 5090, 2026-09-18):

$ uv run rukh engine check --elo 1400 --plies 40
path <repo>\tools\stockfish\stockfish.exe
name Stockfish 19
uci_elo_min 1320
uci_elo_max 3190
elo 1400
plies_played 35
result 1-0
nodes_hint 40736

Los campos significan lo siguiente: uci_elo_min y uci_elo_max son el rango de UCI_Elo que Stockfish acepta, elo la fuerza que le hemos pedido, plies_played las medias jugadas que se han jugado antes del mate o del límite (--plies 40) y nodes_hint la suma de nodos que el motor ha declarado buscar. plies_played, result y nodes_hint cambian de una ejecución a otra: la búsqueda de Stockfish está limitada por tiempo (50 ms por jugada), y el rival aleatorio tampoco repite partida.

Los dos números que importan son uci_elo_min 1320 y uci_elo_max 3190: es el rango en el que podemos pedirle a Stockfish que juegue peor de lo que sabe. Con eso estimaremos el Elo de cada modelo jugando cien partidas por nivel.

MLflow

Cada entrenamiento se registra en MLflowMLflowRegistro de experimentos: cada entrenamiento guarda su configuración, sus métricas por paso y sus artefactos en una base SQLite local (`rukh mlflow ui` la abre en el navegador). Las model cards del curso se generan desde ahí para que ningún número se escriba a mano. con su configuración, sus métricas por paso y sus artefactos, en una base SQLite local. Las model cards de los módulos siguientes se generan desde ahí, para que ningún número de la tabla única se escriba a mano.

Terminal
uv run rukh mlflow ui
# abre http://127.0.0.1:5000 sobre sqlite:///<repo>/mlruns/mlflow.db

Node 24 y pnpm 10 para las webs

Las dos webs son sitios estáticos hechos con Astro. Necesitas Node 24 y pnpm 10 (corepack enable lo activa). En cada repo web, pnpm install y pnpm dev; pnpm check, pnpm lint, pnpm test, pnpm build, pnpm e2e y pnpm lighthouse son la cadena completa que también corre en la CI.

// Ejercicio 01Comprueba tu entorno

Ejecuta uv run rukh info --json y responde: ¿qué versión de torch tienes, con qué índice, y qué ruta de Stockfish detecta? Si stockfish es null, ejecuta el script de descarga y vuelve a mirar. Después, uv run pytest -m unit -q tiene que estar en verde.

// SoluciónVer la solución

La clave torch termina en +cu128 cuando el extra correcto está instalado (+cpu en la CI). stockfish se resuelve en este orden: variable de entorno RUKH_STOCKFISH, después tools/stockfish/stockfish(.exe), después stockfish en el PATH. Si sigue en null, la causa más habitual en Windows es que el zip se descomprimió con una carpeta intermedia: el script lo normaliza, pero comprueba que el ejecutable está exactamente en tools/stockfish/.

Los datos

Lichess es el servidor de ajedrez libre más grande del mundo y publica todo lo que se juega en él. Sus volcados mensuales, las copias en Hugging Face (Lichess/standard-chess-games, más de 6 700 millones de partidas), los seis millones de puzles y los casi 400 millones de posiciones evaluadas con Stockfish están bajo CC0: dominio público. Es la razón por la que este curso no necesita pedir permiso a nadie.

Un mes reciente ocupa unos 73 GB en parquetParquetFormato de fichero columnar y comprimido. Lichess publica sus partidas así en Hugging Face, y DuckDB puede filtrarlas en remoto leyendo solo las columnas y los bloques necesarios (predicate pushdown), lo que hace posible recortar 73 GB a unos pocos sin descargarlo todo.. No vamos a descargarlo. El recorte de trabajo se hace con DuckDBDuckDBBase de datos analítica embebida que ejecuta SQL sobre ficheros parquet locales o remotos (`hf://`). Es la herramienta con la que `rukh data fetch` aplica los filtros del recorte antes de materializar nada en disco. leyendo los ficheros en remoto (hf://…) y aplicando los filtros antes de traer nada: solo se materializa lo que pasa el filtro (predicate pushdown). Con dos meses salen entre tres y seis millones de partidas, unos 2-3 GB de texto UCI, del orden del volumen con el que Karvonen entrenó modelos de 25-50M (16M partidas para el de 50M), los modelos públicos que usaremos como referencia.

Terminal
uv run rukh data fetch --config configs/data/lichess-2025-01-02.yaml --dry-run

Salida real en la máquina de referencia (RTX 5090, 2026-09-18):

$ uv run rukh data fetch --config configs/data/lichess-2025-01-02.yaml --dry-run
dataset: Lichess/standard-chess-games
months: 2025-01, 2025-02
mode: dry-run (nothing written)
query:
SELECT *, '2025-01' AS month
FROM read_parquet('hf://datasets/Lichess/standard-chess-games/data/year=2025/month=01/*.parquet', hive_partitioning = false)
WHERE WhiteElo >= 1800 AND BlackElo >= 1800
AND TRY_CAST(split_part(TimeControl, '+', 1) AS INTEGER) >= 180
AND Termination IN ('Normal', 'Time forfeit')
AND Event NOT ILIKE '%variant%'
UNION ALL
SELECT *, '2025-02' AS month
FROM read_parquet('hf://datasets/Lichess/standard-chess-games/data/year=2025/month=02/*.parquet', hive_partitioning = false)
WHERE WhiteElo >= 1800 AND BlackElo >= 1800
AND TRY_CAST(split_part(TimeControl, '+', 1) AS INTEGER) >= 180
AND Termination IN ('Normal', 'Time forfeit')
AND Event NOT ILIKE '%variant%'
out_dir: <repo>/data/raw
outputs:
year=2025/month=01/games.parquet
year=2025/month=02/games.parquet
manifest: manifest.json

Dos detalles de la consulta no son filtros pero evitan sorpresas. TRY_CAST en vez de CAST: las partidas por correspondencia llegan con TimeControl = '-', y un CAST a entero abortaría la consulta entera; con TRY_CAST dan NULL, no pasan el >= 180 y se descartan, que es lo que queremos. Y hive_partitioning = false: DuckDB reconoce los nombres de carpeta year=…/month=… de la ruta y, si no se lo impides, los inyecta como columnas year y month, que chocarían con el alias '2025-01' AS month con el que etiquetamos cada partida.

Cada filtro tiene una razón, y conviene tenerlas claras porque son decisiones que condicionan lo que el modelo aprende:

Filtro Por qué
WhiteElo >= 1800 AND BlackElo >= 1800 Calidad: por debajo de 1800 abundan los errores groseros y el modelo los imitaría. Los Elo de Lichess son Glicko-2; a este nivel la diferencia con Elo clásico es irrelevante.
Base de tiempo ≥ 180 s (TimeControl es base+incremento) Excluye bullet, donde se juega a ciegas contra el reloj. Blitz (3+0) y superiores tienen jugadas pensadas.
Termination IN ('Normal', 'Time forfeit') Excluye abandonos, desconexiones y partidas anuladas por trampas (Rules infraction, Abandoned).
≥ 20 pliesPly (media jugada)Una jugada de un solo bando. `1. e4 e5` son dos plies y una jugada completa. Los filtros del recorte y las longitudes de secuencia del modelo se cuentan en plies porque es lo que ve el modelo: un token por ply. Una partida de cinco jugadas no enseña nada; el conteo se hace en M1 después de convertir a UCI, porque hay que parsear el movetext.
Sin Event de variantes Chess960, crazyhouse o atomic tienen otras reglas y romperían la legalidad que queremos medir.

Además, se eliminan los comentarios %clk y %eval del movetext y las jugadas pasan de

SANSANNotación algebraica estándar, la de los libros: `Nf3`, `O-O`, `exd5+`. Es compacta para humanos pero ambigua sin el tablero (hay que saber qué caballo puede ir a f3). Lichess publica las partidas en SAN; Rukh las convierte a UCI con `python-chess`. a UCI con python-chess, verificando la legalidad de cada una (una partida corrupta se descarta entera). El resultado se guarda en data/raw/ con un manifiestoManifiesto de datosFichero JSON que acompaña a cada recorte con los filtros exactos, los meses, los conteos y el hash de cada fichero. Sin manifiesto no hay reproducibilidad: es lo que permite decir qué datos vio un modelo. JSON: filtros exactos, meses, conteos y hash de cada fichero.

La descarga real tarda entre una y dos horas: se lanza al terminar esta lección y se recoge en M1. Los splits se hacen por mes (entrenar con enero-febrero, validar con un tercer mes), no al azar, para evitar fugas por partidas repetidas de los mismos jugadores.

// Ejercicio 02Lee el plan antes de descargar

Ejecuta el --dry-run y localiza en la SQL los cuatro filtros. Después cambia min_elo a 2000 en una copia del YAML y vuelve a ejecutarlo: ¿qué cambia en la consulta? ¿Qué pasa si añades una clave que no existe, por ejemplo min_rating?

// SoluciónVer la solución

Solo cambia la comparación de WhiteElo y BlackElo; el resto de la consulta es idéntico. Una clave desconocida hace que rukh falle antes de tocar la red con un error de validación: las configuraciones son modelos pydantic con extra="forbid", y es intencionado. Una errata en un fichero de configuración que se ignora en silencio es la forma más cara de perder una noche de GPU.

El tablero

La demo (rukh.borjaglez.com) es una sola pantalla: tablero, panel del modelo y lista de jugadas. En este hito no hay modelo todavía; el rival es “primera jugada legal”, un oponente de mentira que sirve para probar todo lo demás. Se activa con ?mock=1 y es el que usa la CI.

Dos librerías hacen el trabajo:

  • cm-chessboard dibuja el tablero en SVG, responsive, con las casillas en los tokens de color del tema (las piezas son el sprite estándar de la librería), y gestiona la entrada con ratón y con el dedo (tocar-tocar y arrastrar).
  • chess.js conoce las reglas: genera las jugadas legales de una posición, aplica una jugada, detecta jaque mate, tablas y promociones, y exporta PGNPGNFormato de fichero de partidas: cabeceras entre corchetes (`[White "..."]`, `[Result "1-0"]`) y las jugadas en SAN, con comentarios opcionales como `%clk` o `%eval`. La demo exporta PGN; los volcados de Lichess lo usan como origen. y FENFENCadena de texto que describe una posición completa: piezas por fila, turno, derechos de enroque, casilla al paso y contadores de jugadas. Es la clave con la que se cruzan las posiciones de las partidas con las evaluaciones públicas de Stockfish..

historial

e2e4 e7e5 g1f3 …

modelo

logits por token

máscara legal

chess.js · moves()

muestreo

temperatura, top-k

la jugada elegida vuelve al historial y al tablero

fig. 01Quién decide qué en la demo: el modelo produce logits (una puntuación bruta por token antes de convertirla en probabilidades); el navegador los filtra con las reglas y muestrea.

La decisión de diseño importante es esta: la legalidad la decide el navegador, el modelo solo propone. El modelo devuelve un logitLogitsPuntuación bruta que el modelo asigna a cada token del vocabulario antes de convertirla en probabilidades (con softmax). En Rukh hay un logit por jugada UCI posible; la demo pone a menos infinito los de las jugadas ilegales antes de muestrear, y la temperatura y el top-k actúan sobre ellos. por token, una puntuación bruta que se convierte en una distribución de probabilidad sobre todo su vocabulario (unas 1 900 jugadas UCI posibles); chess.js calcula cuáles son legales en la posición actual, el navegador pone a cero las demás y muestrea entre las que quedan. Así el modelo nunca hace una jugada ilegal en la demo, aunque la proponga.

Y como la propone, se puede medir. El modo “sin máscara” de la demo (a partir de M2) enseña la jugada cruda que el modelo quería hacer y la marca si es ilegal; la tasa de legalidad sin máscara es la primera columna de la tabla única precisamente porque mide cuánto ha aprendido las reglas un modelo al que nadie se las ha programado.

// demoTablero con jugadas legales contra 'primera jugada legal'Sin modelo todavía: el rival elige la primera jugada legal que devuelve chess.js. Sirve para probar el tablero, la lista de jugadas, deshacer, nueva partida y exportar PGN.
Abrir la demo?mock=1

// Ejercicio 03Gana en cuatro jugadas al rival de mentira

Abre la demo con ?mock=1 y juega. El rival hace siempre la primera jugada legal en el orden que devuelve chess.js. ¿Puedes darle mate en pocas jugadas? Exporta el PGN y mira las cabeceras.

// SoluciónVer la solución

chess.js enumera las jugadas legales recorriendo el tablero desde a8 hacia h1, así que la primera pieza negra que encuentra con jugada es el caballo de b8: tras 1. e4 responde Nc6, y después mueve la torre de a8 adelante y atrás (Rb8, Ra8) mientras nadie se lo impida. Con 1. e4 Nc6 2. Bc4 Rb8 3. Qh5 Ra8 4. Qxf7# hay mate en cuatro (comprobado con chess.js). El PGN lleva [White "Humano"], [Black "Primera jugada legal"] y el resultado 1-0. Que el rival de mentira se deje ganar así de fácil es exactamente el punto de partida: en M2 se sustituye por el decoder entrenado y la partida deja de ser tan fácil.

Qué has aprendido

En este módulo no has entrenado nada, y eso es a propósito. Lo que tienes ahora es lo que casi nadie tiene cuando empieza un proyecto de ML: un entorno reproducible (uv.lock con torch fijado y vigilado por un test), un juez verificado (Stockfish con UCI_Elo en 1320-3190), un sistema de registro (MLflow local), un recorte de datos con cada filtro justificado y un manifiesto, y una demo en la que la legalidad está resuelta antes de que exista el modelo.

Cómo se mide el módulo 0: uv run rukh info muestra cuda: True; uv run pytest -m unit -q en verde; uv run rukh engine check devuelve el rango de UCI_Elo; el --dry-run imprime la SQL con los cuatro filtros; y la demo permite jugar una partida completa contra “primera jugada legal” en ordenador y en móvil. Cuando la descarga real termine, el manifiesto tendrá los conteos por mes.

Lo siguiente es M1: de la partida en SAN a tensores. Ahí aparece la primera decisión de modelado del curso, qué es un token, y se compara con datos, no con opiniones.

La cheatsheet del módulo, ocho preguntas con su respuesta corta, está justo debajo.

// cheatsheet M0

Ocho preguntas para llevarte

01¿Qué es un modelo de lenguaje de ajedrez?
Una red que predice la siguiente jugada dada la secuencia de jugadas anteriores. Se entrena con la misma pérdida que un LLM de texto (siguiente token), pero cada token es una jugada, así que las reglas y la estrategia emergen de predecir bien.
02¿Por qué el ajedrez es un buen dominio para aprender IA generativa?
Es cerrado y verificable: un motor decide si una jugada es legal y cuánto vale, así que las recompensas son gratis y objetivas. Los datos son abiertos y CC0 (Lichess), los modelos son pequeños (decenas de millones de parámetros) y la demo se entiende en cinco segundos.
03¿Qué diferencia hay entre SAN, UCI, FEN y PGN?
SAN escribe una jugada como en los libros (Nf3) y depende del tablero; UCI escribe origen y destino (g1f3) y no depende de nada; FEN describe una posición completa en una línea; PGN es el fichero de una partida entera con cabeceras y jugadas en SAN.
04¿Qué papel tiene Stockfish en este curso?
Es el juez, no el jugador. Con UCI_LimitStrength y UCI_Elo se convierte en un rival calibrado para estimar Elo; a poca profundidad da recompensas baratas para DPO y GRPO; y sus evaluaciones públicas etiquetan posiciones para el encoder.
05¿Qué filtros aplica el recorte de datos y por qué?
Ambos Elo ≥ 1800 (calidad), base de tiempo ≥ 180 s (excluye bullet, donde se juega a ciegas contra el reloj), terminación Normal o Time forfeit (excluye abandonos y trampas), ≥ 20 plies (partidas reales) y sin variantes. Se documenta todo en un manifiesto JSON.
06¿Por qué la legalidad la decide el navegador y no el modelo?
Porque el modelo solo propone una distribución sobre tokens; chess.js conoce las reglas y enmascara las jugadas ilegales antes de muestrear. Medir cuántas propone legales sin máscara es, además, una métrica de cuánto ha aprendido las reglas.
07¿Para qué sirve MLflow si ya tengo los logs?
Para que cada entrenamiento quede registrado con su configuración, sus métricas por paso y sus artefactos, comparable con los demás. Las model cards se generan desde MLflow para que ningún número de la tabla se escriba a mano.
08¿Cómo se mide el progreso en cada módulo?
Siempre con la misma tabla: tasa de jugadas legales sin máscara, exactitud top-1 y top-3 en validación, puzles resueltos por dificultad y Elo estimado contra Stockfish limitado con intervalo de confianza. Sin número no hay model card.
Todas las cheatsheets, imprimibles →