0Pricing
AI Engineering Academy · Leçon

Points de contrôle et reprise des tâches

Enregistrez l’état de l’agent à chaque étape terminée afin qu’une tâche de longue durée puisse reprendre depuis le dernier point de contrôle réussi plutôt que de recommencer depuis le début après une défaillance.

Points de contrôle et reprise des tâches est une leçon AI Engineering Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.

Le problème des agents à longue durée d’exécution

Un agent exécutant une tâche de recherche en 50 étapes peut fonctionner pendant 30 minutes. S’il échoue à l’étape 47 à cause d’un dépassement de délai d’une API ou du redémarrage d’un serveur, recommencer depuis le début fait perdre tout le travail effectué et consomme des jetons. La création de points de contrôle conserve l’état de l’agent après chaque étape terminée afin que la tâche puisse reprendre au dernier point réussi plutôt que depuis le début. C’est essentiel pour toute tâche d’agent durant plus de quelques minutes.

Quel état de l’agent conserver

L’état d’un agent comprend : la définition de la tâche, les étapes terminées avec leurs appels d’outils et leurs observations, l’index de l’étape en cours, les éventuels résultats accumulés (fichiers écrits, données recueillies), ainsi que des métadonnées telles que l’heure de début et l’utilisation totale de jetons. Conservez tous ces éléments après chaque étape terminée. L’état doit être sérialisable : préférez JSON aux objets Python pour garantir la portabilité.

from dataclasses import dataclass, field
from typing import List, Any, Optional

@dataclass
class AgentStep:
    step_index: int
    thought: str
    tool_name: str
    tool_args: dict
    observation: str
    tokens_used: int
    completed_at: str

@dataclass
class AgentCheckpoint:
    task_id: str
    task_description: str
    status: str  # 'running', 'completed', 'failed'
    current_step: int
    completed_steps: List[AgentStep] = field(default_factory=list)
    accumulated_results: dict = field(default_factory=dict)
    total_tokens: int = 0
    final_answer: Optional[str] = None

Enregistrer les points de contrôle après chaque étape

Après chaque étape réussie, sérialisez le point de contrôle et écrivez-le dans un espace de stockage persistant. Utilisez une base de données ou un stockage d’objets (S3, Redis) plutôt qu’un disque local, afin que le point de contrôle survive aux redémarrages du serveur. Incluez le task_id dans la clé pour pouvoir récupérer le point de contrôle d’une tâche précise. Écrivez également une entrée dans le journal des étapes pour chaque étape, afin de conserver une piste d’audit même si le fichier du point de contrôle est endommagé.

import json
import redis
from dataclasses import asdict

redis_client = redis.Redis()

def save_checkpoint(checkpoint: AgentCheckpoint):
    key = f'agent:checkpoint:{checkpoint.task_id}'
    data = json.dumps(asdict(checkpoint), default=str)
    redis_client.set(key, data, ex=86400)  # 24h TTL
    # Also append to step log
    log_key = f'agent:log:{checkpoint.task_id}'
    if checkpoint.completed_steps:
        last = checkpoint.completed_steps[-1]
        redis_client.rpush(log_key, json.dumps(asdict(last), default=str))

def load_checkpoint(task_id: str) -> AgentCheckpoint | None:
    key = f'agent:checkpoint:{task_id}'
    data = redis_client.get(key)
    if data:
        return AgentCheckpoint(**json.loads(data))
    return None

Reprendre à partir d’un point de contrôle

Lors de la reprise d’une tâche, chargez le point de contrôle et reconstruisez l’historique des messages de l’agent à partir des étapes terminées. Démarrez la boucle d’exécution à l’index de la prochaine étape non terminée. Le modèle reçoit ses réflexions et observations précédentes dans l’historique des messages, ce qui lui fournit tout le contexte du travail déjà effectué et lui permet de continuer sans le répéter.

async def resume_or_start(task_id: str, task_description: str) -> str:
    checkpoint = load_checkpoint(task_id)
    if checkpoint and checkpoint.status == 'running':
        print(f'Resuming task {task_id} from step {checkpoint.current_step}')
        messages = rebuild_history(checkpoint)
        start_step = checkpoint.current_step
    else:
        print(f'Starting new task {task_id}')
        checkpoint = AgentCheckpoint(task_id=task_id, task_description=task_description, status='running', current_step=0)
        messages = [{'role': 'user', 'content': task_description}]
        start_step = 0
        save_checkpoint(checkpoint)
    return await run_agent_from(checkpoint, messages, start_step)

