IA

Agents IA autonomes : construire un agent avec Claude Agent SDK

Les agents IA ne sont plus de la science-fiction. Avec le Claude Agent SDK, vous pouvez créer des agents autonomes qui exécutent des tâches complexes : analyser du code, rechercher des informations, automatiser des workflows. Ce tutoriel vous guide pas à pas. Qu'est-ce qu'un agent IA ? Un agent IA est un système qui : * Reçoit un objectif de haut niveau * Décompose cet objectif en sous-tâches * Exécute des actions via des outils * Itère jusqu'à atteindre l'objectif ┌────────────────────

Jean-Michel Helem

Jean-Michel Helem

21 janvier 2026 · 8 min de lecture

Agents IA autonomes : construire un agent avec Claude Agent SDK

Les agents IA ne sont plus de la science-fiction. Avec le Claude Agent SDK, vous pouvez créer des agents autonomes qui exécutent des tâches complexes : analyser du code, rechercher des informations, automatiser des workflows. Ce tutoriel vous guide pas à pas.

Qu'est-ce qu'un agent IA ?

Un agent IA est un système qui :

  • Reçoit un objectif de haut niveau
  • Décompose cet objectif en sous-tâches
  • Exécute des actions via des outils
  • Itère jusqu'à atteindre l'objectif
┌─────────────────────────────────────────────────────────┐
│                    BOUCLE AGENT                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Objectif ─────▶ Réflexion ─────▶ Action              │
│       ▲                              │                  │
│       │                              ▼                  │
│       └──────── Observation ◀─────── Tool              │
│                                                         │
└─────────────────────────────────────────────────────────┘

Agent vs Chatbot

AspectChatbotAgent
InteractionQuestion → RéponseObjectif → Résultat
ActionsAucune (texte uniquement)Exécute des outils
AutonomieNulleÉlevée
ItérationUne seuleBoucle jusqu'à succès

Installation et configuration

Prérequis

# Python 3.10+
python --version

# Créer un environnement virtuel
python -m venv venv
source venv/bin/activate  # Linux/Mac
# ou
.\venv\Scripts\activate   # Windows

Installation du SDK

pip install anthropic

Configuration API

# config.py
import os
from anthropic import Anthropic

# Charger la clé API depuis l'environnement
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

if not client.api_key:
    raise ValueError("ANTHROPIC_API_KEY environment variable not set")
# Exporter la clé API
export ANTHROPIC_API_KEY="sk-ant-api..."

Architecture d'un agent

Composants essentiels

# agent.py
from dataclasses import dataclass
from typing import List, Dict, Any, Callable

@dataclass
class Tool:
    """Définition d'un outil disponible pour l'agent."""
    name: str
    description: str
    parameters: Dict[str, Any]
    function: Callable

@dataclass
class Message:
    """Message dans la conversation."""
    role: str  # "user", "assistant", "tool"
    content: str

class Agent:
    """Agent IA avec tools et mémoire."""

    def __init__(self, client, model: str = "claude-sonnet-4-20250514"):
        self.client = client
        self.model = model
        self.tools: List[Tool] = []
        self.messages: List[Message] = []
        self.system_prompt = ""

    def add_tool(self, tool: Tool):
        """Ajouter un outil à l'agent."""
        self.tools.append(tool)

    def set_system_prompt(self, prompt: str):
        """Définir le prompt système."""
        self.system_prompt = prompt

    def run(self, objective: str, max_iterations: int = 10) -> str:
        """Exécuter l'agent jusqu'à atteindre l'objectif."""
        self.messages.append(Message(role="user", content=objective))

        for i in range(max_iterations):
            response = self._call_model()

            if response.stop_reason == "end_turn":
                # L'agent a terminé
                return response.content[0].text

            if response.stop_reason == "tool_use":
                # L'agent veut utiliser un outil
                self._handle_tool_calls(response)

        return "Max iterations reached without completion"

Créer des outils

Outil : Lire un fichier

# tools/file_tools.py
import os

