§

Mjukvarufabrik — Agentbaserad kodning med provenans

När en LLM skriver kod, vem skrev koden? Kan du spåra varje beslut i en session med 47 anrop tillbaka till sitt resonemang?

Forskning discovery modeLLM decision chainssecurity gatesreplaymodel substitution
— — — — —

Forskning | discovery mode, LLM-beslutskedjor, säkerhetsgrindar, omspelning med modellbyte


Scenariot

Klara Nilsson är utvecklare på Proliminal. Hon ber en LLM att lägga till XBRL-export till Liminaras VSME-hållbarhetsrapporteringspack. Uppgiften låter enkel — “läs VSME-specen, lägg till XBRL-utdata vid sidan av den befintliga PDF-renderaren” — men LLM-sessionen som följer involverar 47 LLM-anrop över 25 minuter, som förgrenar sig genom analys, design, kodgenerering, testskrivning, testkörning, feldiagnos och iteration.

Detta är discovery mode: planen är inte känd i förväg. LLM:en beslutar vad den ska göra härnäst baserat på vad den just lärt sig. Varje beslut utökar DAG:en.


Sessionen, steg för steg

  DISCOVERY MODE — DAG grows as the LLM works
  ═══════════════════════════════════════════════

  Call 1    ┌─── read VSME spec ──────────┐
  (plan)    │ "I need to understand the    │
            │  VSME disclosure structure"  │──→ vsme-spec-summary (förseglad)
            └─────────────────────────────┘

  Calls 2-4  ┌─── analyze existing code ──┐
  (read)     │  report renderer module     │
             │  PDF template structure     │──→ code-analysis (förseglad)
             │  artifact schema            │
             └─────────────────────────────┘

  Call 5     ┌─── design decision ────────┐
  (plan)     │ "Use ex_xbrl or hand-      │
             │  build XBRL XML?"          │
             │                            │
             │  Decision: use ex_xbrl     │──→ decision record:
             │  Reason: "maintained,       │    library_choice = "ex_xbrl"
             │  covers ESRS taxonomy,      │    rationale = "maintained, ESRS
             │  1,200 GitHub stars"         │    taxonomy, type-safe"
             └─────────────────────────────┘

  Calls 6-12 ┌─── design XBRL mapping ───┐
  (plan)     │  B1-B11 → XBRL concepts   │
             │  data types, units,        │──→ xbrl-schema-mapping (förseglad)
             │  period references          │    sha256:c4a1...
             └─────────────────────────────┘

  Calls 13-28 ┌── generate code ──────────┐
  (write)     │  xbrl_renderer.ex         │
              │  xbrl_template.ex         │──→ patchset (förseglad)
              │  mix.exs dep addition     │    sha256:8f72...
              └───────────────────────────┘

              ┌── SECURITY GATE ──────────┐
              │  Human approves file       │──→ decision record:
              │  writes before they        │    gate = "file_write"
              │  touch disk                │    approved_by = "klara.nilsson"
              └───────────────────────────┘

  Calls 29-34 ┌── write tests ───────────┐
  (write)     │  xbrl_renderer_test.exs  │──→ test-patchset (förseglad)
              │  12 test cases            │    sha256:a903...
              └───────────────────────────┘

  Calls 35-38 ┌── run tests ─────────────┐
  (exec)      │  mix test                │──→ test-result (förseglad)
              │  9 pass, 3 fail          │    sha256:d2e1...
              └───────────────────────────┘

  Calls 39-43 ┌── diagnose + fix ────────┐
  (iterate)   │  "period ref format      │
              │   wrong for B3 energy    │──→ fix-patchset (förseglad)
              │   disclosure — XBRL       │    sha256:1b44...
              │   expects duration not   │
              │   instant"                │
              └───────────────────────────┘

  Calls 44-46 ┌── re-run tests ──────────┐
  (exec)      │  mix test                │──→ test-result-v2 (förseglad)
              │  12 pass, 0 fail         │    sha256:7f93...
              └───────────────────────────┘

              ┌── SECURITY GATE ──────────┐
              │  Human approves PR        │──→ decision record:
              │  creation                 │    gate = "pr_create"
              └───────────────────────────┘

  Call 47     ┌── open PR ────────────────┐
  (side-fx)   │  PR #142: "Add XBRL      │──→ delivery-receipt (förseglad)
              │  export to VSME pack"     │    sha256:e4b8...
              └───────────────────────────┘

DAG:en innehåller 47 LLM-beslutsposter, 2 mänskliga grindgodkännanden, 11 intermediära artefakter och 1 leverans. Varje steg är förseglat med ett unikt fingeravtryck. Hela sessionen — inklusive den återvändsgränd där periodformatet var fel — bevaras.


Spåra ett beslut

