Trois mois après la mise en place, l'audit interne d'Assurances Ventoux demande pour la version en production : quel jeu, quel score, quelle exécution, qui l'a déclenchée. La réponse est dans ML Metadata, à condition de ne pas l'avoir effacée en nettoyant. À l'inverse, un projet de test qui garde des centaines d'exécutions, d'artefacts et un TensorBoard oublié coûte et encombre. Il faut savoir ce que le pipeline laisse derrière lui, ce qui se paie, et comment tout retirer avec preuve.
Le mécanisme
Chaque exécution laisse quatre types de traces dans Vertex AI, plus les fichiers du bucket :
| Ressource | Créée par | Contenu | Facturé |
|---|---|---|---|
| PipelineJob | job.submit() | paramètres, état des tâches, labels | 0,03 $ par exécution |
| Contexte ML Metadata | chaque exécution, chaque expérience, chaque run d'expérience | regroupement logique | non (stockage de métadonnées) |
| Artefact ML Metadata | chaque Output[...] | URI, métadonnées (accuracy, fingerprint…) | non |
| Exécution ML Metadata | chaque tâche | entrées, sorties, état | non |
| CustomJob | chaque tâche démarrée | la machine | au temps machine |
| Objets Cloud Storage | cloud-up, racine du pipeline | données, registre, artefacts | au stockage |
flowchart LR
E["Contexte<br/>expérience lab-vertex-ai-pipelines"] --> R1["Contexte<br/>run local-v2"]
E --> R2["Contexte<br/>run local-v1"]
E --> PJ["Contexte<br/>exécution prompt-gate-20261016151726"]
PJ --> X1["Exécution<br/>prepare-dataset"]
PJ --> X2["Exécution<br/>load-production"]
X1 -.-> A1["Artefact<br/>dataset (empreinte)"]
BK[("Bucket gs://…-lab-vertex-ai-pipelines")]
Le nettoyage du lab procède dans l'ordre inverse de la création : exécutions, artefacts, exécutions ML Metadata, contextes, expérience en dernier, puis le bucket. Il identifie ce qui appartient au lab par le label formation-lab=vertex-ai-pipelines (jobs, bucket) et par le préfixe lab-vertex-ai-pipelines (contextes, dont le filtre de l'API ne fait pas de « commence par » : run.py filtre côté client).
La mise en œuvre
make cloud-status PROJECT=mon-projet # ce qui existe encore
make cloud-down PROJECT=mon-projet # supprime tout, puis échoue si quelque chose subsistejobs, contexts, artifacts, executions = lab_resources()
# Les expériences en dernier : leur suppression emporte leurs runs, déjà traités ici.
contexts.sort(key=lambda c: c.schema_title == "system.Experiment")
for r in [*jobs, *artifacts, *executions, *contexts]:
try:
if getattr(r, "schema_title", "") == "system.Experiment":
aiplatform.Experiment(r.name).delete()
else:
r.delete()
except NotFound: # déjà supprimé en cascade (contexte d'un job supprimé, par exemple)
passLa cible cloud-down se termine par run.py status --strict, qui renvoie un code non nul si un seul job, contexte, artefact ou exécution du lab subsiste, et par une vérification que plus aucun bucket ne porte le label.
Coûts mesurés et estimés
| Poste | Mesure du 2026-10-16 | Ordre de grandeur pour une exécution Vertex complète |
|---|---|---|
| Gemini 2.5 Flash | 38 appels, 19 374 tokens (make run) | identique : moins d'un centime |
| Frais Vertex AI Pipelines | 1 exécution soumise | 0,03 $ par exécution |
| Machines | aucune (quota) | 6 tâches de 1 à 3 minutes sur e2-standard-4 à environ 0,15 $/h : quelques centimes |
| Stockage | 2 objets, quelques Ko | négligeable |
Pour 400 tickets de référence au lieu de 19, le poste Gemini passe à quelques dizaines de centimes par exécution ; c'est lui qui domine, pas l'orchestration.
Résultat obtenu (2026-10-16)
État avant nettoyage, puis make cloud-down :
$ make cloud-status PROJECT=<projet>
Bucket :
<projet>-lab-vertex-ai-pipelines
Vertex AI :
PipelineJob 1
Context 7
Artifact 1
Execution 3
$ make cloud-down PROJECT=<projet>
Supprimés : 1 exécutions, 7 contextes, 1 artefacts, 3 exécutions ML Metadata
Removing gs://<projet>-lab-vertex-ai-pipelines/data/reference.jsonl#1792156634460218...
Removing gs://<projet>-lab-vertex-ai-pipelines/registry/production.json#1792156637227920...
Removing gs://<projet>-lab-vertex-ai-pipelines/...
PipelineJob 0
Context 0
Artifact 0
Execution 0
Vérifié : plus aucune ressource du lab.Vérification indépendante, avec d'autres outils que ceux du lab :
$ gcloud storage ls gs://<projet>-lab-vertex-ai-pipelines
ERROR: (gcloud.storage.ls) gs://<projet>-lab-vertex-ai-pipelines not found: 404.
$ curl ... /pipelineJobs?filter=labels.formation-lab="vertex-ai-pipelines"
{}
$ curl ... /schedules
{}
$ gcloud ai tensorboards list --region europe-west1
Listed 0 items.Les 7 contextes : l'expérience, quatre runs d'expérience issus de deux make run, et deux contextes créés par l'exécution Vertex.
Ce qui casse en production
- TensorBoard créé sans le vouloir.
aiplatform.init(experiment=...)peut créer une instance TensorBoard de sauvegarde, facturée au stockage, que la suppression de l'expérience laisse en place. Parade :experiment_tensorboard=False, etgcloud ai tensorboards listdans les vérifications. - Nettoyer la production avec le lab. Un filtre trop large (
startswith("lab")) supprimerait le lignage d'autres équipes. Parade : préfixe complet du lab et label dédié ; en production, une politique de rétention plutôt qu'une suppression. - Suppression partielle silencieuse. Une erreur à mi-chemin laisse des contextes orphelins que plus personne ne liste. Parade : la vérification stricte en fin de
cloud-down, qui échoue tant qu'il reste quelque chose. - Bucket versionné ou verrouillé. Une politique de rétention empêche la suppression des objets. Parade :
cloud-upcrée un bucket sans rétention ; sur un bucket existant, vérifiezgcloud storage buckets describeavant de l'utiliser comme racine.
La règle de décision
En production, gardez le lignage : c'est votre preuve d'audit, et son stockage ne coûte presque rien. Dans un projet de test, supprimez tout à la fin et vérifiez par un outil indépendant. Le poste de coût à surveiller est l'évaluation par le modèle, proportionnelle à la taille du jeu et au nombre d'exécutions, pas l'orchestration.
Pratique
make cloud-down affiche « Supprimés : 1 exécutions, 7 contextes… » puis échoue sur la vérification stricte avec Context 1. Que faites-vous ?
Le nettoyage est idempotent ; un contexte créé pendant la suppression (une exécution qui finissait) ou un échec transitoire disparaît au second passage. L'ignorer laisse un orphelin que personne ne listera plus. Supprimer le magasin entier effacerait le lignage de tous les autres pipelines du projet.
Votre directeur financier s'inquiète du coût de Vertex AI Pipelines si l'on passe à une exécution par fusion, soit environ 60 par mois, avec un jeu de 400 tickets. Quel poste regardez-vous en premier ?
Les frais de pipeline font moins de 2 $ par mois. Les évaluations font 48 000 appels par mois sans cache, et c'est là que le cache (production inchangée, même jeu) et la taille du jeu comptent. Le stockage de métadonnées est négligeable.
Exercice. Ajoutez à run.py status le décompte des planifications du lab (aiplatform.PipelineJobSchedule.list) pour que make cloud-down échoue si une planification subsiste.
Voir la solution
Dans lab_resources() :
schedules = [s for s in aiplatform.PipelineJobSchedule.list()
if s.display_name.startswith(f"lab-{SLUG}")]
return jobs, contexts, artifacts, executions, schedulespuis ajoutez "Schedule" aux noms de status() et *schedules en tête de la liste supprimée par cleanup() : une planification active recréerait des exécutions pendant le nettoyage. C'est pourquoi elle se supprime en premier.
- Je sais ce qu'une exécution laisse dans Vertex AI et dans Cloud Storage.
- Je supprime dans l'ordre inverse de la création, avec un nettoyage relançable.
- Je termine par une vérification qui échoue s'il reste une ressource, et par un outil indépendant.
- Je chiffre une exécution, et je sais que le poste Gemini domine.