Aller au contenu principal

Context variables et mémoire d'agent

Context variables et mémoire d’agent

Un agent sans mémoire oublie tout entre chaque exécution. Les context variables permettent d’injecter un état partagé dans l’agent et ses tools. La mémoire conversationnelle permet de maintenir le fil d’une conversation multi-tours.

Context variables : état partagé

Le RunContextWrapper est un objet typé qui circule entre l’agent, ses tools et ses guardrails :

from dataclasses import dataclass, field
from agents import Agent, Runner, RunContextWrapper, function_tool

@dataclass
class SessionUtilisateur:
    user_id: str
    nom: str
    email: str
    panier: list[dict] = field(default_factory=list)
    preferences: dict = field(default_factory=dict)

@function_tool
def ajouter_au_panier(
    ctx: RunContextWrapper[SessionUtilisateur],
    produit: str,
    quantite: int = 1,
) -> str:
    """Ajoute un produit au panier de l'utilisateur."""
    ctx.context.panier.append({"produit": produit, "quantite": quantite})
    total = len(ctx.context.panier)
    return f"{produit} (x{quantite}) ajouté au panier. Total : {total} article(s)."

@function_tool
def voir_panier(ctx: RunContextWrapper[SessionUtilisateur]) -> str:
    """Affiche le contenu du panier."""
    if not ctx.context.panier:
        return "Votre panier est vide."
    lignes = [f"- {item['produit']} x{item['quantite']}" for item in ctx.context.panier]
    return f"Panier de {ctx.context.nom} :\n" + "\n".join(lignes)

agent = Agent(
    name="Agent boutique",
    instructions="Vous aidez les clients à faire leurs achats.",
    tools=[ajouter_au_panier, voir_panier],
    model="gpt-5.3",
)

session = SessionUtilisateur(
    user_id="usr_789",
    nom="Marie Dupont",
    email="[email protected]",
)

# Le contexte est partagé entre tous les tools
result = Runner.run_sync(agent, "Ajoutez un laptop et deux souris à mon panier", context=session)
print(result.final_output)
print(f"Panier après exécution : {session.panier}")

Le contexte est mutable : les tools peuvent le modifier, et les modifications persistent après l’exécution.

Instructions dynamiques

Les instructions peuvent être une fonction qui reçoit le contexte :

def instructions_dynamiques(ctx: RunContextWrapper[SessionUtilisateur]) -> str:
    prefs = ctx.context.preferences
    langue = prefs.get("langue", "français")
    return f"""Vous êtes un assistant commercial.
    Le client s'appelle {ctx.context.nom}.
    Son email est {ctx.context.email}.
    Il a {len(ctx.context.panier)} article(s) dans son panier.
    Communiquez en {langue}."""

agent = Agent(
    name="Agent personnalisé",
    instructions=instructions_dynamiques,
    tools=[ajouter_au_panier, voir_panier],
    model="gpt-5.3",
)

Les instructions sont évaluées à chaque tour de la boucle agent, ce qui permet une personnalisation en temps réel.

Mémoire conversationnelle multi-tours

Pour maintenir une conversation sur plusieurs échanges, passez l’historique :

from agents import Agent, Runner

agent = Agent(
    name="Assistant",
    instructions="Vous êtes un assistant qui se souvient de la conversation.",
    model="gpt-5.3",
)

historique = []

async def converser(message_utilisateur: str) -> str:
    # Ajouter le message de l'utilisateur à l'historique
    historique.append({"role": "user", "content": message_utilisateur})

    # Exécuter l'agent avec l'historique complet
    result = await Runner.run(agent, historique)

    # Sauvegarder les nouveaux échanges pour le prochain tour
    historique.clear()
    historique.extend(result.to_input_list())

    return result.final_output

# Simulation d'une conversation
async def demo():
    print(await converser("Je m'appelle Thomas."))
    print(await converser("Quel est mon prénom ?"))  # L'agent se souvient
    print(await converser("Recommandez-moi un bon restaurant à Paris."))

Pattern : mémoire persistante avec base de données

Pour une mémoire qui survit aux redémarrages, stockez l’historique en base :

import json
from agents import Agent, Runner, function_tool

@function_tool
async def sauvegarder_note(ctx: RunContextWrapper, cle: str, valeur: str) -> str:
    """Sauvegarde une information pour s'en souvenir plus tard."""
    # En production : écriture en base de données
    notes = ctx.context.notes
    notes[cle] = valeur
    return f"Note sauvegardée : {cle} = {valeur}"

@function_tool
async def recuperer_note(ctx: RunContextWrapper, cle: str) -> str:
    """Récupère une information sauvegardée précédemment."""
    notes = ctx.context.notes
    valeur = notes.get(cle)
    if valeur:
        return f"{cle} = {valeur}"
    return f"Aucune note trouvée pour '{cle}'"

@dataclass
class ContexteMemoire:
    user_id: str
    notes: dict = field(default_factory=dict)

agent = Agent(
    name="Assistant avec mémoire",
    instructions="""Vous êtes un assistant avec une mémoire persistante.
    Utilisez sauvegarder_note pour retenir des informations importantes
    et recuperer_note pour les retrouver.""",
    tools=[sauvegarder_note, recuperer_note],
    model="gpt-5.3",
)

Contexte vs. instructions : quand utiliser quoi

BesoinSolution
Données utilisateur (nom, rôle)Context variables
Comportement de l’agentInstructions (statiques ou dynamiques)
Historique de conversationresult.to_input_list()
Mémoire long termeTools + base de données
Configuration runtimeContext variables

Points clés à retenir

  • RunContextWrapper transporte un état typé accessible par tous les tools
  • Le contexte est mutable : les tools peuvent le modifier pendant l’exécution
  • Les instructions dynamiques (fonctions) s’adaptent au contexte à chaque tour
  • L’historique conversationnel se maintient avec result.to_input_list()
  • Pour une mémoire persistante, combinez des tools de sauvegarde avec une base de données