The context sets, the packaged knowledge bases and the example bundles are replaced by one fictitious example set about IT operations in an invented organisation: three context sets (serverrom-2027, driftsavtale-2027 and the two-base drift-og-avtale-2027), two synthetic knowledge bases under src/portfolio_optimiser/data/kunnskapsbaser and two example bundles under src/portfolio_optimiser/data/bundles. Numbers, codes and structural values in tests and fixtures are kept; names, ids and wording change. Dated measurement documents that only recorded runs on the replaced material are deleted. Gate figures measured on the new set are not comparable with earlier ones. The exclusion gate from the previous commit is green: 0 tracked files hit outside the shared/ subtree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
160 lines
11 KiB
Markdown
160 lines
11 KiB
Markdown
# S2c / MAJOR-3 — `read_bundle`s kontekstkostnad: målingen
|
||
|
||
**Ordre:** `20260902T151931Z-250257273-from-.claude`. Bindende ordretekst: ordreutkast M3 i
|
||
[`docs/2026-09-02-misjonsreview-v2.md`](2026-09-02-misjonsreview-v2.md) § 7.
|
||
**Regelen ordren gir: MÅL FØRST, så ÉN søm.** Denne fila er tallene med nevner — skrevet og
|
||
committet FØR noe ble endret, og utvidet med ETTER-tabellen i sømmens egen commit.
|
||
|
||
## 0. Instrumentet, og hvorfor det er troverdig
|
||
|
||
Efemert, i `/tmp/claude-s2c/` — utenfor repoet, som forrige gang (`misjonsreview-v2` § 8): et
|
||
måleinstrument i `tests/` ville vært en beslutning om å beholde det, og ordren ba om en måling,
|
||
ikke en ny produksjonsflate. Tokens telles med `tiktoken`s `o200k_base`, kjørt med
|
||
`uv run --with tiktoken` (`tiktoken` er fortsatt IKKE en prosjekt-avhengighet).
|
||
|
||
**Kjent-positiv kontroll FØR noe ble målt** (Verifiseringsloven ansikt 4 — en spørring skal bevises
|
||
å KUNNE finne): instrumentet ble kjørt mot de tre eksempelbundlene som den gang lå i commons (`bygg-energi-mikro`
|
||
og to energi-eksempler, her `energi-a` og `energi-b`; de to siste er i dag erstattet av de fiktive
|
||
`klientpark-energi` og `driftssenter-kjoling`, og tallene her er målt på de gamle) og reproduserte commons' egne
|
||
publiserte fasittall for `okf.bundle_context` **eksakt** — 3 861 / 10 406 / 12 595. Et instrument
|
||
som ikke treffer en kjent fasit måler seg selv.
|
||
|
||
**Prompten måles som tekst + `function_call` + `function_result`, ALDRI `.text` alene.** Den
|
||
skriptede klientens egen `sink` samler kun `.text`, og en prompt hvis hele nyttelast er et
|
||
verktøyresultat måles da til noen få tegn. Den fella er dokumentert i `misjonsreview-v2` vedlegg A
|
||
og er unngått her ved en egen wrapper på `ScriptedChatClient._inner_get_response`.
|
||
|
||
**Kjøringen er ekte, ikke rekonstruert:** hver rad er én `run.main([... --explore ...])` gjennom
|
||
CLI-døra, med navigatøren skriptet som `list_bundles → read_bundle → tekst` (trinn-manuset fra økt
|
||
67, MAJOR-1 b). At navigatøren faktisk ÅPNET basen er lest av kjøringens eget artefakt
|
||
(`{run_id}-exploration.json`s `tool_calls`), ikke antatt.
|
||
|
||
**Ærlighetsgrense, uttalt:** kopi-telleren er en 160-tegns skive fra MIDTEN av `read_bundle`s
|
||
nyttelast (samme sonde som `misjonsreview-v2` § 4). Skiva finnes i dag i BEGGE de to strengene
|
||
kjøringen sender — utforskningens `function_result` OG debattens `okf.bundle_context` — fordi de er
|
||
den samme strengen. Kolonnene under skiller dem derfor på ROLLE, som er det eneste som skiller
|
||
dem: `manager`/`navigator`/`hypothesiser` er utforskningen (sømmen denne ordren rører),
|
||
`proposer`/`checker` er debatten (som ordren forbyr å røre).
|
||
|
||
## 1. FØR — hva `read_bundle` koster, og hvor mange prompts det rir med i
|
||
|
||
Per base, én CLI-kjøring med `--explore`:
|
||
|
||
| Base | `read_bundle`-nyttelast | Prompts totalt | Prompt-tokens totalt | Utforsknings-kopier | Tokens i dem | Debatt-kopier | Tokens i dem |
|
||
|---|---:|---:|---:|---:|---:|---:|---:|
|
||
| `bygg-energi-mikro` | 12 005 tegn / **3 861 tok** | 15 | 35 773 | **5** | 22 013 | 3 | 11 738 |
|
||
| `energi-a` | 32 201 tegn / **10 406 tok** | 19 | 88 881 | **5** | 54 623 | 3 | 31 376 |
|
||
| `energi-b` | 39 583 tegn / **12 595 tok** | 19 | 106 519 | **5** | 65 698 | 3 | 37 943 |
|
||
|
||
**De fem utforsknings-kopiene, navngitt** (energi-b-tall): navigatørens egen prompt etter kallet
|
||
(12 770) · managerens tre progress-ledger-/final-prompts (13 527 / 13 549 / 13 075) ·
|
||
hypotesiserens ene tur (12 777). Ett `read_bundle`-kall gjør hele basen til et `function_result`,
|
||
og det resultatet rir med i **hver senere prompt i samme samtale** — deltakerne deler
|
||
samtalehistorikk, så ingen av dem betaler for å be om det.
|
||
|
||
**Selve nyttelasten, som andel av alt:** 5 kopier × nyttelast = 19 305 / 52 030 / **62 975** tokens,
|
||
altså **54 % / 59 % / 59 %** av ALLE prompt-tokens i kjøringen. Debattens tre kopier (11 738 /
|
||
31 376 / 37 943 = 33 % / 35 % / 36 %) kommer fra `run.py`s `okf.bundle_context` og er en EGEN
|
||
beslutning (PM) — de er med i tabellen som nevner og som ETTER-kontroll, ikke som mål.
|
||
|
||
**Nevneren for «hvor mange prompts»:** 15 / 19 / 19 prompts i alt, hvorav 8 bærer konteksten.
|
||
Tallet 5 gjelder DETTE manuset (én navigatør-tur, én hypotesiser-tur, tre manager-prompts).
|
||
`misjonsreview-v2` § 4 målte 7× på bibliotek-armen med et annet manus (to hypotesiser-turer, fire
|
||
navigatør-prompts); begge er sanne om sitt eget manus, og ingen av dem er en påstand om hva en
|
||
LEVENDE manager gjør. Multiplikatoren er en egenskap ved samtalehistorikken, ikke ved manuset:
|
||
den er antall prompts etter kallet, og den er ≥ 1 uansett.
|
||
|
||
## 2. Hva basene faktisk inneholder — premisset for formen
|
||
|
||
| Base | Filer navigert | Konseptfiler | `type: verdict` | Rot-`index.md` (body) |
|
||
|---|---:|---:|---:|---:|
|
||
| `bygg-energi-mikro` | 6 | 4 | 1 | 1 884 tegn |
|
||
| `energi-a` | 7 | 5 | 1 | 3 646 tegn |
|
||
| `energi-b` | 7 | 5 | 1 | 4 763 tegn |
|
||
|
||
**Ett premiss felt før noe ble bygget på det:** «basens indeks er selve navigasjonsprosaen, så den
|
||
hører hjemme i `read_bundle`». Energi-b-basens rot-indeks er alene **4 763 tegn ≈ 1 400 o200k-tokens**
|
||
— altså nesten hele ordrens tak på 1 500 for HELE kallet. Å legge den inn ville brukt opp budsjettet
|
||
på et felt katalogen alt gir et bundet utdrag av (`_CATALOGUE_EXCERPT_CHARS`), og som fortsatt er
|
||
nøyaktig ett `read_file(id, "index.md")` unna. `read_bundle` bærer derfor konseptlista og ikke
|
||
indeksbodyen.
|
||
|
||
## 3. Hva prefiks-caching ville kjøpt — notert, IKKE bygget
|
||
|
||
Ordren ber om notatet, ikke mekanismen. Regnet av prompt-prefiksene i `misjonsreview-v2` § 4
|
||
(ikke av en fakturering — leverandørens faktiske cache-treff er UMÅLT): ≈ 60 % av debattens og
|
||
≈ 40 % av utforskningens prompt-tokens er prefiks-cachebart som koden står i dag, uten én
|
||
kodeendring, fordi prefiksene allerede er byte-stabile. Caching virker på KOPIENE; et
|
||
kontekstbudsjett virker på GRUNNTALLET de multipliserer. De to utelukker ikke hverandre, og
|
||
denne ordren gjør bare det siste.
|
||
|
||
## 4. Verifiseringslogg (FØR-halvdelen)
|
||
|
||
| # | Påstand | Kommando → resultat |
|
||
|---|---|---|
|
||
| 1 | Instrumentet treffer en kjent fasit | `uv run --with tiktoken python /tmp/claude-s2c/measure.py <repo>` → 3 861 / 10 406 / 12 595 = commons' publiserte tall |
|
||
| 2 | Kjøringen ÅPNET basen (ikke bare listet den) | `measure-exploration.json`s `tool_calls` → `list_bundles`, `read_bundle(<base>)` |
|
||
| 3 | Prompt-størrelse inkluderer verktøyresultatet | wrapper på `_inner_get_response` serialiserer `contents` (`function_call`/`function_result`), ikke `.text` |
|
||
| 4 | Nevner | 15 / 19 / 19 prompts per kjøring; 8 bærer konteksten i alle tre |
|
||
| 5 | Indeksbodyen alene sprenger nesten hele taket | `len(navigate_bundle(energi_b).index_summary)` → 4 763 tegn ≈ 1 400 tok |
|
||
|
||
---
|
||
|
||
## 5. ETTER — samme instrument, samme kommandoer, samme baser
|
||
|
||
`read_bundle` returnerer nå katalogformen: én oppføring per konseptdokument (`name`, `type`,
|
||
`title`, `chars`), med `read_file` som neste trinn.
|
||
|
||
| Base | Nyttelast før → etter | Kopier i utforsknings-prompts | Nyttelast × kopier | Utforsknings-prompts totalt | Hele kjøringen |
|
||
|---|---|---:|---|---:|---|
|
||
| `bygg-energi-mikro` | 3 861 → **163 tok** | 5 → 5 | 19 305 → **815** | 23 726 → **5 434** (−77 %) | 35 773 → 17 481 (−51 %) |
|
||
| `energi-a` | 10 406 → **237 tok** | 5 → 5 | 52 030 → **1 185** | 56 314 → **5 742** (−90 %) | 88 881 → 38 309 (−57 %) |
|
||
| `energi-b` | 12 595 → **259 tok** | 5 → 5 | 62 975 → **1 295** | 67 415 → **5 973** (−91 %) | 106 519 → 45 077 (−58 %) |
|
||
|
||
**Ordrens eget kriterium, verifisert direkte:** `read_bundle` over energi-b-basen er **748 tegn / 259
|
||
o200k-tokens** — under taket på 1 500. Målt forhold 2,89 tegn/token for denne norske markdownen;
|
||
det er dét som lar gaten bounde TEGN uten å gjette (se testens docstring, som uttaler avviket).
|
||
|
||
**«Ikke utløs»-beviset er en MÅLING, ikke en forsikring.** Ordren forbyr å røre `okf.bundle_context`,
|
||
nav-goldenene og `run.py`. Debattens tre kontekstkopier kommer fra nettopp den sømmen, og de er
|
||
**byte-identiske før og etter** i alle tre baser — 12 047 / 32 567 / 39 104 prompt-tokens, hver
|
||
enkelt prompt uendret. Det er et sterkere utsagn enn «jeg rørte den ikke». **Mekanismen, sagt én
|
||
gang:** identiteten er fastslått ved å sammenligne de per-prompt `proposer`/`checker`-tokentallene
|
||
FØR og ETTER, ikke ved sondens `carries`-flagg — flagget svarer på «bærer denne prompten
|
||
`read_bundle`s nyttelast», og etter endringen er svaret naturlig nei for debatten, siden lista ikke
|
||
finnes i debattens prompts. Et flagg som skifter av riktig grunn er ikke et bevis på uendrethet;
|
||
tokentallene er. I tillegg: hele suiten
|
||
grønn (**1 195 passed / 5 skipped**, mot 1 189/5 før — supersett, 0 fjernet), tre byte-fasiter OK,
|
||
og golden `demo-transcript.stdout` UENDRET (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`).
|
||
|
||
**Et fravær som var et instrumentfeil, ikke et faktum (Verifiseringsloven ansikt 4).** Den første
|
||
ETTER-kjøringen rapporterte **0 kopier** på energi-a og energi-b. Det var ikke sant: sonden var en
|
||
160-tegns skive fra MIDTEN av nyttelasten, og den nye nyttelasten er kort nok til at midten treffer
|
||
norske tegn, som prompten serialiserer escaped (`å`) mens sonden holdt dem rå. Sonden ble byttet
|
||
til et konseptfilnavn (ASCII, ordrett i begge), og svaret ble 5 — samme tall som før endringen.
|
||
Et negativt resultat fra en ødelagt spørring er ikke en måling. **Alle tre basene er re-målt med
|
||
den rettede sonden** (ikke bare de to som viste feilen): en tabellcelle som stammet fra et
|
||
instrument man selv har erklært ødelagt, er nøyaktig dét dette avsnittet advarer mot.
|
||
|
||
## 6. Ærlighetsgrenser, uttalt
|
||
|
||
1. **Dette er ikke «−59 % kostnad».** Det målte utsagnet er at `read_bundle`s EGET bidrag faller fra
|
||
5 × 12 595 til 5 × 259 tokens på energi-b-basen. En navigatør som deretter åpner *k* dokumenter
|
||
betaler *k* `read_file`-resultater, og en som åpner ALT betaler omtrent de samme bytene — bare
|
||
per kall. Gevinsten er at den betaler for det den valgte, og at hvert resultat rir fra SITT eget
|
||
kall og framover i stedet for at alt rir fra det første.
|
||
2. **At en LEVENDE modell velger BEDRE med en liste enn med hele konteksten er IKKE bevist.** Samme
|
||
klasse som structured-output-grensen: det som er bevist er formen og prisen, ikke dømmekraften.
|
||
3. **Multiplikatoren 5 gjelder dette manuset.** Se § 1.
|
||
4. **Debattens 3× står urørt** og er PM-ens beslutning, ikke denne ordrens.
|
||
5. **Prefiks-caching er notert (§ 3), ikke bygget** — ordren ba om notatet.
|
||
|
||
## 7. Verifiseringslogg (ETTER-halvdelen)
|
||
|
||
| # | Påstand | Kommando → resultat |
|
||
|---|---|---|
|
||
| 6 | Gaten er rød uten sømmen | 7 mutasjoner mot HELE suiten, alle røde: se CLAUDE.md-raden |
|
||
| 7 | Grønn kontroll | `uv run pytest -q` → 1 195 passed / 5 skipped (før: 1 189/5) |
|
||
| 8 | Golden uendret | `PYTHONIOENCODING=utf-8 uv run python -m portfolio_optimiser.simulation \| shasum` → `ea8c5347…` |
|
||
| 9 | Debatten urørt | ETTER-kjøringens `proposer`/`checker`-prompts identiske med FØR-kjøringens, per prompt |
|
||
| 10 | Lint + typer | `uv run ruff check .` → All checks passed; `uv run mypy src` → no issues, 35 files |
|