docs(fase1a): lokal ende-til-ende feller på et 600 s-tak ingen har valgt
Fase 1a kjørt, og den leverte funn — ikke beviset. Stigen: endepunkt (http 200) → --live-dry-run (exit 0) → --scripted-replies (HELE loopen, null modellkall, ValidatedProposal) → ekte modell. De tre første grønne; den fjerde exit 1. Trinn 3 er det som gjør rapporten verdt noe: loopen er bevist sammenhengende UTEN en modell i bildet, så feilen i trinn 4 er attribuerbar til levende modell-output. Fire funn, alle målt: 1. Den lokale klienten har ingen timeout-søm (backends.py:130) — kjøringen døde etter 3 x 600 s = 30 min på SDK-standardverdier. Ikke fikset her: produksjons- kode krever feilende test først. 2. qwen3:4b er resonnerende og brukte hele budsjettet på tankerekka — målt 4,0 tok/s, og 256 tokens ga TOMT svar. 3. Planens `--max-*`-flagg for token-tak finnes ikke; takene er kompilert inn. Inert lokalt, bærende for 1b: en betalt kjøring ville gått under et 100k-tak ingen har valgt. 4. Prompten (4388 tokens) ble STILLE kuttet mot Ollamas 4096-vindu, keep=4 — altså røk formatinstruksjonene først. Advarselen står i Ollamas logg, ikke i vår. Kjøring 2 (qwen2.5:3b uten tankemodus, 16k kontekst) lukket funn 4 men traff samme vegg: 600 s-taket er bindende på denne maskinvaren UANSETT modell — en 1B-modell brukte også over ti minutter per kall mens CPU-en strupet seg til 54 %. Ærlighetsgrense: null vellykkede modellkall. Prompt-former, VERDICT-linja og runde-taket er fortsatt uverifiserte mot en levende modell. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X737cbkG2uAXJ2Bvhf6X5M
This commit is contained in:
parent
5871e397a3
commit
d2deb8ea38
1 changed files with 182 additions and 0 deletions
182
docs/2026-08-13-fase1a-lokal-ende-til-ende.md
Normal file
182
docs/2026-08-13-fase1a-lokal-ende-til-ende.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
# Fase 1a — første ende-til-ende-kjøring mot en levende modell (lokal)
|
||||
|
||||
**Dato:** 2026-08-13 · **Tre:** `5871e39` (v1.0.0 + de to presentasjonsfilene) · **Profil:** `local`
|
||||
|
||||
Rammeverket hadde aldri kjørt mot en levende språkmodell — bare mot skriptede stand-ins.
|
||||
Denne rapporten er den første målingen av hva som faktisk skjer når en ekte modell settes
|
||||
inn i loopen. Fase 1a er den **gratis** halvdelen: en lokal Ollama-kjøring feller alt som
|
||||
ikke er autentisering, slik at den betalte Foundry-kjøringen (1b) bare har RBAC, token og
|
||||
deployment-navn igjen å feile på.
|
||||
|
||||
> **Ærlighetsgrense.** Alt under er målt på ÉN maskin (Intel i9-9880H, CPU-only) med SMÅ
|
||||
> lokale modeller. Tallene beskriver denne maskinen og disse modellene — ikke rammeverkets
|
||||
> ytelse generelt, og ikke hva en Foundry-modell vil gjøre.
|
||||
|
||||
## Miljø
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Maskin | Intel Core i9-9880H, 16 logiske kjerner, ingen GPU-akselerasjon for Ollama |
|
||||
| Ollama | 0.16.3 |
|
||||
| Python / MAF | 3.12.13 / `agent-framework-core` 1.9.0 |
|
||||
| Kunnskapsbase | `shared/examples/bygg-energi-mikro`, prosjekt `BYGG-KONTOR-NORD` |
|
||||
| Tak | `max_rounds=3`, `max_tokens=100000` (kompilert inn, se funn 3) |
|
||||
|
||||
## Stigen: fire trinn, der de tre første er gratis
|
||||
|
||||
Rekkefølgen er ikke tilfeldig. Hvert trinn gjør neste trinns feil attribuerbar.
|
||||
|
||||
| # | Hva | Utfall |
|
||||
|---|---|---|
|
||||
| 1 | Endepunkt nås (`GET /api/tags`) | **http 200** |
|
||||
| 2 | `--live-dry-run` — bygger klient, stopper før første kall | **exit 0**, `models={proposer: qwen3:4b, checker: qwen3:4b}` |
|
||||
| 3 | `--scripted-replies` — HELE loopen, null modellkall | **exit 0**, `ValidatedProposal (decision=approved)` |
|
||||
| 4 | Ekte kjøring mot levende modell | se under |
|
||||
|
||||
Trinn 3 er det som gjør rapporten verdt noe: det beviser at argumentrekka, kunnskapsbasen,
|
||||
debatten, den deterministiske validatoren og domsavsigelsen henger sammen **uten** en modell
|
||||
i bildet. Enhver feil i trinn 4 er dermed forårsaket av levende modell-output, ikke av
|
||||
oppsettet.
|
||||
|
||||
Merk at et grønt trinn 2 **ikke** beviser at endepunktet virker: `--live-dry-run` bygger
|
||||
klienten og stopper før første kall, så det går grønt også med serveren nede. Trinn 1 er
|
||||
derfor en egen måling, ikke en formalitet.
|
||||
|
||||
## Kjøring 1 — den shippede konfigurasjonen (`qwen3:4b`)
|
||||
|
||||
```
|
||||
uv run python -m portfolio_optimiser.run BYGG-KONTOR-NORD --profile local \
|
||||
--docs-dir shared/examples/bygg-energi-mikro \
|
||||
--bundle-dir shared/examples/bygg-energi-mikro
|
||||
```
|
||||
|
||||
**Utfall: exit 1 etter 1804 sekunder (30 minutter), null forslag produsert.**
|
||||
|
||||
```
|
||||
agent_framework.exceptions.ChatClientException: OpenAIChatCompletionClient service failed
|
||||
to complete the prompt: Request timed out. (APITimeoutError)
|
||||
```
|
||||
|
||||
Årsakskjeden er målt hele veien, ikke resonnert:
|
||||
|
||||
1. **Ollamas logg viser tre kall, hvert avbrutt på nøyaktig `10m0s`.** 3 × 600 s = 1800 s,
|
||||
som er de 1804 sekundene kjøringen brukte. Det er OpenAI-SDK-ens standard timeout på
|
||||
600 s pluss dens to standard retries — ingen av delene er valgt av oss (funn 1).
|
||||
2. **Modellen rakk aldri å svare.** Målt direkte på samme maskin: `qwen3:4b` genererer
|
||||
**4,0 tokens/sekund**. Et 600-sekunders vindu rommer altså ~2400 tokens.
|
||||
3. **Og den brukte alt på å tenke.** `qwen3` er en resonnerende modell. I en kontrollmåling
|
||||
med 256 tokens til rådighet var svaret **tomt** — hele budsjettet gikk med til den interne
|
||||
tankerekka `<think>`, før ett eneste tegn av det faktiske svaret ble skrevet (funn 2).
|
||||
|
||||
## Funn
|
||||
|
||||
### Funn 1 — den lokale klienten har ingen timeout-søm (rammeverket)
|
||||
|
||||
`backends.py:130` konstruerer `OpenAIChatCompletionClient(model=..., api_key=..., base_url=...)`.
|
||||
Målt mot klassens signatur: den tar **ingen** `timeout`- eller `max_retries`-parameter. Eneste
|
||||
vei inn er å bygge og sende inn en egen `async_client`.
|
||||
|
||||
Konsekvensen er at enhver maskin som er tregere enn modellen krever, feiler etter 30 minutter
|
||||
med en melding som ser ut som et nettverksproblem. Det er ikke en nettverksfeil — det er et
|
||||
tak ingen har valgt.
|
||||
|
||||
**Ikke fikset her.** Det er produksjonskode og krever en feilende test først (Iron Law), altså
|
||||
egen økt. Formen på fiksen er kjent: la `create_chat_client` bygge `AsyncOpenAI(timeout=…,
|
||||
max_retries=…)` med verdier fra env, og la en load-bearing test bli rød når sømmen kobles fra.
|
||||
|
||||
### Funn 2 — en resonnerende modell bruker hele budsjettet på å tenke
|
||||
|
||||
Dette er ikke en defekt i rammeverket, men det er en **forutsetning ingen hadde skrevet ned**:
|
||||
modell-mappets lokale rolle pekte på `qwen3:4b`, og en resonnerende modell på treg maskinvare
|
||||
når aldri fram til svaret. For den lokale profilen bør standardvalget være en modell **uten**
|
||||
tankemodus — eller tankemodus må skrus av eksplisitt.
|
||||
|
||||
### Funn 3 — planens token-tak-mekanisme finnes ikke i CLI-en
|
||||
|
||||
Planens fase 1b foreskriver «harde token-tak (`--max-*`-flaggene i `run.py`)». **Målt: slike
|
||||
flagg finnes ikke.** Takene er kompilert inn — `_DEFAULT_MAX_ROUNDS = 3`,
|
||||
`_DEFAULT_MAX_TOKENS = 100_000` (`run.py:110-111`) — og kan ikke settes fra kommandolinjen.
|
||||
|
||||
Gratis og uvesentlig lokalt. **For 1b er det bærende:** en betalt kjøring vil ellers gå under
|
||||
et 100 000-tokens tak ingen har tatt stilling til. Dette er en beslutning for operatøren før
|
||||
den første Foundry-kjøringen, ikke noe som skal oppdages under den.
|
||||
|
||||
### Funn 4 — kunnskapsbasen sprengte standard kontekstvindu, stille
|
||||
|
||||
Ollamas logg, kjøring 1:
|
||||
|
||||
```
|
||||
level=WARN msg="truncating input prompt" limit=4096 prompt=4388 keep=4 new=4096
|
||||
```
|
||||
|
||||
Ollamas standard kontekstvindu er 4096 tokens; prompten fra den navigerte kunnskapsbasen er
|
||||
4388. Overskytende ble **kuttet** — og `keep=4` betyr at bare fire tokens fra starten ble
|
||||
bevart, altså at instruksjonene om svarformat sto først i det som røk.
|
||||
|
||||
Advarselen står i Ollamas logg, ikke i vår. En operatør som kjører lokalt ser den ikke.
|
||||
Rettet for kjøring 2 ved å starte serveren med `OLLAMA_CONTEXT_LENGTH=16384`; at dette må
|
||||
gjøres bør stå i den lokale oppskriften.
|
||||
|
||||
## Kjøring 2 — med funn 2 og 4 kompensert
|
||||
|
||||
Modell byttet til `qwen2.5:3b` (**uten** tankemodus) via en modell-map utenfor treet
|
||||
(`PORTFOLIO_MODEL_MAP`), og serveren restartet med `OLLAMA_CONTEXT_LENGTH=16384`. Repoets egen
|
||||
konfig er urørt — dette er en erklært overstyring, ikke den shippede oppsettet.
|
||||
|
||||
**Utfall: samme vegg.** To kall, begge avbrutt på nøyaktig `10m0s`. Kjøringen ble stoppet manuelt
|
||||
framfor å brenne det tredje forsøket mot et kjent utfall.
|
||||
|
||||
Ett funn ble likevel lukket: **null trunkering** i loggen. Det større kontekstvinduet løser funn 4.
|
||||
|
||||
**Diagnosen flyttet seg, og det er den viktigste setningen i rapporten:** flaskehalsen er ikke
|
||||
tankemodusen. Det er at 600-sekunders-taket er bindende på denne maskinvaren **uansett modell**.
|
||||
En kontrollmåling med `llama3.2:1b` — en modell på 1,3 GB — brukte også over ti minutter på ett
|
||||
kall med denne kontekststørrelsen, mens maskinen strupet seg fra 62 % til 54 % klokkefrekvens.
|
||||
|
||||
## Hva som er bevist, og hva som ikke er
|
||||
|
||||
**Bevist:** loopen henger sammen fra ende til annen og produserer et validert forslag — men med
|
||||
skriptede svar, null modellkall (trinn 3).
|
||||
|
||||
**IKKE bevist:** at en levende modell produserer noe loopen kan konsumere. **Null vellykkede
|
||||
modellkall.** Dermed står disse fortsatt åpne, og de var hele grunnen til at 1a finnes:
|
||||
|
||||
- om prompt-formene gir parsebar JSON fra en ekte modell
|
||||
- om checkeren faktisk avslutter med `VERDICT:`-linja
|
||||
- om runde-taket oppfører seg som forutsatt
|
||||
- om validatoren avviser kandidater av riktig grunn
|
||||
|
||||
## Hva dette betyr for 1b
|
||||
|
||||
**Fase 1a har levert funn, ikke beviset.** Den lokale halvdelen kan ikke fullføre på denne
|
||||
maskinvaren uten at funn 1 fikses først — timeout-sømmen er dermed en *forutsetning* for lokal
|
||||
ende-til-ende, ikke en forbedring.
|
||||
|
||||
To veier videre, og de utelukker ikke hverandre:
|
||||
|
||||
1. **Bygg timeout-sømmen** (egen økt, feilende test → fiks → målt mutasjon). Da blir lokal profil
|
||||
kjørbar på treg maskinvare, og 1b arver en klient med et tak noen har valgt.
|
||||
2. **Gå til 1b.** En Foundry-modell svarer på sekunder, ikke minutter — 600 s er ikke bindende der.
|
||||
Ende-til-ende blir da bevist mot ekte modell, som er det operatøren opprinnelig ba om.
|
||||
|
||||
**Kostnad er ikke en begrensning for 1b.** Regnet på Anthropics egen prisliste for Claude Haiku 4.5
|
||||
($1,00/MTok inn, $5,00/MTok ut — Claude er tilgjengelig på Microsoft Foundry til standard
|
||||
API-priser): ~54 000 input-tokens + ~5 000 output-tokens per full kjøring ≈ **$0,08**, altså under
|
||||
én krone. Ti kjøringer er en tier. Det som gjenstår for 1b er ikke penger, men Azure-oppsettet:
|
||||
ressurs, prosjekt, én billig deployment og rollen `Foundry User` — portal-steg bare operatøren kan
|
||||
gjøre.
|
||||
|
||||
**Uverifisert, må måles før det bygges på:** om MAFs `FoundryChatClient` kan binde en
|
||||
Claude-deployment. Foundry-tilgjengeligheten er dokumentert; klient-kompatibiliteten er det ikke.
|
||||
|
||||
## Verifiseringslogg
|
||||
|
||||
| Påstand | Kilde |
|
||||
|---|---|
|
||||
| Trinn 1–3 grønne; trinn 4 exit 1 etter 1804 s | egne kjøringer, stdout/stderr fanget ordrett |
|
||||
| 3 × 600 s timeout + 2 retries er SDK-standard | Ollamas logg (`500 \| 10m0s` × 3) + `agent_framework` traceback |
|
||||
| Klienten har ingen `timeout`-parameter | `inspect.signature(OpenAIChatCompletionClient.__init__)` |
|
||||
| `qwen3:4b` gir 4,0 tok/s; 256 tokens ga tomt svar | `/api/generate` med `eval_count`/`eval_duration` |
|
||||
| Prompt 4388 tokens, kuttet mot 4096 | Ollamas `truncating input prompt`-advarsel |
|
||||
| `_DEFAULT_MAX_ROUNDS = 3`, `_DEFAULT_MAX_TOKENS = 100_000` | `run.py:110-111`; CLI-en har ingen `--max-*` |
|
||||
| Haiku 4.5-priser; Claude på Foundry til standard API-rater | Anthropics modell-/prisdokumentasjon (`claude-api`-skillen) |
|
||||
Loading…
Add table
Add a link
Reference in a new issue