Agentobservabilitet: hooks, Alloy och Grafana
> Vi kopplade in Claude Code och Codex i en gemensam Grafana-stack med OpenTelemetry och Alloy, och använde sedan spårningar och loggar för att hitta och åtgärda agentbeteenden vid källan.
Agentsystem kraschar på märkliga sätt.
Ibland är modellen problemet. Ibland är verktyget problemet. Ibland fungerar din MCP-server perfekt, men agenten valde fel specialist, eller lade halva sessionen på skalarbete du inte förväntade dig, eller brände kostnad tyst i en loop som såg produktiv ut utifrån.
Om du inte kan se skillnaden driver du egentligen inte ett agentsystem. Du gissar.
Så vi byggde en observabilitetsstack för vårt eget arbetsflöde: Claude Code, Codex, Claude-hookhändelser, Codex-notify-händelser, inbyggd OpenTelemetry, Grafana Alloy och Grafana Cloud på andra sidan.
Det intressanta är inte “vi byggde en instrumentpanel”. Det intressanta är att vi var tvungna att dela upp telemetrin i två separata strömmar, eftersom ingen enskild källa gav oss hela bilden.
Problemet: agenttelemetri är fragmenterad
Moderna kodningsagenter avger redan viss telemetri. Det hjälper, men det räcker inte.
Inbyggd OTEL är bra på att besvara frågor som:
- Hur många förfrågningar gjorde vi?
- Vad kostade en session?
- Var finns spannen och spårningarna?
- Ökade latensen plötsligt?
Den är betydligt sämre på att besvara frågor som:
- Vilken MCP-server lutade agenten sig mot?
- Låg det här felet i
Bash, ett inbyggt filverktyg, eller ett MCP-anrop? - Vilken skill aktiverades egentligen?
- Vilken typ av subagent dirigerades ut?
- Utförde sessionen användbart arbete, eller bara stampade den på stället?
Den andra kategorin av frågor ligger närmare hooks än spårningar.
Men motsatsen gäller också: några av de viktigaste prestandafrågorna ligger närmare spårningar än hooks.
Om du vill veta var latensen faktiskt ackumulerades, vilka spann som var långsamma, eller om sessionen brände tid på modellanrop kontra verktygsexekvering, behöver du spårningsdata lika väl som semantiska händelser.
Arkitekturen vi landade i
Vi kör två telemetrivägar parallellt.
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
Den uppdelningen är avsiktlig.
Den är också asymmetrisk. Claude Code ger oss en mycket rikare yta av livscykelhooks. Codex ger oss inbyggd OTEL plus en notify-yta, så vi normaliserar tunnare turnavslutningshändelser till samma loggschema istället för att låtsas att båda körtiderna exponerar samma kontroller.
Inbyggd OTEL ger oss basströmmen: loggar och spårningar från själva körtiden, plus mått där körtiden faktiskt avger dem.
Hook- och notify-händelser ger oss det semantiska lagret: sådant som PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, SubagentStop, SkillActivated, och den klassificerade metadata vi faktiskt bryr oss om när vi felsöker agentbeteende. Claude Code bidrar med den rikare händelseströmmen här. Codex bidrar med en tunnare men fortfarande användbar normaliserad ström.
Varför hooks finns över huvud taget
Vår hook-pipeline berikar händelser innan de når Loki.
Istället för att bara säga “ett verktyg kördes” klassificerar vi händelsen i fält som:
tool_type: builtin, mcp, skill, agent, bashmcp_server: vilken MCP-backend som hanterade anropetbash_cli: skalkommandots familjsubagent_type: vilken typ av specialist som dirigerades utagent_tool: om källan var Claude Code eller Codex
Det betyder att vi kan ställa frågor som spelar roll operativt:
{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"
Det där är inte skrytfält. De är skillnaden mellan “agenten kändes långsam” och “agenten spenderade de senaste tio minuterna i skaltunga git-operationer med hög andel verktygsfel”.
En implementationsdetalj som ser konstigare ut än den är: den delade Loki-strömmen använder fortfarande service_name="claude-code-hooks" som etikett även när händelsen kom från Codex. Den verkliga uppdelningen mellan körtiderna sker på agent_tool.
Varför Alloy sitter i mitten
Grafana Alloy är inte bara en vidarebefordrare i den här uppsättningen. Det är policygränsen.
Vi pekar den inbyggda OTEL-strömmen från Claude Code och Codex mot en lokal Alloy-proxy på localhost:4318, och låter sedan Alloy städa upp payloaden innan den vidarebefordras till Grafana Cloud.
Det spelar roll eftersom rå agenttelemetri är full av högkardinalitetsfält som är användbara för analys men förskräckliga som indexerade etiketter:
session_idprompt_id- tokenantal
- varaktigheter
- blobbar med verktygsparametrar
Om du indexerar allt får du en etikettexplosion och en dålig dag.
Så Alloy gör tre saker åt oss:
- Håller en mycket liten uppsättning lågkardinalitetsetiketter indexerade.
- Flyttar brusiga men användbara fält till strukturerad metadata.
- Slänger ren brus helt och hållet.
Den viktiga idén är enkel: observera mer, indexera mindre.
Varför hookströmmen går förbi Alloy
Hookströmmen är redan formad för Loki.
När send_event.py skickar en händelse har vi redan bestämt vilka fält som förtjänar etikettbehandling och vilka som hör hemma i den strukturerade JSON-kroppen. Den strömmen går rakt till Grafana Clouds OTLP-gateway istället för att ta ytterligare ett varv genom Alloy.
Systemet har alltså en tydlig arbetsfördelning:
- Alloy tämjer den råa inbyggda OTEL-strömmen.
- Hook-berikning gör semantiska händelser sökbara.
Det håller arkitekturen enklare än att försöka tvinga allt genom en enda väg.
Vad instrumentpanelen faktiskt visar
Skärmdumpen nedan är från en av observabilitetspanelerna bakom vårt agentarbetsflöde. Det är inte ett benchmark, och siffrorna är bara ett ögonblicksutsnitt. Poängen är formen på datan: aktivitetsflöde, verktygsanrop, fel, prompter, och nedbrytningar efter agent, inbyggda verktyg, MCP-användning, skalkommandon och skills.
Det användbara är att den här panelen lever på samma Grafana-stack som resten av agenttelemetrin. Vi kan filtrera på källagent och verktygsfamilj och titta över körtiderna utan att behöva uppfinna en separat observabilitetsberättelse för varje system.
Varför spårningar spelar större roll än de först verkar
Loggar berättar vilken kategori av arbete som skedde. Spårningar berättar hur arbetet utspelade sig över tid.
Den distinktionen spelar roll i agentsystem eftersom “långsamt” är för trubbigt för att vara användbart.
En spårning kan berätta om smärtan kom från:
- modellatens
- verktygsexekveringstid
- upprepade återförsök
- en särskilt kostsam MCP-interaktion
- en lång svans av små operationer som såg ofarliga ut var för sig
I praktiken använder vi hookströmmen och Tempo-spårningar tillsammans.
- Hook-loggar svarar på: vilken typ av sak hände?
- Spårningar svarar på: vart tog tiden vägen?
Kombinationen är det som förvandlar observabilitet från en instrumentpanel till en förklaring.
Var Codex passar in
Codex är en del av samma stack, men det är inte identiskt med Claude Code.
För Codex kopplar vi in två delar:
- Inbyggd OTEL från Codex till Alloy
- En notify-webhook till
codex_notify.py, som mappar turnavslutningar till samma Loki-schema vi använder för hook-händelser
Det ger oss ett enhetligt filter som agent_tool="codex-cli" inom samma loggström.
Den ärliga brasklappen: Codex notify-payload är i dagsläget tunnare än Claude Codes hook-payload, eftersom det inte är samma typ av integrationsyta. I vår nuvarande uppsättning kan Codex turnavslutningar normaliseras till det delade schemat, men rik verktyg-för-verktyg-extraktion är fortfarande bättre i den inbyggda OTEL-strömmen än i notify-bryggan.
Det är inte ett skäl att undvika inlägget. Det är själva poängen med inlägget. Riktiga observabilitetssystem byggs ihop av ofullständiga signaler.
Grafana över MCP ändrar spelplanen
Det större skiftet är att Grafana inte bara är en plats människor besöker i en webbläsare.
I det här repot exponerar vi också Grafana via MCP. Det betyder att en agent kan fråga Loki, Prometheus och Tempo direkt istället för att vänta på att en människa manuellt ska inspektera instrumentpanelerna först.
Det förvandlar observabilitet till en aktiv insats i arbetsflödet.
En agent kan fråga:
- Vilka verktygsfamiljer misslyckades mest den senaste timmen?
- Vilken MCP-server dominerade en session?
- Minskade nyligen genomförda ändringar verktygsfelen, eller flyttade de bara arbetet till mer skaltunga vägar med samma misstag?
- Vilka spårningar visar högst latens eller upprepade återförsök?
När du har det där är du väldigt nära en självförbättringsloop.
Från instrumentpanel till återkopplingsloop
Det här är den del vi tycker är mest intressant.
När observabilitetsstacken går att fråga från agentlagret slutar telemetrin vara en passiv rapporteringsyta och blir en styrsignal.
Loopen ser ut så här:
- Agentaktivitet avger spårningar, mått och berikade hook-loggar.
- Grafana lagrar beviset i Loki, Tempo och Prometheus där mått finns.
- Agenter frågar det beviset via Grafana MCP.
- Systemet identifierar dålig verktygsmix, sköra skills, svag dirigering eller skaltunga arbetsflöden som fortsätter producera undvikbara misstag.
- Agenter eller operatörer justerar prompter, agentkonfigurationer, skill-beskrivningar, dirigeringsregler eller verktygsåtkomst.
- Nästa session ger en ny telemetriform, och cykeln upprepas.
Så tar man sig från “intressant instrumentpanel” till “mätbart förbättringssystem”.
Målet är inte att maximera en verktygskategori. Det är att landa i rätt mix av CLI, inbyggda verktyg, MCP-anrop och skills för det arbete som faktiskt utförs.
Vad det här låter oss besvara
När båda körtiderna landar i samma Grafana-stack kan vi besvara operativa frågor mycket snabbare:
- Är fel koncentrerade till en verktygsfamilj?
- Skapar skaltunga arbetsflöden undvikbara misstag där ett verktyg på högre nivå borde finnas?
- Vilka MCP-servrar bär arbetsbördan?
- Betalar vi för agentaktivitet som inte producerar meningsfulla framsteg?
- Är en session ohälsosam på grund av modellen, verktygen eller orkestreringslagret?
Det här är särskilt användbart i arbetsflöden med flera agenter, där “agenten var upptagen” nästan inte säger någonting.
Om en specialist ständigt dirigeras ut och producerar hög felfrekvens är det ett dirigerings- eller promptutformningsproblem.
Om en MCP-server dominerar alla anrop kan det vara god arkitektur, eller ett tecken på att allt annat är dödvikt.
Om verktygsfel ökar samtidigt som kostnaden förblir hög har du ett operativt problem, inte ett kvalitetsproblem.
Om skalarbete fortsätter misslyckas på förutsägbara, undvikbara sätt där ett verktyg på högre nivå borde finnas, är det en produktsignal.
Om en skill aktiveras ständigt men inte förbättrar utfallet är det en prompt- eller dirigeringssignal.
Den verkliga lärdomen
Den djupare lärdomen här är att agentobservabilitet behöver både körtidstelemetri och arbetsflödestelemetri.
Körtidstelemetri berättar vad systemet gjorde.
Arbetsflödestelemetri berättar vad agenten trodde att den gjorde.
Vi behöver båda.
Om du bara behåller spårningar och räknare missar du det semantiska lagret. Om du bara behåller hook-händelser missar du latens, spann och den bredare körtidsbilden.
Och om du behåller båda men aldrig matar tillbaka dem till agentlagret har du övervakning, inte anpassning.
Kombinationen är det som gör systemet tillräckligt förklarbart för att driva och tillräckligt justerbart för att förbättra.
Vad som fortfarande är ofullständigt
Det finns fortfarande skarpa kanter.
- Inte varje hook-händelse innehåller den varaktighets- och tokendata vi skulle vilja ha.
- En del av de bästa tidsvyerna kommer fortfarande från Tempo-spårningar, inte hook-loggarna.
- Codex är idag mindre semantiskt rikt än Claude Code i den berikade händelseströmmen.
- Skärmdumpen av instrumentpanelen är en levande driftsyta, inte en polerad marknadsföringsartefakt.
Den sista punkten är avsiktlig. Vi föredrar att visa den riktiga instrumentpanelen framför att låtsas att agentsystem magiskt förklarar sig själva.
Varför det här spelar roll för Maguyva
Maguyva handlar om att ge agenter bättre kodintelligens. Men när agenter väl utför användbart arbete uppstår omedelbart ett nytt krav: du behöver se hur de beter sig.
Sökkvalitet, dirigeringskvalitet, verktygsval och kontexteffektivitet blir alla observerbara problem.
Det är därför vi tycker att det här är värt att skriva om. Den framtida agentstacken är inte bara prompter och verktyg. Det är prompter, verktyg och instrumenteringslagret som berättar om helheten faktiskt fungerar.
Om du bygger seriösa agentarbetsflöden är observabilitet inte valfri infrastruktur. Det är en del av produkten.
Relaterad läsning
Mer från byggloggen för Maguyva
Varför vi uppgraderade kodsökningen till voyage-4-large_
Vi flyttade våra kodinbäddningar till voyage-4-large — för närvarande etta på den offentliga RTEB-topplistan för kodhämtning. Den ärliga versionen: avvägningen vi gör, vad vi faktiskt indexerar, och varför vi betalar för premiuminbäddningar.
Rekursiv självförbättring för språk: att slita fram kodintelligens över ~280 språk_
Vi stöder kodintelligens för cirka 280 språk. Ingen människa kan granska det för hand. Så vi byggde en rekursiv självförbättringsloop för språk — stickprov, LLM som domare, fixa en sak, omvalidera — och kör den med en flotta av isolerade agenter tills extraktionen faktiskt är korrekt, inte bara grön.
Multimodal fusionssökning: att välja rätt hämtare för varje sökfråga_
En sökfråga som 'var är parseConfig definierad' vill ha en annan typ av sökning än 'hur fungerar auth'. Maguyva klassificerar avsikten, viktar fyra hämtningslägen därefter, och slår samman resultaten med viktad Reciprocal Rank Fusion.