Articles / Comment créer un agent IA : la boucle complète en Python

agents ia

Comment créer un agent IA : la boucle complète en Python

Finn ·

Pour créer un agent IA, il faut trois pièces : un modèle de langage appelé par API, des outils décrits au modèle (un nom, une description, des paramètres) et une boucle. À chaque tour, le modèle demande un outil, ton code l'exécute et renvoie le résultat, jusqu'à ce que le modèle réponde ou qu'un plafond de tours l'arrête. Voici le programme complet, en Python, avec un exemple dont tu connais la bonne réponse.

Anthropic distingue les workflows, où le code fixe l'ordre des étapes, des agents, où le modèle décide lui-même de ses étapes et des outils qu'il appelle (Building effective agents, décembre 2024). La différence tient dans la boucle ci-dessous. Il te faut Python 3.10 ou plus, la bibliothèque anthropic (pip install anthropic) et une clé API dans la variable ANTHROPIC_API_KEY.

Étape 1 : une tâche vérifiable et des outils qui ne font que lire

Prends une tâche dont tu connais le résultat à l'avance et donne d'abord des outils sans effet sur le monde : lister, lire, chercher. Ici, l'agent reçoit deux outils, lister un dossier et lire un fichier, et une consigne : trouver les factures impayées et donner le total. Les trois factures sont fictives, clients compris. Crée-les avant de lancer le programme :

mkdir factures
printf 'Facture 2026-07-012\nClient : Atelier Brume\nMontant TTC : 1200 EUR\nStatut : payée le 02/08/2026\n' > factures/2026-07-012.txt
printf 'Facture 2026-08-015\nClient : Studio Malo\nMontant TTC : 850 EUR\nÉchéance : 05/09/2026\nStatut : impayée\n' > factures/2026-08-015.txt
printf 'Facture 2026-08-019\nClient : Maison Ravel\nMontant TTC : 1000 EUR\nÉchéance : 20/09/2026\nStatut : impayée\n' > factures/2026-08-019.txt

La bonne réponse est connue : deux factures impayées, 2026-08-015 et 2026-08-019, pour 1 850 EUR.

Étape 2 : décrire chaque outil au modèle

Pour le modèle, un outil est fait de trois champs : un nom, une description et un schéma JSON de paramètres. Il ne voit jamais ton code ; il choisit un outil d'après sa description et remplit les paramètres d'après le schéma (documentation du tool use). La description décide donc de tout : « Renvoie le contenu texte d'un fichier » suffit ici, parce que la consigne dit déjà quoi chercher. Si l'agent appelle le mauvais outil ou oublie un paramètre, la documentation conseille d'abord d'enrichir la description. Les noms des paramètres sont ceux de ta fonction Python : la boucle les lui passe tels quels.

Un agent IA, c'est une boucle : le modèle demande un outil, ton code l'exécute, le résultat repart au modèle, jusqu'à la réponse ou au plafond.

Étape 3 : écrire la boucle

Voici le programme entier. Copie-le dans agent.py, à côté du dossier factures.

import json
import os
import anthropic

def lister_fichiers(dossier: str) -> str:
    return "\n".join(sorted(os.listdir(dossier)))

def lire_fichier(chemin: str) -> str:
    with open(chemin, encoding="utf-8") as f:
        return f.read()

OUTILS = {"lister_fichiers": lister_fichiers, "lire_fichier": lire_fichier}

tools = [
    {
        "name": "lister_fichiers",
        "description": "Liste les fichiers d'un dossier, un nom par ligne.",
        "input_schema": {
            "type": "object",
            "properties": {"dossier": {"type": "string", "description": "Chemin du dossier, ex. factures"}},
            "required": ["dossier"],
        },
    },
    {
        "name": "lire_fichier",
        "description": "Renvoie le contenu texte d'un fichier.",
        "input_schema": {
            "type": "object",
            "properties": {"chemin": {"type": "string", "description": "Chemin du fichier, ex. factures/2026-08-015.txt"}},
            "required": ["chemin"],
        },
    },
]

TACHE = "Dans le dossier factures, trouve les factures impayées. Donne leurs numéros et le total TTC restant dû."
MAX_TOURS = 10

