Artikel Drift · Incidenter

En AI-agent gjorde fel i produktion: så rekonstruerar du vad som hände

Larmet kommer sällan från agenten själv. Det kommer från en kund som fått samma bekräftelsemejl två gånger, en betalningsleverantör som flaggar en avvikelse, eller en kollega som undrar varför priserna i produktion plötsligt är fel. Agentloggen som skulle ha svarat på exakt vad som hände finns kanske inte, eller täcker bara halva förloppet. Kvar har du det som redan låg där innan något gick fel: git-historik, driftloggar, API-loggar och databasens egna spår. Det räcker längre än det känns som mitt i chocken.

Senast granskad: 17 aug 2026

En ensam gestalt med en lykta går längs en smal, ljusgrön spricka som löper över ett mörkt, vått golv av plattor, med spridda mörka fotspår utmed sprickan. Till vänster svävar fristående, oregelbundet sammanflätade ljusa trådar och noder utan koppling till gestalten.
Följ sprickan bakåt, ett fotavtryck i taget, för att hitta punkten där den började.
Den här texten handlar om att rekonstruera en incident som redan har hänt, med den ofullständiga spårning du faktiskt har. Vill du i stället bygga framåtriktad spårning så att nästa incident blir enkel att felsöka — vilket schema du bör logga för varje verktygsanrop, innan något går fel — är det ägt av engelskspråkiga Agent Trace Observability. Den här artikeln förutsätter att du inte hann göra det, och visar vad du gör ändå.

Kortfattat

Utan en fullständig agentlogg måste tidslinjen byggas genom att korrelera flera ofullständiga källor: git (vad ändrades, av vem, i vilken ordning — inklusive reflog för det som senare skrivits över), CI/CD-loggar (vad kördes och deployades, men bara inom leverantörens retentionsfönster), API- och åtkomstloggar (vilka anrop gick ut och när) och databasens egna spår (auditlogg om en fanns i förväg, annars transaktionsloggens återställningsfönster). Första steget är alltid att frysa det som finns innan retentionsfönstren stänger det. Därefter normaliseras alla tidsstämplar till en tidszon, källorna läggs bredvid varandra på en gemensam tidslinje, och blast radius bedöms utifrån vilka rader och nedströmssystem som faktiskt låg i skrivvägen — inte utifrån vilken kod som ändrades.

Frys det som finns, innan du gör något annat

Instinkten är att börja rätta felet. Gör det om det aktivt skadar, men innan du gräver vidare: kopiera ut det som finns i loggkällorna till en plats du kontrollerar. CI-leverantörer sparar körningsloggar en begränsad tid — GitHub Actions sparar loggar och artefakter i 90 dagar som standard om inget annat är konfigurerat [1] — och molnleverantörers egna händelsehistorik har liknande gränser: AWS CloudTrails sökbara händelsehistorik täcker de senaste 90 dagarnas hanteringshändelser utan att någon aktivt satt upp en trail, men är inte permanent och saknar vissa händelsetyper [2]. Ligger incidenten inom de fönstren finns spåret kvar just nu — men om någon kör en rutinmässig städning, roterar en logggrupp eller ett nattligt jobb rensar gamla körningar innan du hunnit exportera, är det borta för gott. Exportera loggarna som filer, inte skärmdumpar av en instrumentpanel som kan ändra vy.

Samma sak gäller git. Har agenten committat och sedan blivit rebased, force-pushad eller squashad över av ett efterföljande jobb, ligger de ursprungliga commit-objekten ofta kvar lokalt i git reflog tills skräpsamlingen städar bort dem. Kör git reflog show --all och git fsck --unreachable på den maskin eller den CI-runner-cache där agenten faktiskt jobbade, innan någon kör git gc där.


Bygg tidslinjen ur fyra källor

Ingen enskild källa räcker. Varje källa svarar bara på en del av frågan "vad hände, i vilken ordning".

1
Git — vad ändrades och när. git log --all --reflog --date=iso-strict --format='%H %ad %an %s' ger dig varje commit inklusive de som senare försvunnit från en branch. Kolla författar- och committer-fält separat: en agent som commitar med en tjänsteanvändare syns där, men om agenten i stället pushade direkt mot plattformens API utan lokal commit-historik är det plattformens egen audit-logg (inte repots historik) som måste kontrolleras i stället.
2
CI/CD — vad byggdes och deployades. Deployloggen kopplar en commit till en faktisk produktionsutrullning, och den tidsstämpeln är ofta viktigare än commit-tiden: en agent kan ha committat en ändring timmar innan den faktiskt gick live via ett schemalagt jobb. Leta efter vilket jobb som triggade deployen — manuellt, på push, eller ett agent-initierat API-anrop mot CI-systemet.
3
API- och åtkomstloggar — vilka anrop gick faktiskt ut. Reverse proxy, load balancer eller applikationens egna access-loggar visar metod, väg, status och tidsstämpel per anrop. Här ser du om agenten anropade samma endpoint flera gånger i snabb följd — ofta signaturen på en icke-idempotent åtgärd som körts om vid ett omförsök, samma mönster som beskrivs för vanlig integrationskod i API-integrationer med AI.
4
Databasen — vad som faktiskt skrevs. Fanns en radnivå-auditlogg eller pgAudit-liknande loggning aktiverad i förväg går det att se gammalt och nytt värde, användare och tidsstämpel per ändrad rad [3]. Fanns ingen sådan loggning innan incidenten finns normalt ingen retroaktiv "vem ändrade den här raden"-historik i databasen själv — du är då hänvisad till transaktionsloggens återställningsfönster (point-in-time recovery): återställ en kopia till strax före misstänkt tidpunkt och diffa mot nuvarande tillstånd, eller till en applikationslogg som råkade skriva ut det gamla värdet innan skrivningen.
Klockskillnad förstör tidslinjen tystare än du tror

