IntermédiaireAgents IA
15 min de lecture12 vues

Construire un Agent IA avec Python

Apprenez à créer un agent IA fonctionnel en Python : tool calling avec l'API OpenAI/Anthropic, framework Agno, gestion de la mémoire et déploiement.

Prérequis

Avant de commencer, assurez-vous d''avoir :

  • Des bases en Python
  • Une clé API OpenAI ou Anthropic
  • Compréhension des LLMs (voir notre cours sur les LLMs)

L''API de Tool Calling : la Brique de Base

Tous les grands LLMs proposent aujourd''hui une API de tool calling (ou function calling). Le principe : vous décrivez les outils disponibles au modèle, et il décide quand les utiliser.

Avec l''API OpenAI

import openai

# 1. Définir les outils disponibles
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Obtenir la météo actuelle d''une ville",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Nom de la ville"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

# 2. Envoyer la requête avec les outils
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Quel temps fait-il à Paris ?"}],
    tools=tools,
)

# 3. Le modèle décide d''appeler l''outil
tool_call = response.choices[0].message.tool_calls[0]
print(tool_call.function.name)       # "get_weather"
print(tool_call.function.arguments)  # {"city": "Paris"}

Avec l''API Anthropic (Claude)

Anthropic propose deux approches : la méthode classique avec un schéma JSON, et le nouveau décorateur @beta_tool qui simplifie tout.

Méthode classique :

import anthropic

client = anthropic.Anthropic()

# Définir l''outil avec un schéma JSON
tools = [
    {
        "name": "get_weather",
        "description": "Obtenir la météo d''une ville",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "Nom de la ville"}
            },
            "required": ["city"]
        }
    }
]

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Météo à Lyon ?"}]
)

Méthode moderne avec @beta_tool (recommandée) :

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()

@beta_tool
def get_weather(city: str) -> str:
    """Obtenir la météo d''une ville.

    Args:
        city: Nom de la ville
    Returns:
        Les données météo au format JSON.
    """
    return json.dumps({"city": city, "temp": 18, "condition": "nuageux"})

# tool_runner gère automatiquement la boucle agent !
runner = client.beta.messages.tool_runner(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    tools=[get_weather],
    messages=[{"role": "user", "content": "Météo à Lyon ?"}],
)
for message in runner:
    print(message)

Le décorateur @beta_tool génère automatiquement le schéma JSON à partir des type hints Python. Et tool_runner gère toute la boucle agent pour vous — plus besoin de coder la boucle while manuellement !

Tool calling vs Function calling : C''est la même chose. OpenAI a d''abord utilisé le terme "function calling" avant de le renommer "tool calling". Anthropic utilise directement "tool use".


La Boucle Agent en Code

Voici la boucle agent complète, étape par étape :

Implémentation Python

import json

def run_agent(user_message: str, tools: list, tool_functions: dict):
    """Boucle agent complète avec tool calling."""
    messages = [{"role": "user", "content": user_message}]

    while True:
        # Appel au LLM
        response = openai.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
        )

        choice = response.choices[0]

        # Si le LLM veut utiliser un outil
        if choice.finish_reason == "tool_calls":
            # Ajouter la réponse du LLM à l''historique
            messages.append(choice.message)

            # Exécuter chaque outil demandé
            for tool_call in choice.message.tool_calls:
                fn_name = tool_call.function.name
                fn_args = json.loads(tool_call.function.arguments)

                # Appeler la vraie fonction Python
                result = tool_functions[fn_name](**fn_args)

                # Renvoyer le résultat au LLM
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": json.dumps(result),
                })

        # Sinon, c''est la réponse finale
        else:
            return choice.message.content

Utilisation

# Définir les fonctions réelles
def get_weather(city: str) -> dict:
    # Ici on appellerait une vraie API météo
    return {"city": city, "temp": 18, "condition": "nuageux"}

def search_restaurants(city: str, cuisine: str = "") -> dict:
    return {"results": [f"Le Bon {cuisine} à {city}", f"Chez Marcel - {city}"]}

# Mapper les noms aux fonctions
tool_functions = {
    "get_weather": get_weather,
    "search_restaurants": search_restaurants,
}

