Spring til indhold

Tidligere version (v3) (2026-06-10, "Koherens-reparation + genus-fixes")

Dette er en arkiveret tidligere udgave af denne post. Den aktuelle version kan være ændret. Læs den aktuelle version →

Naturalness-passen i v2 havde fjernet firehose-sektionens lead-ins (prompts.jsonl/tools.jsonl-overskrifterne) og efterladt 'Det er bevidst.' uden antecedent — begge genoprettet i kompakt form. Genus rettet ('et ToolCall', 'et File-opslag'), heading 'Erkendelsen kom fra dig' → 'fra en prompt' (passede ikke længere til brødteksten), og 'Næste indlæg'-sektionen droppet til fordel for series_footer.

← Alle indlæg
v3

Grafen var der hele tiden

Pks brain ingest producerede ikke en log. Den producerede en graf. Fire nodes, tre edges, og en join-akse jeg ikke kunne se før product-cli viste mig hvordan en typed DAG ser ud andre steder. Det 3. indlæg om hvordan AI-hjernen er bygget op under motorhjelmen.

Da jeg startede pks brain 14. maj havde jeg ikke en graf i hovedet. Jeg havde fire JSONL-filer. Der gik et par uger før jeg så at det var én og samme ting.

Det her er det 3. indlæg i serien om pks brain. I det første gennemgik jeg hvorfor AI-hjernen eksisterer. I det andet viste jeg hvad den producerer.

Erkendelsen kom fra en prompt

Jeg sad i en session og troede stadig vi byggede én ting. Så skrev jeg den her prompt:

human prompt
1 lines
there are two things here, the original brain ingest thing was not for the graph build, we have worked on it as two seperate features, so make sure to investigate the correct things about our graph that connects files and sessions from toolcalls and prompts.

Den prompt opdelte det i to: ingest-laget (deterministisk firehose-dump) og graf-laget (det der binder noderne sammen).

Fire nodes, tre edges

DAG'en pks brain producerer består af præcis fire node-typer:

  1. Session — én Claude Code-session, identificeret ved UUID. Lever som en JSONL-fil i ~/.claude/projects/<projekt>/<uuid>.jsonl.
  2. Prompt — én brugerprompt inde i en session. Tekst, timestamp, eventuel slash-command.
  3. ToolCall — én tool-invokation drevet af en prompt. Værktøjsnavn, varighed, fejl-flag, parent assistant-uuid.
  4. File — én fil rørt af et ToolCall. Filsti, op (read/write/edit/multi-edit), success-flag.

Mellem dem er der tre edges — alle peger samme vej:

Visuelt er det den her struktur fra det første indlæg — værd at have foran sig:

DAG-diagram med fire nodes (Session, Prompt, ToolCall, File) arrangeret som et rektangel, forbundet med tre edges: contains, drove, wrote/edited/read

Det er en DAG — alle edges peger nedad, ingen cykler. Det er det der gør queries forudsigelige. Et File-opslag kan aldrig ende tilbage hos sig selv.

Firehose-rækker er edges forklædt som tabelrækker

Ingest-fasen skriver fire JSONL-filer i ~/.pks-cli/brain/. Kigger man kun på filerne, ligner hver række en almindelig tabelrække. Men hver række er også en edge i grafen.

prompts.jsonl — én række pr. brugerprompt. I grafen bliver rækken til to ting: Prompt-noden og Session → Prompt-edgen:

{
  "sessionId": "1a475348-b3f7-4038-8256-2f364859c3d2",
  "projectSlug": "-workspaces-agentic-live-www--...",
  "timestampUtc": "2026-04-06T15:28:44.458Z",
  "promptId": "0c2762b1-7e6f-45bd-8372-7b499b8a6edd",
  "uuid": "85993c63-296b-4d22-a029-9ddacb9a9db5",
  "text": "Project: pks-agent-inbox\nan email inbox for agents\n\nScope: scarfold\n\n...",
  "textHash": "d39524e8cbdf3c4c",
  "cwd": "/workspaces/agentic-live-www/.agentics/_work/...",
  "gitBranch": "task/mnmzxlux-85f5wh",
  "length": 1901,
  "isSlash": false
}

tools.jsonl — én række pr. tool-invokation. Rækken bliver til ToolCall-noden og edgen tilbage til Prompt via parentAssistantUuid:

{
  "sessionId": "agent-a6e61f715b03161e3",
  "projectSlug": "-tmp-pks-runner-jobs-...",
  "timestampUtc": "2026-04-12T15:40:17.614Z",
  "toolName": "Glob",
  "toolUseId": "toolu_014X92w1RkizWovUjbToPuTf",
  "inputDigest": "f7361c8f7acae6c4",
  "inputPreview": "{\"pattern\":\"/tmp/pks-runner-jobs/.../**/*\"}",
  "parentAssistantUuid": "c0c06cbd-5884-4885-85bb-5d27f47348f0",
  "durationMs": 16,
  "isError": false,
  "resultSize": 1217,
  "isMcp": false,
  "isSubagent": false
}

files.jsonl — én række pr. fil-operation. Repræsenterer både File-noden og wrote/edited/read-edgen fra ToolCall:

{
  "sessionId": "agent-a6e61f715b03161e3",
  "projectSlug": "-tmp-pks-runner-jobs-...",
  "timestampUtc": "2026-04-12T15:45:18.075Z",
  "op": "write",
  "filePath": "/tmp/pks-runner-jobs/.../design-system/components.md",
  "success": true
}

errors.jsonl — her markerer jeg de ToolCalls der fejlede. Fejlen er metadata på ToolCall, ikke en node i sig selv.

Joinet er gratis fordi der er en timestamp

De fire firehoses har ingen foreign keys mellem sig — kun (sessionId, timestampUtc) til fælles. Det er nok. Har jeg en fil og vil finde prompten bag ændringen, er opskriften: filtrér files.jsonlfilePath, og tag seneste prompt i samme session før fileTs.

I det 4. indlæg bruger pks brain commit-plan præcis den lookup. Den bygger ikke grafen i memory først.

Deterministisk vs. AI-syntetiseret

Ingest kører på 1–2 sekunder fordi der ikke er en model i loopet — 100% deterministisk. Det er bevidst: hele firehose'en skal kunne genberegnes gratis.

Oven på den deterministiske graf ligger to AI-lag:

extract — modellen laver et markdown-resumé pr. session ud fra prompts.jsonl, tools.jsonl og files.jsonl filtreret til den session. synth + wiki — finder klynger af sessions med samme tema (extract-tekst, filstier, tool-mønstre) og skriver én wiki-side pr. klynge.

AI-laget koster tokens, og det er fortolkning — ikke sandhed. Derfor holder jeg dem adskilt: jeg kan ændre extract-skillet og genkøre extracts uden at røre ingest. Og hvis ingest er ødelagt, ser jeg det med det samme i stedet for at opdage det via en wiki der lyver troværdigt.

Hvorfor det er en DAG og ikke "bare" en tabel

Jeg overvejede faktisk at slå alle fire firehoses sammen til én bredtabel: (session_id, prompt_id, tool_call_id, file_path, timestamp). Det ville fungere for de fleste queries.

Som tabel bliver det bare tilstand. Som DAG har hver edge sit eget timestamp, og jeg kan se præcis hvilken prompt der førte til hvilken ændring. Det er den sekvens der gør commit-plan-queryen i det 4. indlæg mulig — staged filer ind, reverse query over grafen, commit-messages ud.