CI-runnern, applikationsservern, databasen och en tredjeparts webhook-avsändare har sällan exakt synkade klockor, och loggar sällan i samma tidszon. Normalisera varje tidsstämpel till UTC innan du lägger källorna bredvid varandra, och räkna med en felmarginal på sekunder snarare än att anta millisekundexakt ordning mellan system du inte själv kontrollerar. Verkar två händelser från olika system ha skett i "omöjlig" ordning är klockskillnad den första förklaringen att utesluta, inte den sista.


Bedöm blast radius: rader, inte kodrader

Ett vanligt misstag är att bedöma skadan utifrån hur stor kodändringen var. En enda rad i ett migrationsskript kan ha berört en hel tabell; tusen rader refaktorerad kod kan ha noll produktionseffekt om deployen aldrig gick live. Fråga i stället: vilka rader, användare, köer eller nedströmssystem låg faktiskt i skrivvägen under incidentfönstret?

Räkna raderna, inte antagandet. Om databasen har en auditlogg eller om du kan återställa en kopia till strax före incidenten: kör en faktisk diff eller COUNT(*) mot villkoret som beskriver ändringen, i stället för att gissa "förmodligen bara några stycken".
Sidoeffekter räknas separat från datan. Skickades mejl, push-notiser, betalningar eller webhooks som en konsekvens av skrivningen? De kan ha gått ut även om du senare rullar tillbaka datan — en återställd databasrad skickar inte tillbaka ett redan avsänt mejl. Detta är samma idempotensproblem som beskrivs för migrationer i Databasmigreringar med AI, tillämpat baklänges på en redan skedd skada i stället för framåt som en försiktighetsåtgärd.
Bekräfta att felet stannat. Om agenten fortfarande kör, eller om ett schemalagt jobb kan trigga om samma sekvens, är blast radius inte en färdig siffra förrän du vet att inflödet är stoppat. En provkörning mot en kopia av produktionsmiljön, inte mot produktion, är hur du bekräftar det.

Provet: en gemensam tidslinje på tio minuter

Ett konkret sätt att testa om du faktiskt har en användbar tidslinje: exportera de fyra källorna ovan till separata textfiler, normalisera varje tidsstämpel till UTC, och slå ihop dem sorterat på tid i en enda fil eller kalkylark — en rad per händelse, med källa, tidsstämpel och en kort beskrivning. Provet är godkänt när du kan peka på en enskild rad i den sammanslagna listan och säga exakt vilken händelse som var den första i kedjan: det första commitet, den första deployen, det första onormala API-anropet eller den första oväntade skrivningen. Kan du inte peka på en första händelse har du fortfarande luckor, och nästa steg är att leta efter vilken av de fyra källorna som saknas eller är för kort för att täcka hela fönstret — inte att gissa dig fram till en slutsats.


Efter incidenten: vad som ska finnas nästa gång

En rekonstruktion i efterhand är alltid dyrare i tid och mer osäker i resultat än loggning som fanns på plats innan. Utifrån vad som saknades den här gången, en kort lista att gå igenom innan nästa agentkörning mot produktion:

Sätt en explicit retention på CI/CD-loggar som matchar hur länge en incident realistiskt kan ligga oupptäckt hos dig — inte leverantörens standardvärde per automatik.
Aktivera radnivå-auditloggning på tabeller en agent har skrivrättighet till, innan agenten får skrivrättigheten, inte efter första incidenten.
Kräv idempotensnycklar på varje agent-initierad åtgärd som har en extern sidoeffekt (mejl, betalning, webhook), så att ett omförsök inte upprepar effekten.
Om du vill gå längre och logga varje verktygsanrop strukturerat i förväg, i stället för att pussla ihop det i efterhand: det är exakt vad Agent Trace Observability går igenom.

Källor

[1] GitHub Docs, Configuring the retention period for GitHub Actions artifacts and logs — standardretention 90 dagar; konfigurerbart 1–400 dagar för privata repon, 1–90 dagar för publika repon. [2] AWS-dokumentation, Working with CloudTrail event history — 90 dagars sökbar händelsehistorik för hanteringshändelser utan aktiv trail-konfiguration. [3] CYBERTEC PostgreSQL, Row change auditing options for PostgreSQL, och PostgreSQL Wiki, Audit trigger 91plus — trigger- och tilläggsbaserad radnivåloggning. Samtliga källor lästa 17 augusti 2026. Retentionsvärden och funktioner nämns som exempel på kategorier av spår, inte som rekommendation av en enskild leverantör.