# Lancer l''agent
answer = run_agent(
    "Quel temps fait-il à Paris et recommande-moi un restaurant italien ?",
    tools=tools_definition,
    tool_functions=tool_functions,
)
print(answer)
# "Il fait 18°C à Paris (nuageux). Je vous recommande Le Bon italien à Paris !"

Ajouter de la Mémoire

Un agent sans mémoire oublie tout entre chaque conversation. Il existe plusieurs stratégies :

Mémoire conversationnelle simple

class ConversationMemory:
    def __init__(self, max_messages: int = 50):
        self.messages: list[dict] = []
        self.max_messages = max_messages

    def add(self, role: str, content: str):
        self.messages.append({"role": role, "content": content})
        # Garder seulement les N derniers messages
        if len(self.messages) > self.max_messages:
            self.messages = self.messages[-self.max_messages:]

    def get_context(self) -> list[dict]:
        return self.messages.copy()

Mémoire avec résumé

Pour les conversations longues, on peut résumer les anciens messages :

class SummarizedMemory:
    def __init__(self):
        self.summary = ""
        self.recent_messages: list[dict] = []

    def add(self, role: str, content: str):
        self.recent_messages.append({"role": role, "content": content})

        # Tous les 20 messages, résumer
        if len(self.recent_messages) > 20:
            self.summary = self._summarize()
            self.recent_messages = self.recent_messages[-5:]

    def _summarize(self) -> str:
        # Demander au LLM de résumer la conversation
        prompt = f"Résume cette conversation en 3-4 phrases :\n{self.recent_messages}"
        # ... appel LLM ...
        return summary

    def get_context(self) -> list[dict]:
        messages = []
        if self.summary:
            messages.append({
                "role": "system",
                "content": f"Résumé de la conversation précédente : {self.summary}"
            })
        messages.extend(self.recent_messages)
        return messages

Framework : Agno

Agno est un framework Python open source conçu pour construire des agents IA. Sa philosophie : des fonctions Python simples comme outils, pas de schéma JSON, pas de décorateurs spéciaux.

Installation

pip install agno

Agent avec Agno

from agno.agent import Agent
from agno.models.anthropic import Claude

# 1. Définir des outils = simples fonctions Python
def get_weather(city: str) -> str:
    """Obtenir la météo actuelle d''une ville.

    Args:
        city: Nom de la ville.
    Returns:
        str: Les données météo de la ville.
    """
    # Ici on appellerait une vraie API météo
    return f"Il fait 18°C à {city}, ciel partiellement nuageux."

def search_restaurants(city: str, cuisine: str = "") -> str:
    """Chercher des restaurants dans une ville.

    Args:
        city: Nom de la ville.
        cuisine: Type de cuisine (optionnel).
    Returns:
        str: Liste de restaurants trouvés.
    """
    return f"Restaurants {cuisine} à {city} : Le Bon Goût, Chez Marcel"

# 2. Créer l''agent (une seule ligne !)
agent = Agent(
    model=Claude(id="claude-sonnet-4-5"),
    tools=[get_weather, search_restaurants],
    instructions=["Réponds toujours en français."],
    markdown=True,
)

# 3. Lancer l''agent
agent.print_response(
    "Quel temps fait-il à Paris et recommande-moi un restaurant italien ?",
    stream=True,
)

Pas besoin de décorateur @tool ni de schéma JSON manuel. Agno lit les type hints et la docstring de vos fonctions Python pour générer automatiquement la description des outils pour le LLM.

Agno avec différents modèles

Agno supporte tous les grands LLMs — changer de modèle = changer une ligne :

from agno.models.openai import OpenAIChat
from agno.models.anthropic import Claude
from agno.models.google import Gemini

# OpenAI
agent_gpt = Agent(model=OpenAIChat(id="gpt-4o"), tools=[get_weather])

# Anthropic
agent_claude = Agent(model=Claude(id="claude-sonnet-4-5"), tools=[get_weather])

# Google
agent_gemini = Agent(model=Gemini(id="gemini-2.5-flash"), tools=[get_weather])

Mémoire intégrée avec Agno

