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

Anthropic abandonne sa safety pledge : ce que ça signifie vraiment pour les développeurs
IA

Anthropic abandonne sa safety pledge : ce que ça signifie vraiment pour les développeurs

Fin février 2026, Anthropic a officiellement abandonné ce qui constituait le socle de son positionnement depuis sa création : la safety pledge, l'engagement formel de ne pas publier de systèmes IA de plus en plus puissants tant que la société n'était pas convaincue qu'ils ne pourraient pas causer de dommages graves. Cette volte-face a déclenché un débat intense dans la communauté IA, amplifié par une couverture médiatique qui mélange souvent les enjeux philosophiques à long terme avec les implic

Jean-Michel Helem · 13 mars 2026 · 5 min
Gemini 3 Flash dans Google Search : ce que les développeurs doivent anticiper
IA

Gemini 3 Flash dans Google Search : ce que les développeurs doivent anticiper

Le déploiement de Gemini 3 Flash dans Google Search marque un tournant dans la façon dont les développeurs découvrent et consomment du contenu technique en ligne. Les AI Overviews — ces résumés générés par IA qui apparaissent au-dessus des résultats organiques — couvrent désormais une large part des requêtes techniques. Pour les créateurs de contenu tech, les développeurs qui cherchent des solutions, et les équipes qui investissent dans le SEO technique, ce changement exige une adaptation de str

Jean-Michel Helem · 12 mars 2026 · 2 min
IA agentique : comment garder le contrôle avec le human-in-the-loop
IA

IA agentique : comment garder le contrôle avec le human-in-the-loop

En 2026, "agents IA" est devenu le terme le plus utilisé — et le moins bien défini — de la tech. Toutes les organisations veulent "déployer des agents", mais peu ont réfléchi à la question fondamentale : à quel moment doit-on maintenir un humain dans la boucle de décision ? Répondre à cette question sans y réfléchir, c'est soit paralyser vos agents avec une supervision constante, soit laisser des systèmes autonomes prendre des décisions aux conséquences potentiellement sévères. Ce guide propo

Jean-Michel Helem · 10 mars 2026 · 5 min