Zum Inhalt springen
cd /blog

Agent-Observability: Hooks, Alloy und Grafana

[Observability][Grafana][OpenTelemetry][Architektur]

> Wir haben Claude Code und Codex mit OpenTelemetry und Alloy in einen gemeinsamen Grafana-Stack eingebunden und dann mit Traces und Logs Probleme im Agent-Verhalten direkt an der Quelle gefunden und behoben.

Agent-Systeme versagen auf seltsame Weise.

Manchmal ist das Modell das Problem. Manchmal ist das Tool das Problem. Manchmal ist dein MCP-Server völlig in Ordnung, aber der Agent hat den falschen Spezialisten gewählt, oder die halbe Session mit Shell-Arbeit verbracht, die du nicht erwartet hast, oder stillschweigend Kosten in einer Schleife verbrannt, die von außen produktiv aussah.

Wenn du den Unterschied nicht sehen kannst, betreibst du kein Agent-System — du rätst.

Deshalb haben wir einen Observability-Stack für unseren eigenen Workflow gebaut: Claude Code, Codex, Claude-Hook-Events, Codex-Notify-Events, natives OpenTelemetry, Grafana Alloy und auf der anderen Seite Grafana Cloud.

Der interessante Teil ist nicht „wir haben ein Dashboard gebaut“. Der interessante Teil ist, dass wir Telemetrie in zwei unterschiedliche Streams aufteilen mussten, weil kein einzelner Feed das ganze Bild lieferte.

Das Problem: Agent-Telemetrie ist fragmentiert

Moderne Coding-Agenten senden bereits etwas Telemetrie. Das hilft, reicht aber nicht.

Natives OTEL beantwortet Fragen gut wie:

  • Wie viele Requests haben wir gemacht?
  • Wie viel hat eine Session gekostet?
  • Wo liegen die Spans und Traces?
  • Gab es einen Latenz-Spike?

Es ist deutlich schlechter darin, Fragen zu beantworten wie:

  • Auf welchen MCP-Server hat sich der Agent gestützt?
  • Lag dieser Fehler in Bash, einem eingebauten Datei-Tool oder einem MCP-Call?
  • Welcher Skill wurde tatsächlich aktiviert?
  • Welcher Subagent-Typ wurde eingesetzt?
  • Hat die Session sinnvolle Arbeit geleistet oder nur im Kreis gedreht?

Diese zweite Klasse von Fragen liegt näher an Hooks als an Traces.

Aber das Umgekehrte gilt auch: Einige der wichtigsten Performance-Fragen liegen näher an Traces als an Hooks.

Wenn du wissen willst, wo sich Latenz tatsächlich angesammelt hat, welche Spans langsam waren, oder ob die Session Zeit in Modellaufrufen statt in Tool-Ausführung verbrannt hat, brauchst du Trace-Daten genauso wie semantische Events.

Die Architektur, bei der wir gelandet sind

Wir betreiben zwei Telemetrie-Pfade parallel.

Claude Code
  native OTEL -> Alloy -> Grafana Cloud
  hooks        -> send_event.py -> Grafana Cloud Loki

Codex
  native OTEL -> Alloy -> Grafana Cloud
  notify hook -> codex_notify.py -> shared Loki schema

Diese Trennung ist bewusst.

Sie ist auch asymmetrisch. Claude Code liefert uns eine deutlich reichhaltigere Lifecycle-Hook-Oberfläche. Codex liefert uns natives OTEL plus eine Notify-Oberfläche, weshalb wir dünnere Turn-Completion-Events in dasselbe Log-Schema normalisieren, statt so zu tun, als würden beide Runtimes dieselben Kontrollmöglichkeiten bieten.

Natives OTEL liefert uns den Basis-Stream: Logs und Traces direkt aus der Runtime, plus Metriken, wo die Runtime sie tatsächlich ausgibt.

Hook- und Notify-Events liefern uns die semantische Schicht: Dinge wie PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, SubagentStop, SkillActivated und die klassifizierten Metadaten, die uns beim Debuggen von Agent-Verhalten tatsächlich interessieren. Claude Code liefert hier den reichhaltigeren Event-Stream. Codex liefert einen dünneren, aber immer noch nützlichen normalisierten Stream.

Warum es Hooks überhaupt gibt

Unsere Hook-Pipeline reichert Events an, bevor sie bei Loki ankommen.