def read_file(path: str) -> str:
    """Lire le contenu d'un fichier."""
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return f.read()
    except FileNotFoundError:
        return f"Error: File not found: {path}"
    except Exception as e:
        return f"Error reading file: {str(e)}"

read_file_tool = {
    "name": "read_file",
    "description": "Read the contents of a file at the given path",
    "input_schema": {
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "The path to the file to read"
            }
        },
        "required": ["path"]
    }
}

Outil : Écrire un fichier

def write_file(path: str, content: str) -> str:
    """Écrire du contenu dans un fichier."""
    try:
        os.makedirs(os.path.dirname(path), exist_ok=True)
        with open(path, 'w', encoding='utf-8') as f:
            f.write(content)
        return f"Successfully wrote to {path}"
    except Exception as e:
        return f"Error writing file: {str(e)}"

write_file_tool = {
    "name": "write_file",
    "description": "Write content to a file at the given path",
    "input_schema": {
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "The path to the file to write"
            },
            "content": {
                "type": "string",
                "description": "The content to write to the file"
            }
        },
        "required": ["path", "content"]
    }
}

Outil : Exécuter une commande shell

import subprocess

def run_command(command: str, timeout: int = 30) -> str:
    """Exécuter une commande shell."""
    try:
        result = subprocess.run(
            command,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout
        )
        output = result.stdout
        if result.stderr:
            output += f"\nStderr: {result.stderr}"
        if result.returncode != 0:
            output += f"\nExit code: {result.returncode}"
        return output or "Command completed with no output"
    except subprocess.TimeoutExpired:
        return f"Command timed out after {timeout} seconds"
    except Exception as e:
        return f"Error executing command: {str(e)}"

run_command_tool = {
    "name": "run_command",
    "description": "Execute a shell command and return the output",
    "input_schema": {
        "type": "object",
        "properties": {
            "command": {
                "type": "string",
                "description": "The shell command to execute"
            },
            "timeout": {
                "type": "integer",
                "description": "Timeout in seconds (default: 30)",
                "default": 30
            }
        },
        "required": ["command"]
    }
}

Outil : Recherche web

import requests

def web_search(query: str, num_results: int = 5) -> str:
    """Rechercher sur le web via SerpAPI ou similaire."""
    api_key = os.environ.get("SERPAPI_KEY")
    if not api_key:
        return "Error: SERPAPI_KEY not configured"

    try:
        response = requests.get(
            "https://serpapi.com/search",
            params={
                "q": query,
                "api_key": api_key,
                "num": num_results
            }
        )
        data = response.json()

        results = []
        for item in data.get("organic_results", [])[:num_results]:
            results.append(f"- {item['title']}: {item['link']}")

        return "\n".join(results) if results else "No results found"
    except Exception as e:
        return f"Error searching: {str(e)}"

web_search_tool = {
    "name": "web_search",
    "description": "Search the web for information",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "The search query"
            },
            "num_results": {
                "type": "integer",
                "description": "Number of results to return (default: 5)",
                "default": 5
            }
        },
        "required": ["query"]
    }
}

Agent complet : Code Reviewer

Implémentation

# code_reviewer_agent.py
from anthropic import Anthropic
import os
import json

client = Anthropic()

# Définition des outils
tools = [
    {
        "name": "read_file",
        "description": "Read the contents of a file",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "File path to read"}
            },
            "required": ["path"]
        }
    },
    {
        "name": "list_directory",
        "description": "List files in a directory",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "Directory path"}
            },
            "required": ["path"]
        }
    },
    {
        "name": "write_review",
        "description": "Write the code review to a file",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "Output file path"},
                "content": {"type": "string", "description": "Review content"}
            },
            "required": ["path", "content"]
        }
    }
]

# Implémentation des outils
def execute_tool(name: str, input_data: dict) -> str:
    if name == "read_file":
        try:
            with open(input_data["path"], 'r') as f:
                return f.read()
        except Exception as e:
            return f"Error: {e}"

    elif name == "list_directory":
        try:
            files = os.listdir(input_data["path"])
            return "\n".join(files)
        except Exception as e:
            return f"Error: {e}"

    elif name == "write_review":
        try:
            with open(input_data["path"], 'w') as f:
                f.write(input_data["content"])
            return f"Review written to {input_data['path']}"
        except Exception as e:
            return f"Error: {e}"

    return f"Unknown tool: {name}"