Två veckor senare frågar en granskare: “Varför valde modellen ex_xbrl istället för att bygga XBRL-XML manuellt?”

Svaret är inte “för att LLM:en sa det.” Svaret är ett spår:

  1. Navigera till körning sf-run-2026-03-14-xbrl i observationsgränssnittet
  2. Hitta beslutsposten vid anrop 5: beslutspost
  3. Läs den registrerade motiveringen: “ex_xbrl — underhållet bibliotek med 1 200 GitHub-stjärnor, täcker ESRS-taxonomin, erbjuder typsäkra concept builders. Att bygga XML manuellt riskerar namnrymdsfel och saknar validering. Bygg-kontra-köp talar för biblioteket i en v1.”
  4. Se indata-artefakterna som informerade detta beslut: VSME-spec-sammanfattningen, kodanalysen av den befintliga renderaren
  5. Se vad som hände nedströms: schema-mappningen (anrop 6-12) berodde på detta val

Beslutet är inte bara registrerat — det är positionerat i sitt kausala sammanhang. Granskaren kan se vilken information som var tillgänglig när beslutet fattades och vilka konsekvenser som följde.


Omspelning med en annan modell

Klara vill jämföra: tänk om hon hade använt claude-sonnet-4 istället för claude-haiku-4 för denna session?

replay sf-run-2026-03-14-xbrl
  --override model=claude-sonnet-4

Omspelningen kör om varje LLM-anrop med den nya modellen. DAG-strukturen utvecklas i samma discovery-mönster — planen är inte fast — men indata till varje beslutspunkt är desamma. Sonnet kanske väljer ett annat bibliotek vid anrop 5. Om den gör det, divergerar den nedströms DAG:en från den punkten: annan schema-mappning, annan genererad kod, andra testresultat.

Resultatet: två kompletta körningsspår, jämförbara sida vid sida. Samma uppgift, samma startkontext, olika modell. Varje divergenspunkt är synlig. Kostnadsskillnaden är synlig. Kvalitetsskillnaden (klarade testerna första försöket?) är synlig.

Deterministiska steg (applicera patchar, köra tester) återanvänder cachade resultat när indata är identiska. Bara LLM-anropen och deras nedströms beroenden körs om.


Vad du kan fråga efteråt

FrågaHur den besvaras
”Varför valde modellen ex_xbrl?”Beslutspost vid anrop 5: motivering, indata-artefakter, nedströmskonsekvenser. Fullt kausalt sammanhang.
”Vilken information hade modellen när den designade schema-mappningen?”Spåra indata till anrop 6-12: VSME-spec-sammanfattningen (artefakt), kodanalysen (artefakt) och bibliotekvalsbeslutet (anrop 5).
”Varför fallerade tre tester vid första körningen?”Testresultatartefakt (sha256:d2e1…) visar felen. Diagnos vid anrop 39-43 förklarar: XBRL-periodformat-missmatch. Fixen är en separat artefakt (sha256:1b44…).
”Vad skulle denna session kosta med en annan modell?”Spela om med --override model=claude-sonnet-4. Jämför: totala tokens, väggklocktid, test-godkännandegrad, antal iterationscykler.
”Kom modellen åt några filer utanför VSME-packet?”Granska verktygsanropsposterna i alla 47 beslut. Varje filläsning, varje kataloglistning är registrerad. Scope-överträdelser kan upptäckas i efterhand.
”Kan vi reproducera exakt den PR som skapades?”Spela om den ursprungliga körningen (inga åsidosättningar). Varje beslut spelas om från lagrade poster. Patchset-artefakten är identisk (samma hash). PR-kroppen är identisk.

Före och efter

Idag: Klara använder ett LLM-kodningsverktyg. Det producerar en PR. PR:en ser rimlig ut. Hon mergar den. Tre månader senare frågar någon varför ex_xbrl valdes istället för det ESRS-specifika biblioteket som det finska teamet använder. Ingen vet. Resonemanget hände i ett chattfönster som inte längre finns. Beslutet fattades av en modell som sedan dess har avvecklats.

Med provenans: Hela kodningssessionen är en körning. Varje LLM-anrop är en beslutspost. Bibliotekvalet spåras till specifikt resonemang informerat av specifik kontext. Återvändsgränden (fel periodformat) bevaras — den dokumenterar vad som inte fungerade och varför. När någon frågar “varför?” finns svaret, är sökbart och verifierbart. När en bättre modell blir tillgänglig kan sessionen spelas om för att jämföra utfall utan att göra om de mänskliga delarna (uppgiftsbeskrivningen, grindgodkännandena återanvänder lagrade beslut om de inte explicit omgrindar).


Söker utvecklingsteam som utforskar provenans för AI-assisterad kodning — särskilt de med revisionskrav på AI-genererad kod. [Kontakt ->]

— — — — —