Cada salida de un step se persiste en el almacén de artifacts, versionada y ligada a la ejecución que la produjo. Eso convierte tres cosas difíciles en consultas triviales:
| Pregunta | Sin artifacts | Con artifacts |
|---|---|---|
| ¿Qué datos entrenaron este modelo? | Arqueología en el historial de Git y la memoria de alguien | Está enlazado |
| ¿Cambió el dataset desde la última vez? | Comparar ficheros a mano | Cambia el hash, cambia la versión |
| ¿Puedo comparar el modelo de enero con el de febrero? | Si alguien guardó los dos | Los dos están, con su procedencia |
La palabra clave es inmutable: un artifact no se sobrescribe. Si los datos de entrada cambian un solo bit, se genera una versión nueva y la anterior sigue existiendo. Eso es lo que permite «viajar en el tiempo» a la versión exacta de un dataset — y sin esa capacidad, depurar un error de predicción en producción es adivinar.
Si un step se va a ejecutar con exactamente las mismas entradas y el mismo código que la última vez, su resultado ya está guardado: no hace falta volver a calcularlo. De ahí el patrón que se nota desde el primer día — cambias un hiperparámetro del entrenamiento y la carga y la limpieza de datos no se repiten.
La parte que hay que entender bien es qué invalida el caché:
| Si cambia… | ¿Se reejecuta? |
|---|---|
| El código dentro de la función del step | Sí |
| Un parámetro que se le pasa | Sí |
| La salida del step anterior | Sí — y en cascada, todo lo que venga después |
| Código de otro step que no le afecta | No |
| Nada | No: se reutiliza el resultado |
Y el caso que confunde a todo el mundo la primera vez: un step que nunca usa el caché. La causa casi siempre es que su salida cambia en cada ejecución aunque los datos sean los mismos — una marca de tiempo, un identificador aleatorio, un orden no determinista. El paso siguiente ve una entrada distinta y se reejecuta, y con él todo el resto.
// un timestamp dentro de un step invalida el caché de todo lo que venga detrásLa consecuencia práctica: los steps que quieras cachear deben ser puros — mismas entradas, misma salida, sin efectos secundarios. Escribir un fichero local desde dentro de un step es la forma más rápida de romper esa pureza sin darse cuenta.
Con los artifacts versionados y las dependencias declaradas, cada modelo arrastra su historia completa: qué datos crudos entraron, qué transformaciones se aplicaron, con qué código y en qué ejecución. Eso es el linaje, y su utilidad se ve en el peor momento posible: cuando un modelo en producción empieza a fallar y hay que averiguar si cambió el código o cambiaron los datos.
Sin linaje esa pregunta no tiene respuesta y se responde con opiniones. Con linaje es una comparación entre dos ejecuciones.
Monta un pipeline mínimo de tres pasos y ejecútalo dos veces seguidas sin tocar nada: la segunda
debería saltarse todo. Después haz tres experimentos, uno cada vez, volviendo a ejecutar entre
medias: cambia un parámetro del último paso, cambia un comentario dentro del primero, y añade un
datetime.now() a la salida del primero.
Anota qué se reejecuta en cada caso. El tercero es el que enseña de verdad: verás que un solo valor no determinista al principio del grafo tira abajo el caché de todo lo que viene después.