Agent-Observability: Hooks, Alloy und Grafana
> 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, bashmcp_server: welches MCP-Backend den Call bearbeitet hatbash_cli: die Shell-Befehlsfamiliesubagent_type: welche Art von Spezialist eingesetzt wurdeagent_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_idprompt_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:
- Hält eine sehr kleine Menge Low-Cardinality-Labels indiziert.
- Verschiebt verrauschte, aber nützliche Felder in strukturierte Metadaten.
- 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.
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:
- Agent-Aktivität erzeugt Traces, Metriken und angereicherte Hook-Logs.
- Grafana speichert die Evidenz in Loki, Tempo und Prometheus, wo Metriken existieren.
- Agenten fragen diese Evidenz über Grafana MCP ab.
- Das System identifiziert schlechte Tool-Mischungen, brüchige Skills, schwaches Routing oder shell-lastige Workflows, die weiterhin vermeidbare Fehler produzieren.
- Agenten oder Operatoren passen Prompts, Agent-Configs, Skill-Beschreibungen, Routing-Regeln oder Tool-Zugriff an.
- 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
Warum wir unsere Code-Suche auf voyage-4-large upgegradet haben_
Wir haben unsere Code-Embeddings auf voyage-4-large umgestellt — aktuell die Nummer eins im öffentlichen RTEB-Code-Retrieval-Leaderboard. Die ehrliche Version: der Trade-off, den wir eingehen, was wir tatsächlich indizieren, und warum wir für Premium-Embeddings bezahlen.
Language Recursive Self-Improvement: Code Intelligence über ~280 Sprachen hinweg grinden_
Wir unterstützen Code Intelligence für ~280 Sprachen. Das kann kein Mensch von Hand auditieren. Also haben wir eine Language-Recursive-Self-Improvement-Loop gebaut — Stichprobe, LLM-as-Judge, eine Sache reparieren, erneut validieren — und lassen sie mit einer Flotte isolierter Agenten laufen, bis die Extraktion tatsächlich stimmt, nicht nur grün ist.
Multi-Modal Fusion Search: Für jede Query den richtigen Retriever wählen_
Eine Query wie 'wo ist parseConfig definiert' braucht eine andere Suche als 'wie funktioniert Auth'. Maguyva klassifiziert die Intention, gewichtet vier Retrieval-Modalitäten entsprechend und fusioniert die Ergebnisse mit gewichteter Reciprocal Rank Fusion.