# Prompt système
SYSTEM_PROMPT = """You are an expert code reviewer. Your task is to:

1. Explore the codebase using the list_directory and read_file tools
2. Analyze the code for:
   - Security vulnerabilities
   - Performance issues
   - Code quality and maintainability
   - Best practices violations
3. Write a detailed review using the write_review tool

Be thorough but constructive. Provide specific line references and suggestions.
Focus on actionable improvements."""

def run_code_review(project_path: str, output_path: str = "review.md"):
    """Exécuter une code review automatique."""

    messages = [
        {
            "role": "user",
            "content": f"Please review the code in {project_path} and write your review to {output_path}"
        }
    ]

    print(f"Starting code review of {project_path}...")

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            system=SYSTEM_PROMPT,
            tools=tools,
            messages=messages
        )

        # Ajouter la réponse de l'assistant
        messages.append({
            "role": "assistant",
            "content": response.content
        })

        # Vérifier si terminé
        if response.stop_reason == "end_turn":
            print("Code review completed!")
            # Extraire le texte final
            for block in response.content:
                if hasattr(block, 'text'):
                    print(block.text)
            break

        # Gérer les appels d'outils
        if response.stop_reason == "tool_use":
            tool_results = []

            for block in response.content:
                if block.type == "tool_use":
                    print(f"Using tool: {block.name}")
                    result = execute_tool(block.name, block.input)

                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

            messages.append({
                "role": "user",
                "content": tool_results
            })

if __name__ == "__main__":
    import sys
    project = sys.argv[1] if len(sys.argv) > 1 else "."
    run_code_review(project)

Utilisation

# Exécuter la code review
python code_reviewer_agent.py ./src

# Résultat dans review.md
cat review.md

Gestion de la mémoire

Mémoire de conversation

class ConversationMemory:
    """Mémoire de conversation avec limite de tokens."""

    def __init__(self, max_tokens: int = 100000):
        self.messages = []
        self.max_tokens = max_tokens

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

    def _trim_if_needed(self):
        """Supprimer les messages les plus anciens si nécessaire."""
        while self._estimate_tokens() > self.max_tokens and len(self.messages) > 2:
            # Garder le premier message (objectif) et supprimer le suivant
            self.messages.pop(1)

    def _estimate_tokens(self) -> int:
        """Estimation grossière du nombre de tokens."""
        total_chars = sum(len(m["content"]) for m in self.messages)
        return total_chars // 4  # ~4 chars per token

    def get_messages(self) -> list:
        return self.messages.copy()

Mémoire persistante avec SQLite

import sqlite3
import json
from datetime import datetime

class PersistentMemory:
    """Mémoire persistante pour l'agent."""

    def __init__(self, db_path: str = "agent_memory.db"):
        self.conn = sqlite3.connect(db_path)
        self._create_tables()

    def _create_tables(self):
        self.conn.execute("""
            CREATE TABLE IF NOT EXISTS facts (
                id INTEGER PRIMARY KEY,
                key TEXT UNIQUE,
                value TEXT,
                created_at TIMESTAMP,
                updated_at TIMESTAMP
            )
        """)

        self.conn.execute("""
            CREATE TABLE IF NOT EXISTS conversations (
                id INTEGER PRIMARY KEY,
                session_id TEXT,
                messages TEXT,
                created_at TIMESTAMP
            )
        """)
        self.conn.commit()

    def remember(self, key: str, value: str):
        """Mémoriser un fait."""
        now = datetime.now()
        self.conn.execute("""
            INSERT OR REPLACE INTO facts (key, value, created_at, updated_at)
            VALUES (?, ?, COALESCE((SELECT created_at FROM facts WHERE key = ?), ?), ?)
        """, (key, value, key, now, now))
        self.conn.commit()

    def recall(self, key: str) -> str:
        """Rappeler un fait."""
        cursor = self.conn.execute(
            "SELECT value FROM facts WHERE key = ?", (key,)
        )
        row = cursor.fetchone()
        return row[0] if row else None

    def search(self, query: str) -> list:
        """Rechercher dans la mémoire."""
        cursor = self.conn.execute(
            "SELECT key, value FROM facts WHERE key LIKE ? OR value LIKE ?",
            (f"%{query}%", f"%{query}%")
        )
        return cursor.fetchall()

    def save_conversation(self, session_id: str, messages: list):
        """Sauvegarder une conversation."""
        self.conn.execute(
            "INSERT INTO conversations (session_id, messages, created_at) VALUES (?, ?, ?)",
            (session_id, json.dumps(messages), datetime.now())
        )
        self.conn.commit()