client = anthropic.Anthropic()  # lit la clé dans ANTHROPIC_API_KEY
messages = [{"role": "user", "content": TACHE}]
entree = sortie = 0

for tour in range(MAX_TOURS):
    reponse = client.messages.create(
        model="claude-opus-5",
        max_tokens=16000,  # plafond de sortie par tour, pas une dépense
        system="Lis les fichiers avec les outils avant de répondre.",
        tools=tools,
        messages=messages,
    )
    entree += reponse.usage.input_tokens
    sortie += reponse.usage.output_tokens
    messages.append({"role": "assistant", "content": reponse.content})

    if reponse.stop_reason != "tool_use":
        break

    resultats = []
    for bloc in reponse.content:
        if bloc.type != "tool_use":
            continue
        print(f"tour {tour + 1} : {bloc.name} {json.dumps(bloc.input, ensure_ascii=False)}")
        try:
            contenu = OUTILS[bloc.name](**bloc.input)
            resultats.append({"type": "tool_result", "tool_use_id": bloc.id, "content": contenu})
        except Exception as e:
            resultats.append({"type": "tool_result", "tool_use_id": bloc.id, "content": f"Erreur : {e}", "is_error": True})
    messages.append({"role": "user", "content": resultats})

print("arrêt :", reponse.stop_reason)
if reponse.stop_reason == "tool_use":
    print(f"plafond de {MAX_TOURS} tours atteint, l'agent n'a pas fini")
for bloc in reponse.content:
    if bloc.type == "text":
        print(bloc.text)
print(f"tokens : {entree} en entrée, {sortie} en sortie")

Quatre règles de l'API font cette boucle (gestion des appels d'outils).

L'historique repart entier à chaque tour. L'API ne garde rien entre deux appels : la liste messages est la mémoire de l'agent, et le coût grossit avec elle.

Le champ stop_reason pilote tout. Tant qu'il vaut tool_use, le modèle attend des résultats. Toute autre valeur arrête la boucle : end_turn est la réponse finale, max_tokens une sortie tronquée, refusal un refus. Le programme affiche la raison pour que tu ne confondes jamais une réponse et un arrêt.

Chaque résultat porte l'identifiant de l'appel. Le bloc tool_result répond au bloc tool_use par son id, et tous les résultats d'un tour repartent dans un seul message, avant tout texte. Le modèle peut demander plusieurs outils dans le même tour, par exemple lire les trois factures d'un coup ; la boucle les exécute tous, puis renvoie les résultats ensemble.

Une erreur d'outil est un résultat comme un autre. Fichier absent, nom d'outil inconnu : la boucle renvoie le message d'erreur avec is_error à vrai, et le modèle décide de la suite. Un message explicite (« fichier introuvable, vérifie le chemin ») l'aide plus qu'un simple « échec ».

Étape 4 : lancer, lire la trace, vérifier

Lance python agent.py. Le programme affiche chaque appel d'outil, la raison d'arrêt, la réponse et le nombre de tokens. Sur cet exemple, la trace attendue comprend un appel à lister_fichiers, trois lectures en un ou plusieurs tours, arrêt : end_turn, puis une réponse qui nomme 2026-08-015 et 2026-08-019 pour 1 850 EUR. Vérifie dans cet ordre : les outils appelés (l'agent a lu les trois fichiers, ou il a deviné ?), la raison d'arrêt, la réponse.

Puis provoque une condition défavorable : supprime la ligne « Statut » de la facture 2026-08-019 et relance. Un agent utile signale le manque au lieu de classer la facture d'office. S'il tranche sans le dire, ajoute à la consigne système : « si le statut manque, dis-le, ne suppose pas ». C'est ce test, pas la démonstration qui marche, qui te dit si tu peux lui confier de vraies factures.

Le coût se lit sur la dernière ligne. Au 14 septembre 2026, la page des modèles affiche 5 $ par million de tokens en entrée et 25 $ en sortie pour Claude Opus 5, 2 $ et 10 $ pour Claude Sonnet 5, 1 $ et 5 $ pour Claude Haiku 4.5. Multiplie les deux nombres affichés par ces tarifs : sur trois fichiers de quelques lignes, le total se lit en centimes.

Étape 5 : poser le plafond avant d'ajouter un outil qui agit

MAX_TOURS est la première protection, pas un détail. Sans plafond, un agent qui tourne en rond sur une erreur consomme ton budget jusqu'à ce que tu le remarques ; Anthropic cite d'ailleurs le nombre maximal d'itérations comme condition d'arrêt courante. Si l'agent épuise ses dix tours, le programme le dit, et c'est un cas à regarder, pas un plafond à relever.

Ensuite seulement, ajoute un outil qui agit : écrire un fichier, envoyer un email, créer une ligne dans ton outil de facturation. Dès lors, il peut se tromper sans que tu le voies. Les quatre pièces à poser à ce moment, validateur de sortie, état durable, plafond de dépense et notification, sont détaillées dans le seuil où un agent IA gratuit casse. Pour transformer la boucle en agent utile au quotidien, l'exemple du trieur d'emails monté en six étapes donne la méthode : contrat de sortie, exemples annotés, seuil d'autonomie par catégorie.

Traite aussi ce qu'un outil renvoie (email reçu, page web, fichier d'un client) comme une donnée non fiable, qui peut contenir des instructions adressées au modèle : elle reste dans les blocs tool_result, jamais dans la consigne système.

