Normal path: execute → tool → observe.
Le problème (côté prod)
Rien n’a changé.
Sauf :
- un prompt “un peu”
- un tool output qui a un nouveau champ
- une version de modèle
- un index de retrieval
L’agent “marche”. Mais différemment. Et tu ne le vois pas tout de suite.
Pourquoi ça casse en prod
- output modèle non stable
- tools qui driftent
- prompts traités comme du texte, pas comme du code
- drift visible d’abord sur coût/latence/tool calls, pas sur “success rate”
Fix : golden tasks + replay + canary + alerting sur deltas.
Exemple d’implémentation (code réel)
Harness minimal de drift (baseline vs candidate) :
from dataclasses import dataclass
@dataclass(frozen=True)
class GoldenTask:
id: str
input: str
def run_agent(version: str, task: GoldenTask) -> dict:
return agent_run(version=version, input=task.input) # (pseudo)
def score(run: dict) -> dict:
return {
"stop_reason": run.get("stop_reason"),
"tool_calls": int(run.get("tool_calls", 0)),
"tokens": int(run.get("tokens_total", 0)),
}
def drift_check(tasks: list[GoldenTask], *, baseline: str, candidate: str) -> None:
for t in tasks:
b = score(run_agent(baseline, t))
c = score(run_agent(candidate, t))
if c["stop_reason"] != b["stop_reason"]:
raise RuntimeError(f"[{t.id}] stop_reason drift: {b['stop_reason']} -> {c['stop_reason']}")
if c["tool_calls"] > b["tool_calls"] + 3:
raise RuntimeError(f"[{t.id}] tool_calls drift: {b['tool_calls']} -> {c['tool_calls']}")export function score(run) {
return {
stopReason: run.stop_reason,
toolCalls: Number(run.tool_calls || 0),
tokens: Number(run.tokens_total || 0),
};
}
export function driftCheck(tasks, { baseline, candidate, runAgent }) {
for (const t of tasks) {
const b = score(runAgent(baseline, t));
const c = score(runAgent(candidate, t));
if (c.stopReason !== b.stopReason) {
throw new Error("[" + t.id + "] stop_reason drift: " + b.stopReason + " -> " + c.stopReason);
}
if (c.toolCalls > b.toolCalls + 3) {
throw new Error("[" + t.id + "] tool_calls drift: " + b.toolCalls + " -> " + c.toolCalls);
}
}
}Incident réel (avec chiffres)
Upgrade de modèle sur un agent support. Pas de canary, pas de golden tasks.
Le nouveau modèle était “plus thorough” et appelait search.read plus souvent.
Impact (24h) :
- tool calls/run : 2.8 → 9.6
- latence p95 : 2.7s → 7.4s
- spend : +$460
Fix :
- golden tasks avec seuils de drift (tool calls, stop reasons)
- canary 1% + auto-rollback sur spikes
- replay de traces réelles redacted
- dashboards (tokens, tool calls, stop reasons, latence)
Compromis
- maintenance des golden tasks
- complexité canary/rollback
- drift peut être “bon”, mais il faut le mesurer pour décider
Quand NE PAS l’utiliser
- low-stakes : tu peux être plus souple (surveille le spend quand même)
- distrib de tâches instable : commence par smoke tests
- replay impossible (PII) : tâches synthétiques + budgets stricts
Checklist (copier-coller)
- [ ] Golden tasks “réalistes”
- [ ] Replay set redacted
- [ ] Canary + rollback triggers
- [ ] Seuils drift (tool calls/tokens/latence/stop reasons)
- [ ] Versions pin
- [ ] Revue “what changed” régulière
Config par défaut sûre (JSON/YAML)
releases:
canary_percent: 1
rollback_on:
tool_calls_per_run_increase_pct: 50
tokens_per_request_increase_pct: 50
latency_p95_increase_pct: 50
eval:
golden_tasks_required: true
drift_thresholds:
tool_calls_delta: 3
stop_reason_changes: 0
FAQ (3–5)
Utilisé par les patterns
Pannes associées
Gouvernance requise
Q : La dérive est toujours mauvaise ?
R : Non. Mais la dérive non mesurée est mauvaise. Sans métriques, tu ne sais pas si c’est mieux ou juste plus cher.
Q : Quoi monitorer en premier ?
R : Tool calls/run, tokens/request, latence p95, stop reasons. Ça bouge avant les plaintes.
Q : Canary pour chaque edit de prompt ?
R : Pour du high-stakes/high-traffic : oui. Les prompts, c’est du code.
Q : Replay de traces en sécurité ?
R : Redaction PII, args hash quand possible, et snapshots des tool outputs.
Pages liées (3–6 liens)
- Foundations: Pourquoi les agents échouent · Limites des LLM et agents
- Failure: Incidents tokens · Budget explosion
- Governance: Tool permissions
- Production stack: Production stack