Agents IA autonomes : construire un agent avec Claude Agent SDK

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