Logging d'agents

Les logs enregistrent les actions de l'agent, les prompts et les appels d'outils.
Sur cette page
  1. Idée en 30 secondes
  2. ProblĂšme principal
  3. Comment ça fonctionne
  4. Quels événements logger en premier
  5. Quand l'utiliser
  6. Exemple d'implementation
  7. Erreurs typiques
  8. Seule la reponse finale est loggee
  9. Pas d'identifiants stables (run_id, trace_id)
  10. Raw prompts ou raw args logs sans redaction
  11. tool_result et stop_reason non logges
  12. Auto-verification
  13. FAQ
  14. Pages liees

Idée en 30 secondes

Le logging d'agents répond à une question simple : qu'est-ce qui s'est exactement passé pendant le run.

Pour cela, il faut des événements structurés avec corrélation via run_id et trace_id.

Sans cela, en incident on voit souvent seulement la réponse finale, pas le chemin qui y mÚne.

ProblĂšme principal

Dans un backend classique, quelques logs par requĂȘte suffisent souvent.

Dans un systĂšme agentique, une requĂȘte peut inclure reasoning, tool calls, retries et plusieurs Ă©tapes modĂšle. Si on logge seulement le final, il devient difficile de voir oĂč le systĂšme a cassĂ©.

En production, cela ressemble souvent a :

  • un utilisateur se plaint d'une mauvaise rĂ©ponse ;
  • les coĂ»ts ou la latency montent par vagues ;
  • un log contient une erreur isolĂ©e sans contexte run.

C'est pourquoi les agents ont besoin de logs structurés sur tout le cycle de vie du run, pas de logs aléatoires.

Comment ça fonctionne

L'idée de base est simple : chaque étape importante est enregistrée comme événement structuré distinct.