Code ou outil visuel ?

La même boucle existe sans code. Dans n8n, le nœud AI Agent relie un modèle de conversation et au moins un outil, et l'agent choisit quels outils appeler (documentation n8n). Si tu veux un validateur strict sur la sortie, une mémoire que tu inspectes et des tests à relancer à chaque changement de consigne, le code te les donne directement. Si l'agent enchaîne surtout des services déjà connectés (agenda, CRM, messagerie), l'outil visuel va plus vite.

Pour un agent qui travaille sur du code, Claude Code est déjà cette boucle, avec ses outils de lecture, d'écriture et d'exécution intégrés ; le seuil où Claude Code devient utile est le même qu'ici.

La prochaine décision tient en une ligne : quel est le premier fichier réel que tu mets dans factures, et quel total attends-tu avant de lancer ?

Cet article vous a servi ?

Recevez les meilleurs articles, sélectionnés avec soin pour vous faire gagner du temps.

À lire ensuite

Le fine-tuning d'un LLM consiste à poursuivre l'entraînement d'un modèle existant sur tes propres exemples, pour qu'il adopte un comportement précis : classer, répondre dans un ton, trancher dans un format. Il n'apprend pas au modèle des faits qui changent et, face à un prompt mis en cache, il ne réduit la facture que s'il permet de descendre d'une taille de modèle. La décision tient en trois questions.

OpenClaw ne fournit aucune intégration de courtier : la liste officielle des outils intégrés ne contient ni broker ni marché. Le trading arrive donc par une skill tierce, que la documentation demande de traiter comme du code non fiable, ou par un programme que tu écris. Ce qui décide de tout ensuite, c'est qui détient la clé qui passe l'ordre : le modèle, ou ton code.

À la une

Un pivot, c'est souvent le mot poli qu'on emploie devant les investisseurs quand la première entreprise est morte et qu'on a décidé d'en construire une autre. Et c'est très bien. Pas parce que l'échec serait noble, mais parce que la chance a besoin d'exposition : chaque marché où l'on entre, chaque produit qu'on livre, chaque canal qu'on teste est une surface de plus où quelque chose d'inattendu peut se poser.

Articles Marketing

Le prix de LinkedIn Premium dépend de la formule, du pays et de la facturation. Au 9 septembre 2026, LinkedIn affiche Sales Navigator Core à partir de 120,99 € par mois ou 1 088,88 € par an, taxes éventuellement en plus. Pour Career et Business, vérifie l'offre de ton compte. Compare le montant débité et l'engagement, pas seulement un équivalent mensuel.

Projets

Marques

AgentMail

L'infrastructure email des agents IA : créer une boîte par API, envoyer, recevoir, et laisser un agent agir sur des messages structurés.

Mailivery

Le warm up email en service : des conversations automatisées qui bâtissent la réputation d'un domaine d'envoi avant les campagnes.

L'essentiel, par mail.

Ce qui marche, ce qui ne marche pas, ce que je referais autrement. Envoyé quand j'ai quelque chose d'utile à dire.