Normal path: execute → tool → observe.
Problem (aus der Praxis)
Nichts hat sich geändert.
Außer:
- jemand hat einen Prompt “minimal” geändert
- ein Tool liefert ein neues Feld
- Model Version wurde gebumpt
- Retrieval Index updated
Der Agent “funktioniert” noch.
Aber er ist langsamer. Callt andere Tools. Trifft andere Decisions. Verpasst Edge Cases. Niemand merkt es, bis ein User es merkt.
Das ist silent drift: Verhalten ändert sich, ohne dass es sofort als Failure auffällt.
Warum das in Production bricht
1) Model Output ist nicht stabil
Selbst ohne Version Changes gibt’s Varianz. Mit Version Changes shiftet es garantiert.
Ohne Messung siehst du Drift nicht.
2) Tools driften auch
Tool Outputs ändern sich:
- schema
- error payloads
- ordering
- defaults
Agents sind oft sensitiv → Drift.
3) Prompts sind Code (werden aber nicht so behandelt)
Prompt edits shippen ohne:
- Tests
- Rollbacks
- Canary
- Metrics
Dann hast du “wir haben einen Satz geändert und jetzt callt er http.get 10× öfter”.
4) Drift zeigt sich als Cost/Latenz vor Correctness
Early Warnings:
- tokens/request
- tool calls/run
- p95 latency
- stop reasons
Wenn du nur success rate anschaust, verpasst du’s.
5) Fix = Feedback Loop: Golden Tasks + Replay + Canary
Production-shaped eval loop:
- golden tasks (real workload)
- replay von real traces (redacted)
- canary rollout für model/prompt/tool changes
- alerting auf behavior deltas
Implementierungsbeispiel (echter Code)
Minimal Golden Tasks Harness:
- baseline vs candidate
- compare stop reasons und tool calls
- fail bei zu großer Delta
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);
}
}
}Crude, aber fängt das häufigste Drift:
- stop reasons ändern sich (new timeouts/loops)
- tool-call inflation (cost drift)
Dann addest du correctness checks. Aber starte mit operational drift — einfacher zu messen, oft erstes Signal.
Echter Incident (mit Zahlen)
Wir upgraded eine Model Version für einen Support Agent. Kein Canary, keine Golden Tasks.
Neues Modell war “gründlicher” und callte search.read öfter.
Impact über 24 Stunden:
- tool calls/run: 2.8 → 9.6
- p95 latency: 2.7s → 7.4s
- spend: +$460 vs baseline
- correctness drop war nicht obvious → niemand merkte es bis zur Rechnung
Fix:
- golden tasks mit drift thresholds
- canary rollout (1%) + auto-rollback
- replay redacted traces wöchentlich
- dashboards: tokens, tool calls, stop reasons, latency
Drift ist nicht spannend. Es ist wie Prod bricht, wenn keiner hinschaut.
Abwägungen
- Golden Task Suites brauchen Pflege.
- Canary addet Rollout Komplexität (wert).
- Manche Drift ist “gut” (bessere Answers). Trotzdem messen, um zu entscheiden.
Wann du es NICHT nutzen solltest
- Low-stakes informational agents: du kannst lockerer sein (spend trotzdem monitoren).
- Wenn Task Distribution noch nicht stabil ist: smoke tests starten, golden tasks wachsen lassen.
- Wenn Replay wegen PII schwierig ist: synthetic tasks + strikte budgets.
Checkliste (Copy/Paste)
- [ ] Golden tasks für real workload
- [ ] Replay set aus real traces (redacted)
- [ ] Canary rollout + rollback triggers
- [ ] Drift thresholds: tool calls, tokens, latency, stop reasons
- [ ] Versions pinned per release
- [ ] Wöchentliches “what changed” Review
Sicheres Default-Config-Snippet (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)
Von Patterns genutzt
Verwandte Failures
Q: Ist Drift immer schlecht?
A: Nein. Aber ungemessener Drift ist schlecht. Ohne Metrics weißt du nicht ob’s “besser” oder nur teurer/langsamer ist.
Q: Was monitore ich zuerst?
A: Tool calls/run, tokens/request, latency p95, stop reasons. Die bewegen sich vor Correctness-Complaints.
Q: Canary für jeden Prompt Edit?
A: Für high-traffic/high-stakes Agents: ja. Prompts wie Code behandeln.
Q: Wie replaye ich Prod Traces sicher?
A: PII redaction, args hashes statt raw args, tool results aus Snapshots.
Verwandte Seiten (3–6 Links)
- Foundations: Warum Agents scheitern · LLM-Limits
- Failure: Token Overuse · Budget Explosion
- Governance: Tool Permissions
- Production stack: Production Stack