Agno fournit une gestion de mémoire native, bien plus puissante que notre implémentation manuelle :

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.memory import MemoryManager
from agno.models.anthropic import Claude

# Base de données pour la persistance
db = SqliteDb(db_file="tmp/agents.db")

# Le MemoryManager extrait automatiquement les faits importants
memory_manager = MemoryManager(
    model=Claude(id="claude-sonnet-4-5"),
    db=db,
)

agent = Agent(
    model=Claude(id="claude-sonnet-4-5"),
    db=db,
    memory_manager=memory_manager,
    enable_agentic_memory=True,       # L''agent gère sa propre mémoire
    add_history_to_context=True,      # Historique dans le contexte
    num_history_runs=5,               # 5 dernières conversations
    markdown=True,
)

# Première conversation
agent.print_response("Je m''appelle Alice et je travaille chez Acme", stream=True)

# Plus tard... l''agent se souvient !
agent.print_response("Quel est mon nom ?", stream=True)
# → "Vous vous appelez Alice et vous travaillez chez Acme."

Avec enable_agentic_memory, l''agent extrait automatiquement les informations importantes de chaque conversation (noms, préférences, faits clés) et les stocke dans SQLite. Plus besoin de gérer la mémoire manuellement !

Agno simplifie énormément la création d''agents : définissez vos outils comme de simples fonctions Python, créez un Agent, et Agno gère toute la boucle ReAct, la mémoire et le streaming automatiquement.


Gestion des Erreurs et Garde-fous

Un agent en production doit être robuste :

Limiter les itérations

MAX_ITERATIONS = 10

def run_agent_safe(user_message: str, tools: list, tool_functions: dict):
    """Boucle agent avec limite d''itérations."""
    messages = [{"role": "user", "content": user_message}]

    for iteration in range(MAX_ITERATIONS):
        response = call_llm(messages, tools)

        if response.has_tool_calls():
            execute_tools(response, tool_functions, messages)
        else:
            return response.content

    # Si on arrive ici, l''agent tourne en boucle
    return "Désolé, je n''ai pas pu résoudre cette tâche dans le temps imparti."

Valider les appels d''outils

def delete_file(path: str) -> str:
    """Supprime un fichier (DANGEREUX)."""
    # Garde-fou : ne jamais supprimer en dehors du dossier autorisé
    allowed_dir = "/tmp/agent-workspace/"
    if not path.startswith(allowed_dir):
        return "ERREUR : Suppression interdite en dehors du workspace"

    os.remove(path)
    return f"Fichier {path} supprimé"

Supervision humaine (Human-in-the-loop)

def send_email(to: str, subject: str, body: str) -> str:
    """Envoie un email (nécessite validation humaine)."""
    print(f"\n--- VALIDATION REQUISE ---")
    print(f"À : {to}")
    print(f"Sujet : {subject}")
    print(f"Corps : {body}")
    confirm = input("Envoyer ? (oui/non) : ")

    if confirm.lower() == "oui":
        # Envoyer réellement l''email
        return "Email envoyé avec succès"
    return "Envoi annulé par l''utilisateur"

En production, ne faites JAMAIS confiance à un agent pour des actions irréversibles sans validation. Un LLM peut mal interpréter une instruction et supprimer les mauvais fichiers, envoyer un email au mauvais destinataire, etc.


Architecture d''un Agent Complet


Résumé

ConceptDescription
Tool CallingAPI qui permet au LLM de choisir et appeler des fonctions
Boucle AgentWhile loop qui exécute les outils jusqu''à la réponse finale
MémoireStockage du contexte (court/long terme) entre les tours
AgnoFramework Python pour créer des agents avec de simples fonctions
Garde-fousLimites d''itérations, validation humaine, sandboxing

Prochaine étape : Dans le cours avancé, nous verrons les architectures multi-agents avec Agno Team, où plusieurs agents collaborent pour résoudre des tâches complexes, et le déploiement en production avec AgentOS.

Specialiste IA — Master Intelligence Artificielle

Diplome d'un Master en Intelligence Artificielle, je travaille au quotidien sur des projets IA en entreprise. J'ai cree IwanttolearnAI pour rendre l'apprentissage de l'IA accessible a tous, gratuitement.