Reconstruire l’historique des messages à partir des étapes

La clé de la reprise consiste à reconstruire fidèlement l’historique des messages à partir des étapes conservées. Chaque étape terminée correspond à un message de l’assistant (réflexion et appel d’outil) et à un message de l’outil (observation). Rejouez toutes les étapes terminées sous forme de messages avant de continuer, afin que le modèle dispose du même contexte que s’il n’avait jamais été interrompu.

def rebuild_history(checkpoint: AgentCheckpoint) -> list:
    messages = [{'role': 'user', 'content': checkpoint.task_description}]
    for step in checkpoint.completed_steps:
        # Reconstruct the agent's reasoning message
        messages.append({
            'role': 'assistant',
            'content': f'Thought: {step.thought}\nAction: {step.tool_name}({step.tool_args})'
        })
        # Reconstruct the tool observation
        messages.append({
            'role': 'user',
            'content': f'Observation: {step.observation}'
        })
    return messages

Appels d’outils idempotents

Si une étape a été partiellement terminée avant une panne (l’outil a été appelé, mais l’observation n’a pas été enregistrée), la reprise peut appeler à nouveau l’outil. Concevez des outils idempotents : les appeler deux fois avec les mêmes arguments doit produire le même résultat qu’un seul appel. Pour les opérations d’écriture (création de fichiers, envoi de courriels), utilisez des clés de déduplication afin d’éviter les effets en double, même si l’outil est appelé plusieurs fois.

async def idempotent_write_file(content: str, path: str, task_id: str, step: int) -> str:
    dedup_key = f'{task_id}:step_{step}:write:{path}'
    if redis_client.exists(dedup_key):
        return f'File {path} already written (dedup key present)'
    with open(path, 'w') as f:
        f.write(content)
    redis_client.set(dedup_key, '1', ex=3600)
    return f'Successfully wrote {len(content)} chars to {path}'

Nettoyage et conservation des points de contrôle

Les points de contrôle consomment de l’espace de stockage et ne doivent pas s’accumuler indéfiniment. Définissez une politique de conservation : supprimez les points de contrôle des tâches terminées après 24 heures, ceux des tâches échouées après 7 jours (pour l’analyse a posteriori), et ne supprimez jamais automatiquement ceux des tâches en cours. Mettez en œuvre une tâche de nettoyage en arrière-plan qui s’exécute toutes les heures et purge les points de contrôle expirés conformément à la politique.

from datetime import datetime, timedelta

RETENTION = {
    'completed': timedelta(hours=24),
    'failed': timedelta(days=7),
    'running': None  # never auto-delete
}

def cleanup_expired_checkpoints():
    now = datetime.utcnow()
    for key in redis_client.scan_iter('agent:checkpoint:*'):
        data = json.loads(redis_client.get(key))
        status = data.get('status', 'running')
        retention = RETENTION.get(status)
        if retention is None:
            continue
        started = datetime.fromisoformat(data.get('started_at', str(now)))
        if now - started > retention:
            redis_client.delete(key)

Les points de contrôle dans LangGraph

LangGraph prend nativement en charge les points de contrôle grâce à ses classes MemorySaver et SqliteSaver. Attachez un gestionnaire de points de contrôle à votre graphe : chaque exécution de nœud sera alors automatiquement conservée. Pour reprendre l’exécution, appelez graph.invoke avec le même thread_id. LangGraph gère la reconstruction de l’historique et le suivi des étapes ; vous n’avez donc pas besoin d’implémenter vous-même la logique des points de contrôle.

from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph

# Create graph with persistent checkpointer
checkpointer = SqliteSaver.from_conn_string('/tmp/agent_state.db')
graph = StateGraph(AgentState)
graph.add_node('reason', reason_node)
graph.add_node('act', act_node)
# ... add edges ...
app = graph.compile(checkpointer=checkpointer)

# Run with thread_id - LangGraph auto-checkpoints
config = {'configurable': {'thread_id': 'task_abc123'}}
result = await app.ainvoke({'task': 'Research climate change'}, config)

# Resume same thread - LangGraph loads from checkpoint
result = await app.ainvoke({'task': 'Continue'}, config)

Points de contrôle distribués pour les agents parallèles

