Aller au contenu principal

Stratégies de contexte pour les agents

Le défi du contexte pour les agents

Un agent autonome enchaîne des dizaines d’appels API : il réfléchit, utilise des outils, lit des résultats, et itère. À chaque étape, le contexte grandit. Sans stratégie de gestion, un agent atteint la limite de contexte en quelques itérations. Cette leçon vous montre comment concevoir des agents qui gèrent efficacement leur mémoire.

Architecture mémoire d’un agent

Mémoire à trois niveaux

from dataclasses import dataclass, field

@dataclass
class MemoireAgent:
    """Système de mémoire à trois niveaux pour un agent."""

    # Niveau 1 : mémoire de travail (contexte courant)
    travail: list[dict] = field(default_factory=list)

    # Niveau 2 : mémoire épisodique (résumés des étapes passées)
    episodes: list[str] = field(default_factory=list)

    # Niveau 3 : mémoire sémantique (faits permanents)
    faits: list[str] = field(default_factory=list)

    max_tokens_travail: int = 20_000
    max_episodes: int = 10

    def ajouter_action(self, action: str, resultat: str):
        """Ajoute une action à la mémoire de travail."""
        self.travail.append({
            "action": action,
            "resultat": resultat[:2000],  # Tronquer les résultats longs
        })
        self._verifier_compaction()

    def ajouter_fait(self, fait: str):
        """Ajoute un fait permanent."""
        if fait not in self.faits:
            self.faits.append(fait)

    def _verifier_compaction(self):
        tokens = sum(
            len(str(e)) // 4 for e in self.travail
        )
        if tokens > self.max_tokens_travail:
            self._compacter()

    def _compacter(self):
        """Compacte la mémoire de travail en épisode."""
        import openai
        client = openai.OpenAI()

        texte = "\n".join(
            f"Action: {a[action]}\nRésultat: {a[resultat]}"
            for a in self.travail[:-2]
        )

        response = client.responses.create(
            model="gpt-5.3",
            input=(
                "Résumez ces actions d'agent en 3-5 points clés. "
                "Gardez les faits, résultats et décisions importants.\n\n"
                f"{texte}"
            ),
            max_output_tokens=300,
        )

        self.episodes.append(response.output_text)
        self.travail = self.travail[-2:]  # Garder les 2 dernières actions

        # Limiter le nombre d'épisodes
        if len(self.episodes) > self.max_episodes:
            self.episodes = self.episodes[-self.max_episodes:]

    def construire_contexte(self) -> str:
        """Construit le contexte complet pour le prochain appel."""
        parties = []

        if self.faits:
            parties.append(
                "Faits établis :\n" + "\n".join(f"- {f}" for f in self.faits)
            )

        if self.episodes:
            parties.append(
                "Historique résumé :\n" + "\n---\n".join(self.episodes)
            )

        if self.travail:
            parties.append(
                "Actions récentes :\n" + "\n".join(
                    f"- {a[action]}{a[resultat][:500]}"
                    for a in self.travail
                )
            )

        return "\n\n".join(parties)

Stratégies pour les résultats d’outils

Tronquer intelligemment

Les outils retournent souvent des résultats volumineux (pages web, résultats SQL, fichiers). Tronquez-les intelligemment :

def tronquer_resultat_outil(
    resultat: str,
    max_tokens: int = 2000,
    strategie: str = "debut_fin",
) -> str:
    """Tronque un résultat d'outil en préservant l'information clé."""
    tokens_estimes = len(resultat) // 4

    if tokens_estimes <= max_tokens:
        return resultat

    max_chars = max_tokens * 4

    match strategie:
        case "debut":
            return resultat[:max_chars] + "\n[... tronqué]"

        case "debut_fin":
            moitie = max_chars // 2
            return (
                resultat[:moitie]
                + "\n[... milieu tronqué ...]\n"
                + resultat[-moitie:]
            )

        case "resume":
            import openai
            client = openai.OpenAI()
            response = client.responses.create(
                model="gpt-5.3",
                input=f"Résumez ces données en gardant l'essentiel :\n{resultat}",
                max_output_tokens=max_tokens,
            )
            return response.output_text

    return resultat[:max_chars]

Filtrer les résultats avant injection

def filtrer_resultat_sql(resultat: str, question: str) -> str:
    """Filtre un résultat SQL pour ne garder que le pertinent."""
    lignes = resultat.strip().split("\n")

    if len(lignes) <= 20:
        return resultat

    # Garder l'en-tête et les premières/dernières lignes
    en_tete = lignes[0]
    premieres = lignes[1:6]
    dernieres = lignes[-5:]

    return (
        f"{en_tete}\n"
        + "\n".join(premieres)
        + f"\n[... {len(lignes) - 11} lignes omises ...]\n"
        + "\n".join(dernieres)
        + f"\n\nTotal : {len(lignes) - 1} lignes"
    )

Gestion du contexte multi-agents

Quand plusieurs sous-agents travaillent en parallèle, partagez le contexte minimal :

@dataclass
class ContextePartage:
    """Contexte partagé entre sous-agents."""
    objectif: str
    contraintes: list[str]
    resultats_partiels: dict[str, str] = field(default_factory=dict)

    def contexte_pour_sous_agent(self, role: str) -> str:
        """Génère un contexte ciblé pour un sous-agent."""
        return (
            f"Objectif global : {self.objectif}\n"
            f"Votre rôle : {role}\n"
            f"Contraintes : {, .join(self.contraintes)}\n"
            f"Résultats des autres agents :\n"
            + "\n".join(
                f"- {k}: {v[:200]}"
                for k, v in self.resultats_partiels.items()
            )
        )

Points clés à retenir

  • Structurez la mémoire de l’agent en trois niveaux : travail, épisodique, sémantique
  • Compactez automatiquement quand la mémoire de travail dépasse le seuil
  • Tronquez les résultats d’outils avant de les injecter dans le contexte
  • Pour les multi-agents, partagez un contexte minimal et ciblé par rôle