Statt nur zu sagen „ein Tool ist gelaufen“, klassifizieren wir das Event in Felder wie:

  • tool_type: builtin, mcp, skill, agent, bash
  • mcp_server: welches MCP-Backend den Call bearbeitet hat
  • bash_cli: die Shell-Befehlsfamilie
  • subagent_type: welche Art von Spezialist eingesetzt wurde
  • agent_tool: ob die Quelle Claude Code oder Codex war

Das bedeutet, wir können Fragen stellen, die operativ wichtig sind:

{service_name="claude-code-hooks"} | agent_tool="codex-cli"
{service_name="claude-code-hooks"} | tool_type="mcp"
{service_name="claude-code-hooks"} | json | bash_cli="git"

Das sind keine Eitelkeitsfelder. Sie sind der Unterschied zwischen „der Agent fühlte sich langsam an“ und „der Agent hat die letzten zehn Minuten in shell-lastigen Git-Operationen mit hoher Tool-Fehlerrate verbracht“.

Ein Implementierungsdetail, das seltsamer wirkt, als es ist: Der gemeinsame Loki-Stream verwendet weiterhin service_name="claude-code-hooks" als Label, selbst wenn das Event von Codex stammt. Die eigentliche Trennung zwischen den Runtimes passiert bei agent_tool.

Warum Alloy in der Mitte sitzt

Grafana Alloy ist in diesem Setup nicht nur ein Forwarder. Es ist die Policy-Grenze.

Wir richten den nativen OTEL-Stream von Claude Code und Codex auf einen lokalen Alloy-Proxy auf localhost:4318, und lassen Alloy dann den Payload bereinigen, bevor er an Grafana Cloud weitergeleitet wird.

Das ist wichtig, weil rohe Agent-Telemetrie voller High-Cardinality-Felder ist, die für die Analyse nützlich, aber als indizierte Labels furchtbar sind:

  • session_id
  • prompt_id
  • Token-Zahlen
  • Dauern
  • Werkzeugparameter-Pakete

Wenn du alles indizierst, bekommst du eine Label-Explosion und einen schlechten Tag.

Alloy erledigt für uns also drei Dinge:

  1. Hält eine sehr kleine Menge Low-Cardinality-Labels indiziert.
  2. Verschiebt verrauschte, aber nützliche Felder in strukturierte Metadaten.
  3. Verwirft reines Rauschen vollständig.

Die wichtige Idee ist einfach: mehr beobachten, weniger indizieren.

Warum der Hook-Stream Alloy umgeht

Der Hook-Stream ist bereits für Loki zugeschnitten.

Wenn send_event.py ein Event pusht, haben wir bereits entschieden, welche Felder eine Label-Behandlung verdienen und welche in den strukturierten JSON-Body gehören. Dieser Stream geht direkt zum OTLP-Gateway von Grafana Cloud, statt noch einmal durch Alloy zu laufen.

Das System hat also eine klare Arbeitsteilung:

  • Alloy bändigt den rohen nativen OTEL-Stream.
  • Hook-Anreicherung macht semantische Events abfragbar.

Das hält die Architektur einfacher, als zu versuchen, alles durch einen einzigen Pfad zu zwingen.

Was das Dashboard tatsächlich zeigt

Der Screenshot unten stammt von einem der Observability-Dashboards hinter unserem Agent-Workflow. Es ist kein Benchmark, und die Zahlen sind nur eine Momentaufnahme. Der Punkt ist die Form der Daten: Activity Feed, Tool Calls, Fehler, Prompts und Aufschlüsselungen nach Agent, eingebauten Tools, MCP-Nutzung, Shell-Befehlen und Skills.

Der nützliche Teil ist, dass dieses Dashboard auf demselben Grafana-Stack lebt wie der Rest der Agent-Telemetrie. Wir können nach Quell-Agent und Tool-Familie filtern und über Runtimes hinweg schauen, ohne für jedes System eine eigene Observability-Geschichte zu erfinden.

Grafana-Dashboard mit Activity Feed, Tool-Call-Zählungen, Fehlern, Prompts und Aufschlüsselungen nach Agenten, eingebauten Tools, MCP-Nutzung, CLI-Befehlen und Skills für einen Agent-Workflow.
Eines der Live-Dashboards hinter unserem Agent-Workflow. Dieser Screenshot ist nur ein Ausschnitt eines gemeinsamen Grafana-Stacks, der auch Telemetrie von unseren anderen Agent-Runtimes und -Systemen empfängt. Klicke auf das Bild für die Version in voller Auflösung.