Minimum par événement :

  • run_id et trace_id pour la corrĂ©lation ;
  • event (ce qui s'est passĂ©) ;
  • timestamp ;
  • status (ok / error) quand c'est pertinent ;
  • champs clĂ©s de l'Ă©tape (tool, latency, stop_reason, etc.).

Quels événements logger en premier

EvenementCe qu'il faut enregistrer
run_startedrun_id, trace_id, request_id, user_id
agent_stepstep_type, step_index, tool
tool_calltool_name, args_hash
tool_resulttool_name, latency_ms, status, error_class
llm_resultmodel, token usage, latency_ms, status
run_finishedstop_reason, total_steps, total_latency_ms

En production, les raw prompts et raw tool args ne sont generalement pas ecrits sans redaction. Le plus souvent, on stocke un hash ou une version anonymisee.

Quand l'utiliser

Le logging detaille n'est pas toujours necessaire.

Pour un scenario single-shot simple, des logs minimaux request -> response peuvent suffire.

Mais des qu'il y a tools, retries, plusieurs etapes ou cout eleve, sans logging structure il devient difficile de :

  • debugger les incidents ;
  • expliquer les couts ;
  • regler les alertes de facon stable.

Exemple d'implementation

Ci-dessous, un exemple simplifie de structured logging dans runtime et tool gateway. Dans cet exemple, les raw args ne sont pas logs : on enregistre args_hash. agent_step trace l'etape elle-meme, alors que tool_call et tool_result detaillent separement le debut et le resultat d'appel d'outil.

PYTHON
import hashlib
import json
import logging
import time
import uuid

logger = logging.getLogger("agent")


def stable_hash(value):
    payload = json.dumps(
        value,
        sort_keys=True,
        ensure_ascii=False,
        default=str,  # pour datetime et types complexes; en systemes critiques preferer un format stable (ex. ISO 8601)
    ).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()


def log_event(event, **fields):
    logger.info(event, extra={"event": event, **fields})


def run_agent(agent, task, user_id=None, request_id=None):
    run_id = str(uuid.uuid4())
    trace_id = str(uuid.uuid4())
    started_at = time.time()
    steps = 0
    stop_reason = "max_steps"
    run_status = "ok"

    log_event(
        "run_started",
        run_id=run_id,
        trace_id=trace_id,
        user_id=user_id,
        request_id=request_id,
        task_hash=stable_hash(task),
    )

    try:
        for step in agent.iter(task):  # step: reasoning ou tool execution
            steps += 1
            step_started_at = time.time()
            step_type = step.type
            tool_name = getattr(step, "tool_name", None)

            log_event(
                "agent_step",
                run_id=run_id,
                trace_id=trace_id,
                step_index=steps,
                step_type=step_type,
                tool=tool_name,
            )

            if step_type == "tool_call":
                args = getattr(step, "args", {})
                log_event(
                    "tool_call",
                    run_id=run_id,
                    trace_id=trace_id,
                    tool=tool_name,
                    args_hash=stable_hash(args),
                )

            try:
                result = step.execute()
                latency_ms = int((time.time() - step_started_at) * 1000)

                if step_type == "tool_call":
                    log_event(
                        "tool_result",
                        run_id=run_id,
                        trace_id=trace_id,
                        tool=tool_name,
                        latency_ms=latency_ms,
                        status="ok",
                    )
                else:
                    token_usage = getattr(result, "token_usage", None)
                    log_event(
                        "llm_result",
                        run_id=run_id,
                        trace_id=trace_id,
                        step_type=step_type,
                        model=getattr(step, "model", None),
                        token_usage=token_usage,
                        latency_ms=latency_ms,
                        status="ok",
                    )
            except Exception as error:
                latency_ms = int((time.time() - step_started_at) * 1000)
                result_event = "tool_result" if step_type == "tool_call" else "llm_result"
                log_event(
                    result_event,
                    run_id=run_id,
                    trace_id=trace_id,
                    step_type=step_type,
                    tool=tool_name,
                    model=getattr(step, "model", None),
                    latency_ms=latency_ms,
                    status="error",
                    error_class=type(error).__name__,
                    error_message=str(error),
                )
                run_status = "error"
                stop_reason = "tool_error" if step_type == "tool_call" else "step_error"
                raise

            if result.is_final:
                stop_reason = "completed"
                break
    finally:
        log_event(
            "run_finished",
            run_id=run_id,
            trace_id=trace_id,
            status=run_status,
            stop_reason=stop_reason,
            total_steps=steps,
            total_latency_ms=int((time.time() - started_at) * 1000),
        )

En production, ces evenements sont generalement envoyes vers un systeme de logs centralise (par exemple ELK, Datadog ou ClickHouse), puis utilises pour dashboards et alertes.

Cet exemple suffit pour :

  • trouver un tool call problematique ;
  • calculer la latency par etape ;
  • comprendre pourquoi le run s'est arrete.

Par exemple, une entree JSON peut ressembler a :

JSON
{
  "timestamp": "2026-03-21T15:17:00Z",
  "event": "tool_result",
  "run_id": "run_9fd2",
  "trace_id": "tr_9fd2",
  "tool": "search_docs",
  "latency_ms": 410,
  "status": "ok"
}

Erreurs typiques

Meme quand le logging est deja en place, les incidents restent souvent difficiles a analyser a cause des erreurs ci-dessous.

Seule la reponse finale est loggee

Sans evenements intermediaires, on ne voit pas comment l'agent est arrive au resultat. Dans ce mode, meme un incident simple prend trop de temps.

Pas d'identifiants stables (run_id, trace_id)

Quand les evenements ne sont pas correles, impossible de reconstruire un run complet. En production, cela transforme souvent le debugging en recherche manuelle entre services.

Raw prompts ou raw args logs sans redaction

C'est un risque direct de fuite de donnees personnelles ou sensibles. Il est plus sur de stocker un hash, des champs rediges ou une version anonymisee.

tool_result et stop_reason non logges

Si tool_result et stop_reason manquent, il est difficile de comprendre ce qui a casse. Ces trous masquent souvent echec d'outil ou une phase precoce de spam d'outils.

Auto-verification

Ci-dessous, une checklist courte de base pour le logging d'agents avant release.

Progression: 0/9

⚠ L'observability de base manque

Le systÚme sera difficile à déboguer en production. Commencez par run_id, structured logs et tracing des tool calls.

FAQ

Q : Quelle difference entre logging et tracing ?
R : Le logging repond a "que s'est-il passe" et enregistre les evenements. Le tracing montre "comment exactement" via la sequence d'etapes et leurs liens.

Q : Que logger en premier si le logging est quasi absent ?
R : Commencez par la base : run_id, trace_id, run_started, tool_call, tool_result, run_finished, stop_reason. C'est deja suffisant pour un debugging de base.

Q : Peut-on logger les prompts complets ?
R : Par defaut, mieux vaut non. En production, les prompts contiennent souvent des donnees sensibles. Plus sur : hash ou version redigee.

Q : Comment savoir si le logging est suffisant ?
R : Si vous pouvez reconstruire la sequence d'un run problematique et trouver le point de panne en 5-10 minutes, votre niveau de base fonctionne.

Pages liees

Suite du sujet :

⏱ 6 min de lecture ‱ Mis Ă  jour 9 avril 2026DifficultĂ©: ★★★
Intégré : contrÎle en productionOnceOnly
Ajoutez des garde-fous aux agents tool-calling
Livrez ce pattern avec de la gouvernance :
  • Budgets (steps / plafonds de coĂ»t)
  • Permissions outils (allowlist / blocklist)
  • Kill switch & arrĂȘt incident
  • Idempotence & dĂ©duplication
  • Audit logs & traçabilitĂ©
Mention intĂ©grĂ©e : OnceOnly est une couche de contrĂŽle pour des systĂšmes d’agents en prod.

Auteur

Nick — ingĂ©nieur qui construit une infrastructure pour des agents IA en production.

Focus : patterns d’agents, modes de dĂ©faillance, contrĂŽle du runtime et fiabilitĂ© des systĂšmes.

🔗 GitHub: https://github.com/mykolademyanov


Note éditoriale

Cette documentation est assistĂ©e par l’IA, avec une responsabilitĂ© Ă©ditoriale humaine pour l’exactitude, la clartĂ© et la pertinence en production.

Le contenu s’appuie sur des dĂ©faillances rĂ©elles, des post-mortems et des incidents opĂ©rationnels dans des systĂšmes d’agents IA dĂ©ployĂ©s.