portfolio-optimiser/docs/2026-08-13-fase1a-lokal-ende-til-ende.md
Kjell Tore Guttormsen d2deb8ea38 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
2026-08-13 20:12:50 +02:00

9.5 KiB
Raw Blame History

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 13 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)