CU-analyzere — fra portal via git til worker⚓︎
Kai bruker Azure AI Content Understanding (CU) til å klassifisere og ekstrahere saksdokumenter. Analyzerne — én classifier + ekstraksjon-analyzere per virkemiddel — er git-eide, immutable og innholds-adresserte. Denne siden forklarer jobbflyten og hvorfor den er som den er.
Prinsippet i én setning
Git er kilde til sannhet, portalen er sandkasse. Ingenting en gjør i CU-portalen påvirker Kai før det er eksportert til git, reviewet i PR og deployet — og deploy sletter aldri noe.
Delene⚓︎
| Del | Hva | Hvor |
|---|---|---|
| Analyzer-definisjoner | JSON per virkemiddel: classifier.json + analyzers/<logisk-navn>.json |
contentunderstanding/virkemidler/<vmid>-<slug>/ |
| Deploy-verktøyet | export / lock / deploy (rå REST mot CU, idempotent PUT, aldri DELETE) |
tools/DeployContentUnderstanding |
| Lockfilen | Mapper virkemiddel-id → deployet rot-analyzer-id. Eneste runtime-peker. | contentunderstanding/analyzers.lock.json |
| CU-ressursene | Der analyzerne faktisk lever, én per miljø | aif-kai-test, aif-kai-prod |
| Drift-vakter | Feiler bygget ved lockfile-drift eller kode↔skjema-avvik | AnalyzerSchemaGuardTests (Architecture.Tests) |
Innholds-adresserte id-er⚓︎
En analyzer-id er <virkemiddelid>_<slug>_<hash8>, der hashen beregnes av definisjons-innholdet.
Classifier-roten hasher også leaf-id-ene den refererer (Merkle-struktur): endres én leaf, får både
leafen og roten ny id. Konsekvenser:
- Immutabilitet — en deployet analyzer endres aldri; en endring blir en ny analyzer ved siden av.
- Reproduserbarhet —
case_document_extractions.analyzer_idbokfører nøyaktig hvilken analyzer som ekstraherte et dokument, og facts-blobene er keyet på analyzer-id (…/facts/{analyzerId}/{filnavn}.json). Samme id ⇒ samme definisjon, alltid. - Trygg deploy —
deployPUT-er bare id-er som mangler og sletter aldri; gamle analyzere (og sandkasse-prosjekter i portalen) blir stående.
CU-navneregler
CU forbyr - i analyzer-id-er; verktøyet saniterer selv til _. API-versjon: 2025-11-01.
Jobbflyten⚓︎
flowchart LR
subgraph Sandkasse
Portal[CU-portalen<br/>aif-kai-test]
end
subgraph Git["Git (kilde til sannhet)"]
Def[contentunderstanding/<br/>definisjons-JSON]
Lock[analyzers.lock.json]
end
subgraph Miljø
CU[(CU-ressurs<br/>aif-kai-test/-prod)]
Worker[kai-worker<br/>lockfile innbakt i image]
end
Portal -- "1 · export (stdout → fil)" --> Def
Def -- "2 · PR-review" --> Def
Def -- "3 · deploy --env" --> CU
Def -. "deploy/lock skriver" .-> Lock
Lock -- "4 · merge → kai-worker-pipeline<br/>baker lockfile inn i image" --> Worker
Worker -- "RotIdFor(virkemiddelId)" --> CU
- Forfatt/eksperimenter i portalen (eller rediger JSON-en direkte i git). Portalen er kun et verksted — det du lager der er inert for Kai.
- Eksporter til git:
dotnet run --project tools/DeployContentUnderstanding -- export <analyzerId>skriver renset definisjon til stdout; redirect til riktig fil undercontentunderstanding/virkemidler/…. Commit definisjonen og lockfilen sammen, åpne PR — analyzerne reviewes som all annen kode. - Deploy git → CU:
… -- deploy --env test|prodskriver først lockfilen, deretter idempotent PUT av manglende id-er (leaves før rot). Trygt å kjøre før merge: worker peker fortsatt på gammel id via sin innbakte lockfile. - Merge → runtime:
kai-worker-pipelinen path-trigges avcontentunderstanding/, kjører drift-vaktene og baker den nye lockfilen inn i worker-imaget. Først når det imaget er rullet ut slårFetchDocumentsopp den nye rot-id-en (AnalyzerLockOptions.RotIdFor) — dette er byttepunktet.
Rekkefølgen er ikke valgfri
Deploy mot CU-ressursen (steg 3) må skje før worker-imaget med ny lockfile rulles ut (steg 4) — ellers peker workeren på en rot-id som ikke finnes i CU. Motsatt rekkefølge er ufarlig: gammel analyzer finnes fortsatt, og ny lockfile uten deploy fanges av drift-vakten.
Eksisterende saker re-ekstraheres ikke automatisk ved analyzer-bytte — ekstraksjoner er bokført per analyzer-id, og re-kjøring er et bevisst valg (re-trigg av ExtractDocument).
Vaktene (automatisk i CI)⚓︎
Lockfile_MatchesDefinitions— rebergener rot-id-ene fra definisjonene; haranalyzers.lock.jsondrevet fra innholdet feiler bygget. Fiks: kjør… -- lockog commit.EveryRequiredField_ExistsInSomeAnalyzerDefinition— hvert felt koden faktisk leser (RequiredAnalyzerFieldsi Application-laget) må finnes i en definisjon. Skjemaet kan dermed ikke drive fra koden uten at bygget sier fra.
Manuelt vs. automatisk i dag⚓︎
| Steg | Status |
|---|---|
| Lockfile-drift-vakt + kode↔skjema-vakt | Automatisk (CI, path-trigger) |
| Lockfile bakes inn i worker-image + rulles ut | Automatisk (kai-worker-pipelinen) |
deploy --env (git → CU-ressursen) |
Manuelt operatørsteg |
Automatisert deploy-pipeline (egen arketype + service principal med Cognitive Services Contributor
på aif-kai-{test,prod}) er bevisst utsatt og sporet som egen infra-oppgave (AB#21086). Den må også
overta forutsetningen som i dag er manuell:
Forutsetning på fersk CU-ressurs
Før første deploy mot en uberørt CU-ressurs må ressursens ModelDeployments-defaults være satt
(gpt-4.1, gpt-4.1-mini, text-embedding-3-large) og de tilsvarende Azure
OpenAI-deploymentene finnes. Verktøyet rører aldri defaults-endepunktet. Se
contentunderstanding/README.md i repoet.
Videre lesning⚓︎
- Operatør-oppskriften (kommandoer, fallgruver):
contentunderstanding/README.mdi repoet. - Oppstrøms datakilder: Kildesystemer — ERS og Mimir.