Leçon 2 — Construire l'agent pas à pas

L'agent analyste

5 min de lecture

L'agent analyste

Installer

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt      # google-adk==2.10.0, python-dotenv, pandas, pytest
cp .env.example .env                 # collez votre clé d'API Gemini

L'agent : une ligne pour l'exécution de code

files/agent.py
root_agent = Agent(
    name="analyste_donnees",
    model=MODEL,
    description="Analyse un CSV de ventes en exécutant du code Python et produit un rapport avec graphique.",
    instruction=INSTRUCTION,
    code_executor=BuiltInCodeExecutor(),  # pas d'autres outils dans cet agent
)

Ce n'est pas un élément de tools : c'est un paramètre à part, code_executor. Dans le code source d'ADK 2.10, BuiltInCodeExecutor.process_llm_request ajoute simplement types.Tool(code_execution=types.ToolCodeExecution()) à la requête, si le modèle est un modèle Gemini.

L'instruction

files/agent.py
INSTRUCTION = """Tu es analyste de données. Tu réponds en français.
Règles :
- Tout chiffre que tu donnes doit venir d'un code Python que tu as exécuté. Jamais de calcul de tête.
- Commence par charger les données reçues dans un DataFrame pandas (io.StringIO), en recopiant le CSV EN ENTIER,
  puis vérifie le nombre de lignes avec len(df).
- Trace UN graphique matplotlib lisible (titre, axes, légende) : chiffre d'affaires mensuel par catégorie.
Rapport final en Markdown avec ces sections :
## Chiffres clés (CA total, CA par région, CA par catégorie)
## Tendances (évolution mensuelle, saisonnalité, catégorie qui progresse le plus)
## Recommandations (3 actions concrètes, chacune justifiée par un chiffre)
## Limites de l'analyse"""
ConsigneProblème évité
« Tout chiffre vient d'un code exécuté »les totaux approximatifs
« Recopie le CSV EN ENTIER puis len(df) »un DataFrame tronqué silencieusement (le modèle s'arrête à 50 lignes « pour l'exemple »)
« UN graphique, titre, axes, légende »dix graphiques illisibles
Sections imposéesun rapport impossible à comparer d'une semaine à l'autre
« Limites de l'analyse »des recommandations présentées comme des certitudes

Pourquoi demander explicitement len(df) au début ?

Le message : question + CSV

files/agent.py
def construire_message(question: str, chemin_csv: str) -> types.Content:
    """Fonction pure : la question + le contenu du CSV, dans un seul message utilisateur."""
    csv = Path(chemin_csv).read_text(encoding="utf-8").strip()
    n = csv.count("\n")
    if n > MAX_LIGNES:
        raise ValueError(f"{n} lignes : trop pour le prompt (max {MAX_LIGNES}). Pré-agrégez d'abord.")
    return types.Content(role="user", parts=[types.Part(
        text=f"{question}\n\nDonnées ({n} lignes, fichier {Path(chemin_csv).name}) :\n```csv\n{csv}\n```")])

Le nombre de lignes est annoncé dans le message : le modèle peut le comparer à son len(df).

flowchart TB
  Q["Question"] --> M["construire_message()"]
  F["ventes.csv"] --> M
  M -->|"plus de 2000 lignes"| E["ValueError : pré-agréger"]
  M -->|"sinon"| C["types.Content(role='user') : texte + bloc csv"]
  C --> RUN["runner.run_async(new_message=...)"]

Lire les événements

Les réponses d'un agent avec exécution de code contiennent plusieurs types de parties (Part) :

PartieContenu
executable_codele code Python écrit par le modèle
code_execution_resultoutcome (OUTCOME_OK, OUTCOME_FAILED…) et output (la sortie standard)
inline_dataune image produite (ADK la range en artefact, leçon suivante)
textle texte du modèle, dont le rapport final
files/run.py
for p in ev.content.parts if ev.content else []:
    if p.executable_code:
        nb_codes += 1
        print(f"--- code exécuté n°{nb_codes} ---\n{p.executable_code.code[-400:]}")
    if p.code_execution_result:
        print(f"--- résultat ({p.code_execution_result.outcome.name}) ---\n"
              f"{(p.code_execution_result.output or '')[:600]}")

Dans quelle partie d'un événement trouvez-vous le total calculé par pandas dans le bac à sable ?

Exercice : une question ciblée

Modifiez QUESTION dans run.py pour demander uniquement : « Quel mois a le plus fort CA pour la catégorie Enceintes, et de combien dépasse-t-il la moyenne mensuelle de cette catégorie ? ». Comment vérifiez-vous la réponse sans le modèle ?

Voir la solution
import pandas as pd
d = pd.read_csv("ventes.csv")
e = d[d.categorie == "Enceintes"].groupby("mois").chiffre_affaires.sum()
print(e.idxmax(), e.max(), round(e.max() - e.mean(), 2))

On obtient 2025-12, 22 995 € ; la moyenne mensuelle est de 12 687,50 €, donc un écart de 10 307,50 €. Comparez ces valeurs au rapport, ou mieux : ajoutez-les à reference() et verifier() dans run.py.

Résultat obtenu

Run réel (gemini-3.5-flash, 2026-10-15) : l'agent a exécuté 3 blocs de code, tous OUTCOME_OK. Fin du dernier bloc et sa sortie :

summer_ca = df[df['mois'].isin(['2025-07', '2025-08'])]['chiffre_affaires'].sum()
summer_pct = summer_ca / ca_total * 100
print(f"CA Jul + Août: {summer_ca:,} € ({summer_pct:.2f}%)")

--- résultat (OUTCOME_OK) ---
CA Nov + Déc: 106,092 € (25.59%)
CA Jul + Août: 52,548 € (12.67%)

Je sais faire

  • Activer l'exécution de code avec code_executor=BuiltInCodeExecutor()
  • Écrire une instruction qui force le calcul par code et un format stable
  • Transporter un CSV dans le message avec un plafond de taille
  • Lire executable_code, code_execution_result et text dans les événements
Suivant