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

Bases vectorielles 2026 : Pinecone vs Weaviate vs pgvector
IA

Bases vectorielles 2026 : Pinecone vs Weaviate vs pgvector

Toute application IA serieuse construite en 2026 stocke des vecteurs. RAG sur documentation, recherche semantique sur catalogue produit, recommandations contextualisees, detection d'anomalies, analyse de logs : la liste des cas d'usage s'allonge chaque mois. Le choix de la base vectorielle qui sous-tend ces applications n'est pas neutre. Une mauvaise decision se paie en couts cloud multiplies par cinq, en latences qui ruinent l'experience utilisateur, ou en heures d'ops perdues a maintenir une i

Jean-Michel Helem · 12 mai 2026 · 7 min
RAG pour developpeurs : enrichir Cursor et Claude Code
IA

RAG pour developpeurs : enrichir Cursor et Claude Code

Demandez a Cursor ou Claude Code de modifier une fonction dans un projet de 50 fichiers, vous obtiendrez generalement un resultat correct. Demandez la meme chose dans un monorepo de 5000 fichiers avec dix services, trois langages et quinze ans d'historique, et la qualite chute brutalement. L'agent ne voit qu'une fraction du contexte. Il invente des imports, propose des conventions etrangeres au projet, ignore des helpers existants et duplique du code deja ecrit ailleurs. Le probleme n'est pas la

Jean-Michel Helem · 11 mai 2026 · 8 min
Stack complete du dev IA-first en 2026
IA

Stack complete du dev IA-first en 2026

En 2024, integrer l'IA dans son workflow de developpement etait un avantage concurrentiel. En 2025, c'est devenu une pratique courante. En 2026, ne pas avoir de stack IA-first est un handicap mesurable : les developpeurs equipes produisent 3 a 5 fois plus vite sur les taches repetitives, avec un taux de bugs en baisse de 30 a 40 % selon les benchmarks internes des equipes qui ont franchi le pas. Mais construire cette stack ne s'improvise pas. Entre les IDE augmentes, les agents CLI, les protocol

Jean-Michel Helem · 8 mai 2026 · 9 min