Warum Traces wichtiger sind, als sie zunächst wirken

Logs sagen uns, welche Kategorie von Arbeit stattgefunden hat. Traces sagen uns, wie sich die Arbeit über die Zeit entfaltet hat.

Dieser Unterschied ist in Agent-Systemen wichtig, weil „langsam“ zu stumpf ist, um nützlich zu sein.

Ein Trace kann uns sagen, ob der Schmerz kam von:

  • Modell-Latenz
  • Tool-Ausführungszeit
  • wiederholten Retries
  • einer besonders teuren MCP-Interaktion
  • einem langen Schwanz kleiner Operationen, die isoliert betrachtet harmlos aussahen

In der Praxis nutzen wir den Hook-Stream und Tempo-Traces zusammen.

  • Hook-Logs beantworten: Was für eine Art von Sache ist passiert?
  • Traces beantworten: Wo ist die Zeit hingegangen?

Die Kombination ist das, was Observability von einem Dashboard in eine Erklärung verwandelt.

Wo Codex hineinpasst

Codex ist Teil desselben Stacks, aber nicht identisch mit Claude Code.

Für Codex verdrahten wir zwei Teile:

  • Natives OTEL von Codex in Alloy
  • Ein Notify-Webhook in codex_notify.py, der Turn-Completions auf dasselbe Loki-Schema abbildet, das wir für Hook-Events verwenden

Das gibt uns einen einheitlichen Filter wie agent_tool="codex-cli" innerhalb desselben Log-Streams.

Der ehrliche Vorbehalt: Codex’ Notify-Payload ist derzeit dünner als Claude Codes Hook-Payload, weil es nicht dieselbe Art von Integrationsoberfläche ist. In unserem heutigen Setup können Codex-Turn-Completions in das gemeinsame Schema normalisiert werden, aber eine reichhaltige Tool-für-Tool-Extraktion ist im nativen OTEL-Stream immer noch besser als in der Notify-Bridge.

Das ist kein Grund, den Post zu meiden. Es ist der Punkt des Posts. Echte Observability-Systeme werden aus unperfekten Signalen zusammengesetzt.

Grafana über MCP verändert das Spiel

Die größere Verschiebung ist, dass Grafana nicht nur ein Ort ist, den Menschen im Browser besuchen.

In diesem Repo exponieren wir Grafana auch über MCP. Das bedeutet, ein Agent kann Loki, Prometheus und Tempo direkt abfragen, statt darauf zu warten, dass ein Mensch zuerst manuell die Dashboards inspiziert.

Das macht Observability zu einem aktiven Input für den Workflow.

Ein Agent kann fragen:

  • Welche Tool-Familien sind in der letzten Stunde am häufigsten fehlgeschlagen?
  • Welcher MCP-Server hat eine Session dominiert?
  • Haben aktuelle Änderungen Tool-Fehler reduziert, oder die Arbeit nur in shell-lastigere Pfade mit denselben Fehlern verschoben?
  • Welche Traces zeigen die höchste Latenz oder wiederholte Retries?

Sobald du das hast, bist du sehr nah an einer Self-Improvement-Loop.

Vom Dashboard zur Feedback-Loop

Das ist der Teil, den wir am interessantesten finden.

Sobald der Observability-Stack von der Agent-Schicht aus abfragbar ist, hört Telemetrie auf, eine passive Reporting-Oberfläche zu sein, und wird zu einem Kontrollsignal.

Die Loop sieht so aus:

  1. Agent-Aktivität erzeugt Traces, Metriken und angereicherte Hook-Logs.
  2. Grafana speichert die Evidenz in Loki, Tempo und Prometheus, wo Metriken existieren.
  3. Agenten fragen diese Evidenz über Grafana MCP ab.
  4. Das System identifiziert schlechte Tool-Mischungen, brüchige Skills, schwaches Routing oder shell-lastige Workflows, die weiterhin vermeidbare Fehler produzieren.
  5. Agenten oder Operatoren passen Prompts, Agent-Configs, Skill-Beschreibungen, Routing-Regeln oder Tool-Zugriff an.
  6. Die nächste Session erzeugt eine neue Telemetrie-Form, und der Zyklus wiederholt sich.

