Graphique et contrôle des chiffres
Où passe l'image ?
Quand le code matplotlib du modèle produit une figure, Gemini la renvoie comme une partie inline_data (image PNG). Avec BuiltInCodeExecutor, ADK 2.10 la range dans le service d'artefacts (un stockage de fichiers lié à la session) et remplace la partie par le texte Saved as artifact: <nom>.png.
flowchart LR
MPL["matplotlib dans le bac à sable"] --> G["Gemini : partie inline_data image/png"]
G --> P["Post-traitement ADK (BuiltInCodeExecutor)"]
P -->|"save_artifact"| AS[("InMemoryArtifactService")]
P -->|"texte"| T["'Saved as artifact: 20261015_163646.png'"]
AS -->|"load_artifact dans run.py"| F["graphique_20261015_163646.png sur disque"]
Une artefact ADK est un fichier binaire versionné, rattaché à une application, un utilisateur et une session. InMemoryRunner fournit un InMemoryArtifactService ; en production on utilise par exemple GcsArtifactService (Cloud Storage).
# Les images produites par le bac à sable sont rangées par ADK dans le service d'artefacts.
for nom in await runner.artifact_service.list_artifact_keys(app_name="analyse", user_id="moi", session_id=s.id):
image = await runner.artifact_service.load_artifact(app_name="analyse", user_id="moi",
session_id=s.id, filename=nom)
open(f"graphique_{nom}", "wb").write(image.inline_data.data)
print("Graphique enregistré :", f"graphique_{nom}")Et on ignore ce texte technique quand on assemble le rapport :
if p.text and ev.is_final_response() and not p.text.startswith("Saved as artifact"):
rapport += p.textAvec BuiltInCodeExecutor et InMemoryRunner, où trouvez-vous le PNG du graphique après l'exécution ?
Le bac à sable est chez Google : il ne peut rien écrire sur votre disque. ADK reçoit l'image en inline_data et la sauvegarde comme artefact de la session.
Voici le graphique réellement produit le 2026-10-15 :
Contrôler les chiffres
Même avec l'exécution de code, le texte final est écrit par le modèle : il peut recopier un chiffre de travers. On calcule donc nous-mêmes la vérité terrain avec pandas et on vérifie que le rapport la cite.
def reference(chemin_csv: str) -> dict:
"""Les chiffres attendus, calculés par nous avec pandas : la « vérité terrain »."""
d = pd.read_csv(chemin_csv)
par_region = d.groupby("region").chiffre_affaires.sum()
return {"ca_total": int(d.chiffre_affaires.sum()),
"meilleure_region": par_region.idxmax(), "ca_meilleure_region": int(par_region.max())}
def verifier(rapport: str, ref: dict) -> dict:
"""Le rapport cite-t-il les bons chiffres ? (on ignore les séparateurs : 414 644 = 414,644 = 414644)"""
nombres = {int(re.sub(r"[\s .,]", "", n)) for n in re.findall(r"\d[\d\s .,]*\d|\d", rapport)
if re.sub(r"[\s .,]", "", n).isdigit()}
return {"ca_total": ref["ca_total"] in nombres,
"meilleure_region": ref["meilleure_region"] in rapport and ref["ca_meilleure_region"] in nombres}| Format dans le rapport | Normalisé en | Reconnu ? |
|---|---|---|
414 644 € (espace, ou espace fine ) | 414644 | oui |
414,644 € (style anglais) | 414644 | oui |
environ 415 000 € | 415000 | non : c'est voulu, un arrondi n'est pas le chiffre exact |
414,6 k€ | 4146 | non |
Le rapport écrit « CA total : environ 415 000 € ». Que renvoie verifier() pour ca_total ?
Le contrôle cherche le chiffre exact. Un arrondi est un signe que le modèle n'a pas recopié le résultat du code, ce qu'on veut justement détecter. Le test test_reference_et_controle vérifie ce cas.
Exercice : contrôler le CA par catégorie
Étendez reference() et verifier() pour vérifier que le CA de chaque catégorie apparaît dans le rapport.
Voir la solution
def reference(chemin_csv):
d = pd.read_csv(chemin_csv)
par_region = d.groupby("region").chiffre_affaires.sum()
return {"ca_total": int(d.chiffre_affaires.sum()),
"meilleure_region": par_region.idxmax(), "ca_meilleure_region": int(par_region.max()),
"ca_categories": {c: int(v) for c, v in d.groupby("categorie").chiffre_affaires.sum().items()}}
# dans verifier(), ajoutez :
# "categories": all(v in nombres for v in ref["ca_categories"].values()),Valeurs attendues : Casques 185 654, Enceintes 152 250, Accessoires 76 740. Le rapport réel du 2026-10-15 les contient toutes les trois. Pensez à mettre à jour test_reference_et_controle, qui compare reference() à un dictionnaire exact.
Résultat obtenu
Fin du run réel (gemini-3.5-flash, 2026-10-15) :
Graphique enregistré : graphique_20261015_163646.png
=== RAPPORT ===
## Chiffres clés
- **Chiffre d'affaires total 2025** : **414 644 €** (pour 14 746 unités vendues)
- **Chiffre d'affaires par région** :
- **Sud** : **123 269 €** (29,73 % du CA total)
[...]
CONTRÔLE : {'ca_total': True, 'meilleure_region': True}Je sais faire
- Expliquer le chemin d'une image depuis le bac à sable jusqu'au disque
- Lister et charger des artefacts d'une session ADK
- Calculer une vérité terrain et contrôler les chiffres d'un rapport
- Repérer les limites d'une normalisation de nombres