Orchestration multi-agents

Architecture superviseur

class SupervisorAgent:
    """Agent superviseur qui délègue à des agents spécialisés."""

    def __init__(self, client):
        self.client = client
        self.specialists = {}

    def register_specialist(self, name: str, agent, description: str):
        """Enregistrer un agent spécialisé."""
        self.specialists[name] = {
            "agent": agent,
            "description": description
        }

    def run(self, objective: str) -> str:
        """Exécuter avec délégation aux spécialistes."""

        # Créer le prompt avec les spécialistes disponibles
        specialists_desc = "\n".join([
            f"- {name}: {info['description']}"
            for name, info in self.specialists.items()
        ])

        system_prompt = f"""You are a supervisor agent. You can delegate tasks to specialists:

{specialists_desc}

To delegate, respond with:
DELEGATE: <specialist_name>
TASK: <task description>

When the task is complete, synthesize the results."""

        messages = [{"role": "user", "content": objective}]

        while True:
            response = self.client.messages.create(
                model="claude-sonnet-4-20250514",
                max_tokens=4096,
                system=system_prompt,
                messages=messages
            )

            text = response.content[0].text

            # Vérifier s'il y a une délégation
            if "DELEGATE:" in text:
                specialist_name = self._extract_specialist(text)
                task = self._extract_task(text)

                if specialist_name in self.specialists:
                    print(f"Delegating to {specialist_name}: {task}")
                    result = self.specialists[specialist_name]["agent"].run(task)

                    messages.append({"role": "assistant", "content": text})
                    messages.append({
                        "role": "user",
                        "content": f"Result from {specialist_name}:\n{result}"
                    })
                else:
                    messages.append({
                        "role": "user",
                        "content": f"Unknown specialist: {specialist_name}"
                    })
            else:
                # Pas de délégation, réponse finale
                return text

    def _extract_specialist(self, text: str) -> str:
        for line in text.split("\n"):
            if line.startswith("DELEGATE:"):
                return line.replace("DELEGATE:", "").strip()
        return ""

    def _extract_task(self, text: str) -> str:
        for line in text.split("\n"):
            if line.startswith("TASK:"):
                return line.replace("TASK:", "").strip()
        return ""

Exemple d'utilisation multi-agents

# Créer les agents spécialisés
code_agent = Agent(client)
code_agent.set_system_prompt("You are a code analysis expert.")
code_agent.add_tool(read_file_tool)

docs_agent = Agent(client)
docs_agent.set_system_prompt("You are a documentation expert.")
docs_agent.add_tool(write_file_tool)

# Créer le superviseur
supervisor = SupervisorAgent(client)
supervisor.register_specialist("code_analyzer", code_agent, "Analyzes code quality and security")
supervisor.register_specialist("doc_writer", docs_agent, "Writes documentation")

# Exécuter
result = supervisor.run(
    "Analyze the src/ directory and generate documentation for the main modules"
)
print(result)

Bonnes pratiques

Sécurité

# Toujours valider les chemins de fichiers
import os