So kommt man von „interessantes Dashboard“ zu „messbares Verbesserungssystem“.

Das Ziel ist nicht, eine Tool-Kategorie zu maximieren. Es ist, für die tatsächlich geleistete Arbeit auf die richtige Mischung aus CLI, eingebauten Tools, MCP-Calls und Skills zu kommen.

Was uns das zu beantworten erlaubt

Sobald beide Runtimes im selben Grafana-Stack landen, können wir operative Fragen viel schneller beantworten:

  • Konzentrieren sich Fehler auf eine Tool-Familie?
  • Erzeugen shell-lastige Workflows vermeidbare Fehler, wo eigentlich ein High-Level-Tool existieren sollte?
  • Welche MCP-Server tragen die Arbeitslast?
  • Zahlen wir für Agent-Aktivität, die keinen sinnvollen Fortschritt erzeugt?
  • Ist eine Session ungesund wegen des Modells, der Tools oder der Orchestrierungsschicht?

Das ist besonders nützlich in Multi-Agent-Workflows, wo „der Agent war beschäftigt“ fast nichts aussagt.

Wenn ein Spezialist ständig eingesetzt wird und hohe Fehlerraten produziert, ist das ein Routing- oder Prompt-Shaping-Problem.

Wenn ein MCP-Server alle Calls dominiert, kann das gute Architektur sein oder ein Zeichen, dass alles andere totes Gewicht ist.

Wenn Tool-Fehler ansteigen, während die Kosten hoch bleiben, hast du ein operatives Problem, kein Qualitätsproblem.

Wenn Shell-Arbeit weiterhin auf vorhersehbare, vermeidbare Weise fehlschlägt, wo eigentlich ein High-Level-Tool existieren sollte, ist das ein Produktsignal.

Wenn ein Skill ständig aktiviert wird, aber die Ergebnisse nicht verbessert, ist das ein Prompt- oder Routing-Signal.

Die eigentliche Lektion

Die tiefere Lektion hier ist, dass Agent-Observability sowohl Runtime-Telemetrie als auch Workflow-Telemetrie braucht.

Runtime-Telemetrie sagt dir, was das System getan hat.

Workflow-Telemetrie sagt dir, was der Agent dachte, dass er tut.

Wir brauchen beides.

Wenn du nur Traces und Zähler behältst, verpasst du die semantische Schicht. Wenn du nur Hook-Events behältst, verpasst du Latenz, Spans und das größere Runtime-Bild.

Und wenn du beides behältst, sie aber nie in die Agent-Schicht zurückspielst, hast du Monitoring, keine Anpassung.

Die Kombination ist das, was das System erklärbar genug macht, um es zu betreiben, und feinjustierbar genug, um es zu verbessern.

Was noch unperfekt ist

Es gibt immer noch raue Kanten.

  • Nicht jedes Hook-Event enthält die Dauer- und Token-Daten, die wir gerne hätten.
  • Einige der besten Timing-Ansichten kommen immer noch von Tempo-Traces, nicht von den Hook-Logs.
  • Codex ist heute im angereicherten Event-Stream semantisch weniger reichhaltig als Claude Code.
  • Der Dashboard-Screenshot ist eine echte Betriebsoberfläche, kein poliertes Marketing-Artefakt.

Der letzte Punkt ist beabsichtigt. Wir zeigen lieber das echte Instrumentenbrett, als so zu tun, als würden sich Agent-Systeme magisch von selbst erklären.

Warum das für Maguyva wichtig ist

Bei Maguyva geht es darum, Agenten bessere Code Intelligence zu geben. Aber sobald Agenten tatsächlich nützliche Arbeit leisten, taucht sofort eine neue Anforderung auf: Du musst sehen, wie sie sich verhalten.

Suchqualität, Routing-Qualität, Tool-Auswahl und Context-Effizienz werden allesamt zu beobachtbaren Problemen.

Deshalb glauben wir, dass es sich lohnt, darüber zu schreiben. Der zukünftige Agent-Stack besteht nicht nur aus Prompts und Tools. Es sind Prompts, Tools und die Instrumentierungsschicht, die dir sagt, ob das Ganze funktioniert.

Wenn du ernsthafte Agent-Workflows baust, ist Observability keine optionale Infrastruktur. Sie ist Teil des Produkts.

Weiterführende Artikel

Mehr aus dem Maguyva-Buildlog