Warum Monitoring für AI Agents anders ist
Traditionelles App-Monitoring trackt CPU, RAM, HTTP-Status-Codes. Das reicht für AI Agents nicht. Du musst zusätzlich überwachen: Token-Verbrauch (direkte Kosten), Model-Latenz (User Experience), Tool-Call-Erfolgsrate (funktioniert der Agent?) und Prompt-Qualität (halluziniert er?).
Ohne Monitoring lernst du erst beim Monatsende aus der Abrechnung, dass dein Agent 10x teurer war als geplant. Oder noch schlimmer: du merkst erst Tage später, dass er stille Fehler produziert hat.
Die 4 Monitoring-Dimensionen
💰Kosten-Tracking
Der wichtigste Metric. Claude Code Agents verbrauchen Tokens — und Tokens kosten Geld. Tracke immer: Input-Tokens, Output-Tokens, Cache-Hits (günstiger), Model (Haiku vs Sonnet vs Opus).
# Token-Kosten pro Request loggen (Node.js)
const COST_PER_TOKEN = {
'claude-haiku-4-5': { input: 0.00000025, output: 0.00000125 },
'claude-sonnet-4-6': { input: 0.000003, output: 0.000015 },
'claude-opus-4-6': { input: 0.000015, output: 0.000075 }
};
function logUsage(response, taskName) {
const { input_tokens, output_tokens, model } = response.usage;
const rates = COST_PER_TOKEN[model];
const cost = (input_tokens * rates.input) + (output_tokens * rates.output);
fs.appendFileSync('/logs/agent-costs.jsonl', JSON.stringify({
ts: Date.now(),
task: taskName,
model,
input_tokens,
output_tokens,
cost_usd: cost.toFixed(6)
}) + '\n');
// Tages-Budget-Check
if (getDailyCost() > DAILY_BUDGET_USD) {
alertHighCost(cost);
}
}
⏱️Latenz & Performance
Wie lange braucht jeder Agent-Schritt? Besonders wichtig bei interaktiven Anwendungen. Tracke: Time-to-First-Token (TTFT), Total Response Time, Tool-Call-Dauer.
# Latenz-Wrapper für jeden API-Call
async function timedApiCall(fn, label) {
const start = Date.now();
try {
const result = await fn();
logMetric({ label, duration_ms: Date.now() - start, status: 'ok' });
return result;
} catch (err) {
logMetric({ label, duration_ms: Date.now() - start, status: 'error', error: err.message });
throw err;
}
}
# Nutzung:
const response = await timedApiCall(
() => claude.messages.create({...}),
'strategy-analysis'
);
🔧Tool-Call-Erfolgsrate
Welche Tools ruft der Agent auf? Welche schlagen fehl? Eine hohe Fehlerrate bei einem bestimmten Tool zeigt einen Bug im Tool oder eine schlechte Prompt-Instruktion.
# Tool-Calls instrumentieren
function instrumentedTool(name, fn) {
return async (...args) => {
const callId = crypto.randomUUID();
const start = Date.now();
logger.info({ event: 'tool_call_start', tool: name, callId });
try {
const result = await fn(...args);
logger.info({
event: 'tool_call_success', tool: name,
callId, duration_ms: Date.now() - start
});
return result;
} catch (err) {
logger.error({
event: 'tool_call_error', tool: name,
callId, error: err.message, duration_ms: Date.now() - start
});
throw err;
}
};
}
# Alle Tools wrappen:
tools.readFile = instrumentedTool('readFile', tools.readFile);
🧠Output-Qualität
Schwierigster Metric: Liefert der Agent das richtige Ergebnis? Basics: Länge (zu kurz = truncated?), Format-Compliance (valides JSON zurückgegeben?), Konfidenz-Score wenn verfügbar.
# Strukturierten Output validieren
function validateAgentOutput(output, schema) {
const issues = [];
// Längen-Check
if (output.length < 50) issues.push('OUTPUT_TOO_SHORT');
if (output.length > 50000) issues.push('OUTPUT_TOO_LONG');
// Schema-Validation wenn erwartet
if (schema) {
try {
const parsed = JSON.parse(output);
schema.parse(parsed); // Zod schema
} catch (e) {
issues.push(`SCHEMA_VIOLATION: ${e.message}`);
}
}
if (issues.length > 0) {
logger.warn({ event: 'output_quality_issue', issues });
}
return issues.length === 0;
}
Structured Logging mit JSONL
Das wichtigste Logging-Format für Agents: JSONL (JSON Lines). Jede Zeile ist ein valides JSON — einfach anzuhängen, leicht abzufragen, kompatibel mit allen Log-Aggregations-Tools.
# Vollständiges Agent-Log-Format
{
"ts": 1746173400000,
"session_id": "sess_abc123",
"agent": "strategy-agent",
"event": "task_complete",
"task": "weekly-report",
"duration_ms": 12450,
"tokens": { "input": 4200, "output": 1800, "cache_read": 8000 },
"cost_usd": "0.0392",
"model": "claude-sonnet-4-6",
"tools_called": ["readFile", "writeFile", "searchWeb"],
"tool_errors": 0,
"output_bytes": 4200,
"status": "ok"
}
# Abfragen mit jq:
cat /logs/agent.jsonl | jq 'select(.event == "task_complete") | .cost_usd' | \
awk '{sum += $1} END {print "Total: $" sum}'
# Fehler der letzten Stunde:
cat /logs/agent.jsonl | jq 'select(.status == "error" and .ts > now - 3600)'
Alert-System: Was wann eskalieren
| Alert |
Schwelle |
Severity |
Aktion |
| Tageskosten |
> 2× Budget |
P0 |
Agent stoppen, sofort benachrichtigen |
| API-Fehlerrate |
> 10% in 5 Min |
P0 |
Retry-Limit, Slack-Alert |
| Latenz p95 |
> 30s |
P1 |
Log + prüfen, kein Auto-Stop |
| Tool-Fehlerrate |
> 20% für ein Tool |
P1 |
Telegram-Nachricht, manuell prüfen |
| Kein Output |
> 15 Min Pause |
P1 |
Heartbeat-Check, ggf. Restart |
| Schema-Violations |
> 5 in einer Stunde |
P2 |
Log-Analyse, Prompt reviewen |
# Alert-Dispatcher (Node.js)
async function sendAlert({ severity, title, details }) {
const msg = `[${severity}] ${title}\n\n${details}`;
if (severity === 'P0') {
// Telegram für sofortige Eskalation
await telegram.sendMessage({ chat_id: CHAT_ID, text: msg });
// Zusätzlich: Agent pausieren
await pauseAgent();
} else if (severity === 'P1') {
// Nur Telegram, kein Auto-Stop
await telegram.sendMessage({ chat_id: CHAT_ID, text: msg });
} else {
// P2: nur ins Log
logger.warn({ alert: title, details });
}
}
Simple Monitoring-Dashboard
Du brauchst kein Grafana. Ein minimales HTML-Dashboard das /logs/agent.jsonl auswertet reicht für den Start:
Agent Monitoring Dashboard — Live Preview
# Express-Server für Mini-Dashboard (dashboard.js)
const express = require('express');
const app = express();
app.get('/metrics', (req, res) => {
const logs = fs.readFileSync('/logs/agent.jsonl', 'utf8')
.split('\n').filter(Boolean).map(JSON.parse);
const today = logs.filter(l => l.ts > Date.now() - 86400000);
res.json({
total_cost_today: today.reduce((s, l) => s + parseFloat(l.cost_usd || 0), 0).toFixed(4),
success_rate: today.filter(l => l.status === 'ok').length / today.length,
avg_latency_ms: today.reduce((s, l) => s + (l.duration_ms || 0), 0) / today.length,
task_count: today.length
});
});
app.listen(3001);
console.log('Dashboard: http://localhost:3001/metrics');
Kosten unter Kontrolle halten
Tipp: Budget-Limiter einbauen. Setze einen harten Tages-Stop im Code — kein externes Monitoring kann so schnell reagieren wie eine im Agent eingebaute Kostenbremse.
# Budget-Guard (lädt täglich zurück)
class BudgetGuard {
constructor(dailyLimitUsd) {
this.limit = dailyLimitUsd;
this.spent = 0;
this.resetAt = this.getTomorrowMidnight();
}
check(estimatedCost) {
if (Date.now() > this.resetAt) {
this.spent = 0;
this.resetAt = this.getTomorrowMidnight();
}
if (this.spent + estimatedCost > this.limit) {
throw new Error(
`Budget exceeded: $${this.spent.toFixed(2)} of $${this.limit} used today`
);
}
this.spent += estimatedCost;
}
}
# VOR jedem API-Call:
budget.check(estimateCost(inputTokens, model));
Langzeit-Analyse mit SQLite
JSONL ist gut für kurzfristiges Monitoring. Für Trend-Analysen über Wochen/Monate: JSONL täglich in SQLite importieren.
# JSONL → SQLite (täglich per Cron)
import sqlite3, json
def import_logs(jsonl_path, db_path):
conn = sqlite3.connect(db_path)
conn.execute('''CREATE TABLE IF NOT EXISTS agent_logs (
ts INTEGER, agent TEXT, task TEXT,
cost_usd REAL, tokens_total INTEGER,
duration_ms INTEGER, status TEXT
)''')
with open(jsonl_path) as f:
for line in f:
log = json.loads(line)
conn.execute('INSERT INTO agent_logs VALUES (?,?,?,?,?,?,?)', [
log['ts'], log['agent'], log.get('task'),
float(log.get('cost_usd', 0)),
log.get('tokens', {}).get('input', 0) + log.get('tokens', {}).get('output', 0),
log.get('duration_ms'), log.get('status')
])
conn.commit()
# Wöchentliche Kostenentwicklung abfragen:
# SELECT strftime('%Y-W%W', ts/1000, 'unixepoch') week, SUM(cost_usd) FROM agent_logs GROUP BY week
Vorsicht: Keine Secrets in Logs! Niemals API Keys, Passwörter oder PII in Agent-Logs schreiben. Truncate lange Tool-Inputs auf 200 Zeichen, maskiere Patterns wie sk-ant-... oder Bearer ....
Integration in bestehende Monitoring-Stacks
Wenn du bereits Grafana, Datadog oder ähnliches nutzt: JSONL-Logs lassen sich einfach integrieren.
# Prometheus-Metrics-Endpoint (für Grafana)
const client = require('prom-client');
const agentCost = new client.Counter({
name: 'agent_cost_usd_total',
help: 'Total cost in USD',
labelNames: ['agent', 'model']
});
const taskDuration = new client.Histogram({
name: 'agent_task_duration_ms',
help: 'Task duration in milliseconds',
buckets: [1000, 5000, 15000, 30000, 60000]
});
# Nach jedem Task:
agentCost.inc({ agent: 'strategy', model }, cost);
taskDuration.observe(duration_ms);
Monitoring-Templates im Kurs
Im Claude Code Mastery Kurs findest du fertige Monitoring-Templates: Budget-Guard, JSONL-Logger, Mini-Dashboard und Alerting für Telegram — direkt einsetzbar für deinen Agent-Stack.
14 Tage kostenlos testen →