Lorsque plusieurs agents travaillent en parallèle sur des sous-tâches, chacun doit disposer de son propre espace de noms de points de contrôle. Utilisez une structure de clés hiérarchique : parent_task_id:sub_task_id. Le point de contrôle de l’agent parent enregistre les sous-tâches terminées et leurs résultats. Lorsque le parent reprend son exécution, il réutilise les résultats des sous-tâches terminées depuis le point de contrôle au lieu de les exécuter à nouveau.

async def parallel_with_checkpoints(parent_id: str, subtasks: list) -> list:
    results = []
    for i, subtask in enumerate(subtasks):
        sub_id = f'{parent_id}:sub_{i}'
        # Check if subtask already completed
        existing = load_checkpoint(sub_id)
        if existing and existing.status == 'completed':
            print(f'Sub-task {i} already done, using cached result')
            results.append(existing.final_answer)
        else:
            result = await run_agent(sub_id, subtask)
            results.append(result)
    return results

Tester le comportement lors de la reprise

Écrivez des tests d’intégration qui provoquent délibérément une panne de l’agent au milieu d’une tâche et vérifiez que la reprise produit le résultat final correct. Simulez une panne en levant une exception à un index d’étape précis. Après la reprise, vérifiez que seules les étapes situées après la panne sont réexécutées, et non celles qui la précèdent. Testez également que les appels d’outils idempotents ne produisent pas d’effets secondaires en double lorsqu’une étape est rejouée.

import pytest

@pytest.mark.asyncio
async def test_resumption_from_step_3():
    task_id = 'test_resume_001'
    # Run until step 3, then crash
    with pytest.raises(SimulatedCrash):
        await run_agent_crashing_at(task_id, 'Research AI trends', crash_at_step=3)

    checkpoint = load_checkpoint(task_id)
    assert checkpoint.current_step == 3
    assert len(checkpoint.completed_steps) == 3

    # Resume and complete
    result = await resume_or_start(task_id, 'Research AI trends')
    assert result is not None
    # Verify only steps 4+ were re-executed
    assert checkpoint_step_was_not_replayed(task_id, step=0)

Versionner les points de contrôle lors des modifications de schéma

Lorsque vous modifiez le schéma AgentCheckpoint (en ajoutant ou en renommant des champs), les anciens points de contrôle stockés deviennent incompatibles. Gérez cette situation avec un versionnage des points de contrôle : ajoutez un champ checkpoint_version et écrivez des fonctions de migration pour mettre à niveau les anciens points de contrôle vers le nouveau schéma lors de leur chargement. Cela évite les pannes lorsque les agents reprennent leur exécution après un déploiement ayant modifié le format des points de contrôle.

def load_and_migrate_checkpoint(task_id: str) -> AgentCheckpoint:
    raw = json.loads(redis_client.get(f'agent:checkpoint:{task_id}'))
    version = raw.get('checkpoint_version', '1.0')
    if version == '1.0':
        # Migrate: add new fields added in v2.0
        raw['checkpoint_version'] = '2.0'
        raw['accumulated_results'] = raw.get('accumulated_results', {})
        raw['total_tokens'] = raw.get('total_tokens', 0)
    return AgentCheckpoint(**raw)

Vérification rapide

Vérifiez votre compréhension de la création de points de contrôle et de la reprise des tâches dans les agents.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que la création de points de contrôle sérialise l’état de l’agent après chaque étape, afin que les tâches longues survivent aux défaillances sans recommencer depuis le début, que la reconstruction de l’historique des messages à partir des étapes conservées donne au modèle tout le contexte nécessaire lors de la reprise, et que les outils idempotents avec des clés de déduplication empêchent les effets secondaires en double lorsque les étapes sont rejouées. Nous allons maintenant concevoir les déclencheurs d’escalade avec intervention humaine.

Questions Fréquemment Posées

La leçon « Points de contrôle et reprise des tâches » est-elle gratuite ?

Oui — le texte complet de « Points de contrôle et reprise des tâches » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Points de contrôle et reprise des tâches » ?

Enregistrez l’état de l’agent à chaque étape terminée afin qu’une tâche de longue durée puisse reprendre depuis le dernier point de contrôle réussi plutôt que de recommencer depuis le début après une… Tu pratiques AI Engineering Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer AI Engineering Academy ?

Aucune expérience préalable n'est requise. AI Engineering Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Points de contrôle et reprise des tâches » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon AI Engineering Academy ?

Oui. Chaque leçon AI Engineering Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Classer les modes de défaillance des agents
  2. Autocorrection et invites réflexives
  3. Points de contrôle et reprise des tâches
  4. Escalade avec intervention humaine
← Retour à AI Engineering Academy