def safe_path(base_dir: str, user_path: str) -> str:
    """Valider que le chemin reste dans le répertoire autorisé."""
    full_path = os.path.realpath(os.path.join(base_dir, user_path))
    if not full_path.startswith(os.path.realpath(base_dir)):
        raise ValueError("Path traversal detected")
    return full_path

# Limiter les commandes shell autorisées
ALLOWED_COMMANDS = ["ls", "cat", "grep", "find", "wc"]

def safe_command(command: str) -> bool:
    """Vérifier que la commande est autorisée."""
    cmd = command.split()[0]
    return cmd in ALLOWED_COMMANDS

Gestion des erreurs

def robust_tool_execution(tool_func, *args, max_retries: int = 3, **kwargs):
    """Exécuter un outil avec retry."""
    for attempt in range(max_retries):
        try:
            return tool_func(*args, **kwargs)
        except Exception as e:
            if attempt == max_retries - 1:
                return f"Tool failed after {max_retries} attempts: {str(e)}"
            time.sleep(2 ** attempt)  # Exponential backoff

Logging

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("agent")

class LoggedAgent(Agent):
    def run(self, objective: str, **kwargs) -> str:
        logger.info(f"Starting agent with objective: {objective}")
        try:
            result = super().run(objective, **kwargs)
            logger.info(f"Agent completed successfully")
            return result
        except Exception as e:
            logger.error(f"Agent failed: {str(e)}")
            raise

Conclusion

Le Claude Agent SDK permet de créer des agents IA puissants et autonomes. Les clés du succès :

  • Outils bien définis : descriptions claires, validation des entrées
  • Mémoire efficace : conversation + persistance pour les tâches longues
  • Sécurité : validation des chemins, commandes autorisées, timeouts
  • Observabilité : logging, métriques, traces

Commencez simple avec un agent mono-tâche, puis évoluez vers l'orchestration multi-agents selon vos besoins.


Pour les fondamentaux des agents : MCP : le protocole standardisé pour l'IA agentique

Pour choisir votre assistant : Claude Code vs Cursor vs GitHub Copilot : comparatif 2026

Articles similaires

Vibe coding avec Spring Boot : retour d'experience
Vibe Coding

Vibe coding avec Spring Boot : retour d'experience

Le vibe coding fait des merveilles sur les projets JavaScript et Python. Mais des qu'on passe a l'ecosysteme Java et Spring Boot, la donne change. La verbosite du langage, la complexite des annotations et l'epaisseur du framework creent un terrain de jeu tres different pour les assistants IA. Apres trois mois de vibe coding quotidien sur des microservices Spring Boot en production, voici un retour d'experience sans filtre : ce qui accelere reellement le developpement, ce qui genere plus de probl

Jean-Michel Helem · 29 avril 2026 · 8 min
Multi-agents : faire collaborer plusieurs IA
IA

Multi-agents : faire collaborer plusieurs IA

Vous avez un agent qui code. Il fonctionne bien sur des taches isolees -- un bug a corriger, une fonction a ecrire, un test a ajouter. Mais quand vous lui demandez de gerer un projet entier, les limites apparaissent. Le contexte deborde, les erreurs s'accumulent, et vous passez plus de temps a superviser qu'a produire. La reponse n'est pas un agent plus puissant. C'est plusieurs agents qui travaillent ensemble, chacun maitrisant une specialite. Le multi-agent n'est pas une mode -- c'est l'evolut

Jean-Michel Helem · 28 avril 2026 · 9 min
Claude Code workflows : 10 astuces de productivite
Claude Code

Claude Code workflows : 10 astuces de productivite

Vous utilisez Claude Code au quotidien, mais vous avez le sentiment de ne pas exploiter tout son potentiel ? Apres plusieurs mois d'usage intensif, certains patterns reviennent systematiquement chez les developpeurs les plus productifs. Ces astuces ne sont pas des gadgets : elles changent concretement la facon dont vous interagissez avec votre codebase, vos outils et votre pipeline CI/CD. Voici 10 techniques eprouvees pour tirer le maximum de Claude Code en 2026. Si vous debutez avec l'outil,

Jean-Michel Helem · 27 avril 2026 · 7 min