feat(ms-ai-architect): add plugin to open marketplace (v1.5.0 baseline)

Initial addition of ms-ai-architect plugin to the open-source marketplace.
Private content excluded: orchestrator/ (Linear tooling), docs/utredning/
(client investigation), generated test reports and PDF export script.
skill-gen tooling moved from orchestrator/ to scripts/skill-gen/.

Security scan: WARNING (risk 20/100) — no secrets, no injection found.
False positive fixed: added gitleaks:allow to Python variable reference
in output-validation-grounding-verification.md line 109.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-04-07 17:17:17 +02:00
commit 6a7632146e
490 changed files with 213249 additions and 2 deletions

View file

@ -0,0 +1,385 @@
# Azure Arc for AI Management
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Azure Arc er Microsofts svar på utfordringen med å administrere AI-arbeidsbelastninger på tvers av hybride og multicloud-miljøer. For norsk offentlig sektor, der data kan befinne seg i egne datasentre, på Azure Local-klynger eller hos tredjeparts skyleverandorer, gir Arc en sentralisert kontrollflate som gjor det mulig å behandle alle Kubernetes-klynger som forsteklasses Azure-ressurser.
Med Azure Arc-enabled Kubernetes kan organisasjoner koble sammen klynger som kjorer lokalt, i Azure eller hos andre skyleverandorer, og administrere dem fra Azure Portal med ensartede policyer, overvaking og sikkerhetskontroller. Dette er spesielt verdifullt for AI-arbeidsbelastninger som krever GPU-akselerasjon, modellversjonering og sentralisert governance.
For offentlige virksomheter i Norge betyr dette at man kan overholde krav til datasuverenitet og plassering av data, samtidig som man drar nytte av Azures ML-plattform for trening og inferens pa tvers av distribuerte miljoer.
---
## Arkitekturoversikt
Azure Arc for AI Management bygger pa tre lag:
```
┌─────────────────────────────────────────────────┐
│ Azure Control Plane │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Azure │ │ Azure │ │ Azure Machine │ │
│ │ Policy │ │ Monitor │ │ Learning │ │
│ └────┬─────┘ └────┬─────┘ └───────┬──────────┘ │
│ │ │ │ │
│ └─────────────┼───────────────┘ │
│ │ Azure Arc │
└─────────────────────┼───────────────────────────-┘
┌─────────────┼──────────────┐
│ │ │
┌────▼───┐ ┌────▼───┐ ┌─────▼──────┐
│ On-prem│ │ Azure │ │ Multi-cloud│
│ K8s │ │ AKS │ │ K8s │
│ Cluster│ │ Cluster│ │ Cluster │
└────────┘ └────────┘ └────────────┘
```
---
## Arc-enabled Kubernetes for AI
### Tilkobling av klynger
Azure Arc-enabled Kubernetes lar deg koble eksisterende Kubernetes-klynger til Azure for sentralisert administrasjon. Klynger kan kjore pa:
| Plattform | Stotte | Beskrivelse |
|-----------|--------|-------------|
| AKS i Azure | Innebygd | Fullt administrert Kubernetes i skyen |
| AKS pa Azure Local | GA | Kubernetes pa egne servere med Azure-integrasjon |
| Arc-enabled K8s (on-prem) | GA | Alle CNCF-sertifiserte klynger lokalt |
| Arc-enabled K8s (multicloud) | GA | AWS EKS, Google GKE, etc. |
| Edge-enheter | GA | Azure Stack Edge, IoT Edge-enheter |
### Tilkobling med Azure CLI
```bash
# Koble en on-premises klynge til Azure Arc
az connectedk8s connect \
--name my-ai-cluster \
--resource-group ai-rg \
--location norwayeast
# Verifiser tilkobling
az connectedk8s show \
--name my-ai-cluster \
--resource-group ai-rg
```
### Arc-agenter
Nar en klynge kobles til Arc, installeres flere agenter:
| Agent | Funksjon |
|-------|----------|
| `clusteridentityoperator` | Administrerer managed identity for klyngen |
| `clusterconnectoperator` | Hndterer klynge-tilkobling |
| `configoperator` | Overvaker konfigurasjonsendringer |
| `controlleroperator` | Orkestrerer andre agenter |
| `fluxoperator` | GitOps-basert konfigurasjonsadministrasjon |
| `extensionoperator` | Installerer og administrerer klynge-extensions |
---
## Sentralisert ML-modellforvaltning
### Azure Machine Learning Extension
Azure Machine Learning-extensionen er kjernen i AI-forvaltning pa Arc-enabled klynger. Den lar deg bruke Arc-klynger som compute targets for bade trening og inferens.
**Installasjon:**
```bash
# Installer ML-extension pa Arc-enabled klynge
az k8s-extension create \
--name aml-extension \
--extension-type Microsoft.AzureML.Kubernetes \
--cluster-type connectedClusters \
--cluster-name my-ai-cluster \
--resource-group ai-rg \
--scope cluster \
--configuration-settings \
enableTraining=True \
enableInference=True \
inferenceRouterServiceType=LoadBalancer \
allowInsecureConnections=True
```
**Koble til ML workspace:**
```bash
# Attach klynge til Azure ML workspace
az ml compute attach \
--resource-group ai-workspace-rg \
--workspace-name ai-workspace \
--type Kubernetes \
--name arc-compute \
--resource-id "/subscriptions/<sub>/resourceGroups/ai-rg/providers/Microsoft.Kubernetes/connectedClusters/my-ai-cluster" \
--namespace ai-workloads
```
### Bruksmonster for Kubernetes Compute
| Monster | Data | Trening | Inferens | Bruksomrade |
|---------|------|---------|----------|-------------|
| Sky-forst | Sky | Azure | Azure | Standard ML-pipeline |
| Hybrid trening | Lokalt | Lokalt | Sky | Datasuverenitiet, global tilgang |
| Hybrid inferens | Sky | Sky | Lokalt | Latens, compliance |
| Full lokal | Lokalt | Lokalt | Lokalt | Strengt regulert |
| Multi-sky | Distribuert | Begge | Begge | Elastisitet + kontroll |
### KAITO - Kubernetes AI Toolchain Operator
KAITO forenkler deployment av open-source LLM-er pa Arc-enabled Kubernetes:
```yaml
# workspace-phi4.yaml - Deploy Phi-4-mini pa Arc-klynge
apiVersion: kaito.sh/v1alpha1
kind: Workspace
metadata:
name: workspace-phi-4-mini
spec:
resource:
instanceType: Standard_NC4_A2
labelSelector:
matchLabels:
apps: llm-inference
inference:
preset:
name: phi-4-mini-instruct
```
**Stottede GPU-modeller for KAITO pa Azure Local:**
| GPU | VM SKU | Stottede modeller |
|-----|--------|-------------------|
| NVIDIA T4 | Standard_NK6 | Phi-3-mini-4k |
| NVIDIA A2 | Standard_NC4, NC8 | Phi-3-mini, Phi-3.5-mini |
| NVIDIA A16 | Standard_NC16, NC32 | Phi-4-mini, Mistral-7B, Qwen2.5 |
---
## Policy og Compliance Enforcement
### Azure Policy for Kubernetes
Azure Policy kan handheve governance-regler pa tvers av alle Arc-enabled klynger. For AI-arbeidsbelastninger er dette kritisk for a sikre:
- Konsistente sikkerhetsinnstillinger pa tvers av klynger
- Modell-deployment kun til godkjente noder
- Overholdelse av dataklassifisering og suverenitetskrav
- Standardiserte konfigurasjoner for GPU-ressurser
**Installasjon av Policy-extension:**
```bash
# Installer Azure Policy pa Arc-klynge
az k8s-extension create \
--cluster-type connectedClusters \
--cluster-name my-ai-cluster \
--resource-group ai-rg \
--extension-type Microsoft.PolicyInsights \
--name azurepolicy
```
### Innebygde policyer for AI-governance
| Policy | Kategori | Effekt |
|--------|----------|--------|
| Kubernetes-klynger bor ikke tillate privilegerte containere | Sikkerhet | Deny |
| Kubernetes-klynger bor bruke interne lastbalanserere | Nettverk | Deny |
| Kubernetes-klynger bor ha Azure Policy-addon | Compliance | Audit |
| Kubernetes-klynger bor kun bruke godkjente container images | Supply chain | Deny |
| Kubernetes-klynger bor ha resursgrenser | Ressurs | Audit |
### Tilpassede policyer for AI
```json
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Kubernetes/connectedClusters"
},
{
"field": "tags['ai-workload']",
"exists": true
}
]
},
"then": {
"effect": "auditIfNotExists",
"details": {
"type": "Microsoft.KubernetesConfiguration/extensions",
"existenceCondition": {
"field": "Microsoft.KubernetesConfiguration/extensions/extensionType",
"equals": "Microsoft.AzureML.Kubernetes"
}
}
}
}
```
---
## Multi-cluster AI Governance
### Azure Kubernetes Fleet Manager
For organisasjoner med mange AI-klynger gir Fleet Manager sentralisert koordinering:
| Funksjon | Beskrivelse |
|----------|-------------|
| Cluster grouping | Grupper klynger etter formål (trening, inferens, edge) |
| Update orchestration | Koordinerte oppdateringer pa tvers av klynger |
| Configuration propagation | Distribuer GitOps-konfigurasjoner til mange klynger |
| Multi-cluster networking | Service discovery pa tvers av klynger |
### GitOps-basert AI-modell-distribusjon
Bruk Flux v2 for a distribuere AI-modeller og konfigurasjoner:
```bash
# Konfigurer GitOps med Flux v2 for modell-deployment
az k8s-configuration flux create \
--name ai-model-config \
--cluster-name my-ai-cluster \
--resource-group ai-rg \
--cluster-type connectedClusters \
--namespace ai-models \
--scope namespace \
--url https://github.com/org/ai-model-configs \
--branch main \
--kustomization name=models path=./models prune=true
```
### Overvaking med Azure Monitor
```bash
# Aktiver Container Insights pa Arc-klynge
az k8s-extension create \
--name azuremonitor-containers \
--cluster-name my-ai-cluster \
--resource-group ai-rg \
--cluster-type connectedClusters \
--extension-type Microsoft.AzureMonitor.Containers
```
**Viktige metrikker a overvake for AI-klynger:**
| Metrikk | Beskrivelse | Terskel |
|---------|-------------|---------|
| GPU-utnyttelse | Prosent GPU-bruk per node | >80% = skaler opp |
| GPU-minne | VRAM-forbruk | >90% = advarsel |
| Inferens-latens | P95 responstid | <500ms for real-time |
| Modell-versjon | Aktiv modellversjon | Match med registeret |
| Pod-restarts | Antall omstarter | >3 = undersok |
---
## Sikkerhetsarkitektur for Arc AI
### Defense in Depth
```
┌────────────────────────────────────────┐
│ 1. Azure RBAC │
│ ┌──────────────────────────────────┐ │
│ │ 2. Kubernetes RBAC │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ 3. Network Policy │ │ │
│ │ │ ┌──────────────────────┐ │ │ │
│ │ │ │ 4. Pod Security │ │ │ │
│ │ │ │ ┌────────────────┐ │ │ │ │
│ │ │ │ │ 5. Container │ │ │ │ │
│ │ │ │ │ Security │ │ │ │ │
│ │ │ │ └────────────────┘ │ │ │ │
│ │ │ └──────────────────────┘ │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
```
### Microsoft Defender for Kubernetes
Defender gir trusselbeskyttelse for alle Arc-enabled klynger:
- Runtime-trusselbeskyttelse
- Sarbarhetsskanning av container images
- Sikkerhetskonfigurasjonskontroller
- Integrasjon med Microsoft Sentinel for SIEM
### Hemmelighetshaandtering
```bash
# Installer Azure Key Vault Secrets Provider
az k8s-extension create \
--cluster-type connectedClusters \
--cluster-name my-ai-cluster \
--resource-group ai-rg \
--extension-type Microsoft.AzureKeyVaultSecretsProvider \
--name akvsecretsprovider
```
---
## Relevans for norsk offentlig sektor
### Datasuverenitetshensyn
| Krav | Arc-losning |
|------|-------------|
| Data ma forbli i Norge | On-prem klynge med Arc management |
| Sentralisert policy | Azure Policy handheves fra Norway East |
| Auditlog | Azure Monitor med lokal lagring |
| Kryptering | Key Vault med CMK i Norway East |
| Tilgangskontroll | Azure RBAC + Kubernetes RBAC |
### Anbefalte Azure-regioner
| Region | Bruk | Data residency |
|--------|------|----------------|
| Norway East | Primaer kontrollflate | Norge |
| Norway West | DR/backup | Norge |
| West Europe | Fallback, utvidede tjenester | EU/EFTA |
### NSM-krav og Arc
Nasjonal sikkerhetsmyndighet (NSM) sine grunnprinsipper for IKT-sikkerhet kan mappes mot Arc-kapabiliteter:
| NSM-prinsipp | Arc-kontroll |
|--------------|--------------|
| Kartlegg enheter og programvare | Arc inventory og tagging |
| Ha kontroll pa nettverk og systemer | Azure Policy, Network Policy |
| Beskytt data | Kryptering, Key Vault |
| Overlapp/overvak | Azure Monitor, Defender |
| Styring og kontroll | RBAC, governance hierarki |
---
## Begrensninger og hensyn
| Begrensning | Beskrivelse | Workaround |
|-------------|-------------|------------|
| Outbound connectivity | Arc krever utgaende HTTPS | Proxy-stotte tilgjengelig |
| Extension-kompatibilitet | Ikke alle extensions stotter alle distribusjoner | Sjekk kompatibilitetsmatrise |
| GPU-stotte | KAITO pa Arc kun for Azure Local (preview) | Bruk Azure ML extension for andre |
| Skalering | Ingen auto-scaling for Kubernetes compute i ML | Manuell skalering |
| Modellkatalog | Model Catalog ikke stottet pa Kubernetes endpoints | Bruk custom modeller |
---
## For Cosmo
- **Azure Arc er brokken mellom lokale AI-klynger og Azures skybaserte administrasjon** — alle Kubernetes-klynger blir forsteklasses Azure-ressurser med policy, overvaking og ML-integrasjon.
- **KAITO (Kubernetes AI Toolchain Operator) forenkler LLM-deployment** pa Arc-enabled klynger, spesielt pa Azure Local med GPU-stotte for Phi-4, Mistral og Qwen-modeller.
- **Azure Policy for Kubernetes handhever governance pa tvers av alle klynger** — fra on-prem til multicloud — med innebygde og tilpassede policyer for AI-arbeidsbelastninger.
- **For norsk offentlig sektor er Arc losningen for "data forblir lokalt, styring fra skyen"** — kontrollflaten i Norway East, data pa egne servere, med full auditlog og kryptering.
- **Multi-cluster governance med Fleet Manager og GitOps** gir skalerbar, deklarativ modell- og konfigurasjonsstyring for distribuerte AI-miljoer.

View file

@ -0,0 +1,366 @@
# Azure Confidential Computing for AI
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Azure Confidential Computing (ACC) beskytter data under prosessering (data-in-use) ved hjelp av hardware-baserte Trusted Execution Environments (TEE). For AI-arbeidsbelastninger betyr dette at modeller og inferensdata kan beskyttes mot uautorisert tilgang — inkludert fra skyoperatoren selv. Dette er en gamechanger for organisasjoner som prosesserer sensitive data med AI.
For norsk offentlig sektor losner ACC en fundamental utfordring: hvordan bruke sky-basert AI-kraftig hardware (GPU-er, akseleratorer) for sensitive data uten a kompromittere datasikkerheten. NSM Grunnprinsipper og Schrems II-krav kan ivaretas ved at data aldri eksisteres i klartekst utenfor TEE — selv Microsoft som skyoperator kan ikke se dataene.
Microsoft tilbyr flere ACC-tilbud for AI: Confidential VMs basert pa AMD SEV-SNP for CPU-baserte arbeidsbelastninger, Confidential GPU VMs med NVIDIA H100 for GPU-akselerert AI, Confidential Containers pa ACI og AKS, og Azure Attestation for verifisering av TEE-integritet.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| AMD SEV-SNP | Minnesikring for hele VM | CPU-basert TEE |
| Intel TDX | Trust Domain Extensions for VM-isolasjon | CPU-basert TEE (Preview) |
| Intel SGX | Application-level enclaves | Enclave-basert TEE |
| NVIDIA H100 TEE | GPU-basert confidential computing | Confidential GPU VM |
| Azure Attestation | Verifisering av TEE-tilstand | PaaS-tjeneste |
| Confidential VMs | Kryptert VM-minne | DCasv5, ECasv5, NCCadsH100v5 |
| Confidential Containers | Container-isolasjon i TEE | ACI, AKS |
| Azure Key Vault mHSM | Nokkelhandtering i HSM | FIPS 140-2 Level 3 |
---
## TEE-Enabled Model Execution
### Confidential VM for AI-inferens
```bash
# Opprett Confidential VM for AI-arbeidslast (AMD SEV-SNP)
az vm create \
--resource-group rg-confidential-ai \
--name vm-confidential-inference \
--image "Canonical:0001-com-ubuntu-confidential-vm-jammy:22_04-lts-cvm:latest" \
--size Standard_DC4as_v5 \
--security-type ConfidentialVM \
--os-disk-security-encryption-type VMGuestStateOnly \
--enable-vtpm true \
--enable-secure-boot true \
--admin-username azureuser \
--generate-ssh-keys
# Installer AI runtime
az vm run-command invoke \
--resource-group rg-confidential-ai \
--name vm-confidential-inference \
--command-id RunShellScript \
--scripts "
pip install onnxruntime torch transformers
# Modell og data er kryptert i minnet av AMD SEV-SNP
"
```
### Confidential GPU VM for AI (NVIDIA H100)
```bash
# Opprett Confidential GPU VM med NVIDIA H100 TEE
az vm create \
--resource-group rg-confidential-ai \
--name vm-confidential-gpu \
--image "microsoft-dsvm:ubuntu-hpc:2204:latest" \
--size Standard_NCCads_H100_v5 \
--security-type ConfidentialVM \
--os-disk-security-encryption-type DiskWithVMGuestState \
--enable-vtpm true \
--admin-username azureuser \
--generate-ssh-keys
```
### Linked CPU-GPU TEE-arkitektur
```
┌─────────────────────────────────────────┐
│ Confidential GPU VM │
│ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ CPU TEE │ │ GPU TEE │ │
│ │ (AMD SNP) │←→│ (NVIDIA H100) │ │
│ │ │ │ │ │
│ │ - Datainntak │ │ - Inferens │ │
│ │ - Pre/post │ │ - Training │ │
│ │ - Orkestrering│ │ - Tensor ops │ │
│ └──────────────┘ └────────────────┘ │
│ ↑ ↑ │
│ Kryptert minne Kryptert VRAM │
│ (aldri i klartekst utenfor TEE) │
└─────────────────────────────────────────┘
```
---
## Encrypted Inference Pipelines
### End-to-end kryptert inferens
```python
# Confidential inferens med attestation-basert nokkelfrigivelse
from azure.identity import DefaultAzureCredential
from azure.keyvault.keys import KeyClient
from azure.attestation import AttestationClient
import onnxruntime as ort
class ConfidentialInferencePipeline:
def __init__(self):
self.credential = DefaultAzureCredential()
self.attestation_client = AttestationClient(
endpoint="https://sharedeus.eus.attest.azure.net",
credential=self.credential
)
async def run_confidential_inference(self, encrypted_input: bytes) -> bytes:
"""Kjor inferens med end-to-end kryptering"""
# Steg 1: Generer TEE-attestasjonsrapport
attestation_report = self._generate_attestation()
# Steg 2: Hent dekrypteringsnokkel via Secure Key Release (SKR)
decryption_key = await self._secure_key_release(attestation_report)
# Steg 3: Dekrypter input innenfor TEE
# (Data er kun i klartekst innenfor TEE-minnet)
plaintext_input = self._decrypt_in_tee(encrypted_input, decryption_key)
# Steg 4: Kjor inferens
result = self._run_model(plaintext_input)
# Steg 5: Krypter output for returnerning
encrypted_output = self._encrypt_in_tee(result, decryption_key)
return encrypted_output
def _generate_attestation(self) -> dict:
"""Generer hardware-attestasjonsrapport fra AMD SEV-SNP"""
# Hent SNP attestation report fra /dev/sev-guest
# Rapporten inkluderer:
# - Platform-versjon og firmware
# - VM measurement (hash av VM-konfigurasjon)
# - Runtime measurement
import subprocess
report = subprocess.run(
["snp-report", "--format", "json"],
capture_output=True, text=True
)
return {
"snp_report": report.stdout,
"runtime_data": self._get_runtime_claims()
}
async def _secure_key_release(self, attestation: dict) -> bytes:
"""Frigivelse av nokkel basert pa attestasjon"""
# Azure Attestation verifiserer TEE-tilstand
result = self.attestation_client.attest_snp_vm(
report=attestation["snp_report"],
runtime_data=attestation["runtime_data"]
)
# Kun hvis attestasjon er gyldig, frigir Key Vault nokkelen
key_client = KeyClient(
vault_url="https://myvault.vault.azure.net",
credential=self.credential
)
return key_client.release_key(
name="inference-key",
target_attestation_token=result.token
)
```
### Confidential Containers for AI
```yaml
# Confidential container deployment pa Azure Container Instances
# Container-gruppen kjorer i AMD SEV-SNP TEE
apiVersion: 2023-05-01
name: confidential-inference
location: norwayeast
properties:
confidentialComputeProperties:
ccePolicy: "<base64-encoded-rego-policy>"
containers:
- name: inference-engine
properties:
image: myregistry.azurecr.io/confidential-inference:v1
resources:
requests:
cpu: 4
memoryInGB: 16
environmentVariables:
- name: MODEL_PATH
value: /models/encrypted_model.enc
- name: ATTESTATION_ENDPOINT
value: "https://sharedneu.neu.attest.azure.net"
volumeMounts:
- name: model-volume
mountPath: /models
osType: Linux
sku: Confidential
volumes:
- name: model-volume
azureFile:
shareName: encrypted-models
storageAccountName: mystorageaccount
```
---
## Attestation for Compliance
### Azure Attestation-flyten
```
┌──────────┐ ┌───────────────┐ ┌──────────────┐
│ TEE/CVM │────→│ Azure │────→│ Relying │
│ │ │ Attestation │ │ Party │
│ Generer │ │ │ │ │
│ Evidence │ │ Verifiser │ │ Frigir data/ │
│ │ │ Evaluer policy│ │ noekler │
└──────────┘ └───────────────┘ └──────────────┘
```
### Attestasjonspolicy for AI-arbeidsbelastninger
```json
// SKR-policy for Confidential AI VM
{
"version": "1.0.0",
"anyOf": [
{
"authority": "https://sharedneu.neu.attest.azure.net",
"allOf": [
{
"claim": "x-ms-compliance-status",
"equals": "azure-compliant-cvm"
},
{
"claim": "x-ms-sevsnpvm-is-debuggable",
"equals": "false"
},
{
"claim": "x-ms-sevsnpvm-vmpl",
"equals": "0"
},
{
"claim": "x-ms-isolation-tee.x-ms-attestation-type",
"equals": "sevsnpvm"
}
]
}
]
}
```
### Compliance-rapportering
```python
# Generer compliance-rapport for confidential AI
class ConfidentialAIComplianceReport:
def generate_report(self) -> dict:
return {
"platform": {
"type": "Azure Confidential VM",
"tee": "AMD SEV-SNP",
"firmware_version": self._get_firmware_version(),
"attestation_status": "verified"
},
"data_protection": {
"encryption_at_rest": "AES-256 (Customer-managed key)",
"encryption_in_transit": "TLS 1.3",
"encryption_in_use": "AMD SEV-SNP memory encryption",
"key_management": "Azure Key Vault Managed HSM"
},
"access_control": {
"operator_access": "Denied (TEE-enforced)",
"attestation_required": True,
"secure_key_release": True
},
"compliance_frameworks": [
"GDPR Art. 32 (data-in-use protection)",
"Schrems II (operator cannot access data)",
"NSM Grunnprinsipper (kryptering ved bruk)",
"ISO 27001 A.10 (cryptographic controls)"
],
"audit_trail": {
"attestation_logs": "Azure Monitor",
"key_release_logs": "Key Vault audit log",
"inference_metadata": "Application Insights"
}
}
```
---
## Performance Trade-offs
### Ytelsespavirkning av Confidential Computing
| Arbeidsbelastning | Uten CC | Med CC (CPU TEE) | Med CC (GPU TEE) | Overhead |
|-------------------|---------|-------------------|-------------------|----------|
| ONNX inferens (CPU) | 10 ms | 11-12 ms | N/A | 10-20% |
| PyTorch inferens (GPU) | 5 ms | N/A | 5.5-6 ms | 10-20% |
| LLM generering (GPU) | 30 tok/s | N/A | 25-28 tok/s | 7-17% |
| Embedding-generering | 50 ms/batch | 55-60 ms/batch | 52-55 ms/batch | 4-20% |
| Modell-lasting | 5 s | 7-8 s | 6-7 s | 20-40% |
### Optimalisering for lavere overhead
| Optimalisering | Beskrivelse | Forventet forbedring |
|----------------|-------------|---------------------|
| Batching | Samle flere inferensforesp. | Amortiser TEE-overhead |
| Model caching | Hold modell i TEE-minne | Unnga re-dekryptering |
| Forhands-attestasjon | Cache attestasjonstoken | Reduser latens per kall |
| NUMA-optimalisering | Pin til korrekt NUMA-node | Bedre minneytelse |
| Hugepage-allokering | Bruk store minnesider | Reduser TLB-misser |
---
## Norsk offentlig sektor
### Hvorfor Confidential Computing for norsk offentlig AI?
- **Schrems II-kompatibilitet**: Data er kryptert under prosessering — selv Microsoft kan ikke se innholdet, noe som adresserer europeisk personvernlovgivning
- **NSM-krav**: Grunnprinsipper for IKT-sikkerhet krever kryptering, og CC utvider dette til data-in-use
- **Flerpartisanalyse**: Kommuner og etater kan analysere data sammen uten a eksponere radata for hverandre
- **AI pa sensitive data**: Helsedata (pasientjournaler), persondata (NAV), og justisdata kan prosesseres med AI uten a kompromittere personvern
### Bruksscenarier
| Etat | Scenario | CC-komponent |
|------|----------|--------------|
| NAV | AI-analyse av soknader med persondata | Confidential VM |
| Helsedirektoratet | Prediktiv analyse pa helsejournaler | Confidential GPU VM |
| Politiet | Biometrisk matching | SGX enclaves |
| Skatteetaten | Fraud detection pa skattedata | Confidential Containers |
| Kommuner | Flerparts-analyse av velferdsdata | Confidential ACI |
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| CPU-basert AI, enkel migrasjon | Confidential VM (SEV-SNP) | Ingen kodeendringer |
| GPU-akselerert AI, hoy ytelse | NCCadsH100v5 Confidential GPU VM | Linked CPU+GPU TEE |
| Container-baserte mikrotjenester | Confidential Containers pa ACI/AKS | Container-policy-basert sikkerhet |
| Application-level isolasjon | Intel SGX enclaves | Minst mulig TCB |
| Flerpartssanalyse | Confidential Containers + Attestation | Verifiserbar isolasjon |
| Strengeste compliance-krav | Confidential GPU + mHSM + Attestation | Full stack confidentiality |
---
## For Cosmo
- **Confidential Computing losner data-in-use-problemet for sky-AI** — anbefal Confidential VMs (AMD SEV-SNP) som forste steg for organisasjoner som noler med sky-AI pa grunn av datasikkerhetshensyn
- **Ytelsesoverhead er typisk 10-20%** — dette er akseptabelt for de fleste arbeidsbelastninger og kan optimaliseres med batching og modell-caching innenfor TEE
- **Azure Attestation + Secure Key Release er pabudt for compliance** — modeller og data bor kun dekrypteres etter vellykket attestasjon som beviser at arbeidsmiljoet er integert
- **For norsk offentlig sektor: Confidential GPU VMs (H100) er den mest lovende losningen** for a kjore avansert AI pa sensitive data i skyen — den kombinerer GPU-ytelse med TEE-beskyttelse
- **Dokumenter alltid TEE-stack, attestasjonspolicy og nokkelhandtering** i sikkerhetsarkitekturen — dette er konkret bevis for compliance i DPIA og sikkerhetsrevisjoner

View file

@ -0,0 +1,451 @@
# Azure IoT Hub and AI Pipeline
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Azure IoT Hub er Microsofts sentrale PaaS-tjeneste for toveiskommunikasjon mellom IoT-enheter og skyen. Kombinert med Azure Stream Analytics for sanntidsanalyse og Azure Machine Learning for modelltrening og -scoring, danner IoT Hub kjernen i en enhetlig AI-pipeline fra enhet til innsikt.
For norsk offentlig sektor er denne arkitekturen relevant for scenarioer som smart veginfrastruktur (sanntidsmaling av trafikk og veiforhold), bygg-automatisering (energistyring i offentlige bygninger), miljooverkaking (luft- og vannkvalitet), og prediktiv vedlikehold av kritisk infrastruktur. IoT Hub gir sikker enhetstilkobling, mens Stream Analytics prosesserer data i sanntid, og Azure ML scorer modeller for prediktive innsikter.
Arkitekturen skalerer fra hundrevis til millioner av enheter, med innebygd stoette for meldingsruting, device twins for konfigurasjonstyring, og enkel integrasjon med Azure-dataplatformen (Fabric, Event Hub, Cosmos DB) for langsiktig analyse.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Azure IoT Hub | Sentral enhetskommunikasjon og -styring | PaaS |
| Azure Stream Analytics | Sanntids stromprosessering | SQL-basert |
| Azure Machine Learning | Modelltrening og online scoring | ML Platform |
| Event Hub | Hoyvolum meldingsinntak | Event streaming |
| Azure Cosmos DB | Sanntids operasjonell database | NoSQL |
| Azure Data Lake / Fabric | Langsiktig dataanalyse | Analytics |
| Power BI | Sanntids dashboards | Visualisering |
| IoT Edge | Lokal prosessering pa enheter | Container runtime |
---
## Device-to-Hub Data Flow
### Arkitektur for enhet-til-sky-dataflyt
```
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Sensorer │────→│ IoT Edge │────→│ IoT Hub │
│ (MQTT) │ │ Gateway │ │ │
└──────────┘ └──────────┘ │ - Routing │
│ - Enrichment│
┌──────────┐ │ - Twin mgmt │
│ Direkte │─────────────────────→│ │
│ enheter │ └──────┬───────┘
│ (AMQP) │ │
└──────────┘ ┌─────────┼─────────┐
↓ ↓ ↓
┌──────────┐ ┌────────┐ ┌────────┐
│ Stream │ │ Event │ │ Cosmos │
│ Analytics│ │ Hub │ │ DB │
└──────────┘ └────────┘ └────────┘
↓ ↓ ↓
┌──────────┐ ┌────────┐ ┌────────┐
│ Azure ML │ │ Fabric │ │ Power │
│ Scoring │ │ │ │ BI │
└──────────┘ └────────┘ └────────┘
```
### IoT Hub meldingsruting
```json
// IoT Hub meldingsruting for AI pipeline
{
"routes": [
{
"name": "realtime-to-stream-analytics",
"source": "DeviceMessages",
"condition": "temperature > 0 OR vibration > 0",
"endpointNames": ["stream-analytics-endpoint"],
"isEnabled": true
},
{
"name": "anomalies-to-event-hub",
"source": "DeviceMessages",
"condition": "$body.alert = 'anomaly'",
"endpointNames": ["anomaly-event-hub"],
"isEnabled": true
},
{
"name": "all-data-to-storage",
"source": "DeviceMessages",
"condition": "true",
"endpointNames": ["datalake-storage"],
"isEnabled": true
},
{
"name": "device-lifecycle-to-cosmos",
"source": "DeviceLifecycleEvents",
"condition": "true",
"endpointNames": ["cosmos-db-endpoint"],
"isEnabled": true
}
]
}
```
### Enhetstilkobling med Python SDK
```python
# IoT-enhet sender sensordata til IoT Hub
from azure.iot.device import IoTHubDeviceClient, Message
import json
import time
class SensorDevice:
def __init__(self, connection_string: str):
self.client = IoTHubDeviceClient.create_from_connection_string(
connection_string
)
self.client.connect()
def send_telemetry(self, sensor_data: dict):
"""Send sensordata med metadata for ruting"""
message = Message(
json.dumps(sensor_data),
content_encoding="utf-8",
content_type="application/json"
)
# Egendefinerte properties for meldingsruting
message.custom_properties["sensorType"] = sensor_data.get("type", "unknown")
message.custom_properties["location"] = sensor_data.get("location", "unknown")
# Sett prioritet for anomalier
if sensor_data.get("alert"):
message.custom_properties["priority"] = "high"
self.client.send_message(message)
def start_continuous_telemetry(self, interval_seconds: int = 10):
"""Kontinuerlig sending av sensordata"""
while True:
data = self.read_sensors()
self.send_telemetry(data)
time.sleep(interval_seconds)
def read_sensors(self) -> dict:
"""Les sensorverdier (simulert)"""
import random
return {
"timestamp": time.time(),
"temperature": random.uniform(18.0, 25.0),
"humidity": random.uniform(30.0, 70.0),
"vibration": random.uniform(0.0, 5.0),
"type": "environment",
"location": "building-A-floor-2"
}
```
---
## Stream Processing for AI
### Azure Stream Analytics for IoT AI
```sql
-- Sanntids anomalideteksjon med Stream Analytics
-- Kombinerer sensordata med ML-scoring
-- Query 1: Glidende statistikk per enhet
WITH DeviceStats AS (
SELECT
IoTHub.ConnectionDeviceId AS DeviceId,
System.Timestamp() AS WindowEnd,
AVG(temperature) AS AvgTemp,
STDEV(temperature) AS StdTemp,
MIN(temperature) AS MinTemp,
MAX(temperature) AS MaxTemp,
COUNT(*) AS ReadingCount
FROM
IoTHubInput TIMESTAMP BY EventProcessedUtcTime
GROUP BY
IoTHub.ConnectionDeviceId,
SlidingWindow(minute, 10)
)
-- Query 2: Anomalideteksjon med statistisk terskel
SELECT
ds.DeviceId,
ds.WindowEnd,
ds.AvgTemp,
ds.StdTemp,
CASE
WHEN ds.AvgTemp > (ref.NormalAvg + 3 * ref.NormalStd) THEN 'HIGH_ANOMALY'
WHEN ds.AvgTemp > (ref.NormalAvg + 2 * ref.NormalStd) THEN 'WARNING'
WHEN ds.AvgTemp < (ref.NormalAvg - 3 * ref.NormalStd) THEN 'LOW_ANOMALY'
ELSE 'NORMAL'
END AS Status,
ref.DeviceName,
ref.Location
INTO
AnomalyOutput
FROM
DeviceStats ds
JOIN ReferenceData ref ON ds.DeviceId = ref.DeviceId
WHERE
ds.ReadingCount >= 5 -- Minst 5 malinger for palitelighet
-- Query 3: Dataaggregering for ML-trening
SELECT
IoTHub.ConnectionDeviceId AS DeviceId,
System.Timestamp() AS WindowEnd,
AVG(temperature) AS AvgTemp,
AVG(humidity) AS AvgHumidity,
AVG(vibration) AS AvgVibration,
STDEV(vibration) AS StdVibration,
MAX(vibration) AS PeakVibration,
COUNT(*) AS SampleCount
INTO
MLTrainingOutput
FROM
IoTHubInput TIMESTAMP BY EventProcessedUtcTime
GROUP BY
IoTHub.ConnectionDeviceId,
TumblingWindow(hour, 1)
```
### Stream Analytics med innebygd anomalideteksjon
```sql
-- Bruk innebygd AnomalyDetection-funksjon
SELECT
IoTHub.ConnectionDeviceId AS DeviceId,
temperature,
AnomalyDetection_SpikeAndDip(
temperature,
95, -- Konfidensniaa
120, -- Vindusstoorrelse
'spikesanddips'
) OVER (
PARTITION BY IoTHub.ConnectionDeviceId
LIMIT DURATION(minute, 120)
) AS AnomalyResult
INTO
AnomalyAlertOutput
FROM
IoTHubInput TIMESTAMP BY EventProcessedUtcTime
```
---
## Real-Time Model Scoring
### Azure ML Online Endpoint for IoT-scoring
```python
# Azure ML endpoint for sanntids IoT-scoring
from azure.ai.ml import MLClient
from azure.ai.ml.entities import (
ManagedOnlineEndpoint,
ManagedOnlineDeployment,
Model
)
from azure.identity import DefaultAzureCredential
def deploy_iot_scoring_endpoint(ml_client: MLClient):
"""Deploy sanntids scoring-endpoint for IoT-data"""
# Opprett endpoint
endpoint = ManagedOnlineEndpoint(
name="iot-anomaly-scoring",
auth_mode="key",
description="Anomalideteksjon for IoT-sensordata"
)
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# Deploy modell
deployment = ManagedOnlineDeployment(
name="anomaly-v1",
endpoint_name="iot-anomaly-scoring",
model=Model(path="./models/anomaly_model.pkl"),
code_configuration={
"code": "./scoring",
"scoring_script": "score.py"
},
instance_type="Standard_DS3_v2",
instance_count=2, # Redundans for palitelighet
environment="azureml:sklearn-1.0:1"
)
ml_client.online_deployments.begin_create_or_update(deployment).result()
```
### Scoring-script for IoT-data
```python
# score.py — Azure ML scoring-script for IoT
import json
import joblib
import numpy as np
def init():
global model
model = joblib.load("model/anomaly_model.pkl")
def run(raw_data):
"""Score IoT-sensordata mot prediktiv modell"""
data = json.loads(raw_data)
features = np.array([[
data["avg_temperature"],
data["avg_humidity"],
data["avg_vibration"],
data["std_vibration"],
data["peak_vibration"],
data["sample_count"]
]])
prediction = model.predict(features)[0]
probability = model.predict_proba(features)[0]
return json.dumps({
"device_id": data["device_id"],
"prediction": int(prediction),
"failure_probability": float(max(probability)),
"recommendation": (
"SCHEDULE_MAINTENANCE" if prediction == 1
else "NORMAL_OPERATION"
),
"scored_at": data.get("window_end")
})
```
### Stream Analytics integrert med Azure ML
```sql
-- Kall Azure ML endpoint fra Stream Analytics
WITH ScoringInput AS (
SELECT
IoTHub.ConnectionDeviceId AS device_id,
System.Timestamp() AS window_end,
AVG(temperature) AS avg_temperature,
AVG(humidity) AS avg_humidity,
AVG(vibration) AS avg_vibration,
STDEV(vibration) AS std_vibration,
MAX(vibration) AS peak_vibration,
COUNT(*) AS sample_count
FROM IoTHubInput
TIMESTAMP BY EventProcessedUtcTime
GROUP BY
IoTHub.ConnectionDeviceId,
TumblingWindow(minute, 15)
)
SELECT
si.*,
ml.prediction,
ml.failure_probability,
ml.recommendation
INTO MaintenanceOutput
FROM ScoringInput si
CROSS APPLY AzureMLEndpoint(si) AS ml
WHERE ml.failure_probability > 0.5
```
---
## Scaling Hybrid Ingestion
### Skaleringsarkitektur
| Skala | Enheter | IoT Hub SKU | Stream Analytics SU | Anbefaling |
|-------|---------|-------------|--------------------|----|
| Liten | < 1 000 | S1 (1 enhet) | 6 SU | Standard oppsett |
| Medium | 1 000 - 100 000 | S2 (2 enheter) | 12-24 SU | Partisjonering |
| Stor | 100 000 - 1M | S3 (10 enheter) | 48+ SU | Event Hub routing |
| Enterprise | > 1M | S3 + Event Hub | Dedikert klynge | Multi-hub-arkitektur |
### Hybrid skalering med edge-forbehandling
```python
# Hybrid skaleringsmonster: Edge reduserer skylast
class HybridScalingConfig:
"""Konfigurasjon for hybrid edge-sky skalering"""
@staticmethod
def calculate_cloud_load(
total_devices: int,
messages_per_device_per_hour: int,
edge_aggregation_ratio: float = 0.1 # 10% av data sendes til sky
) -> dict:
"""Beregn skylast med edge-forbehandling"""
raw_messages = total_devices * messages_per_device_per_hour
cloud_messages = int(raw_messages * edge_aggregation_ratio)
bandwidth_reduction = 1 - edge_aggregation_ratio
# IoT Hub dimensjonering
messages_per_day = cloud_messages * 24
if messages_per_day < 400_000:
iot_hub_sku = "S1 (1 enhet)"
elif messages_per_day < 6_000_000:
iot_hub_sku = "S2 (1 enhet)"
else:
units = (messages_per_day // 6_000_000) + 1
iot_hub_sku = f"S2 ({units} enheter)"
return {
"total_devices": total_devices,
"raw_messages_per_hour": raw_messages,
"cloud_messages_per_hour": cloud_messages,
"bandwidth_reduction": f"{bandwidth_reduction*100:.0f}%",
"iot_hub_sku": iot_hub_sku,
"estimated_monthly_cost_nok": cloud_messages * 24 * 30 * 0.001
}
```
---
## Norsk offentlig sektor
### Relevante bruksomrader
| Sektor | Use Case | Enheter | AI-modell |
|--------|----------|---------|-----------|
| Samferdsel | Veisensor-nettverket | ~5 000 | Trafikk-prediksjon, vintervedlikehold |
| Energi | Smart bygg-styring | ~10 000/bygg | Energi-optimalisering |
| Miljoe | Luft/vann-kvalitet | ~500 stasjoner | Forurensnings-varsling |
| Helse | Utstyrsovervaking | ~1 000/sykehus | Prediktiv vedlikehold |
| Kyst | Maritime sensorer | ~2 000 | Vaer-prediksjon, sikkerhet |
### Sikkerhetskrav
- IoT Hub-endepunkt i Norway East
- TLS 1.2+ for all enhetskommunikasjon
- X.509-sertifikater for enhetsautentisering
- DPS (Device Provisioning Service) for automatisk registrering
- NSM-kompatibel nettverkssegmentering
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| < 1 000 enheter, enkel analyse | IoT Hub S1 + Stream Analytics | Lavest kostnad og kompleksitet |
| Sanntids ML-scoring | Stream Analytics + Azure ML endpoint | Integrert ML-scoring i strom |
| Hoeyvolum med edge-forbehandling | IoT Edge + IoT Hub S2/S3 | Redusert skylast og kostnad |
| Langsiktig analyse | IoT Hub + Event Hub + Fabric | Skalerbar historisk analyse |
| Prediktiv vedlikehold | Full pipeline med retraining loop | Kontinuerlig modellforbedring |
| Anomalideteksjon | Stream Analytics innebygd anomali | Raskest a implementere |
---
## For Cosmo
- **IoT Hub + Stream Analytics + Azure ML er den kanoniske AI-pipeline for IoT** — anbefal denne treledds-arkitekturen som standard for alle IoT-AI-scenarier i offentlig sektor
- **Edge-forbehandling reduserer skylast med 90%+** — la IoT Edge aggregere og filtrere data for sensordata sendes til sky, noe som dramatisk reduserer baade kostnader og bandbreddekrav
- **Stream Analytics innebygde anomalideteksjon er raskest a implementere** — bruk AnomalyDetection_SpikeAndDip-funksjonen for rask oppstart for du bygger egne ML-modeller
- **Azure ML Online Endpoints gir sanntids scoring fra Stream Analytics** — bruk CROSS APPLY med AzureMLEndpoint-funksjonen for a integrere avansert ML direkte i strom-prosessering
- **For norsk offentlig sektor: Dimensjoner IoT Hub-kapasitet basert pa cloud-meldinger etter edge-aggregering** — med 90% edge-reduksjon kan selv store sensornettverk klare seg med S1/S2-tieren

View file

@ -0,0 +1,399 @@
# Azure Local for Edge AI Workloads
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Azure Local (tidligere Azure Stack HCI) er Microsofts hyperkonvergerte infrastrukturlosning for a kjore Azure-tjenester lokalt. For AI-arbeidsbelastninger tilbyr Azure Local GPU-akselerasjon, Kubernetes-stotte via AKS enabled by Arc, og lokal tilgang til Azure-tjenester — alt administrert fra Azure Portal.
For norsk offentlig sektor representerer Azure Local en unik mulighet: organisasjoner kan plassere AI-infrastruktur i egne datarom eller hos godkjente driftspartnere, samtidig som de far tilgang til Azures ML-plattform, overvaking og governance-verktoy. Dette gir data residency i Norge uten a ga pa kompromiss med moderne AI-kapabiliteter.
Azure Local stotter et bredt utvalg av NVIDIA GPU-er for AI-inferens og trening, inkludert T4, A2, A16, L4, L40 og L40S. Sammen med AKS enabled by Arc og KAITO (Kubernetes AI Toolchain Operator) kan organisasjoner kjore open-source LLM-er som Phi-4, Mistral og Llama direkte pa egen infrastruktur.
---
## Arkitekturoversikt
```
┌───────────────────────────────────────────────┐
│ Azure Control Plane │
│ Azure Portal │ ML Workspace │ Azure Monitor │
└───────────────────────┬───────────────────────┘
│ Azure Arc
┌───────────────────────▼───────────────────────┐
│ Azure Local Cluster │
│ ┌─────────────────────────────────────────┐ │
│ │ AKS enabled by Arc │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ CPU Node │ │ GPU Node │ │ │
│ │ │ Pool │ │ Pool │ │ │
│ │ │ │ │ NVIDIA │ │ │
│ │ │ Services │ │ T4/A2/L4 │ │ │
│ │ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ ┌───────────────┐ ┌─────────────────────┐ │
│ │ Azure Arc VMs │ │ Storage Spaces │ │
│ │ (DDA/GPU-P) │ │ Direct (S2D) │ │
│ └───────────────┘ └─────────────────────┘ │
└───────────────────────────────────────────────┘
```
---
## GPU-stotte i Azure Local
### GPU-tilordningsmetoder
Azure Local stotter to metoder for GPU-bruk:
| Egenskap | Discrete Device Assignment (DDA) | GPU Partitioning (GPU-P) |
|----------|----------------------------------|--------------------------|
| Ressursmodell | Hel GPU til en VM | Delt GPU mellom flere VM-er |
| VM-tetthet | Lav (1 GPU = 1 VM) | Hoy (1 GPU = mange VM-er) |
| Appkompatibilitet | Full (DX12, OpenGL, CUDA) | Full (DX12, OpenGL, CUDA) |
| VRAM | Opp til full GPU VRAM | Per partisjon |
| Driver i gjest | Leverandor-driver (NVIDIA) | Leverandor-driver (NVIDIA) |
| AKS-stotte | Ja | Nei (kun VM-er) |
| Best for | AI-trening, stor inferens | Lettere inferens, delt bruk |
### Stottede NVIDIA GPU-er
| GPU-modell | DDA (Arc VMs) | DDA (AKS) | GPU-P (VMs) | Typisk bruk |
|------------|---------------|-----------|-------------|-------------|
| NVIDIA T4 | Ja | Ja | Nei | Inferens, lette modeller |
| NVIDIA A2 | Ja | Ja | Ja | Inferens, mellomstore modeller |
| NVIDIA A10 | Ja (unmanaged) | Nei | Ja | Trening og inferens |
| NVIDIA A16 | Ja | Ja | Ja | Multi-bruker inferens |
| NVIDIA A40 | Ja (unmanaged) | Nei | Ja | Tung trening |
| NVIDIA L4 | Ja | Ja | Ja | Moderne inferens |
| NVIDIA L40 | Ja | Ja | Ja | Stor modell-inferens |
| NVIDIA L40S | Ja | Ja | Ja | High-end AI workloads |
### GPU-klargjoring
```powershell
# Sjekk GPU-status pa Azure Local-noder
Get-PnpDevice | Select-Object Status, Class, FriendlyName, InstanceId |
Where-Object { $_.FriendlyName -match "Nvidia" }
# Installer mitigation driver for DDA
# (Kreves for Azure Local 23H2+)
pnputil /add-driver oem_mitigation.inf /install
# Verifiser GPU er dismounted og klar for DDA
Get-VMHostAssignableDevice
```
---
## Cluster-felles ML Stack
### AKS enabled by Azure Arc pa Azure Local
AKS pa Azure Local gir en fullt administrert Kubernetes-opplevelse med GPU-stotte:
**Oppretting av klynge med GPU:**
```bash
# Opprett AKS-klynge pa Azure Local
az aksarc create \
--resource-group ai-edge-rg \
--name ai-edge-cluster \
--custom-location my-custom-location \
--vnet-ids /subscriptions/.../virtualNetworks/ai-vnet
# Legg til GPU node pool
az aksarc nodepool add \
--cluster-name ai-edge-cluster \
--name gpu-pool \
--resource-group ai-edge-rg \
--node-count 2 \
--node-vm-size Standard_NC4_A2 \
--os-type Linux
```
**GPU VM SKU-er tilgjengelige:**
| VM SKU | GPU | vCPU | Minne (GB) | GPU-minne |
|--------|-----|------|------------|-----------|
| Standard_NK6 | T4 (1x) | 6 | 56 | 16 GB |
| Standard_NC4 | A2 (1x) | 4 | 28 | 16 GB |
| Standard_NC8 | A2 (1x) | 8 | 56 | 16 GB |
| Standard_NC16 | A16 (1x) | 16 | 112 | 16 GB |
| Standard_NC32 | A16 (2x) | 32 | 224 | 32 GB |
### KAITO pa Azure Local
Kubernetes AI Toolchain Operator (KAITO) kjorer som en cluster extension:
```bash
# Opprett klynge med KAITO aktivert
az aksarc create \
--resource-group ai-edge-rg \
--name ai-kaito-cluster \
--custom-location my-custom-location \
--vnet-ids /subscriptions/.../virtualNetworks/ai-vnet \
--enable-ai-toolchain-operator
# Aktiver KAITO pa eksisterende klynge
az aksarc update \
--resource-group ai-edge-rg \
--name ai-edge-cluster \
--enable-ai-toolchain-operator
```
**Deploy LLM med KAITO:**
```yaml
# workspace-phi4-mini.yaml
apiVersion: kaito.sh/v1alpha1
kind: Workspace
metadata:
name: workspace-phi-4-mini
spec:
resource:
instanceType: Standard_NC8
labelSelector:
matchLabels:
apps: llm-inference
inference:
preset:
name: phi-4-mini-instruct
```
```bash
# Deploy modellen
kubectl apply -f workspace-phi4-mini.yaml
# Sjekk status
kubectl get workspace -w
# Test inferens
export SERVICE_IP=$(kubectl get svc workspace-phi-4-mini \
-o jsonpath='{.spec.clusterIP}')
kubectl run -it --rm curl --image=curlimages/curl -- \
curl -X POST http://$SERVICE_IP/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "phi-4-mini-instruct",
"prompt": "Forklar fordelene med edge AI for offentlig sektor",
"max_tokens": 200
}'
```
---
## Local Azure Services
### Azure-tjenester som kjorer lokalt
Azure Local gir tilgang til et voksende utvalg Azure-tjenester direkte pa lokale servere:
| Tjeneste | Tilgjengelighet | AI-relevans |
|----------|-----------------|-------------|
| AKS (Arc) | GA | Container-basert ML og inferens |
| Azure Arc VMs | GA | GPU-akselererte VM-er for AI |
| Azure Monitor | GA | Overvaking av AI-arbeidsbelastninger |
| Azure Policy | GA | Governance for AI-deployments |
| Azure Key Vault | GA | Hemmelighetshaandtering for modeller |
| Azure Container Registry | Preview | Lokal container-lagring (disconnected) |
| Azure Arc Data Services | GA | SQL og PostgreSQL for AI-data |
### Lokal Container Registry for disconnected drift
```bash
# Opprett lokal ACR for disconnected miljoer
# (Azure Local med autonome operasjoner)
az acr create \
--name myedgeregistry \
--resource-group ai-edge-rg \
--location autonomous \
--sku Standard
```
---
## Storage-optimalisert inferens
### Storage Spaces Direct (S2D)
Azure Local bruker S2D for distribuert lagring med hoy ytelse:
| Lagringstype | Best for | Ytelse |
|-------------|----------|--------|
| NVMe + SSD tiered | Modell-lasting | <100ms load for 7B modell |
| All-flash NVMe | Real-time inferens | Sub-ms I/O |
| SSD + HDD tiered | Modell-arkiv, batch | Kostnadsoptimalt |
### Persistent Volume for ML-modeller
```yaml
# persistent-volume-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ai-model-storage
namespace: ai-workloads
spec:
accessModes:
- ReadWriteMany
storageClassName: azure-local-ssd
resources:
requests:
storage: 100Gi
---
# Pod med modell-lagring
apiVersion: v1
kind: Pod
metadata:
name: model-server
namespace: ai-workloads
spec:
containers:
- name: inference
image: myregistry/model-server:v1
volumeMounts:
- name: model-data
mountPath: /models
resources:
limits:
nvidia.com/gpu: 1
volumes:
- name: model-data
persistentVolumeClaim:
claimName: ai-model-storage
```
### Caching-strategier for modeller
| Strategi | Beskrivelse | Fordel |
|----------|-------------|--------|
| Pre-load til NVMe | Last modeller ved oppstart | Raskest cold start |
| Shared PVC | ReadWriteMany for flere pods | Effektiv lagring |
| InitContainer | Last modell for main container starter | Paalitelig sekvensering |
| Model sidecar | Egen container for modell-lasting | Separasjon av ansvar |
---
## Hybrid Resilience Patterns
### High Availability for AI pa Azure Local
```
┌─────────────────────────────────────────┐
│ Azure Local HA Cluster │
│ │
│ Node 1 ────────── Node 2 │
│ GPU: L4 GPU: L4 │
│ AI Workload AI Workload │
│ (Active) (Standby) │
│ │ │ │
│ └──── S2D ───────┘ │
│ Replicated Storage │
└─────────────────────────────────────────┘
```
| Resilience-moenster | Beskrivelse | RTO |
|---------------------|-------------|-----|
| Active-Passive GPU | Standby node med GPU ready | 2-5 min |
| Active-Active load balanced | Inferens fordelt pa noder | 0 (graceful) |
| N+1 redundancy | Ekstra node for failover | 1-3 min |
| Stretched cluster | Klynge over to lokasjoner | Automatisk |
### Failover-konfigurasjon
```powershell
# Konfigurer VM failover med GPU re-assignment
# Krever at GPU-er er tilgjengelige pa failover-noden
# Sett foretrukket eier for AI VM
Set-ClusterGroup -Name "AI-Inference-VM" `
-PreferredOwner "Node1", "Node2"
# Aktiver automatisk failback
(Get-ClusterGroup "AI-Inference-VM").AutoFailbackType = 1
```
### Cloud-fallback for Azure Local
Nar lokal kapasitet er utilstrekkelig:
```yaml
# Hybrid inference med sky-fallback
apiVersion: v1
kind: ConfigMap
metadata:
name: inference-config
data:
routing: |
primary:
endpoint: http://local-model-svc:8080/v1/completions
timeout: 5s
fallback:
endpoint: https://my-foundry.openai.azure.com/v1/completions
condition: local_gpu_util > 95% OR local_unavailable
```
---
## Nettverksarkitektur
### Anbefalte nettverkstopologier
| Topologi | Bruk | Krav |
|----------|------|------|
| Converged | Enkle deployments | Min 10 Gbps |
| Hyper-converged | Standard AI-klynge | 25 Gbps + RDMA |
| Switched | Stor skala, mange noder | 25-100 Gbps fabric |
### Minimum nettverkskrav for AI
| Trafikk | Minimum | Anbefalt |
|---------|---------|----------|
| Management (Arc) | 1 Gbps + internett | 10 Gbps |
| Storage (S2D) | 10 Gbps RDMA | 25 Gbps RDMA |
| Compute (GPU) | 10 Gbps | 25 Gbps |
| Client (inferens) | 1 Gbps | 10 Gbps |
---
## Sizing-guide for AI-arbeidsbelastninger
### Anbefalte konfigurasjoner
| Arbeidsbelastning | Noder | GPU per node | Minne per node | Lagring |
|-------------------|-------|-------------|----------------|---------|
| Liten inferens (Phi-3) | 2 | 1x A2 | 64 GB | 1 TB NVMe |
| Medium inferens (Phi-4, Mistral-7B) | 3 | 1x A16 | 128 GB | 2 TB NVMe |
| Stor inferens (Llama-70B) | 4 | 2x L40S | 256 GB | 4 TB NVMe |
| Trening + inferens | 4+ | 2x A40/L40S | 512 GB | 8 TB NVMe |
### Kostnadsestimat (Azure Local)
| Komponent | Estimert kost (NOK) |
|-----------|---------------------|
| 3-node Azure Local cluster | 300,000 - 600,000 |
| NVIDIA A2 GPU (per stk) | 15,000 - 25,000 |
| NVIDIA L4 GPU (per stk) | 25,000 - 40,000 |
| NVIDIA L40S GPU (per stk) | 80,000 - 120,000 |
| Azure Local lisens | Inkludert i Azure-abonnement |
| Azure Arc management | Inkludert (basis) |
| Azure ML extension | Inkludert |
**Merk:** Priser er estimater og varierer med leverandor og konfigurasjon.
---
## For Cosmo
- **Azure Local er den primaere plattformen for on-premises AI i Microsoft-okosystemet** — fullt integrert med Azure Arc for sentralisert styring, men med all databehandling pa egen infrastruktur.
- **GPU-stotten er bred med NVIDIA T4/A2/A16/L4/L40/L40S** — bade DDA (hel GPU) og GPU-P (delt GPU) er tilgjengelig, noe som gir fleksibilitet fra lette inferensoppgaver til tung trening.
- **KAITO pa AKS Arc forenkler LLM-deployment drastisk** — fra GPU-klargjoring til modell-serving med OpenAI-kompatibelt API pa noen fa kubectl-kommandoer.
- **For norsk offentlig sektor gir Azure Local data residency i Norge** med full Azure-administrasjon fra Norway East-regionen — data forlater aldri lokale servere.
- **Hybrid resilience med sky-fallback** sikrer at AI-tjenester forblir tilgjengelige selv ved lokal kapasitetsmangel, med automatisk routing til Azure-endepunkter.

View file

@ -0,0 +1,403 @@
# Data Sovereignty for Norwegian Public Sector
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Datasuverenitet er et av de viktigste temaene nar norsk offentlig sektor vurderer skybaserte AI-losninger. Etter Schrems II-dommen (2020), EUs AI Act (2024), og okt fokus pa digital autonomi i Europa, ma organisasjoner navigere et komplekst landskap av juridiske krav, tekniske kontroller og politiske forventninger.
For AI-arbeidsbelastninger er utfordringene spesielt store: AI-modeller kan inneholde implisitt persondata i sine vekter, treningsdata kan vare sensitivt, og inferensresultater kan avslore informasjon om underlaget. Samtidig er mange av de kraftigste AI-tjenestene kun tilgjengelige fra bestemte Azure-regioner, og noen krever global databehandling.
Denne referansen gir en strukturert oversikt over regulatoriske krav, Microsofts tilbud for datasuverenitet, og praktiske arkitekturmoenstre for norsk offentlig sektor som vil ta i bruk AI pa en trygg og lovlig mate.
---
## Regulatorisk landskap
### Schrems II og konsekvenser
Schrems II-dommen (juli 2020) ugyldiggjorde EU-US Privacy Shield og stilte strengere krav til overforing av persondata til tredjeland:
| Aspekt | Konsekvens for AI i skyen |
|--------|--------------------------|
| Ugyldiggjoring av Privacy Shield | Kan ikke basere dataoverforing til USA pa Privacy Shield |
| Strengere SCC-krav | Standard Contractual Clauses krever tilleggstiltak |
| Risikovurdering pakreves | Ma vurdere om mottakerlandets lovgivning gir tilstrekkelig vern |
| Supplementary measures | Tekniske, organisatoriske og kontraktuelle tiltak ma iverksettes |
**Post-Schrems II tiltak fra Microsoft:**
- EU Data Boundary (EUDB) implementert for a holde data i EU/EFTA
- Standard Contractual Clauses (SCCs) oppdatert
- Data Protection Addendum (DPA) styrket
- Transparensrapporter publisert
### EU-US Data Privacy Framework (2023)
I juli 2023 vedtok EU-kommisjonen EU-US Data Privacy Framework som ny mekanisme for lovlig overforing av persondata til USA:
| Aspekt | Status |
|--------|--------|
| Adequacy decision | Vedtatt juli 2023 |
| Microsoft-sertifisering | Ja, sertifisert under DPF |
| Norsk aksept | Norge (via EOS-avtalen) folger EU-beslutninger |
| Stabilitet | Utfordret av NOYB, men gyldig per 2026 |
| Anbefalinger | Bruk DPF + tekniske tiltak (defense in depth) |
### GDPR/Personvernforordningen
| Krav | Relevans for AI |
|------|-----------------|
| Art. 5 (formaalsbegrensning) | AI-modeller ma brukes til angitt formal |
| Art. 6 (behandlingsgrunnlag) | Samtykke, avtale eller berettiget interesse |
| Art. 22 (automatiserte beslutninger) | Rett til menneskelig inngripen |
| Art. 25 (privacy by design) | Innebygd personvern i AI-systemer |
| Art. 35 (DPIA) | Pakrevd for AI med hoy risiko |
| Art. 44-49 (tredjelands overforing) | Relevant for sky-AI-tjenester |
### EUs AI Act
| Risikokategori | Krav | Eksempler |
|----------------|------|-----------|
| Uakseptabel risiko | Forbudt | Sosial scoring, manipulering |
| Hoy risiko | Strenge krav | Biometrisk ID, kredittscoring |
| Begrenset risiko | Transparenskrav | Chatbots (merking) |
| Minimal risiko | Ingen sarlige krav | Spamfiltre, anbefalinger |
**Norsk implementering:** AI Act folges opp gjennom EOS-avtalen. Datatilsynet er ansvarlig for haandheving.
---
## Norske krav til data residency
### Utredningsinstruksen
Statlige tiltak (inkludert AI-prosjekter) ma folge utredningsinstruksen:
| Krav | Konsekvens for AI-prosjekter |
|------|------------------------------|
| Problemdefinisjon | Klar definisjon av hva AI skal lose |
| Behovsanalyse | Dokumenter hvorfor AI er nodvendig |
| Alternativvurdering | Sammenlign sky/hybrid/lokalt |
| Konsekvensutredning | Personvern, sikkerhet, okonomi |
| Forholdsmessighet | Balanse mellom nytte og risiko |
| Horing | Involver berorte parter |
### Sikkerhetsloven og NSMs krav
For virksomheter underlagt sikkerhetsloven:
| Krav | Implikasjon |
|------|------------|
| Informasjonssikkerhet | AI-systemer som behandler sikkerhetsgradert info |
| Forebyggende sikkerhet | Risikovurdering av AI-leverandorer |
| Personellsikkerhet | Klarering for tilgang til AI-systemer |
| Objektsikkerhet | Fysisk sikring av AI-infrastruktur |
| IKT-sikkerhet | NSMs grunnprinsipper for AI-systemer |
### Digitaliseringsrundskrivet
Regjeringens retningslinjer for offentlig sektors digitalisering:
| Prinsipp | AI-relevans |
|----------|-------------|
| Skyforst-strategi | Sky er forstevalg, men med unntak for sensitiv data |
| Apne data | AI-modeller bor benytte apne datakilder der mulig |
| Deling av data | Samarbeid mellom etater om AI-treningsdata |
| Personvern | DPIA for alle AI-systemer med persondata |
| Tilgjengelighet | AI-tjenester ma vaere universelt utformet |
---
## Azure Data Residency for Norge
### Azure-regioner i Norge
| Region | Tjenester | Formaal |
|--------|-----------|---------|
| Norway East (Oslo) | Fullt tjenesteomfang | Primaerregion |
| Norway West (Stavanger) | Begrenset | DR/backup |
### Azure-tjenester tilgjengelig i Norway East
| Tjenestekategori | Tilgjengelighet | Merknader |
|-----------------|-----------------|-----------|
| Compute (VMs) | GA | Inkl. GPU (NC, ND-serier) |
| Azure Kubernetes Service | GA | Primaer container-plattform |
| Azure Storage | GA | Alle lagringstyper |
| Azure SQL/Cosmos DB | GA | Regional data residency |
| Azure AI Foundry | Begrenset | Ikke alle modeller |
| Azure OpenAI | GA | GPT-4o, GPT-4o-mini |
| Azure AI Services | GA | Vision, Speech, Language |
| Azure Machine Learning | GA | Trening og inferens |
| Azure Key Vault | GA | Hemmelighetshaandtering |
| Azure Monitor | GA | Overvaking |
### Tjenester som IKKE er tilgjengelige i Norway East
| Tjeneste | Naermeste region | Alternativ |
|----------|-----------------|------------|
| Azure OpenAI (GPT-5) | Sweden Central | Bruk EUDB-region |
| Copilot Studio | EU-regioner | Sett tenant til EU |
| Noen AI Foundry-modeller | Sweden/West Europe | Vurder EUDB-scope |
| Azure AI Search (semantic) | West Europe | Kan kreve EU-plassering |
---
## EU Data Boundary (EUDB)
### Hva er EUDB?
EU Data Boundary er Microsofts forpliktelse til a lagre og behandle kundedata og persondata innenfor EU/EFTA for sine enterprise online services:
| Tjeneste | EUDB-stottet | Betingelse |
|----------|-------------|------------|
| Azure (regionale) | Ja | Deploy i EU/EFTA-region |
| Azure (ikke-regionale) | Delvis | Krever konfigurasjon |
| Dynamics 365 | Ja | Tenant i EU geo |
| Power Platform | Ja | Miljo i EU geo |
| Microsoft 365 | Ja | Tenant i EU geo |
### EUDB-land
EU Data Boundary dekker:
- **EU:** Osterrike, Belgia, Bulgaria, Kroatia, Kypros, Tsjekkia, Danmark, Estland, Finland, Frankrike, Tyskland, Hellas, Ungarn, Irland, Italia, Latvia, Litauen, Luxembourg, Malta, Nederland, Polen, Portugal, Romania, Slovakia, Slovenia, Spania, Sverige
- **EFTA:** Liechtenstein, Island, **Norge**, Sveits
### Konfigurering av EUDB for Azure
```bash
# Konfigurer Azure Data Boundary for tenant
az data-boundary create --data-boundary EU --default default
```
```json
// Azure Policy: Tving ressurser til Norway East
{
"if": {
"not": {
"field": "location",
"in": ["norwayeast", "norwaywest", "swedencentral",
"westeurope", "northeurope"]
}
},
"then": {
"effect": "deny"
}
}
```
---
## Microsoft Sovereign Cloud
### Sovereign deployment-modeller
Microsoft tilbyr tre nivaer av suverenitet:
| Modell | Beskrivelse | Kontrollniva | Tilgjengelighet |
|--------|-------------|-------------|-----------------|
| Sovereign Public Cloud | Azure med EUDB + sovereign controls | Hoy | GA (EU/EFTA) |
| Sovereign Private Cloud | Azure Local/M365 Local i eget datasenter | Hoyest | GA |
| National Partner Clouds | Partnerdrevet lokal sky | Variabel | Utvalgte land |
### Sovereign Landing Zone (SLZ)
SLZ er en variant av Azure Landing Zone med innebygde suverenitetskontroller:
| Kontrollniva | Policyer | Bruksomrade |
|-------------|----------|-------------|
| L1 (Basis) | Data residency, godkjente regioner | Standard offentlig sektor |
| L2 (Styrket) | L1 + kryptering med CMK | Sensitiv data |
| L3 (Konfidensielt) | L2 + confidential computing | Sikkerhetsgradert |
**SLZ Policy-kontroller:**
| Policy-ID | Kontroll | Effekt |
|-----------|---------|--------|
| SO.1 | Data residency — godkjente regioner | Deny |
| SO.2 | Kryptering med kundestyrt nokkel (CMK) | Audit/Deny |
| SO.3 | Confidential computing for utvalgte tjenester | Audit |
| SO.4 | Private endpoints for datatilgang | Deny |
### Implementering av SLZ
```bash
# Deploy Sovereign Landing Zone med Bicep
az deployment sub create \
--location norwayeast \
--template-file sovereign-landing-zone.bicep \
--parameters \
allowedLocations='["norwayeast","norwaywest"]' \
requireCMK=true \
enforcePrivateEndpoints=true \
dataClassification="sensitive"
```
---
## Azure Data Classification for AI
### Dataklassifiseringsmatrise
| Klassifisering | Beskrivelse | Sky-tillatelse | Azure-krav |
|---------------|-------------|----------------|------------|
| Apen | Offentlig tilgjengelig | Alle regioner | Standard |
| Intern | Ikke-sensitiv intern data | EU/EFTA | EUDB |
| Fortrolig | Sensitiv, persondata | Norway East/West | CMK + RBAC |
| Strengt fortrolig | Hoy sensitivitet | Norway + spesialtiltak | SLZ L2+ |
| Sikkerhetsgradert | Underlagt sikkerhetsloven | Lokalt / godkjent sky | Azure Local |
### AI-spesifikke datakategorier
| Datakategori | Eksempel | Klassifisering | Behandlingssted |
|-------------|----------|---------------|-----------------|
| Treningsdata | Dokumenter, bilder | Fortrolig+ | Norway East |
| Modellvekter | Fine-tuned modeller | Intern/Fortrolig | Norway East |
| Inferens-input | Brukerforesporsler | Fortrolig | Norway East |
| Inferens-output | AI-svar | Fortrolig | Norway East |
| Systemlogger | Telemetri, metrikker | Intern | EU/EFTA |
| Prompt-logger | Bruker-prompts | Fortrolig | Norway East |
---
## Praktiske arkitekturmoenstre
### Moenster 1: Full sky i Norway East
```
┌─────────────────────────────────────┐
│ Norway East Region │
│ ┌───────────┐ ┌───────────────┐ │
│ │ Azure │ │ Azure AI │ │
│ │ OpenAI │ │ Services │ │
│ │ (GPT-4o) │ │ (Vision,Speech│ │
│ └─────┬─────┘ └──────┬────────┘ │
│ │ │ │
│ ┌─────▼───────────────▼────────┐ │
│ │ Azure ML Workspace │ │
│ │ + Private Endpoints │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Azure Key Vault (CMK) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Best for:** Standard AI-prosjekter uten krav utover GDPR/EUDB.
### Moenster 2: Hybrid med Azure Local
```
┌──────────────────────┐ ┌────────────────────┐
│ Norway East │ │ Eget datasenter │
│ ┌────────────────┐ │ │ ┌──────────────┐ │
│ │ Azure ML │ │ │ │ Azure Local │ │
│ │ (Orchestration)│◄─┼──┼─►│ (AI Inference)│ │
│ └────────────────┘ │ │ │ GPU + Data │ │
│ ┌────────────────┐ │ │ └──────────────┘ │
│ │ Azure Monitor │ │ │ ┌──────────────┐ │
│ │ (Overvaking) │◄─┼──┼──│ Arc Agent │ │
│ └────────────────┘ │ │ └──────────────┘ │
└──────────────────────┘ └────────────────────┘
```
**Best for:** Sensitiv data som ikke kan forlate egne lokaler, men trenger sky-administrasjon.
### Moenster 3: Fullstendig lokal (sovereign private)
```
┌─────────────────────────────────────┐
│ Eget datasenter │
│ ┌──────────────────────────────┐ │
│ │ Azure Local Cluster │ │
│ │ ┌────────┐ ┌───────────┐ │ │
│ │ │ AKS │ │ ONNX │ │ │
│ │ │ (KAITO)│ │ Runtime │ │ │
│ │ └────────┘ └───────────┘ │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ Disconnected │ │ │
│ │ │ AI Containers │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────┘ │
│ Ingen ekstern tilkobling │
└─────────────────────────────────────┘
```
**Best for:** Sikkerhetsgradert data, forsvarssektor, kritisk infrastruktur.
---
## Beslutningstre for datasuverenitet
```
Er dataene sikkerhetsgraderte (Sikkerhetsloven)?
├── Ja → Moenster 3: Azure Local, helt lokalt
│ Ingen sky-tilkobling
│ ONNX Runtime + Disconnected containers
└── Nei → Inneholder dataene personopplysninger?
├── Ja → Er det saerlige kategorier (helse, biometri)?
│ ├── Ja → Moenster 2: Hybrid
│ │ Data lokalt, styring fra Norway East
│ │ DPIA pakrevd, CMK-kryptering
│ │
│ └── Nei → Moenster 1 eller 2
│ Norway East med EUDB
│ Standard GDPR-tiltak
└── Nei → Moenster 1: Full sky
Norway East / EU region
Standard sikkerhetstiltak
```
---
## Compliance-sjekkliste for AI-prosjekter
| # | Kontroll | Ansvarlig | Status |
|---|---------|-----------|--------|
| 1 | DPIA gjennomfort | Personvernombud | |
| 2 | Behandlingsgrunnlag dokumentert | Juridisk | |
| 3 | Dataklassifisering gjennomfort | Informasjonseier | |
| 4 | Azure-region valgt (Norway East) | IT-arkitekt | |
| 5 | EUDB konfigurert | Sky-administrator | |
| 6 | CMK aktivert for sensitiv data | Sikkerhetsansvarlig | |
| 7 | Private endpoints konfigurert | Nettverksansvarlig | |
| 8 | RBAC implementert | IAM-ansvarlig | |
| 9 | Logging og overvaking aktivert | Driftsansvarlig | |
| 10 | AI Act risikoklassifisering | AI-ansvarlig | |
| 11 | Utredningsinstruksen fulgt | Prosjektleder | |
| 12 | ROS-analyse gjennomfort | Sikkerhetsansvarlig | |
| 13 | Leverandorvurdering gjennomfort | Innkjopsansvarlig | |
| 14 | Databehandleravtale inngatt | Juridisk | |
| 15 | Exitstrategi dokumentert | IT-arkitekt | |
---
## Sammenligning: Sovereign Cloud-alternativer
| Egenskap | Azure Sovereign Public | Azure Local (Private) | National Partner Cloud |
|----------|----------------------|----------------------|----------------------|
| Data residency | EU/EFTA (konfiguerbar) | Fullt lokalt | Varierer |
| Kontroll over data | Microsoft-driftet | Kunde-driftet | Partnerdriftet |
| AI-tjenester | Fullt omfang | Begrensede (ONNX, containers) | Varierer |
| Skalerbarhet | Hoy | Begrenset av hardware | Varierer |
| Kostnad | Pay-as-you-go | CAPEX + OPEX | Varierer |
| Compliance (GDPR) | Ja | Ja | Varierer |
| Compliance (NSM) | Delvis | Ja (med tiltak) | Varierer |
| Sikkerhetsgradert | Nei | Mulig | Varierer |
| AI Act compliance | Verktoy tilgjengelig | Kunde-ansvar | Varierer |
---
## For Cosmo
- **Schrems II er ikke lenger den eneste utfordringen** — EU-US Data Privacy Framework (2023), EU Data Boundary, og Sovereign Landing Zone gir et nyansert verktoyskrin for lovlig bruk av Azure AI fra Norge.
- **Norway East-regionen er forstevalg for norsk offentlig sektor** — de fleste AI-tjenester (Azure OpenAI GPT-4o, AI Services, ML) er tilgjengelig der, men noen nyere modeller krever Sweden Central eller West Europe.
- **Tre arkitekturmoenstre dekker hele spekteret** — full sky for standard data, hybrid for sensitiv data, og helt lokalt (Azure Local) for sikkerhetsgradert — alltid med DPIA og risikovurdering.
- **Sovereign Landing Zone med L1-L3 policyer** gir mekanisk haandheving av data residency, kryptering og tilgangskontroll — ikke bare dokumentbaserte lovnader.
- **AI Act-klassifisering ma gjores for hvert AI-prosjekt** — norsk offentlig sektor ma identifisere risikokategori (minimal/begrenset/hoy/uakseptabel) og implementere tilsvarende tiltak for AI-systemer.

View file

@ -0,0 +1,513 @@
# Disconnected AI Scenarios
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Frakoblede (disconnected) AI-scenarioer er situasjoner der AI-arbeidsbelastninger ma kjore uten internettilkobling — enten permanent, periodisk eller i beredskapssituasjoner. For norsk offentlig sektor er dette svart relevant: Forsvaret opererer i omrader uten nettdekning, helsesektoren trenger AI-stotte i ambulanser og utposter, og kritisk infrastruktur (energi, transport) ma fungere uavhengig av skytjenester.
Microsoft tilbyr flere losninger for frakoblet AI: Azure AI Foundry Tools disconnected containers for tradisjonelle AI-tjenester (tale, tekst, bilde), Azure Stack Edge for hardware-basert edge-inferens, Azure Local med disconnected operations for storre Kubernetes-miljoer, og ONNX Runtime for helt lokale modellkjoringer uten skyavhengigheter.
Denne referansen dekker de viktigste moensterne for offline modell-deployment, datarekonsiliering, lokal caching/synkronisering og fallback-strategier — alle med fokus pa palit drift nar nettverkstilkoblingen er ustabil eller fravarende.
---
## Spekter av tilkobling
AI-scenarioer fordeler seg langs et tilkoblingsspektrum:
```
┌─────────────────────────────────────────────────────┐
│ Alltid Sporadisk Periodisk Helt │
│ tilkoblet tilkoblet tilkoblet frakoblet │
│ ●──────────●────────────●────────────● │
│ | | | | │
│ Standard Connected Batch sync Air-gapped │
│ Azure containers + lokale │
│ services (billing) offline ops modeller │
└─────────────────────────────────────────────────────┘
```
| Modus | Nettverkskrav | Azure-tjenester | Billing |
|-------|---------------|-----------------|---------|
| Alltid tilkoblet | Stabilt internett | Alle | Pay-as-you-go |
| Connected containers | Periodisk (billing) | Begrensede | Bruksbasert |
| Periodisk synk | Timer/dager mellom tilkoblinger | Batch-synk | Commitment tier |
| Helt frakoblet | Ingen | Kun lokale | Forhndsbetalt lisens |
---
## Offline Model Deployment
### Azure AI Foundry Tools Disconnected Containers
Microsofts primaere losning for AI-tjenester uten nettverkstilkobling:
| Tjeneste | Container | Disconnected | Status |
|----------|-----------|--------------|--------|
| Speech to Text | speech-to-text | Ja | GA |
| Custom Speech to Text | custom-speech-to-text | Ja | GA |
| Neural Text to Speech | neural-text-to-speech | Ja | GA |
| Translator | text-translation | Ja | GA |
| Language Detection | text-language-detection | Ja | GA |
| Key Phrase Extraction | text-keyphrase | Ja | GA |
| Named Entity Recognition | text-ner | Ja | GA |
| PII Detection | text-pii | Ja | GA |
| Sentiment Analysis | text-sentiment | Ja | GA |
| CLU | clu | Ja | GA |
| Summarization | text-summarization | Ja | Preview |
| Read OCR | vision-read | Ja | GA |
| Document Intelligence | document-intelligence | Ja | GA |
| Content Safety (Text) | contentsafety-text | Ja | Preview |
| Content Safety (Image) | contentsafety-image | Ja | Preview |
| Prompt Shields | contentsafety-promptshields | Ja | Preview |
### Prosess for disconnected deployment
```
┌─────────────────────────────────────────┐
│ 1. Soknad og godkjenning │
│ ├── Enterprise Agreement kreves │
│ ├── Gyldig business case │
│ └── Godkjenning innen 10 dager │
│ │
│ 2. Lisensnedlasting │
│ ├── Kjop commitment tier │
│ ├── Last ned lisensfil │
│ └── Lisensfil har utlopsdato │
│ │
│ 3. Container-nedlasting │
│ ├── Pull fra MCR (online) │
│ ├── Eksporter til tar │
│ └── Overfar til offline-miljo │
│ │
│ 4. Offline deployment │
│ ├── Importer container │
│ ├── Mount lisensfil │
│ └── Kjor uten nettverkstilkobling │
└─────────────────────────────────────────┘
```
### Lisensnedlasting og container-oppsett
```bash
# Steg 1: Last ned lisens (online maskin)
docker run --rm -it \
-v /host/license:/license \
mcr.microsoft.com/azure-cognitive-services/speechservices/speech-to-text \
eula=accept \
billing=https://my-resource.cognitiveservices.azure.com \
apikey=<API_KEY> \
DownloadLicense=True \
Mounts:License=/license
# Steg 2: Eksporter container image
docker save \
mcr.microsoft.com/azure-cognitive-services/speechservices/speech-to-text \
-o speech-to-text.tar
# Steg 3: Overfar til offline-miljo (USB, etc.)
# Kopier speech-to-text.tar og lisensfil
# Steg 4: Importer pa offline-maskin
docker load -i speech-to-text.tar
# Steg 5: Kjor uten nettverkstilkobling
docker run --rm -it -p 5000:5000 \
-v /host/license:/license \
mcr.microsoft.com/azure-cognitive-services/speechservices/speech-to-text \
eula=accept \
Mounts:License=/license \
Mounts:Output=/output
```
### ONNX Runtime — helt lokale modeller
For scenarioer uten Docker- eller lisensbehov:
```python
# Helt lokal inferens med ONNX Runtime
# Ingen skyavhengighet, ingen lisens, ingen Docker
import onnxruntime as ort
import numpy as np
# Last modell fra lokal disk
session = ort.InferenceSession(
"/models/document-classifier.onnx",
providers=['CPUExecutionProvider']
)
# Kjor inferens
input_name = session.get_inputs()[0].name
result = session.run(None, {
input_name: np.array(preprocessed_data)
})
```
---
## Azure Stack Edge i disconnected modus
### Nkkelforskjeller offline vs online
| Funksjon | Online | Disconnected |
|----------|--------|-------------|
| Azure Portal management | Ja | Nei — kun lokal UI |
| Kubernetes workloads | Full Arc-stotte | Lokal kubectl |
| Container registry | Azure Container Registry | Edge Container Registry |
| Overvaking | Azure Monitor | Lokalt Kubernetes dashboard |
| Azure Arc | Full integrasjon | Ikke tilgjengelig |
| VM-styring | Arc-enabled VMs | Lokalt PowerShell/UI |
| GPU workloads | Full stotte | Full stotte (forklargjort) |
### Forberedelse for disconnected drift
```powershell
# Forbered Azure Stack Edge for offline-bruk
# (Gjores mens enheten fortsatt er online)
# 1. Aktiver enhet via Azure Portal
# 2. Enable Kubernetes
Set-AzureDataBoxEdgeRole -Name "Kubernetes" -Activated
# 3. Deploy alle nodvendige container workloads
kubectl apply -f ai-inference-deployment.yaml
# 4. Push container images til Edge Container Registry
docker tag my-model:v1 ecr.edgehostname:31001/my-model:v1
docker push ecr.edgehostname:31001/my-model:v1
# 5. Verifiser at alt kjorer
kubectl get pods -A
# 6. Koble fra nettverket
```
---
## Data Reconciliation Strategies
### Utfordringer med frakoblet data
Nar AI-systemer kjorer offline, oppstar det utfordringer med:
- Data som genereres lokalt ma synkroniseres nar tilkobling gjenopprettes
- Modellresultater fra offline-perioden ma valideres
- Konflikter mellom lokale og sentrale data
- Versjonshaandtering av modeller og konfigurasjoner
### Rekonsilieringsmoenstre
| Moenster | Beskrivelse | Bruksomrade |
|---------|-------------|-------------|
| Store-and-Forward | Buffer lokalt, send nar tilkoblet | IoT-data, loggfiler |
| Event Sourcing | Registrer alle hendelser, replay sentralt | Audit, compliance |
| Last-Write-Wins | Siste endring vinner ved konflikt | Enkle konfigurasjoner |
| Merge/CRDTs | Konfliktfri sammenslaing | Distribuerte datasett |
| Manual Review | Menneske loeser konflikter | Kritiske beslutninger |
### Store-and-Forward med IoT Hub
```python
# Lokal buffering og batch-synkronisering
import json
import os
from datetime import datetime
from pathlib import Path
class OfflineBuffer:
def __init__(self, buffer_dir="/data/offline-buffer"):
self.buffer_dir = Path(buffer_dir)
self.buffer_dir.mkdir(parents=True, exist_ok=True)
def store_result(self, inference_result, metadata):
"""Lagre inferensresultat lokalt."""
entry = {
"timestamp": datetime.utcnow().isoformat(),
"result": inference_result,
"metadata": metadata,
"synced": False
}
filepath = self.buffer_dir / f"{entry['timestamp']}.json"
filepath.write_text(json.dumps(entry))
return filepath
def get_unsynced(self):
"""Hent alle usynkroniserte resultater."""
results = []
for f in sorted(self.buffer_dir.glob("*.json")):
entry = json.loads(f.read_text())
if not entry.get("synced"):
results.append((f, entry))
return results
async def sync_to_cloud(self, iot_client):
"""Synkroniser bufferede resultater til sky."""
unsynced = self.get_unsynced()
for filepath, entry in unsynced:
try:
await iot_client.send_message(
json.dumps(entry)
)
entry["synced"] = True
entry["synced_at"] = datetime.utcnow().isoformat()
filepath.write_text(json.dumps(entry))
except Exception as e:
# Fortsett med neste — proev igjen senere
print(f"Sync feilet for {filepath}: {e}")
break
```
### Modellversjon-rekonsiliering
```yaml
# model-sync-config.yaml
sync:
strategy: "check-on-connect"
model_registry:
cloud: "https://ml-workspace.azureml.net/models"
local: "/models/registry.json"
conflict_resolution: "cloud-wins"
validation:
enabled: true
test_dataset: "/data/validation/standard-test.json"
min_accuracy: 0.95
rollback:
enabled: true
keep_previous: 3
```
---
## Local Cache and Sync
### Flerlags cache-arkitektur
```
┌──────────────────────────────────────────┐
│ Lag 1: In-Memory Cache (Redis) │
│ TTL: 1 time │ Storrelse: 2 GB │
│ Hoyest prioritet, raskest tilgang │
├──────────────────────────────────────────┤
│ Lag 2: Lokal Disk Cache (SSD) │
│ TTL: 7 dager │ Storrelse: 100 GB │
│ Modellvekter, embeddings, resultater │
├──────────────────────────────────────────┤
│ Lag 3: Persistent Storage (S2D/NAS) │
│ Ingen TTL │ Storrelse: 1 TB+ │
│ Full modellhistorikk, treningsdata │
├──────────────────────────────────────────┤
│ Lag 4: Cloud Sync (Azure Blob) │
│ Synk ved tilkobling │
│ Master-kopi av modeller og data │
└──────────────────────────────────────────┘
```
### Synkroniseringslogikk
```python
# Intelligent sync-manager
class SyncManager:
def __init__(self, config):
self.local_store = LocalModelStore(config.local_path)
self.cloud_store = AzureBlobStore(config.connection_string)
self.sync_log = SyncLog(config.log_path)
async def check_connectivity(self):
"""Sjekk om skytilkobling er tilgjengelig."""
try:
await self.cloud_store.ping()
return True
except Exception:
return False
async def sync_models(self):
"""Synkroniser modeller mellom lokal og sky."""
if not await self.check_connectivity():
return SyncResult(status="offline", synced=0)
# Hent manifest fra sky
cloud_manifest = await self.cloud_store.get_manifest()
local_manifest = self.local_store.get_manifest()
updates = []
for model_id, cloud_info in cloud_manifest.items():
local_info = local_manifest.get(model_id)
if not local_info:
# Ny modell — last ned
updates.append(("download", model_id, cloud_info))
elif cloud_info['version'] > local_info['version']:
# Oppdatert modell — last ned ny versjon
updates.append(("update", model_id, cloud_info))
# Utfor oppdateringer med prioritering
for action, model_id, info in sorted(
updates, key=lambda x: x[2].get('priority', 99)
):
try:
await self._download_model(model_id, info)
self.sync_log.record(action, model_id, "success")
except Exception as e:
self.sync_log.record(action, model_id, f"failed: {e}")
# Last opp lokale resultater
await self._upload_offline_results()
return SyncResult(
status="synced",
synced=len(updates)
)
```
---
## Fallback Inference Patterns
### Degraderingsstrategier
| Strategi | Nar | Implementasjon |
|----------|-----|---------------|
| Full model → Lite model | GPU svikter | Fall tilbake til CPU-modell |
| Cloud model → Edge model | Nettverk nede | Bruk lokal kvantisert modell |
| ML-modell → Regler | Modell korrupt | Regelbasert fallback |
| Real-time → Batch | Overbelastning | Buffer foresporsler |
| AI → Manuell | Alt feiler | Eskalering til menneske |
### Implementasjon av fallback-kaskade
```python
class ResilientInferenceEngine:
def __init__(self):
self.engines = [
CloudInference(endpoint="https://foundry.azure.com"),
LocalGPUInference(model_path="/models/full-model.onnx"),
LocalCPUInference(model_path="/models/quantized-int8.onnx"),
RuleBasedFallback(rules_path="/config/rules.json")
]
async def infer(self, input_data, timeout=5.0):
"""Prover inferensmotorer i prioritetsrekkefoolge."""
last_error = None
for engine in self.engines:
try:
result = await asyncio.wait_for(
engine.predict(input_data),
timeout=timeout
)
return InferenceResult(
prediction=result,
engine=engine.name,
confidence=engine.confidence_level,
degraded=(engine != self.engines[0])
)
except asyncio.TimeoutError:
last_error = f"{engine.name}: timeout"
timeout = min(timeout * 2, 30) # Okt timeout for neste
except Exception as e:
last_error = f"{engine.name}: {e}"
continue
# Alle motorer feilet — returner fallback
return InferenceResult(
prediction=None,
engine="none",
confidence=0,
degraded=True,
error=last_error
)
```
### Health monitoring for offline-systemer
```yaml
# health-check-config.yaml
health_checks:
model_health:
interval: 60s
checks:
- name: model_loaded
type: inference_test
input: "test_input.json"
expected_output_shape: [1, 10]
- name: gpu_available
type: nvidia_smi
min_free_memory_mb: 1024
- name: disk_space
type: disk
min_free_gb: 10
degradation_rules:
- condition: "gpu_available == false"
action: "switch_to_cpu_model"
- condition: "disk_space < 5GB"
action: "cleanup_old_models"
- condition: "model_loaded == false"
action: "reload_from_cache"
max_retries: 3
```
---
## Scenarioer for norsk offentlig sektor
### Forsvar og beredskap
| Scenario | Tilkoblingsstatus | Losning |
|----------|-------------------|---------|
| Feltoperasjoner | Helt frakoblet | ONNX Runtime + kvantiserte modeller |
| Kjoretoy/fartoy | Periodisk | Store-and-forward + modellsynk |
| Kommandoplass | Begrenset | Azure Stack Edge disconnected |
| Sambandssystemer | Ustabil | Fallback-kaskade med degradering |
### Helse
| Scenario | Tilkoblingsstatus | Losning |
|----------|-------------------|---------|
| Ambulanse | Ustabil | Lokal bildeanalyse (ONNX) |
| Distriktslege | Periodisk | Disconnected containers (tale, tekst) |
| Sykehus DR | Beredskap | Azure Local med offline-kapasitet |
| Feltsykehus | Frakoblet | Forhndslastede modeller |
### Transport og infrastruktur
| Scenario | Tilkoblingsstatus | Losning |
|----------|-------------------|---------|
| Tunneler | Frakoblet | Edge-inferens med kamerasystem |
| Fartsoyvervaking | Ustabil | Lokal objektdeteksjon |
| Trafikkanalyse | Periodisk | Batch-analyse med synk |
| Fergedrift | Variabel | Hybrid med sky-fallback |
---
## Lisens- og kostnadshensyn
### Disconnected containers prismodell
| Prismodell | Beskrivelse | Krav |
|-----------|-------------|------|
| Commitment tier | Arlig forpliktelse | Enterprise Agreement |
| Per-tjeneste | Betal per container-tjeneste | Godkjent soknad |
| Kalenderars-binding | 12 mnd minimum | Automatisk fornyelse |
### Viktige begrensninger
- Lisensfil har utlopsdato — krever periodisk fornyelse
- Enterprise Agreement eller tilsvarende er obligatorisk
- Godkjenningsprosess tar opptil 10 virkedager
- Ingen SLA for disconnected containers (kunde eier infrastruktur)
- Ikke tilgjengelig i sovereign clouds (kun public cloud for opprettelse)
---
## For Cosmo
- **Azure tilbyr et komplett spekter for frakoblet AI** — fra Foundry Tools disconnected containers (tale, tekst, bilde) til helt lokale ONNX Runtime-modeller uten skyavhengighet.
- **Disconnected containers krever Enterprise Agreement og godkjenning** — lisensfiler har utlopsdato og ma fornyes, men gir tilgang til de samme API-ene som sky-tjenestene.
- **Fallback-kaskader er essensielt for paalitelig edge-AI** — design alltid med degraderingsstrategi: sky → lokal GPU → lokal CPU → regler → manuell.
- **Store-and-forward med rekonsilieringslogikk** loser utfordringen med data som genereres offline — buffer lokalt, synkroniser ved tilkobling, hndter konflikter.
- **For norsk offentlig sektor er frakoblet AI kritisk for beredskap, forsvar og helse** — Azure Stack Edge og ONNX Runtime gir funksjonskapasitet uten internett-avhengighet.

View file

@ -0,0 +1,482 @@
# Edge AI Inferencing Patterns
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Edge AI-inferens handler om a kjore maskinlaeringsmodeller naermest mulig der data oppstar — pa enheter, gateways, lokale servere eller Azure Local-klynger. For norsk offentlig sektor er dette relevant i scenarioer som sanntids videoanalyse, dokumentbehandling i felt, naturspraakbehandling offline, og autonome systemer i omrader med begrenset nettverkstilkobling.
Microsoft tilbyr et bredt spekter av verktoy for edge-inferens: ONNX Runtime som universell inferensmotor, Azure IoT Edge for container-baserte modeller, Azure Stack Edge for hardware-akselerert inferens, og KAITO for LLM-deployment pa Kubernetes. Valget av moenster avhenger av modellstorrelse, latenskrav, tilgjengelig hardware og tilkoblingsstatus.
Denne referansen dekker de viktigste moensterne for modelloptimalisering, akselerasjon, caching og batching/streaming — alle med fokus pa Microsoft Azure-okosystemet og relevans for offentlig sektor.
---
## Model Quantization og Compression
### Hva er kvantisering?
Kvantisering reduserer presisjonen til modellvekter fra hoyere til lavere bit-representasjoner, noe som reduserer modellstorrelse og oker inferenshastighet med minimalt noyaktighetstap.
| Presisjon | Bits | Storrelse (7B modell) | Hastighet | Noyaktighet |
|-----------|------|----------------------|-----------|-------------|
| FP32 | 32 | ~28 GB | Baseline | 100% |
| FP16 | 16 | ~14 GB | 2x | ~99.9% |
| BF16 | 16 | ~14 GB | 2x | ~99.9% |
| INT8 | 8 | ~7 GB | 3-4x | ~99% |
| INT4 | 4 | ~3.5 GB | 5-8x | ~97% |
### Kvantiseringsmetoder i Azure-okosystemet
| Metode | Verktoy | Best for | Presisjon |
|--------|---------|----------|-----------|
| Post-Training Quantization (PTQ) | ONNX Runtime, Olive | Rask konvertering | INT8/INT4 |
| Quantization-Aware Training (QAT) | PyTorch + Azure ML | Hoyest noyaktighet | INT8 |
| GPTQ | HuggingFace + KAITO | LLM-kvantisering | INT4 |
| AWQ | HuggingFace + KAITO | LLM-kvantisering | INT4 |
| Dynamic Quantization | ONNX Runtime | CPU-inferens | INT8 |
### ONNX Runtime-kvantisering
```python
# Kvantiser ONNX-modell til INT8
from onnxruntime.quantization import quantize_dynamic, QuantType
quantize_dynamic(
model_input="model_fp32.onnx",
model_output="model_int8.onnx",
weight_type=QuantType.QInt8,
optimize_model=True
)
```
### Olive — Microsofts modelloptimalisering
Olive er Microsofts verktoy for helhetlig modelloptimalisering:
```python
# olive_config.json
{
"input_model": {
"type": "OnnxModel",
"model_path": "model.onnx"
},
"systems": {
"local_system": {
"type": "LocalSystem",
"accelerators": [{"device": "npu"}]
}
},
"passes": {
"quantization": {
"type": "OnnxQuantization",
"config": {
"quant_mode": "static",
"quant_format": "QDQ",
"calibration_data_reader": "CalibrationDataReader"
}
},
"perf_tuning": {
"type": "OrtPerfTuning",
"config": {
"data_dir": "./calibration_data"
}
}
},
"engine": {
"search_strategy": {
"execution_order": "joint",
"search_algorithm": "exhaustive"
},
"output_dir": "./optimized"
}
}
```
### Modellkomprimering
| Teknikk | Beskrivelse | Storrelsereduksjon | Noyaktighetstap |
|---------|-------------|-------------------|-----------------|
| Pruning | Fjerner uvesentlige vekter | 30-70% | 1-3% |
| Knowledge Distillation | Laerer liten modell fra stor | 50-90% | 2-5% |
| Weight Sharing | Deler vekter mellom lag | 20-40% | <1% |
| Low-Rank Factorization | Dekomponerer vektmatriser | 30-50% | 1-2% |
---
## Real-time Inference Acceleration
### ONNX Runtime Execution Providers
ONNX Runtime stotter flere hardware-akseleratorer gjennom Execution Providers (EP):
| Execution Provider | Hardware | Best for | Latens |
|--------------------|----------|----------|--------|
| CPU EP | x86/ARM CPU | Universell | Basis |
| CUDA EP | NVIDIA GPU | GPU-inferens | 5-50x raskere |
| TensorRT EP | NVIDIA GPU | Optimalisert GPU | 10-100x raskere |
| OpenVINO EP | Intel CPU/GPU/VPU | Intel-optimalisert | 3-20x raskere |
| DirectML EP | Windows GPU/NPU | Windows-enheter | 5-30x raskere |
| QNN EP | Qualcomm NPU | Snapdragon-enheter | 10-50x raskere |
### GPU-akselerert inferens med ONNX Runtime
```python
import onnxruntime as ort
# Konfigurasjon for NVIDIA GPU (TensorRT)
session_options = ort.SessionOptions()
session_options.graph_optimization_level = \
ort.GraphOptimizationLevel.ORT_ENABLE_ALL
providers = [
('TensorrtExecutionProvider', {
'trt_max_workspace_size': 2147483648, # 2 GB
'trt_fp16_enable': True,
'trt_engine_cache_enable': True,
'trt_engine_cache_path': './trt_cache'
}),
('CUDAExecutionProvider', {
'device_id': 0,
'arena_extend_strategy': 'kSameAsRequested',
'gpu_mem_limit': 4 * 1024 * 1024 * 1024, # 4 GB
'cudnn_conv_algo_search': 'DEFAULT'
}),
'CPUExecutionProvider'
]
session = ort.InferenceSession(
"model_fp16.onnx",
sess_options=session_options,
providers=providers
)
# Inferens
result = session.run(None, {"input": input_data})
```
### OpenVINO Model Server (OVMS) pa Edge
For Azure Arc/Azure Local-miljoer der Intel-hardware brukes:
```yaml
# ovms-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ovms-inference
spec:
replicas: 2
template:
spec:
containers:
- name: ovms
image: openvino/model_server:latest
ports:
- containerPort: 9000 # gRPC
- containerPort: 8000 # REST
volumeMounts:
- name: model-store
mountPath: /models
env:
- name: MODELS_CONFIG
value: "/models/config.json"
resources:
limits:
cpu: "4"
memory: "8Gi"
volumes:
- name: model-store
persistentVolumeClaim:
claimName: model-pvc
```
### vLLM for LLM-inferens
KAITO bruker vLLM som standard inferensmotor for LLM-er:
| Funksjon | Beskrivelse | Fordel |
|----------|-------------|--------|
| PagedAttention | Effektiv KV-cache-haandtering | 2-4x gjennomstromning |
| Continuous batching | Dynamisk batching av foresporsler | Redusert latens |
| Tensor parallelism | Fordel modell over GPUer | Storre modeller |
| Quantization support | AWQ, GPTQ, SqueezeLLM | Lavere minnebruk |
| OpenAI-compatible API | Standard API-format | Enkel integrasjon |
---
## Caching Patterns for Edge
### Modellcaching-strategier
| Strategi | Implementasjon | Bruksomrade |
|----------|---------------|-------------|
| Model preloading | Last modell i minne ved oppstart | Sanntids inferens |
| TensorRT engine cache | Cach kompilerte TRT-motorer | GPU-inferens |
| ONNX session cache | Gjenbruk ORT-sesjoner | Repetitive inferenser |
| Result caching | Redis/memcached for resultater | Identiske inputs |
| Embedding cache | Cach vektorrepresentasjoner | RAG pa edge |
### Resultatchaching med Redis pa Edge
```yaml
# redis-cache-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-cache
spec:
template:
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
resources:
limits:
memory: "2Gi"
args:
- "--maxmemory"
- "1.5gb"
- "--maxmemory-policy"
- "allkeys-lru"
```
```python
# Inferens med caching
import redis
import hashlib
import json
cache = redis.Redis(host='inference-cache', port=6379)
def cached_inference(model_session, input_data, ttl=3600):
# Generer cache-nokkel fra input
cache_key = hashlib.sha256(
json.dumps(input_data, sort_keys=True).encode()
).hexdigest()
# Sjekk cache
cached = cache.get(cache_key)
if cached:
return json.loads(cached)
# Kjor inferens
result = model_session.run(None, input_data)
# Lagre i cache
cache.setex(cache_key, ttl, json.dumps(result.tolist()))
return result
```
### KV-cache for LLM-er
For LLM-inferens pa edge er KV-cache (key-value cache) kritisk:
| Teknikk | Beskrivelse | Minnebesparelse |
|---------|-------------|-----------------|
| Standard KV-cache | Full cache for alle tokens | Baseline |
| Sliding window | Begrens cache til siste N tokens | 50-80% |
| Grouped-query attention | Faerre KV-hoder | 4-8x |
| PagedAttention (vLLM) | Sidert minnehaandtering | Dynamisk |
---
## Batching vs. Streaming Inference
### Sammenligning
| Egenskap | Batch Inference | Streaming Inference |
|----------|----------------|---------------------|
| Latens | Hoyere (venter pa batch) | Lavere (umiddelbar) |
| Gjennomstromning | Hoyere (GPU-utnyttelse) | Lavere (per request) |
| GPU-utnyttelse | Optimal (fyller batch) | Variabel |
| Bruksomrade | Dokumentanalyse, batch-scoring | Chat, real-time |
| Skalering | Horisontal (flere workers) | Vertikal (GPU-kapasitet) |
### Beslutningstre
```
Trenger du sanntidssvar (<100ms)?
├── Ja → Streaming inference
│ ├── Enkelt request → Single request pipeline
│ └── Flere samtidige → Continuous batching (vLLM)
└── Nei → Batch inference
├── <1000 elementer → Micro-batch pa GPU
└── >1000 elementer → Azure ML Batch Endpoints
```
### Batch Inference pa Edge
```python
import numpy as np
from collections import deque
import threading
import time
class EdgeBatchInferencer:
def __init__(self, session, batch_size=8, max_wait_ms=50):
self.session = session
self.batch_size = batch_size
self.max_wait = max_wait_ms / 1000
self.queue = deque()
self.lock = threading.Lock()
def infer(self, input_data):
"""Legg til i koe og vent pa batch-resultat."""
event = threading.Event()
result_container = {}
with self.lock:
self.queue.append((input_data, event, result_container))
# Trigger batch hvis full
if len(self.queue) >= self.batch_size:
self._process_batch()
# Vent pa resultat (med timeout)
event.wait(timeout=self.max_wait * 2)
return result_container.get('result')
def _process_batch(self):
"""Prosesser akkumulerte inputs som en batch."""
with self.lock:
items = []
while self.queue and len(items) < self.batch_size:
items.append(self.queue.popleft())
if not items:
return
# Kombiner inputs til batch
batch_input = np.stack([item[0] for item in items])
# Kjor batch-inferens
batch_results = self.session.run(
None, {"input": batch_input}
)
# Distribuer resultater
for i, (_, event, container) in enumerate(items):
container['result'] = batch_results[0][i]
event.set()
```
### Streaming Inference for LLM pa Edge
```python
# Streaming med vLLM-kompatibelt API (KAITO)
import requests
import json
def stream_inference(prompt, service_ip, max_tokens=500):
"""Stream tokens fra lokal LLM pa edge."""
response = requests.post(
f"http://{service_ip}/v1/completions",
json={
"model": "phi-4-mini-instruct",
"prompt": prompt,
"max_tokens": max_tokens,
"stream": True
},
stream=True
)
for line in response.iter_lines():
if line:
data = line.decode('utf-8')
if data.startswith('data: '):
chunk = json.loads(data[6:])
if chunk.get('choices'):
token = chunk['choices'][0].get('text', '')
yield token
```
---
## IoT Edge ML Inference Pattern
### Azure IoT Edge med dynamisk modellasting
```
┌─────────────────────────────────────────┐
│ Azure Cloud │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Blob │ │ IoT Hub │ │
│ │ Storage │ │ (Device Twins) │ │
│ │ (Models) │ │ │ │
│ └────┬─────┘ └────────┬─────────┘ │
└───────┼───────────────────┼─────────────┘
│ │
│ ┌───────────────▼──────────┐
│ │ IoT Edge Runtime │
│ │ ┌─────────────────────┐ │
└───┼─►│ Model Loader Module │ │
│ └──────────┬──────────┘ │
│ ┌──────────▼──────────┐ │
│ │ Inference Module │ │
│ │ (ONNX/LiteRT) │ │
│ └──────────┬──────────┘ │
│ ┌──────────▼──────────┐ │
│ │ Local Storage │ │
│ │ (Model Cache) │ │
│ └─────────────────────┘ │
└──────────────────────────┘
```
### Device Twin-basert modellstyring
```python
# Motta modelloppdatering via IoT Edge Device Twin
from azure.iot.device import IoTHubModuleClient
async def twin_update_handler(patch):
if 'model_version' in patch:
model_url = patch['model_url']
model_version = patch['model_version']
checksum = patch['model_checksum']
# Last ned ny modell
await download_model(model_url, checksum)
# Hot-swap modell uten nedetid
await reload_model(model_version)
# Rapporter tilbake
reported = {
"active_model_version": model_version,
"model_loaded_at": datetime.utcnow().isoformat()
}
client.patch_twin_reported_properties(reported)
```
---
## Ytelsesreferanser
### Typiske inferenstider pa Microsoft edge-hardware
| Hardware | Modell | Oppgave | Latens (ms) |
|----------|--------|---------|-------------|
| Azure Stack Edge Pro GPU (T4) | YOLOv8 | Objektdeteksjon | 8-15 |
| Azure Stack Edge Pro GPU (T4) | ResNet-50 | Bildeklassifisering | 3-5 |
| Azure Local (A2) | Phi-3-mini-4k | Tekst (128 tokens) | 200-400 |
| Azure Local (L4) | Phi-4-mini | Tekst (128 tokens) | 80-150 |
| Azure Local (L40S) | Mistral-7B | Tekst (128 tokens) | 50-100 |
| Intel CPU (Xeon) + OpenVINO | BERT-base | NER | 5-10 |
| CPU (ONNX Runtime) | DistilBERT | Sentiment | 3-8 |
---
## For Cosmo
- **ONNX Runtime er universalmotoren for edge-inferens** — stotter CPU, GPU, NPU via Execution Providers, med TensorRT for NVIDIA og OpenVINO for Intel som de viktigste akseleratorene.
- **Kvantisering (INT8/INT4) er den enkleste og mest effektive optimaliseringen** — reduserer modellstorrelse 2-8x med minimalt noyaktighetstap, spesielt viktig for edge-enheter med begrenset minne.
- **Velg batching for gjennomstromning, streaming for latens** — continuous batching (vLLM/KAITO) gir det beste av begge for LLM-inferens pa edge Kubernetes-klynger.
- **Caching pa flere nivaer er essensielt for edge-ytelse** — TensorRT engine cache, resultat-cache (Redis), og KV-cache for LLM-er reduserer bade latens og GPU-belastning.
- **IoT Edge med Device Twins gir skalerbar modellstyring** for distribuerte edge-enheter — modellversjonering, inkrementell oppdatering og hot-swap uten nedetid.

View file

@ -0,0 +1,443 @@
# Edge-to-Cloud Data Synchronization
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Palitelig datasynkronisering mellom edge og sky er en av de mest komplekse utfordringene i hybrid AI-arkitekturer. Data ma flyte i begge retninger — sensordata og inferensresultater fra edge til sky for langsiktig analyse og modelltrening, og oppdaterte modeller og konfigurasjoner fra sky tilbake til edge. Alt dette ma handtere nettverksavbrudd, konflikter og dataintegritet.
For norsk offentlig sektor er palitelig synkronisering kritisk: inspeksjonsdata fra felt ma garantert na sentrale systemer, AI-modellsoppdateringer ma distribueres til alle edge-stasjoner uten manuell intervensjon, og logging for compliance-formal ma vaere komplett — selv etter langvarige offline-perioder.
Microsoft tilbyr flere synkroniseringsmekanismer: Azure IoT Edge med utvidet offline-stoette (ubegrenset offline-tid med lokal buffring), Azure Container Storage med automatisk sky-sync, Azure Cosmos DB med multi-region replikering, og Azure IoT Hub device twins for konfigurasjonssynkronisering. Valget avhenger av datamengde, konsistenskrav og tilkoblingsprofil.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Azure IoT Edge | Utvidet offline med meldingsbuffring | Container runtime |
| Azure Container Storage | Lokal lagring med automatisk sky-sync | Arc-enabled |
| Azure Cosmos DB | Multi-region database med konflikthondtering | NoSQL / SQL API |
| IoT Hub Device Twins | Konfigurasjonssynkronisering enhet-sky | PaaS |
| Event Hub | Hoeyvolum event-inntak | Event streaming |
| Azure Data Lake | Langsiktig datalagring | Storage Gen2 |
| Delta Lake | ACID-transaksjoner pa datalake | Open source |
---
## Eventual Consistency Patterns
### Konsistensmodeller for edge-sky
| Modell | Beskrivelse | Latens | Brukstilfelle |
|--------|-------------|--------|---------------|
| Sterk konsistens | Alle noder ser samme data samtidig | Hoey | Kritiske transaksjoner |
| Bounded staleness | Data er konsistent innen et tidsvindu | Medium | Nesten-sanntid dashboards |
| Session konsistens | Konsistens innen en enhet-session | Lav | Brukerinteraksjon |
| Eventuell konsistens | Data konvergerer over tid | Lavest | Sensordata, logger |
### Azure Cosmos DB for edge-sky-synkronisering
```python
# Azure Cosmos DB med konfigurerbar konsistens for edge-sky sync
from azure.cosmos import CosmosClient, PartitionKey
from azure.cosmos.documents import ConsistencyLevel
class EdgeCloudSyncStore:
def __init__(self, endpoint: str, key: str):
self.client = CosmosClient(
endpoint, key,
consistency_level=ConsistencyLevel.Session # Bra for edge-sky
)
self.database = self.client.get_database_client("edge-ai-data")
def setup_containers(self):
"""Opprett containere for synkronisert data"""
# Sensordata: Eventuell konsistens, hoey throughput
self.sensor_container = self.database.create_container_if_not_exists(
id="sensor-data",
partition_key=PartitionKey(path="/deviceId"),
default_ttl=86400 * 30 # 30 dagers retention
)
# AI-resultater: Session konsistens
self.ai_results = self.database.create_container_if_not_exists(
id="ai-results",
partition_key=PartitionKey(path="/deviceId"),
default_ttl=86400 * 365 # 1 aars retention
)
# Modellkonfigurasjon: Sterkere konsistens
self.model_config = self.database.create_container_if_not_exists(
id="model-config",
partition_key=PartitionKey(path="/region")
)
def upsert_sensor_data(self, device_id: str, readings: list[dict]):
"""Skriv sensordata med idempotensnokkel for a haandtere re-sync"""
for reading in readings:
reading["id"] = f"{device_id}_{reading['timestamp']}"
reading["deviceId"] = device_id
reading["_etag"] = None # Cosmos DB haandterer versjonering
self.sensor_container.upsert_item(
body=reading,
pre_trigger_include=None,
post_trigger_include=None
)
def get_latest_model_config(self, region: str) -> dict:
"""Hent siste modellkonfigurasjon for en region"""
query = """
SELECT TOP 1 *
FROM c
WHERE c.region = @region
ORDER BY c.updatedAt DESC
"""
items = list(self.model_config.query_items(
query=query,
parameters=[{"name": "@region", "value": region}],
enable_cross_partition_query=False
))
return items[0] if items else None
```
### Event-basert synkronisering
```python
# Event-basert edge-to-cloud synkronisering
import asyncio
import json
import gzip
from datetime import datetime, timedelta
class EventBasedSync:
def __init__(self, local_store, cloud_endpoint: str):
self.local_store = local_store
self.cloud_endpoint = cloud_endpoint
self.sync_log = []
self.last_sync = None
async def sync_outbound(self, max_batch_size: int = 100) -> dict:
"""Synkroniser lokale hendelser til sky"""
# Hent usynkroniserte hendelser
pending = self.local_store.get_unsynced_events(limit=max_batch_size)
if not pending:
return {"status": "up_to_date", "synced": 0}
# Komprimer for overfoering
payload = gzip.compress(
json.dumps([e.__dict__ for e in pending]).encode()
)
try:
# Send til sky-endpoint
response = await self._send_to_cloud(payload)
if response["status"] == "accepted":
# Marker som synkronisert
event_ids = [e.id for e in pending]
self.local_store.mark_synced(event_ids)
self.last_sync = datetime.utcnow()
self.sync_log.append({
"direction": "outbound",
"events": len(event_ids),
"size_bytes": len(payload),
"timestamp": self.last_sync.isoformat()
})
return {
"status": "synced",
"synced": len(event_ids),
"remaining": self.local_store.count_unsynced(),
"compressed_size": len(payload)
}
except ConnectionError:
return {
"status": "offline",
"pending": len(pending),
"retry_after": "next_connectivity"
}
async def sync_inbound(self) -> dict:
"""Hent oppdateringer fra sky (modeller, konfigurasjon)"""
try:
since = self.last_sync or datetime.utcnow() - timedelta(days=7)
updates = await self._fetch_from_cloud(since)
applied = 0
for update in updates:
if update["type"] == "model_update":
await self._apply_model_update(update)
applied += 1
elif update["type"] == "config_change":
await self._apply_config_change(update)
applied += 1
return {"status": "updated", "applied": applied}
except ConnectionError:
return {"status": "offline", "using_cached": True}
```
---
## Delta Sync Optimization
### Inkrementell synkronisering
```python
# Delta-synkronisering for effektiv dataoverfoering
import hashlib
import json
from typing import Optional
class DeltaSyncEngine:
def __init__(self):
self.local_checksums: dict[str, str] = {}
self.sync_watermark: Optional[str] = None
def calculate_delta(self, current_data: dict,
last_synced_data: dict) -> dict:
"""Beregn delta mellom navaerende og sist synkronisert tilstand"""
delta = {
"added": {},
"modified": {},
"deleted": []
}
# Finn nye og endrede elementer
for key, value in current_data.items():
current_hash = self._hash_value(value)
if key not in last_synced_data:
delta["added"][key] = value
elif self._hash_value(last_synced_data[key]) != current_hash:
delta["modified"][key] = value
# Finn slettede elementer
for key in last_synced_data:
if key not in current_data:
delta["deleted"].append(key)
return delta
def apply_delta(self, base_data: dict, delta: dict) -> dict:
"""Anvend delta pa basisdatasettet"""
result = dict(base_data)
# Legg til nye
result.update(delta.get("added", {}))
# Oppdater endrede
result.update(delta.get("modified", {}))
# Fjern slettede
for key in delta.get("deleted", []):
result.pop(key, None)
return result
def compress_delta(self, delta: dict) -> bytes:
"""Komprimer delta for overfoering"""
import gzip
json_bytes = json.dumps(delta, separators=(',', ':')).encode()
compressed = gzip.compress(json_bytes, compresslevel=9)
return compressed
def get_sync_stats(self, delta: dict, compressed: bytes) -> dict:
"""Beregn synkroniseringsstatistikk"""
full_size = len(json.dumps(delta).encode())
return {
"items_changed": (
len(delta.get("added", {})) +
len(delta.get("modified", {})) +
len(delta.get("deleted", []))
),
"full_size_bytes": full_size,
"compressed_size_bytes": len(compressed),
"compression_ratio": f"{(1 - len(compressed)/full_size)*100:.1f}%"
if full_size > 0 else "N/A"
}
def _hash_value(self, value) -> str:
return hashlib.sha256(json.dumps(value, sort_keys=True).encode()).hexdigest()[:16]
```
---
## Conflict Resolution Strategies
### Konflikttyper i edge-sky-synkronisering
| Konflikttype | Arsak | Losningsstrategi |
|-------------|-------|-----------------|
| Write-Write | Samme data endret pa bade edge og sky | LWW eller custom merge |
| Delete-Update | Data slettet pa en side, oppdatert pa annen | Policy-basert (behold eller slett) |
| Schema-conflict | Modellversjon ulik pa edge og sky | Versjonert schema med migrasjon |
| Ordering-conflict | Hendelser mottat i feil rekkefolge | Timestamp-basert reordering |
### Cosmos DB konflikthondtering
```python
# Cosmos DB konflikthondterings-policy
from azure.cosmos import ContainerProxy
class CosmosConflictHandler:
def __init__(self, container: ContainerProxy):
self.container = container
def setup_lww_policy(self):
"""Last-Write-Wins basert pa egendefinert felt"""
# Konfigureres ved container-oppretting
# Cosmos DB bruker _ts (timestamp) som default
pass
def setup_custom_resolution(self):
"""Custom konflikthondtering med stored procedure"""
sproc_body = """
function resolve(incomingItem, existingItem, isTombstone, conflictingItems) {
// For AI-resultater: Behold den med hoeyest confidence
if (incomingItem.ai_confidence > existingItem.ai_confidence) {
return incomingItem;
}
return existingItem;
}
"""
self.container.scripts.create_stored_procedure({
"id": "resolveConflict",
"body": sproc_body
})
def read_conflict_feed(self) -> list[dict]:
"""Les konflikter som krever manuell losning"""
conflicts = list(self.container.list_conflicts())
return [{
"id": c["id"],
"resource_id": c.get("resourceId"),
"conflict_type": c.get("operationType"),
"source_region": c.get("sourceResourceId")
} for c in conflicts]
```
---
## Data Deduplication at Scale
### Dedupliseringsstrategier
```python
# Skalerbar deduplisering for edge-sky-data
import hashlib
from bloom_filter2 import BloomFilter
class EdgeDeduplication:
def __init__(self, expected_items: int = 1_000_000):
# Bloom-filter for hurtig duplikat-sjekk (minneeffektivt)
self.bloom = BloomFilter(
max_elements=expected_items,
error_rate=0.01 # 1% falsk-positiv rate
)
# Eksakt sjekk for bloom-positive
self.recent_hashes: set = set()
self.max_recent = 100_000
def is_duplicate(self, data: dict) -> bool:
"""Sjekk om dataelementet allerede er prosessert"""
data_hash = self._compute_hash(data)
# Hurtig bloom-filter-sjekk
if data_hash not in self.bloom:
return False
# Eksakt sjekk for bekreftelse
return data_hash in self.recent_hashes
def mark_processed(self, data: dict):
"""Marker dataelement som prosessert"""
data_hash = self._compute_hash(data)
self.bloom.add(data_hash)
self.recent_hashes.add(data_hash)
# Begrens minnebruk
if len(self.recent_hashes) > self.max_recent:
# Fjern eldste 20%
to_remove = len(self.recent_hashes) - int(self.max_recent * 0.8)
for _ in range(to_remove):
self.recent_hashes.pop()
def _compute_hash(self, data: dict) -> str:
"""Beregn deterministisk hash av dataelementet"""
# Bruk innholds-hash (ekskluder metadata som timestamp)
content_keys = sorted(k for k in data.keys()
if k not in ("_ts", "synced_at", "sync_id"))
content = {k: data[k] for k in content_keys}
return hashlib.sha256(
json.dumps(content, sort_keys=True).encode()
).hexdigest()
def get_stats(self) -> dict:
return {
"bloom_filter_items": len(self.bloom),
"recent_exact_items": len(self.recent_hashes),
"estimated_memory_mb": (
self.bloom.bitarray.nbytes / 1024 / 1024 +
len(self.recent_hashes) * 64 / 1024 / 1024
)
}
```
---
## Norsk offentlig sektor
### Synkroniseringskrav for offentlig sektor
| Krav | Beskrivelse | Losning |
|------|-------------|---------|
| Dataintegritet | Ingen datatap ved offline/sync | Event sourcing + idempotens |
| Sporbarhet | All synkronisering ma logges | Sync audit log |
| Personvern | Sensitive data ma krypteres i transit | TLS 1.3 + end-to-end |
| Compliance | 7 ars retention for visse datatyper | Immutable storage |
| Konflikthondtering | Sporbar og deterministisk | Policy-basert med audit trail |
### Anbefalte Azure-tjenester per scenario
| Scenario | Primaer-tjeneste | Sekundaer | Konsistens |
|----------|-----------------|-----------|------------|
| IoT-sensordata | IoT Hub + Event Hub | Data Lake | Eventuell |
| AI-resultater | Cosmos DB | Data Lake backup | Session |
| Modellkonfig | IoT Hub Device Twin | Git (GitOps) | Sterk |
| Inspeksjonsdata | Cosmos DB | Blob Storage | Bounded staleness |
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Hoeyvolum sensorer, enveis | IoT Hub → Event Hub → Data Lake | Skalerbart, rimelig |
| Toveis med konfliktfare | Cosmos DB med session-konsistens | Innebygd konflikthondtering |
| Kritisk data, null tap | Event sourcing + Cosmos DB | Idempotent, sporbar |
| Periodisk bulk-sync | Delta sync + Azure Blob | Minimal bandwidth |
| Multi-edge koordinering | Cosmos DB multi-write | Automatisk konflikthondtering |
| Modellpush til edge | IoT Hub Device Twin + Blob SAS | Etablert monster |
---
## For Cosmo
- **Event sourcing med idempotens er gullstandarden** for edge-sky-synkronisering — alle dataelementer faar en unik ID og kan trygt re-sendes uten duplikater
- **Delta-synkronisering reduserer datavolum med 80-95%** sammenlignet med full sync — beregn kun endringer og komprimer med gzip for minimal bandbreddebruk
- **Cosmos DB med session-konsistens er den beste balansen** mellom ytelse og dataintegritet for de fleste edge-sky-scenarier i offentlig sektor
- **Bloom-filter gir O(1) dedupliseringssjekk** med minimal minnebruk — implementer dette pa bade edge og sky-siden for a hindre duplikat-inntak
- **For norsk offentlig sektor: Krav til sporbarhet og retention betyr at ALL synkronisering ma logges** — implementer sync audit log med 7 ars immutable retention for compliance med arkivloven

View file

@ -0,0 +1,448 @@
# Hybrid RAG Architecture
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Hybrid RAG (Retrieval-Augmented Generation) refererer til RAG-arkitekturer der retrieval og generering fordeles mellom lokale (on-premises/edge) og sky-baserte ressurser. Dette moensteret er relevant nar organisasjoner har data som ikke kan eller bor forlate det lokale miljoet, men onsker a kombinere lokale dokumenter med sky-basert kunnskap for bedre svar.
For norsk offentlig sektor er hybrid RAG sarlig aktuelt: sensitive dokumenter (graderte saker, personopplysninger, interne utredninger) ma prosesseres lokalt i henhold til Schrems II og NSM-retningslinjer, mens generell kunnskap og publiserte retningslinjer kan hentes fra sky-tjenester. Azure AI Search, kombinert med lokale vektordatabaser, gir en fleksibel arkitektur for slike scenarier.
Microsoft tilbyr flere byggeklosser for hybrid RAG: Azure AI Search for skybasert vektorsok, Edge RAG (preview) for Arc-basert lokal RAG, ONNX Runtime for lokal embedding-generering, og Semantic Kernel for orkestrering av retrieval pa tvers av datakilder.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Azure AI Search | Skybasert vektorsok og hybrid search | PaaS (GA) |
| Edge RAG | Lokal RAG pa Arc-enabled Kubernetes | Azure Arc (Preview) |
| Local Vector DB | Lokal vektorlagring for sensitive data | ChromaDB, Qdrant, pgvector |
| Embedding Model | Generering av vektorrepresentasjoner | Azure OpenAI, Phi-3/4, ONNX |
| Semantic Kernel | Orkestrering av hybrid retrieval | .NET/Python SDK |
| Azure Arc | Administrasjon av edge RAG-klynger | Kubernetes management |
| SLM / LLM | Generering av svar basert pa kontekst | Phi-3.5/Phi-4, GPT-4o |
---
## Local Embedding og Retrieval
### Lokal embedding med ONNX Runtime
For sensitive data som ikke kan sendes til sky-tjenester, kan embeddings genereres lokalt:
```python
# Lokal embedding-generering med ONNX Runtime
import onnxruntime as ort
import numpy as np
from transformers import AutoTokenizer
class LocalEmbeddingService:
def __init__(self, model_path: str, tokenizer_name: str):
self.session = ort.InferenceSession(
model_path,
providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)
self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
def embed(self, texts: list[str]) -> np.ndarray:
"""Generer embeddings lokalt uten sky-avhengighet"""
encoded = self.tokenizer(
texts,
padding=True,
truncation=True,
max_length=512,
return_tensors="np"
)
outputs = self.session.run(
None,
{
"input_ids": encoded["input_ids"].astype(np.int64),
"attention_mask": encoded["attention_mask"].astype(np.int64)
}
)
# Mean pooling
embeddings = outputs[0]
mask = encoded["attention_mask"][:, :, np.newaxis]
pooled = (embeddings * mask).sum(axis=1) / mask.sum(axis=1)
# Normalisering
norms = np.linalg.norm(pooled, axis=1, keepdims=True)
return pooled / norms
def embed_single(self, text: str) -> np.ndarray:
return self.embed([text])[0]
```
### Lokal vektordatabase med ChromaDB
```python
# Lokal vektordatabase for sensitive dokumenter
import chromadb
from chromadb.config import Settings
class LocalVectorStore:
def __init__(self, persist_directory: str):
self.client = chromadb.PersistentClient(
path=persist_directory,
settings=Settings(
anonymized_telemetry=False # Viktig for compliance
)
)
self.collection = self.client.get_or_create_collection(
name="sensitive_documents",
metadata={"hnsw:space": "cosine"}
)
def add_documents(self, documents: list[dict], embeddings: np.ndarray):
"""Indekser dokumenter med pre-beregnede embeddings"""
self.collection.add(
ids=[doc["id"] for doc in documents],
embeddings=embeddings.tolist(),
documents=[doc["content"] for doc in documents],
metadatas=[{
"source": doc["source"],
"classification": doc["classification"],
"timestamp": doc["timestamp"]
} for doc in documents]
)
def search(self, query_embedding: np.ndarray, n_results: int = 5,
classification_filter: str = None) -> list[dict]:
"""Sok med valgfri klassifiseringsfiltrering"""
where_filter = None
if classification_filter:
where_filter = {"classification": classification_filter}
results = self.collection.query(
query_embeddings=[query_embedding.tolist()],
n_results=n_results,
where=where_filter,
include=["documents", "metadatas", "distances"]
)
return [{
"content": doc,
"metadata": meta,
"score": 1 - dist # Konverter avstand til likhetsscore
} for doc, meta, dist in zip(
results["documents"][0],
results["metadatas"][0],
results["distances"][0]
)]
```
---
## Federated Vector Search
### Arkitektur for foderasjon
Federated vector search kombinerer resultater fra flere vektordatabaser — bade lokale og skybaserte — uten a flytte sensitive data:
```
[Bruker-query]
[Embedding Service (lokal)]
[Federated Search Router]
├── [Lokal VektorDB] → Sensitive dokumenter
├── [Azure AI Search] → Publiserte retningslinjer
└── [Edge RAG Cluster] → Avdelingsdata
[Result Merger & Ranker]
[LLM/SLM Generering]
[Svar til bruker]
```
### Implementering med Semantic Kernel
```csharp
// Federated RAG med Semantic Kernel
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
public class FederatedRagService
{
private readonly IMemoryStore _localStore;
private readonly IMemoryStore _cloudStore;
private readonly ITextEmbeddingGenerationService _localEmbedding;
private readonly Kernel _kernel;
public FederatedRagService(
IMemoryStore localStore,
IMemoryStore cloudStore,
ITextEmbeddingGenerationService localEmbedding,
Kernel kernel)
{
_localStore = localStore;
_cloudStore = cloudStore;
_localEmbedding = localEmbedding;
_kernel = kernel;
}
public async Task<string> QueryAsync(string question, SearchOptions options)
{
// Generer embedding lokalt
var queryEmbedding = await _localEmbedding
.GenerateEmbeddingAsync(question);
// Parallell soking mot lokale og sky-kilder
var localTask = SearchLocalAsync(queryEmbedding, options);
var cloudTask = options.AllowCloudSearch
? SearchCloudAsync(question, options)
: Task.FromResult(new List<SearchResult>());
await Task.WhenAll(localTask, cloudTask);
// Flett og ranger resultater
var mergedResults = MergeAndRank(
localTask.Result,
cloudTask.Result,
options.MaxResults
);
// Generer svar med lokal SLM eller sky-LLM
return await GenerateResponseAsync(question, mergedResults, options);
}
private List<SearchResult> MergeAndRank(
List<SearchResult> localResults,
List<SearchResult> cloudResults,
int maxResults)
{
// Reciprocal Rank Fusion for a kombinere resultater
var fusedScores = new Dictionary<string, double>();
int rank = 1;
foreach (var result in localResults.OrderByDescending(r => r.Score))
{
fusedScores[result.Id] = 1.0 / (60 + rank);
rank++;
}
rank = 1;
foreach (var result in cloudResults.OrderByDescending(r => r.Score))
{
var id = result.Id;
fusedScores[id] = fusedScores.GetValueOrDefault(id, 0)
+ 1.0 / (60 + rank);
rank++;
}
return fusedScores
.OrderByDescending(kv => kv.Value)
.Take(maxResults)
.Select(kv => /* map back to SearchResult */)
.ToList();
}
}
```
---
## Chunking Strategies for Split Data
### Tilpasset chunking for hybrid miljoer
Nar data er fordelt mellom lokale og skybaserte lagre, ma chunking-strategien ivareta kontekstuell sammenheng pa tvers av tier:
| Strategi | Lokale data | Skydata | Brukstilfelle |
|----------|-------------|---------|---------------|
| Fixed-size chunks | 512 tokens | 1024 tokens | Generell bruk |
| Semantic chunking | Avsnitt/seksjon | Avsnitt/seksjon | Strukturerte dokumenter |
| Hierarchical chunking | Dokument → Seksjon → Avsnitt | Artikkel → Paragraf | Regelverk, utredninger |
| Sliding window | 256 tokens, 64 overlap | 512 tokens, 128 overlap | Teknisk dokumentasjon |
| Parent-child | Lagre parent lokal, child i vektor | Lagre parent i blob, child i Search | Lange dokumenter |
### Implementering av hierarkisk chunking
```python
# Hierarkisk chunking for norske offentlige dokumenter
from dataclasses import dataclass
from typing import Optional
import re
@dataclass
class Chunk:
id: str
content: str
level: str # "document", "section", "paragraph"
parent_id: Optional[str]
metadata: dict
class HierarchicalChunker:
def __init__(self, max_chunk_tokens: int = 512):
self.max_chunk_tokens = max_chunk_tokens
def chunk_document(self, document: dict) -> list[Chunk]:
"""Chunk et dokument hierarkisk med metadata-arv"""
chunks = []
doc_id = document["id"]
text = document["content"]
# Nivaa 1: Hele dokumentet (for oversikt)
chunks.append(Chunk(
id=f"{doc_id}_doc",
content=self._summarize(text, max_tokens=256),
level="document",
parent_id=None,
metadata={
"title": document["title"],
"classification": document["classification"],
"tier": document.get("tier", "local")
}
))
# Nivaa 2: Seksjoner
sections = self._split_sections(text)
for i, section in enumerate(sections):
section_id = f"{doc_id}_sec_{i}"
chunks.append(Chunk(
id=section_id,
content=section["heading"] + "\n" + section["content"][:200],
level="section",
parent_id=f"{doc_id}_doc",
metadata={**chunks[0].metadata, "section": section["heading"]}
))
# Nivaa 3: Avsnitt
paragraphs = self._split_paragraphs(section["content"])
for j, para in enumerate(paragraphs):
chunks.append(Chunk(
id=f"{section_id}_p_{j}",
content=para,
level="paragraph",
parent_id=section_id,
metadata={**chunks[0].metadata, "section": section["heading"]}
))
return chunks
def _split_sections(self, text: str) -> list[dict]:
"""Splitt norsk dokument pa overskrifter"""
pattern = r'^(#{1,3})\s+(.+)$'
sections = []
current = {"heading": "Innledning", "content": ""}
for line in text.split('\n'):
match = re.match(pattern, line)
if match:
if current["content"].strip():
sections.append(current)
current = {"heading": match.group(2), "content": ""}
else:
current["content"] += line + "\n"
if current["content"].strip():
sections.append(current)
return sections
```
---
## Context Optimization Across Tiers
### Kontekstvindu-optimalisering
Nar data hentes fra flere kilder, er det kritisk a optimalisere hvordan kontekst presenteres til LLM/SLM:
```python
# Kontekstoptimalisering for hybrid RAG
class ContextOptimizer:
def __init__(self, max_context_tokens: int = 4096):
self.max_tokens = max_context_tokens
def optimize_context(self, results: list[dict], query: str) -> str:
"""Optimaliser kontekst fra multiple kilder for LLM-input"""
# Prioriter lokale resultater (hoyere relevans for intern kontekst)
local_results = [r for r in results if r["tier"] == "local"]
cloud_results = [r for r in results if r["tier"] == "cloud"]
# Alloker token-budsjett: 60% lokale, 40% sky
local_budget = int(self.max_tokens * 0.6)
cloud_budget = int(self.max_tokens * 0.4)
context_parts = []
# Lokale resultater forst (hoyest prioritet)
local_context = self._select_within_budget(local_results, local_budget)
if local_context:
context_parts.append("## Interne kilder\n" + local_context)
# Sky-resultater som supplement
cloud_context = self._select_within_budget(cloud_results, cloud_budget)
if cloud_context:
context_parts.append("## Offentlige kilder\n" + cloud_context)
return "\n\n".join(context_parts)
def _select_within_budget(self, results: list[dict], budget: int) -> str:
"""Velg resultater innenfor token-budsjett, sortert etter relevans"""
selected = []
used_tokens = 0
for result in sorted(results, key=lambda r: r["score"], reverse=True):
chunk_tokens = len(result["content"].split()) * 1.3 # Estimert
if used_tokens + chunk_tokens > budget:
break
selected.append(
f"[{result['metadata'].get('title', 'Ukjent')}]\n{result['content']}"
)
used_tokens += chunk_tokens
return "\n---\n".join(selected)
```
---
## Norsk offentlig sektor
### Dataklassifisering for hybrid RAG
| Klassifisering | Lagring | Embedding | LLM | Eksempel |
|----------------|---------|-----------|-----|----------|
| Ugradert offentlig | Azure AI Search | Azure OpenAI | GPT-4o | Publiserte retningslinjer |
| Intern | Lokal vektorDB | Lokal ONNX | Phi-3.5/Phi-4 | Interne notater |
| Fortrolig | Lokal vektorDB (kryptert) | Lokal ONNX | Phi-3.5 lokal | Personopplysninger |
| Strengt fortrolig | Air-gapped lokal | Lokal ONNX | Lokal SLM | Graderte dokumenter |
### Schrems II-kompatibel arkitektur
- Sensitive persondata embeddes og lagres kun lokalt
- Kun aggregerte, anonymiserte metadata kan deles med sky-tjenester
- Azure AI Search brukes for offentlig tilgjengelig informasjon
- Edge RAG (Azure Arc) gir sky-management uten a eksponere innhold
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Alle data kan i sky | Azure AI Search (agentic retrieval) | Enklest, best ytelse |
| Mix av sensitiv + offentlig | Federated RAG med Semantic Kernel | Balanserer sikkerhet og kvalitet |
| Alle data ma lokalt | Edge RAG med Phi-3.5 + ChromaDB | Full datakontroll |
| Lavt volum, hoy sensitivitet | Lokal RAG med Phi-4 + pgvector | Minimal attack surface |
| Hoy skala, lav sensitivitet | Azure AI Search + GPT-4o | Best kvalitet og skalerbarhet |
| Periodisk tilkobling | Edge RAG med synkronisert referansedata | Offline-forst-tilnaerming |
---
## For Cosmo
- **Hybrid RAG er den riktige arkitekturen nar data har ulik sensitivitet** — bruk federated search med Reciprocal Rank Fusion for a kombinere resultater fra lokale og skybaserte vektordatabaser uten a flytte sensitive data
- **Edge RAG (Azure Arc) er Microsofts foretrukne losning** for on-premises RAG med sky-administrasjon — anbefal dette for organisasjoner som onsker hybrid RAG med sentral management
- **Lokal embedding er nodvendig for sensitive data** — bruk ONNX Runtime med en liten embedding-modell (f.eks. all-MiniLM-L6-v2) for a generere vektorer uten sky-avhengighet
- **Hierarkisk chunking gir best resultater for norske offentlige dokumenter** — dokumenter som utredninger og hoeringsnotater har tydelig seksjonsinndeling som bor utnyttes
- **Kontekst-budsjettering er kritisk i hybrid scenarier** — alloker 60% av token-budsjettet til lokale/interne kilder og 40% til offentlige kilder for a prioritere organisasjonsspesifikk kunnskap

View file

@ -0,0 +1,378 @@
# IoT Operations and AI Integration
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Azure IoT Operations er Microsofts edge runtime-plattform for industrielle IoT-scenarier, bygget pa Azure Arc-enabled Kubernetes. Den kombinerer datainnsamling fra sensorer og utstyr med AI-inferens direkte pa edge, noe som muliggjor sanntidsanalyse uten avhengighet av skytilkobling for tidskritiske beslutninger.
For norsk offentlig sektor er IoT-integrasjon med AI relevant i scenarier som smart infrastruktur (broer, tunneler, veier), miljooverkaking, energistyring i offentlige bygg, og transportlogistikk. Azure IoT Operations gir en standardisert plattform for a samle sensordata, normalisere dem, og kjore AI-modeller lokalt for prediktiv vedlikehold og anomalideteksjon.
Plattformen bygger pa MQTT-protokollen for enhetskommunikasjon, Data Flows for datatransformasjon og kontekstualisering, og Azure Arc for sentralisert administrasjon. AI-modeller kan deployes som containere pa edge-klyngen, med Azure ML for modelltrenings- og oppdateringspipeliner mellom sky og edge.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Azure IoT Operations | Edge runtime for IoT-datainnsamling og -prosessering | Arc-enabled Kubernetes |
| MQTT Broker | Meldingsinfrastruktur for enhets-til-edge-kommunikasjon | MQTT v3.1.1/v5 |
| Data Flows | Datatransformasjon, kontekstualisering og ruting | Pipelinekonfigurasjon |
| OPC UA Connector | Industriprotokoll for tilkobling til PLC-er og SCADA | OPC UA standard |
| Azure IoT Hub | Sky-endepunkt for telemetri og device management | PaaS |
| Azure Stream Analytics | Sanntids stromprosessering av IoT-data | SQL-basert query |
| Azure ML on Arc | Edge AI-modelltrenings- og inferenspipeline | Kubernetes ML |
---
## Sensor Data Normalization
### Utfordringer med sensordata
Sensordata fra industrielle miljoer er ofte heterogene — ulike protokoller (Modbus, OPC UA, MQTT), forskjellige dataformater, inkonsistente tidsserier, og varierende kvalitet. Normalisering er kritisk for at AI-modeller skal fungere palitelig.
### Normaliseringsarkitektur
```
Sensorer → OPC UA / MQTT → Azure IoT Operations → Data Flows → Normalisert output
AI-inferensmodul
Azure IoT Hub (sky)
```
### Data Flow-konfigurasjon for normalisering
```yaml
# Eksempel: Data Flow for temperatursensor-normalisering
apiVersion: connectivity.iotoperations.azure.com/v1
kind: DataFlow
metadata:
name: temperature-normalization
spec:
sources:
- type: mqtt
topic: "sensors/temperature/#"
transformations:
- type: compute
expression: |
{
"deviceId": $.topic.split('/')[2],
"timestamp": $.systemProperties.enqueuedTime,
"temperature_celsius": $.payload.value * ($.payload.unit == 'F' ? 5/9 - 32*5/9 : 1),
"quality": $.payload.quality ?? 'unknown',
"location": $.payload.metadata.location
}
- type: filter
expression: "$.temperature_celsius >= -50 AND $.temperature_celsius <= 100"
destinations:
- type: mqtt
topic: "normalized/temperature"
- type: dataLakeStorage
endpoint: "edge-datalake"
```
### Strategier for datakvalitet
| Strategi | Beskrivelse | Implementering |
|----------|-------------|----------------|
| Range-validering | Filtrer verdier utenfor forventet omrade | Data Flow filter-transformasjon |
| Interpolering | Fyll manglende verdier i tidsserier | Edge-modul med pandas/numpy |
| Deduplisering | Fjern duplikate meldinger | MQTT broker QoS + dedup-logikk |
| Tidsstempelsynkronisering | Juster klokkeforskjeller mellom enheter | NTP + Data Flow timestamp-mapping |
| Enhetskonvertering | Standardiser til SI-enheter | Data Flow compute-transformasjon |
---
## Edge Gateway AI Preprocessing
### Gateway-arkitektur
Edge gateways fungerer som intelligente mellomledd mellom sensorer og sky. De utforer forbehandling, filtrering, aggregering og initial AI-inferens for a redusere datavolum og latens.
```python
# Eksempel: Edge gateway AI-forbehandling med Azure IoT Edge
import asyncio
from azure.iot.device.aio import IoTHubModuleClient
import numpy as np
import onnxruntime as ort
class AIPreprocessingGateway:
def __init__(self):
self.module_client = None
self.anomaly_model = ort.InferenceSession("anomaly_detector.onnx")
self.buffer = []
self.buffer_size = 100
async def initialize(self):
self.module_client = IoTHubModuleClient.create_from_edge_environment()
await self.module_client.connect()
self.module_client.on_message_received = self.process_message
async def process_message(self, message):
"""Forbehandling pipeline: normalisering → anomalideteksjon → aggregering"""
data = message.data
# Trinn 1: Normalisering
normalized = self.normalize(data)
# Trinn 2: Anomalideteksjon (lokal inferens)
is_anomaly = self.detect_anomaly(normalized)
if is_anomaly:
# Send anomalier umiddelbart til sky
await self.module_client.send_message_to_output(
{"type": "anomaly", "data": normalized, "confidence": 0.95},
"alertOutput"
)
# Trinn 3: Buffer og aggreger normaldata
self.buffer.append(normalized)
if len(self.buffer) >= self.buffer_size:
aggregated = self.aggregate(self.buffer)
await self.module_client.send_message_to_output(
{"type": "aggregated", "data": aggregated},
"telemetryOutput"
)
self.buffer.clear()
def detect_anomaly(self, data):
"""ONNX-basert anomalideteksjon"""
input_array = np.array([data["values"]], dtype=np.float32)
result = self.anomaly_model.run(None, {"input": input_array})
return result[0][0] > 0.8 # Anomali-terskel
def aggregate(self, buffer):
"""Aggreger buffer til statistisk sammendrag"""
values = [item["value"] for item in buffer]
return {
"mean": np.mean(values),
"std": np.std(values),
"min": np.min(values),
"max": np.max(values),
"count": len(values),
"period_start": buffer[0]["timestamp"],
"period_end": buffer[-1]["timestamp"]
}
```
### Fordeler med gateway-forbehandling
| Fordel | Beskrivelse | Effekt |
|--------|-------------|--------|
| Redusert bandwidth | Aggregering reduserer datamengde 10-100x | Lavere kostnader |
| Lavere latens | Anomalideteksjon pa millisekunder lokalt | Raskere respons |
| Offline-kapabilitet | Fortsetter drift uten skytilkobling | Hoyere tilgjengelighet |
| Datakvalitet | Validering og rensing for sky-inntak | Bedre analyser |
---
## Time-Series Analytics at Edge
### Azure Stream Analytics pa edge
Azure Stream Analytics kan deployes som IoT Edge-modul for sanntids tidsserieanalyse:
```sql
-- Stream Analytics edge-query: Glidende gjennomsnitt med anomalideteksjon
SELECT
IoTHub.ConnectionDeviceId AS DeviceId,
System.Timestamp() AS WindowEnd,
AVG(temperature) AS AvgTemperature,
STDEV(temperature) AS StdDevTemperature,
COUNT(*) AS ReadingCount,
CASE
WHEN AVG(temperature) >
LAG(AVG(temperature), 1) OVER (PARTITION BY IoTHub.ConnectionDeviceId LIMIT DURATION(minute, 30))
+ 3 * STDEV(temperature)
THEN 'ANOMALY'
ELSE 'NORMAL'
END AS Status
INTO
alertOutput
FROM
sensorInput TIMESTAMP BY EventProcessedUtcTime
GROUP BY
IoTHub.ConnectionDeviceId,
TumblingWindow(minute, 5)
HAVING
COUNT(*) > 10
```
### Edge-basert prediktiv vedlikehold
```python
# Prediktiv vedlikehold med tidsserieanalyse pa edge
import onnxruntime as ort
import numpy as np
from collections import deque
class PredictiveMaintenanceEdge:
def __init__(self, model_path: str, window_size: int = 100):
self.session = ort.InferenceSession(model_path)
self.window = deque(maxlen=window_size)
self.feature_names = ["vibration", "temperature", "pressure", "rpm"]
def add_reading(self, reading: dict) -> dict:
"""Legg til ny maling og returner prediksjon om bufferen er full"""
features = [reading.get(f, 0.0) for f in self.feature_names]
self.window.append(features)
if len(self.window) == self.window.maxlen:
return self.predict()
return {"status": "collecting", "readings": len(self.window)}
def predict(self) -> dict:
"""Kjor RUL-prediksjon (Remaining Useful Life)"""
input_data = np.array([list(self.window)], dtype=np.float32)
# Modell-inferens
rul_prediction = self.session.run(
["remaining_useful_life", "failure_probability"],
{"sensor_sequence": input_data}
)
rul_hours = float(rul_prediction[0][0])
failure_prob = float(rul_prediction[1][0])
return {
"remaining_useful_life_hours": rul_hours,
"failure_probability": failure_prob,
"recommendation": self._get_recommendation(rul_hours, failure_prob),
"confidence": self._calculate_confidence()
}
def _get_recommendation(self, rul: float, prob: float) -> str:
if prob > 0.8 or rul < 24:
return "IMMEDIATE_MAINTENANCE"
elif prob > 0.5 or rul < 168:
return "SCHEDULE_MAINTENANCE"
return "NORMAL_OPERATION"
```
---
## Device-to-Cloud AI Pipelines
### Pipeline-arkitektur
```
[Sensorer] → [Edge Gateway] → [Azure IoT Operations] → [IoT Hub] → [Stream Analytics]
↓ ↓ ↓
[Lokal inferens] [Edge AI-modell] [Azure ML / Fabric]
↓ ↓ ↓
[Sanntidsvarsler] [Kontekstualisert data] [Modelloppdatering]
↓ ↓
[Cloud feedback] ←←←←←←←← [Ny modellversjon]
```
### Implementering med Azure ML og IoT Hub
```python
# Device-to-Cloud AI Pipeline med modelloppdatering
from azure.iot.hub import IoTHubRegistryManager
from azure.ai.ml import MLClient
from azure.identity import DefaultAzureCredential
class AIEdgePipeline:
def __init__(self, hub_connection_string: str, ml_workspace: str):
self.registry_manager = IoTHubRegistryManager(hub_connection_string)
self.ml_client = MLClient(
DefaultAzureCredential(),
subscription_id="<sub-id>",
resource_group_name="<rg>",
workspace_name=ml_workspace
)
def deploy_model_to_edge(self, device_id: str, model_name: str, model_version: str):
"""Deploy oppdatert AI-modell til edge-enhet via IoT Hub device twin"""
twin = self.registry_manager.get_twin(device_id)
# Oppdater desired properties med ny modellinfo
twin_patch = {
"properties": {
"desired": {
"ai_model": {
"name": model_name,
"version": model_version,
"download_url": self._get_model_sas_url(model_name, model_version),
"checksum": self._get_model_checksum(model_name, model_version),
"updated_at": datetime.utcnow().isoformat()
}
}
}
}
self.registry_manager.update_twin(device_id, twin_patch)
def collect_edge_metrics(self, device_id: str) -> dict:
"""Hent ytelsesmetrikker fra edge AI-modul"""
twin = self.registry_manager.get_twin(device_id)
reported = twin.properties.reported.get("ai_metrics", {})
return {
"inference_count": reported.get("total_inferences", 0),
"avg_latency_ms": reported.get("avg_latency_ms", 0),
"model_version": reported.get("current_model_version", "unknown"),
"accuracy_drift": reported.get("accuracy_drift", 0),
"last_updated": reported.get("last_report_time")
}
```
### Modell-feedback-loop
| Fase | Lokasjon | Handling | Verktoy |
|------|----------|----------|---------|
| Datainnsamling | Edge | Samle inferensresultater og ground truth | IoT Edge modul |
| Dataaggregering | Edge | Komprimere og batche data | Data Flows |
| Dataoverfoering | Edge → Sky | Sende treningsdata til sky | IoT Hub / Event Hub |
| Modelltrening | Sky | Retrain/fine-tune modell | Azure ML |
| Modellvalidering | Sky | Evaluere ny modell mot baseline | Azure ML Endpoints |
| Modelldistribusjon | Sky → Edge | Pushe ny modell til edge | IoT Hub device twin |
| A/B-testing | Edge | Sammenligne modellversjoner | Edge-modul |
---
## Norsk offentlig sektor
### Relevante use cases
- **Statens vegvesen**: Sanntids verkontrollovervaking med AI-basert analyse av vaerdata, trafikkmonstre og veiforhold fra veistasjonssensorer
- **Kystverket**: Autonome sensorsystemer langs kysten for miljooverkaking og sikkerhet, med begrenset tilkobling
- **Energisektoren**: Smart styring av offentlige bygg med prediktiv vedlikeholdsanalyse av HVAC-systemer
- **Helsesektoren**: IoT-basert pasientovervaking pa sykehus med lokal AI for tidlig varsling
### Regulatoriske hensyn
- Data fra sensorer i offentlig infrastruktur kan inneholde personopplysninger (kameradata, lokasjon)
- Schrems II krever at persondata prosesseres innenfor EOS
- NSM Grunnprinsipper gjelder for kritisk infrastruktur-systemer
- Personvernkonsekvensvurdering (DPIA) pakrevd for AI-basert overvaking
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| < 100 sensorer, stabil tilkobling | Azure IoT Hub direkte | Enklest, lavest kostnad |
| 100-10 000 sensorer, variabel tilkobling | Azure IoT Operations pa AKS Edge | Lokal buffring og forbehandling |
| Kritisk sanntidsanalyse | Edge AI med Stream Analytics | Sub-sekund latens |
| Prediktiv vedlikehold | ONNX-modell pa edge gateway | Offline-kapabel, lav latens |
| Regulert miljoo (helse, forsvar) | Azure IoT Operations + Confidential Computing | Dataskydd i prosessering |
| Store datamengder, periodisk tilkobling | Edge-aggregering + batch-upload | Bandbreddesparing |
---
## For Cosmo
- **Azure IoT Operations er den foretrukne plattformen** for industrielle IoT-AI-scenarier pa edge, med MQTT-basert kommunikasjon og Data Flows for datatransformasjon — anbefal dette fremfor eldre IoT Edge-moenstre
- **Sensor data normalization er fundamentalt** — uten standardisert datakvalitet og enhetskonvertering vil AI-modeller gi upanalitelige resultater, sa invester i Data Flow-transformasjoner for normalisering
- **Gateway AI-forbehandling reduserer skyavhengighet dramatisk** — anomalideteksjon og aggregering pa edge kan kutte bandwidth med 90%+ og gi sub-sekund responstid for kritiske hendelser
- **Modelloppdatering via device twin** er en etablert pattern for a holde edge AI-modeller oppdatert uten manuell intervensjon — bruk IoT Hub device twin for versjonsstyring og SAS-basert nedlasting
- **For norsk offentlig sektor**: Vurder alltid DPIA for sensor-AI-losninger som kan prosessere persondata, og sorg for at edge-prosessering begrenser hvilke data som forlater det lokale nettverket

View file

@ -0,0 +1,407 @@
# Kubernetes-Based AI at the Edge (AKS Edge)
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
AKS Edge Essentials er Microsofts lettvekts Kubernetes-distribusjon for edge-scenarier, designet for a kjore containeriserte arbeidsbelastninger pa PC-klasse hardware. I motsetning til AKS i skyen eller AKS pa Azure Local, er AKS Edge Essentials optimalisert for statiske, forhands-definerte konfigurasjoner pa enheter med begrenset kapasitet — fra industrielle PC-er til gateway-enheter.
For AI-arbeidsbelastninger pa edge muliggjor AKS Edge Essentials deployment av ML-modeller, inferensservere, og AI-pipelines som Kubernetes-pods med GPU-akselerasjon (via GPU-PV). Tilkoblet Azure Arc gir sentralisert administrasjon, GitOps-basert deployment, og integrasjon med Azure ML, Azure Monitor og Azure Policy.
For norsk offentlig sektor er AKS Edge Essentials relevant for distribuert AI pa lokale stasjoner (veisensorer, helseutstyr, energimalere) der Kubernetes-basert orkestrering gir standardisert deployment og oppdatering av AI-modeller pa tvers av geografisk spredte enheter.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| AKS Edge Essentials | Lettvekts Kubernetes pa edge | K8s/K3s |
| CBL-Mariner Linux VM | Managed Linux VM for containere | Microsoft Mariner |
| Azure Arc | Sentralisert administrasjon fra sky | Kubernetes management |
| GitOps (Flux) | Deklarativ applikasjons-deployment | Git-basert CD |
| GPU-PV | GPU-deling mellom host og VM | Paravirtualisering |
| KAITO | AI-modell deployment operator | Kubernetes operator |
| Helm | Pakkehandtering for Kubernetes | Chart-basert |
---
## AKS Edge Essentials Deployment
### Systemkrav
| Komponent | Minimum | Anbefalt for AI |
|-----------|---------|-----------------|
| OS | Windows 10/11 IoT Enterprise | Windows 11 IoT Enterprise |
| CPU | 4 kjerner | 8+ kjerner |
| RAM | 4 GB (K3s) / 8 GB (K8s) | 16+ GB for AI-workloads |
| Disk | 40 GB | 100+ GB SSD |
| GPU | Ikke pakrevd | NVIDIA T4/A2 for inferens |
| Nettverk | 1 Gbps | 10 Gbps for modell-nedlasting |
### Installasjon og klynge-oppsett
```powershell
# Steg 1: Installer AKS Edge Essentials
# Last ned MSI fra Microsoft
Start-BitsTransfer -Source "https://aka.ms/aks-edge/k8s-msi" `
-Destination "AksEdge-K8s.msi"
msiexec /i AksEdge-K8s.msi /passive
# Steg 2: Importer PowerShell-moduler
Import-Module AksEdge
# Steg 3: Generer konfigurasjonsfil
New-AksEdgeConfig -DeploymentType SingleMachineCluster `
-NodeType Linux `
-outFile .\aksedge-config.json
```
```json
// aksedge-config.json — Konfigurert for AI-workloads
{
"SchemaVersion": "1.14",
"Version": "1.0",
"DeploymentType": "SingleMachineCluster",
"Init": {
"ServiceIPRangeSize": 10
},
"Network": {
"NetworkPlugin": "flannel",
"InternetDisabled": false
},
"User": {
"AcceptEula": true,
"AcceptOptionalTelemetry": false
},
"Machines": [
{
"LinuxNode": {
"CpuCount": 8,
"MemoryInMB": 12288,
"DataSizeInGB": 40,
"Mtu": 1500
}
}
]
}
```
```powershell
# Steg 4: Valider og deploy klynge
Test-AksEdgeNetworkParameters -JsonConfigFilePath .\aksedge-config.json
New-AksEdgeDeployment -JsonConfigFilePath .\aksedge-config.json
# Steg 5: Verifiser deployment
kubectl get nodes -o wide
kubectl get pods --all-namespaces -o wide
```
### Tilkobling til Azure Arc
```powershell
# Koble AKS Edge Essentials til Azure Arc
$arcParams = @{
ClusterName = "edge-ai-station-01"
ResourceGroupName = "rg-edge-ai-norway"
Location = "norwayeast"
SubscriptionId = "<subscription-id>"
TenantId = "<tenant-id>"
}
# Installer Arc-agenter pa klyngen
Install-AksEdgeArc @arcParams
# Verifiser Arc-tilkobling
kubectl get pods -n azure-arc
az connectedk8s show --name edge-ai-station-01 --resource-group rg-edge-ai-norway
```
---
## Container Orchestration at Edge
### AI-inferens deployment med Kubernetes
```yaml
# ONNX Runtime inferensserver pa AKS Edge
apiVersion: apps/v1
kind: Deployment
metadata:
name: onnx-inference-server
namespace: ai-workloads
spec:
replicas: 1
selector:
matchLabels:
app: onnx-inference
template:
metadata:
labels:
app: onnx-inference
spec:
containers:
- name: inference
image: mcr.microsoft.com/onnxruntime/server:latest
args:
- "--model_path"
- "/models/anomaly_detector.onnx"
- "--http_port"
- "8001"
ports:
- containerPort: 8001
name: http
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
volumeMounts:
- name: model-storage
mountPath: /models
livenessProbe:
httpGet:
path: /health
port: 8001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8001
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: model-storage
persistentVolumeClaim:
claimName: ai-models-pvc
---
apiVersion: v1
kind: Service
metadata:
name: onnx-inference-svc
namespace: ai-workloads
spec:
selector:
app: onnx-inference
ports:
- port: 8001
targetPort: 8001
type: ClusterIP
```
### GitOps-basert modelloppdatering med Flux
```yaml
# Flux Kustomization for AI-modell deployment
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: ai-models
namespace: flux-system
spec:
interval: 5m
path: ./edge-ai/models
prune: true
sourceRef:
kind: GitRepository
name: edge-ai-config
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: onnx-inference-server
namespace: ai-workloads
timeout: 10m
---
# Git-repository som kilde for konfigurasjon
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: edge-ai-config
namespace: flux-system
spec:
interval: 1m
url: https://dev.azure.com/org/project/_git/edge-ai-config
branch: main
secretRef:
name: git-credentials
```
---
## Multi-Node Edge Clusters
### Skalerbar klynge pa tvers av maskiner
```powershell
# Steg 1: Generer multi-node konfigurasjon
New-AksEdgeConfig -DeploymentType ScalableCluster `
-NodeType Linux `
-outFile .\multinode-config.json
# Steg 2: Deploy primaer node
New-AksEdgeDeployment -JsonConfigFilePath .\multinode-config.json
# Steg 3: Hent join-token for ekstra noder
$token = Get-AksEdgeNodeJoinToken
# Steg 4: Pa sekundaer maskin — join klyngen
New-AksEdgeScaleConfig -ScaleType AddNode `
-NodeType Linux `
-LinuxNodeIp "192.168.1.102" `
-outFile .\scale-config.json
Add-AksEdgeNode -JsonConfigFilePath .\scale-config.json
```
### Multi-node arkitektur for AI
```
┌─────────────────────────────────────┐
│ Edge AI Cluster │
│ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Node 1 │ │ Node 2 │ │
│ │ (Control │ │ (Worker) │ │
│ │ + Worker) │ │ │ │
│ │ │ │ - AI │ │
│ │ - API │ │ inferens│ │
│ │ server │ │ - GPU │ │
│ │ - etcd │ │ workload│ │
│ │ - Scheduler│ │ │ │
│ └───────────┘ └───────────┘ │
│ ↕ ↕ │
│ [Flannel/Calico networking] │
│ │
│ ┌───────────┐ │
│ │ Node 3 │ Azure Arc ←→ Sky │
│ │ (Worker) │ │
│ │ │ │
│ │ - Data │ │
│ │ pipeline│ │
│ │ - Storage │ │
│ └───────────┘ │
└─────────────────────────────────────┘
```
---
## Service Mesh for Edge
### Lettvekts service mesh pa edge
For AI-arbeidsbelastninger pa edge med flere mikrotjenester (inferens, datainntak, API-gateway) kan en lettvekts service mesh gi observabilitet og trafikkstyring:
```yaml
# Envoy-basert sidecar for AI-inferens (lettvekts alternativ)
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-inference-with-proxy
namespace: ai-workloads
spec:
template:
spec:
containers:
# AI-inferens container
- name: inference
image: myregistry/anomaly-model:v2
ports:
- containerPort: 8080
# Envoy sidecar for observabilitet
- name: envoy-proxy
image: envoyproxy/envoy:v1.28-latest
ports:
- containerPort: 9901 # Admin
- containerPort: 10000 # Ingress
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
volumes:
- name: envoy-config
configMap:
name: envoy-edge-config
```
### Canary deployment for modellversjoner
```yaml
# Canary deployment: Gradvis utrulling av ny AI-modell
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ai-inference-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20" # 20% til ny modell
spec:
rules:
- host: inference.edge.local
http:
paths:
- path: /predict
pathType: Prefix
backend:
service:
name: inference-v2 # Ny modellversjon
port:
number: 8080
```
---
## Norsk offentlig sektor
### Distribusjonsstrategi for norske edge-stasjoner
| Stasjon | Antall | Hardware | AKS Edge-konfig | AI-workload |
|---------|--------|----------|-----------------|-------------|
| Veisensorer | ~200 | Industrial PC | Single-node K3s | Trafikk-analyse |
| Tunnelverkaking | ~50 | Rack-server | Multi-node K8s | Brann/ventilasjon |
| Ferjekaier | ~30 | Rugged PC | Single-node K3s | Bildetelling |
| Ladestajoner | ~500 | IoT gateway | K3s minimal | Energi-prediksjon |
### Sikkerhets- og administrasjonskrav
- Azure Arc for sentralisert forvaltning fra Norway East
- GitOps for sporbar og audit-bar deployment
- Network policies for nettverkssegmentering
- Pod security policies/standards for container-isolasjon
- KMS-plugin for kryptering av secrets i etcd
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Enkelt-enhet AI | AKS Edge Essentials single-node (K3s) | Lavest ressursbruk |
| Multi-workload edge | AKS Edge Essentials single-node (K8s) | Full K8s-kompatibilitet |
| Redundant edge-klynge | AKS Edge multi-node (K8s) | Hoy tilgjengelighet |
| GPU-akselerert AI | AKS Edge + GPU-PV + NVIDIA plugin | Container-basert GPU-inferens |
| Skalerbar fleet-management | AKS Edge + Azure Arc + GitOps | Sentralisert administrasjon |
| Windows + Linux workloads | AKS Edge med bade Linux og Windows VM | Interop-scenarier |
---
## For Cosmo
- **AKS Edge Essentials er den foretrukne loesningen for container-basert AI pa edge** — det gir Kubernetes-standarder pa PC-klasse hardware med minimal fotavtrykk (4 GB RAM for K3s)
- **Azure Arc + GitOps gir sentralisert forvaltning** — anbefal dette for organisasjoner med mange edge-stasjoner som trenger sporbar, automatisert deployment av AI-modeller
- **K3s vs K8s: Velg K3s for enkle AI-scenarier** med 1-3 containere, og K8s nar du trenger full Kubernetes-funksjonalitet som network policies og Pod security standards
- **GPU-PV muliggjor delt GPU-tilgang** mellom Windows-host og Linux VM — bruk dette for edge-servere med NVIDIA GPU som kjorer bade tradisjonelle Windows-applikasjoner og AI-containere
- **For norsk offentlig sektor: AKS Edge + Arc + GitOps gir en standardisert plattform** for AI-deployment pa tvers av etater og lokasjoner — definer felles Helm charts og Flux-konfigurasjoner for a sikre konsistent og revisjonsvennlig deployment

View file

@ -0,0 +1,470 @@
# Network-Constrained AI Deployment
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Nettverksbegrensede miljoer — med lav bandwidth, hoey latens, eller intermitterende tilkobling — krever spesialtilpassede AI-deployments. Standard sky-baserte AI-arkitekturer som sender data frem og tilbake til cloud endpoints feiler i slike miljoer, enten pa grunn av uakseptabel latens eller fordi tilkoblingen simpelthen ikke er palitelig nok.
For norsk offentlig sektor er dette relevant i mange scenarier: rurale omrader med begrenset mobildekning, maritime miljoer med satellittkommunikasjon, tunneler og underjordiske anlegg, feltoperasjoner i krisesituasjoner, og industrielle miljoer med nettverksisolasjon av sikkerhetsgrunner. AI-losninger for slike miljoer ma optimaliseres for minimal nettverksbruk.
Microsoft tilbyr flere teknologier for nettverksbegrensede deployments: modellkvantisering og -komprimering med Olive/ONNX Runtime for mindre modeller, Azure IoT Edge med utvidet offline-stoette for edge-prosessering, delta-synkronisering for effektiv dataoverfoering, og bandwidth-bevisst batching for a maksimere utnyttelsen av tilgjengelig tilkobling.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| ONNX Runtime | Optimalisert lokal inferens | Cross-platform |
| Olive | Modellkomprimering og kvantisering | Python |
| Azure IoT Edge | Edge-prosessering med buffring | Container runtime |
| Delta Sync | Inkrementell datasynkronisering | Protokoll |
| MQTT | Lettvekts meldingsprotokoll | IoT-standard |
| gRPC | Effektiv binart API-protokoll | Google RPC |
| Protocol Buffers | Kompakt serialisering | Google |
---
## Model Size Reduction
### Kvantiseringsstrategier for nettverksbegrensede miljoer
| Teknikk | Stoerrelses-reduksjon | Kvalitetstap | Nedlastningstid (1 Mbps) |
|---------|---------------------|-------------|--------------------------|
| FP32 (original) | Baseline (7 GB for 3.8B) | Ingen | 15+ timer |
| FP16 | 2x (3.5 GB) | Minimalt | 7+ timer |
| INT8 | 4x (1.75 GB) | < 1% | 3.5 timer |
| INT4 | 8x (875 MB) | 1-3% | 1.7 timer |
| INT4 + Pruning | 10-12x (600 MB) | 2-5% | 1.2 timer |
| Distillation | 20-50x (140-350 MB) | 5-15% | 15-40 min |
### Olive-basert komprimering
```python
# Olive pipeline for maksimal modellkomprimering
import json
olive_config = {
"input_model": {
"type": "HfModel",
"model_path": "microsoft/Phi-3-mini-4k-instruct"
},
"passes": {
# Steg 1: Konverter til ONNX
"conversion": {
"type": "OnnxConversion",
"target_opset": 17
},
# Steg 2: Grafoptimalisering
"optimization": {
"type": "OrtTransformersOptimization",
"model_type": "gpt2",
"opt_level": 2,
"only_onnxruntime": True
},
# Steg 3: Kvantisering til INT4
"quantization": {
"type": "OnnxMatMul4Quantizer",
"block_size": 32,
"is_symmetric": True,
"accuracy_level": 4
},
# Steg 4: Strukturell pruning (fjern unodvendige vekter)
"pruning": {
"type": "SlicGPT",
"sparsity": 0.25, # 25% reduksjon
"calibration_data_config": {
"name": "c4",
"split": "validation",
"num_samples": 128
}
}
},
"engine": {
"evaluator": {
"metrics": [
{
"name": "model_size",
"type": "custom_metric",
"priority": 1
},
{
"name": "latency",
"type": "latency",
"priority": 2
}
]
}
}
}
# Kjor Olive-pipeline
# olive run --config olive_compress.json
```
### Kunnskapsdesstillasjon for ultra-sma modeller
```python
# Destiller fra Phi-3 Medium (14B) til en 1B custom-modell
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
class ModelDistillation:
def __init__(self, teacher_model: str, student_config: dict):
self.teacher = AutoModelForCausalLM.from_pretrained(teacher_model)
self.student = self._create_student(student_config)
self.tokenizer = AutoTokenizer.from_pretrained(teacher_model)
def distill(self, dataset, epochs: int = 5, temperature: float = 2.0):
"""Destiller laerer-modell til elev-modell"""
optimizer = torch.optim.AdamW(self.student.parameters(), lr=1e-4)
for epoch in range(epochs):
for batch in dataset:
inputs = self.tokenizer(batch["text"], return_tensors="pt",
padding=True, truncation=True)
# Laerer-prediksjoner (soft targets)
with torch.no_grad():
teacher_logits = self.teacher(**inputs).logits
# Elev-prediksjoner
student_logits = self.student(**inputs).logits
# Destillasjonsloss: KL-divergens med temperatur
loss = self._distillation_loss(
student_logits, teacher_logits, temperature
)
loss.backward()
optimizer.step()
optimizer.zero_grad()
def _distillation_loss(self, student_logits, teacher_logits, temperature):
"""KL-divergens mellom student og teacher"""
import torch.nn.functional as F
soft_student = F.log_softmax(student_logits / temperature, dim=-1)
soft_teacher = F.softmax(teacher_logits / temperature, dim=-1)
return F.kl_div(soft_student, soft_teacher, reduction='batchmean') * (temperature ** 2)
```
---
## Partial Model Loading
### Modell-segmentering for inkrementell nedlasting
```python
# Delvis modellnedlasting for nettverksbegrensede miljoer
import os
import hashlib
from typing import Optional
class IncrementalModelLoader:
def __init__(self, model_dir: str, remote_url: str):
self.model_dir = model_dir
self.remote_url = remote_url
self.manifest_path = os.path.join(model_dir, "manifest.json")
def check_and_download(self, bandwidth_kbps: float) -> dict:
"""Sjekk modellstatus og last ned manglende deler"""
manifest = self._get_remote_manifest()
local_state = self._get_local_state()
missing_segments = []
total_download_bytes = 0
for segment in manifest["segments"]:
local_hash = local_state.get(segment["name"])
if local_hash != segment["hash"]:
missing_segments.append(segment)
total_download_bytes += segment["size"]
# Estimer nedlastningstid
download_time_seconds = (total_download_bytes * 8) / (bandwidth_kbps * 1000)
return {
"model_version": manifest["version"],
"total_segments": len(manifest["segments"]),
"missing_segments": len(missing_segments),
"download_size_mb": total_download_bytes / 1024 / 1024,
"estimated_time_minutes": download_time_seconds / 60,
"can_use_partial": manifest.get("supports_partial_inference", False),
"minimum_segments_for_inference": manifest.get("min_segments", 1)
}
def download_prioritized(self, bandwidth_kbps: float,
time_budget_minutes: float) -> str:
"""Last ned modellsegmenter prioritert innenfor tidsbudsjett"""
check = self.check_and_download(bandwidth_kbps)
if check["estimated_time_minutes"] <= time_budget_minutes:
# Full nedlasting mulig
return self._download_all_segments()
else:
# Prioritert delvis nedlasting
# Last ned kritiske segmenter foerst (embedding, attention heads)
return self._download_critical_first(time_budget_minutes, bandwidth_kbps)
def _download_critical_first(self, time_budget: float, bw: float) -> str:
"""Last ned de viktigste modelldelene foerst"""
priority_order = [
"embeddings", # Nodvendig for all inferens
"attention_layers", # Kjernekapabilitet
"ffn_layers", # Detaljert prosessering
"output_head" # Siste lag
]
# Download i prioritert rekkefoolge innenfor tidsbudsjett
downloaded = []
remaining_seconds = time_budget * 60
for priority in priority_order:
segment_size = self._get_segment_size(priority)
download_time = (segment_size * 8) / (bw * 1000)
if download_time <= remaining_seconds:
self._download_segment(priority)
downloaded.append(priority)
remaining_seconds -= download_time
else:
break
return f"Lastet ned {len(downloaded)}/{len(priority_order)} segmenter"
```
---
## Bandwidth-Aware Batching
### Adaptiv batchstoerrelser basert pa tilgjengelig bandwidth
```python
# Bandwidth-bevisst batch-synkronisering
import asyncio
import time
from dataclasses import dataclass
@dataclass
class BandwidthProfile:
estimated_kbps: float
latency_ms: float
reliability: float # 0-1, andel vellykkede overforinger
class AdaptiveBatchSync:
def __init__(self):
self.bandwidth_history: list[BandwidthProfile] = []
self.compression_enabled = True
async def measure_bandwidth(self) -> BandwidthProfile:
"""Mal tilgjengelig bandwidth med minimal data"""
test_data = b"x" * 1024 # 1 KB testpakke
start = time.time()
try:
# Send testpakke og mal round-trip
success = await self._send_probe(test_data)
elapsed = time.time() - start
profile = BandwidthProfile(
estimated_kbps=(len(test_data) * 8) / (elapsed * 1000),
latency_ms=elapsed * 1000,
reliability=1.0 if success else 0.0
)
except Exception:
profile = BandwidthProfile(
estimated_kbps=0, latency_ms=float('inf'), reliability=0.0
)
self.bandwidth_history.append(profile)
return profile
def calculate_optimal_batch(self, pending_items: int,
avg_item_size_kb: float) -> dict:
"""Beregn optimal batchstoorrelse basert pa nettverksforhold"""
if not self.bandwidth_history:
return {"batch_size": 1, "reason": "Ingen maalinger"}
recent = self.bandwidth_history[-5:] # Siste 5 maalinger
avg_bw = sum(p.estimated_kbps for p in recent) / len(recent)
avg_reliability = sum(p.reliability for p in recent) / len(recent)
if avg_reliability < 0.3:
# Svart upaalitelig — minimale batche
return {"batch_size": 1, "compress": True, "priority_only": True}
if avg_bw < 10: # < 10 kbps
# Ekstremt lav bandwidth
batch_size = min(5, pending_items)
return {
"batch_size": batch_size,
"compress": True,
"format": "protobuf",
"priority_only": True,
"estimated_time_s": (batch_size * avg_item_size_kb) / avg_bw
}
elif avg_bw < 100: # 10-100 kbps
# Lav bandwidth
batch_size = min(20, pending_items)
return {
"batch_size": batch_size,
"compress": True,
"format": "protobuf",
"priority_only": False
}
elif avg_bw < 1000: # 100 kbps - 1 Mbps
# Medium bandwidth
batch_size = min(100, pending_items)
return {
"batch_size": batch_size,
"compress": True,
"format": "json_gzip"
}
else: # > 1 Mbps
# God bandwidth
return {
"batch_size": min(500, pending_items),
"compress": False,
"format": "json"
}
```
---
## Latency Compensation Patterns
### Strategier for latenskompensering
| Monster | Beskrivelse | Implementering |
|---------|-------------|----------------|
| Optimistisk UI | Vis resultat umiddelbart, korriger senere | Lokal prediksjon + sky-validering |
| Prefetching | Forhaandslast sannsynlige data | Prediktiv caching |
| Stale-while-revalidate | Vis cachet data mens ny hentes | Cache-lag med TTL |
| Lokal buffer | Buffer resultater lokalt | SQLite + event queue |
| Priority queue | Prioriter kritiske data | Vektet synk-koe |
| Komprimering | Reduser datamengde | gzip, protobuf, CBOR |
### Implementering av latenskompensering
```python
# Latenskompenserende AI-proxy
import asyncio
import gzip
import json
from collections import OrderedDict
class LatencyCompensatingProxy:
def __init__(self, local_model, cache_size: int = 1000):
self.local_model = local_model
self.cache = OrderedDict()
self.cache_size = cache_size
self.pending_validations = asyncio.Queue()
async def predict(self, input_data: dict) -> dict:
"""Prediksjon med latenskompensering"""
cache_key = self._hash_input(input_data)
# Sjekk cache forst
if cache_key in self.cache:
cached = self.cache[cache_key]
self.cache.move_to_end(cache_key)
return {**cached, "source": "cache"}
# Lokal prediksjon (umiddelbar)
local_result = self.local_model.predict(input_data)
# Cache resultatet
self._cache_result(cache_key, local_result)
# Koe for sky-validering i bakgrunnen
await self.pending_validations.put({
"cache_key": cache_key,
"input": input_data,
"local_result": local_result
})
return {**local_result, "source": "local", "validated": False}
async def background_validator(self):
"""Bakgrunnsvalidering mot sky-modell"""
while True:
item = await self.pending_validations.get()
try:
cloud_result = await self._cloud_predict(item["input"])
# Oppdater cache med validert resultat
self._cache_result(item["cache_key"], {
**cloud_result,
"validated": True
})
# Varsle om avvik
if self._significant_difference(
item["local_result"], cloud_result
):
await self._notify_correction(item, cloud_result)
except Exception:
pass # Sky utilgjengelig — behold lokal prediksjon
def _significant_difference(self, local: dict, cloud: dict) -> bool:
"""Sjekk om sky-resultat avviker vesentlig fra lokalt"""
if local.get("label") != cloud.get("label"):
return True
if abs(local.get("confidence", 0) - cloud.get("confidence", 0)) > 0.2:
return True
return False
```
---
## Norsk offentlig sektor
### Nettverksbegrensede scenarier i Norge
| Scenario | Typisk bandwidth | Latens | Tilgjengelighet |
|----------|-----------------|--------|-----------------|
| Rural mobildekning | 1-10 Mbps | 50-200 ms | 70-90% |
| Maritim (kyst) | 0.5-5 Mbps | 200-600 ms | 60-80% |
| Tunnel/underjordisk | 0 Mbps (isolert) | N/A | 0% |
| Svalbart | 1-50 Mbps | 500+ ms | 80% |
| Beredskap (krise) | 0.1-1 Mbps | Variable | 30-70% |
| Felt (skog/fjell) | 0-5 Mbps | 100-500 ms | 40-80% |
### Anbefalinger
- Dimensjoner alltid for verste-tilfelle tilkobling
- Bruk INT4-kvantiserte modeller som standard for edge-deployment
- Implementer bandwidth-maling for a tilpasse sync-strategi dynamisk
- Protobuf/CBOR for serialisering i stedet for JSON i lav-bandwidth-scenarier
- Prioriter anomalier og kritiske resultater i sync-koeen
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| < 100 kbps | Full lokal inferens + minimal sync | Ikke nok bandwidth for sky-AI |
| 100 kbps - 1 Mbps | Lokal inferens + delta-sync | Synkroniser resultater, ikke radata |
| 1-10 Mbps | Hybrid med progressiv enhancement | Sky-validering av lokale resultater |
| > 10 Mbps | Standard sky-AI med lokal fallback | Normal drift med offline-buffer |
| Intermitterende | Event sourcing + prioritert batch-sync | Palitelig leveranse over tid |
| Satelitt (hoey latens) | Full lokal med periodisk bulk-sync | Latens for hoey for interaktiv sky-AI |
---
## For Cosmo
- **Modellstoerrelse er den viktigste faktoren for nettverksbegrensede deployments** — bruk INT4-kvantisering som standard, og vurder destillasjon for ultralette modeller under 500 MB
- **Bandwidth-bevisst batching er pabudt** — maal tilgjengelig bandwidth kontinuerlig og tilpass batchstoerrelser og kompresjonsformat dynamisk
- **Protobuf/CBOR sparer 60-80% bandwidth** sammenlignet med JSON — bruk binaere serialiseringsformater for all edge-til-sky-kommunikasjon i lav-bandwidth-miljoer
- **Inkrementell modellnedlasting er kritisk** for modelloppdatering over lav bandwidth — last ned kun endrede lag/segmenter, og stoeett delvis modellbruk under nedlasting
- **For norsk offentlig sektor: Design for 100 kbps som worst case** — mange felt-scenarier i rural Norge har begrenset 4G-dekning, og maritime/beredskapsscenarier kan ha enda lavere bandwidth

View file

@ -0,0 +1,491 @@
# Offline-First AI Application Patterns
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Offline-first AI-applikasjoner er designet for a fungere primaert lokalt og synkronisere med skyen nar tilkobling er tilgjengelig. Dette monsteret snur den tradisjonelle sky-forst-tilnaermingen pa hodet: i stedet for a feile nar nettverket er nede, er applikasjonen designet for a operere uavhengig med lokal AI-inferens og datalagring.
For norsk offentlig sektor er offline-first sarlig relevant i felt-scenarioer: vegarbeidere som inspiserer infrastruktur i omrader uten dekning, ambulansepersonell som trenger AI-stoette i rurale omrader, beredskapspersonell under krisesituasjoner der kommunikasjonsinfrastruktur kan vaere nede, og maritime inspeksjoner langs kysten.
Microsoft tilbyr flere byggeklosser for offline-first AI: ONNX Runtime for lokal inferens, Azure IoT Edge for container-basert edge-prosessering med utvidet offline-stoette, Azure Container Storage for lokal persistens med automatisk sky-synkronisering, og Phi-modeller for lokale SLM-kapabiliteter.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| ONNX Runtime | Lokal AI-inferens uten sky | Cross-platform |
| Azure IoT Edge | Utvidet offline-kapabilitet | Container runtime |
| Azure Container Storage | Lokal lagring med sky-sync | Arc-enabled |
| Phi-3/4 SLM | Lokal sprakmodell | MIT-lisens |
| SQLite/LiteDB | Lokal database for offline-data | Embedded DB |
| CRDTs | Konfliktfri replikert datatype | Datastruktur |
| Azure Cosmos DB | Sky-database med offline SDK | Multi-model DB |
---
## Local-First Data Models
### Arkitektur for lokal-forst AI
```
┌─────────────────────────────────────────────┐
│ Offline-First App │
│ │
│ ┌──────────┐ ┌───────────┐ ┌───────────┐ │
│ │ UI Layer │ │ AI Engine │ │ Data Layer│ │
│ │ │ │ │ │ │ │
│ │ - Input │←→│ - ONNX RT │←→│ - SQLite │ │
│ │ - Output │ │ - Phi SLM │ │ - VectorDB│ │
│ │ - Status │ │ - Scoring │ │ - File │ │
│ └──────────┘ └───────────┘ └───────────┘ │
│ ↕ │
│ ┌────────────┐ │
│ │ Sync Engine│ │
│ │ │ │
│ │ - Queue │ │
│ │ - Delta │ │
│ │ - Conflict │ │
│ └──────┬─────┘ │
└─────────────────────────────────────┼────────┘
[Sky (nar tilgjengelig)]
```
### Event-sourcing for offline data
```python
# Event-sourced datamodell for offline-first AI
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
import json
import sqlite3
import uuid
@dataclass
class Event:
id: str = field(default_factory=lambda: str(uuid.uuid4()))
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
type: str = ""
entity_id: str = ""
data: dict = field(default_factory=dict)
synced: bool = False
device_id: str = ""
class OfflineEventStore:
def __init__(self, db_path: str, device_id: str):
self.device_id = device_id
self.conn = sqlite3.connect(db_path)
self._init_schema()
def _init_schema(self):
self.conn.executescript("""
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
timestamp TEXT NOT NULL,
type TEXT NOT NULL,
entity_id TEXT NOT NULL,
data TEXT NOT NULL,
synced INTEGER DEFAULT 0,
device_id TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS ai_results (
id TEXT PRIMARY KEY,
event_id TEXT REFERENCES events(id),
model_version TEXT,
result TEXT,
confidence REAL,
created_at TEXT,
synced INTEGER DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced);
CREATE INDEX IF NOT EXISTS idx_events_entity ON events(entity_id);
""")
def append_event(self, event_type: str, entity_id: str, data: dict) -> Event:
"""Legg til hendelse i lokal event store"""
event = Event(
type=event_type,
entity_id=entity_id,
data=data,
device_id=self.device_id
)
self.conn.execute(
"INSERT INTO events VALUES (?, ?, ?, ?, ?, ?, ?)",
(event.id, event.timestamp, event.type, event.entity_id,
json.dumps(event.data), 0, event.device_id)
)
self.conn.commit()
return event
def store_ai_result(self, event_id: str, model_version: str,
result: dict, confidence: float):
"""Lagre AI-inferensresultat lokalt"""
self.conn.execute(
"INSERT INTO ai_results VALUES (?, ?, ?, ?, ?, ?, ?)",
(str(uuid.uuid4()), event_id, model_version,
json.dumps(result), confidence,
datetime.utcnow().isoformat(), 0)
)
self.conn.commit()
def get_unsynced_events(self, limit: int = 100) -> list[Event]:
"""Hent hendelser som ikke er synkronisert"""
cursor = self.conn.execute(
"SELECT * FROM events WHERE synced = 0 ORDER BY timestamp LIMIT ?",
(limit,)
)
return [Event(*row) for row in cursor.fetchall()]
def mark_synced(self, event_ids: list[str]):
"""Marker hendelser som synkronisert"""
placeholders = ",".join("?" * len(event_ids))
self.conn.execute(
f"UPDATE events SET synced = 1 WHERE id IN ({placeholders})",
event_ids
)
self.conn.commit()
```
---
## Conflict Resolution on Sync
### Konflikthondteringsstrategier
| Strategi | Beskrivelse | Best for |
|----------|-------------|----------|
| Last-Write-Wins (LWW) | Siste endring vinner | Enkle data, lav risiko |
| First-Write-Wins | Forste endring vinner | Uforanderlige hendelser |
| Merge | Kombiner endringer automatisk | Komplementaere felt |
| CRDT | Konfliktfri replikert datatype | Tallere, sett, tekst |
| Custom Resolution | Applikasjonsspesifikk logikk | Komplekse forretningsregler |
### Implementering av konflikthondtering
```python
# Konflikthondtering for offline-first AI-applikasjon
from enum import Enum
from typing import Callable
class ConflictStrategy(Enum):
LAST_WRITE_WINS = "lww"
FIRST_WRITE_WINS = "fww"
MERGE = "merge"
MANUAL = "manual"
class SyncConflictResolver:
def __init__(self, strategy: ConflictStrategy = ConflictStrategy.LAST_WRITE_WINS):
self.strategy = strategy
self.custom_resolvers: dict[str, Callable] = {}
def register_resolver(self, entity_type: str, resolver: Callable):
"""Registrer egendefinert konfliktloeser for en entitetstype"""
self.custom_resolvers[entity_type] = resolver
def resolve(self, local_event: dict, remote_event: dict) -> dict:
"""Los konflikt mellom lokal og fjern hendelse"""
entity_type = local_event.get("type", "")
# Egendefinert resolver har forrang
if entity_type in self.custom_resolvers:
return self.custom_resolvers[entity_type](local_event, remote_event)
if self.strategy == ConflictStrategy.LAST_WRITE_WINS:
return self._last_write_wins(local_event, remote_event)
elif self.strategy == ConflictStrategy.FIRST_WRITE_WINS:
return self._first_write_wins(local_event, remote_event)
elif self.strategy == ConflictStrategy.MERGE:
return self._merge(local_event, remote_event)
else:
return {"conflict": True, "local": local_event, "remote": remote_event}
def _last_write_wins(self, local: dict, remote: dict) -> dict:
local_ts = local.get("timestamp", "")
remote_ts = remote.get("timestamp", "")
return local if local_ts >= remote_ts else remote
def _merge(self, local: dict, remote: dict) -> dict:
"""Merge ved a kombinere ikke-overlappende felt"""
merged = {**remote.get("data", {})}
for key, value in local.get("data", {}).items():
if key not in merged or merged[key] is None:
merged[key] = value
elif key in merged and value != merged[key]:
# Begge har endret — behold begge med suffix
merged[f"{key}_local"] = value
merged[f"{key}_remote"] = merged[key]
return {"data": merged, "merge_status": "auto_merged"}
# Eksempel: Konflikthondtering for AI-inspeksjonsresultater
resolver = SyncConflictResolver(ConflictStrategy.MERGE)
def resolve_inspection(local, remote):
"""Inspeksjoner: Behold den med hoeyest AI-confidence"""
local_conf = local.get("data", {}).get("ai_confidence", 0)
remote_conf = remote.get("data", {}).get("ai_confidence", 0)
winner = local if local_conf >= remote_conf else remote
winner["data"]["conflict_resolved"] = True
winner["data"]["alternative_confidence"] = min(local_conf, remote_conf)
return winner
resolver.register_resolver("inspection_result", resolve_inspection)
```
---
## Progressive Enhancement
### Progressiv AI-kapabilitet
```python
# Progressiv enhancement: Eskalerer AI-kapabilitet basert pa tilkobling
from enum import Enum
import asyncio
class ConnectivityLevel(Enum):
OFFLINE = 0 # Ingen tilkobling
LOW_BANDWIDTH = 1 # < 1 Mbps
CONNECTED = 2 # Normal tilkobling
HIGH_BANDWIDTH = 3 # > 10 Mbps
class ProgressiveAIService:
def __init__(self):
self.local_model = None # Phi-3 Mini INT4 (alltid tilgjengelig)
self.medium_model = None # Phi-3 Small (krever > 16 GB RAM)
self.cloud_client = None # Azure OpenAI (krever tilkobling)
async def classify_document(self, text: str) -> dict:
"""Klassifiser dokument med best tilgjengelig AI"""
connectivity = await self.check_connectivity()
if connectivity >= ConnectivityLevel.HIGH_BANDWIDTH:
# Nivaa 3: Full sky-AI med GPT-4o
return await self._classify_cloud(text, model="gpt-4o")
elif connectivity >= ConnectivityLevel.CONNECTED:
# Nivaa 2: Sky-AI med lettere modell
return await self._classify_cloud(text, model="gpt-4o-mini")
elif connectivity >= ConnectivityLevel.LOW_BANDWIDTH:
# Nivaa 1: Lokal medium modell med sky-validering
local_result = self._classify_local(text, self.medium_model)
# Asynkron validering i bakgrunn nar mulig
asyncio.create_task(self._validate_in_background(text, local_result))
return local_result
else:
# Nivaa 0: Full offline med lokal mini-modell
return self._classify_local(text, self.local_model)
def _classify_local(self, text: str, model) -> dict:
"""Lokal klassifisering med ONNX-modell"""
result = model.predict(text)
return {
"classification": result["label"],
"confidence": result["score"],
"model": "local",
"connectivity": "offline",
"note": "Resultat fra lokal modell — verifiseres ved tilkobling"
}
async def check_connectivity(self) -> ConnectivityLevel:
"""Sjekk navaerende tilkoblingsniva"""
try:
import aiohttp
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=3)) as session:
async with session.get("https://management.azure.com/health") as resp:
if resp.status == 200:
# Estimer bandwidth
return ConnectivityLevel.HIGH_BANDWIDTH
except Exception:
pass
try:
# Proeving med minimal data
import socket
socket.create_connection(("8.8.8.8", 53), timeout=2)
return ConnectivityLevel.LOW_BANDWIDTH
except Exception:
return ConnectivityLevel.OFFLINE
```
### UI-indikasjon av AI-nivaa
```python
# Statusindikator for progressive AI
AI_LEVEL_INFO = {
ConnectivityLevel.OFFLINE: {
"label": "Offline-modus",
"description": "Bruker lokal AI-modell. Resultater synkroniseres ved tilkobling.",
"icon": "offline",
"accuracy": "God (lokal modell)",
"features": ["Klassifisering", "Oppsummering", "Uttrekking"]
},
ConnectivityLevel.LOW_BANDWIDTH: {
"label": "Begrenset tilkobling",
"description": "Lokal AI med bakgrunns-validering.",
"icon": "low_signal",
"accuracy": "God+ (validert i bakgrunn)",
"features": ["Klassifisering", "Oppsummering", "Uttrekking", "Bakgrunns-validering"]
},
ConnectivityLevel.CONNECTED: {
"label": "Tilkoblet",
"description": "Sky-AI med standard modell.",
"icon": "connected",
"accuracy": "Hoey",
"features": ["Alle funksjoner", "RAG", "Avansert analyse"]
},
ConnectivityLevel.HIGH_BANDWIDTH: {
"label": "Full tilkobling",
"description": "Sky-AI med avansert modell.",
"icon": "full_signal",
"accuracy": "Hoeyest",
"features": ["Alle funksjoner", "RAG", "Avansert analyse", "Bildeanalyse"]
}
}
```
---
## Offline Capability Testing
### Testrammeverk for offline AI
```python
# Testrammeverk for offline-first AI-applikasjon
import pytest
from unittest.mock import patch, AsyncMock
class TestOfflineAI:
"""Tester for offline-first AI-funksjonalitet"""
@pytest.fixture
def ai_service(self):
return ProgressiveAIService()
@pytest.fixture
def event_store(self, tmp_path):
return OfflineEventStore(str(tmp_path / "test.db"), "test-device")
def test_offline_classification(self, ai_service):
"""AI-klassifisering skal fungere uten nettverkstilkobling"""
with patch.object(ai_service, 'check_connectivity',
return_value=ConnectivityLevel.OFFLINE):
result = asyncio.run(ai_service.classify_document(
"Vedtak om avslag pa soeknad om byggetillatelse"
))
assert result["classification"] is not None
assert result["connectivity"] == "offline"
assert result["confidence"] > 0.5
def test_event_persistence_offline(self, event_store):
"""Hendelser skal lagres lokalt ved offline"""
event = event_store.append_event(
"inspection", "bridge-001",
{"status": "ok", "notes": "Ingen synlige skader"}
)
assert event.synced is False
assert event.device_id == "test-device"
# Hent usynkroniserte hendelser
unsynced = event_store.get_unsynced_events()
assert len(unsynced) == 1
def test_sync_after_reconnection(self, event_store):
"""Usynkroniserte hendelser skal koes for synkronisering"""
# Simuler 10 offline-hendelser
for i in range(10):
event_store.append_event(
"sensor_reading", f"sensor-{i}",
{"value": i * 1.5}
)
unsynced = event_store.get_unsynced_events()
assert len(unsynced) == 10
# Simuler synkronisering
synced_ids = [e.id for e in unsynced[:5]]
event_store.mark_synced(synced_ids)
remaining = event_store.get_unsynced_events()
assert len(remaining) == 5
def test_conflict_resolution(self):
"""Konflikter ved sync skal loses deterministisk"""
resolver = SyncConflictResolver(ConflictStrategy.LAST_WRITE_WINS)
local = {"timestamp": "2026-02-12T10:00:00", "data": {"status": "ok"}}
remote = {"timestamp": "2026-02-12T09:00:00", "data": {"status": "warning"}}
result = resolver.resolve(local, remote)
assert result["data"]["status"] == "ok" # Nyeste vinner
def test_progressive_enhancement(self, ai_service):
"""AI-kvalitet skal oeke med bedre tilkobling"""
results = {}
for level in ConnectivityLevel:
with patch.object(ai_service, 'check_connectivity',
return_value=level):
result = asyncio.run(ai_service.classify_document("test"))
results[level] = result
# Verifiser at sky-resultat har hoeyere konfidensangivelse
assert results[ConnectivityLevel.OFFLINE]["model"] == "local"
```
---
## Norsk offentlig sektor
### Felt-scenarier som krever offline-first
| Scenario | Etat | Offline-varighet | AI-funksjon |
|----------|------|-----------------|-------------|
| Vegfinspeksjon | SVV | Timer | Skadeklassifisering |
| Ambulanse | Helseetaten | Minutter-timer | Triagering |
| Beredskap | DSB | Dager | Situasjonsanalyse |
| Maritime inspeksjoner | Sjoefartsdir. | Timer-dager | Rapport-generering |
| Grensekontroll | Politiet | Minutter | Dokumentverifisering |
| Skogsbrannberedskap | 110-sentraler | Timer | Risikoanalyse |
### Krav til offline-first i offentlig sektor
- Applikasjonen MA fungere uten nettverkstilkobling
- Lokale AI-resultater MA vaere tydelig merket som "ikke-validert"
- Synkronisering MA skje automatisk ved tilkobling
- Konflikthondtering MA vaere deterministisk og sporbar
- Data MA vaere kryptert lokalt (BitLocker/LUKS)
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Felt-app med periodisk tilkobling | Full offline-first med event sourcing | Data bevares alltid lokalt |
| Sanntids-AI med fallback | Progressiv enhancement | Best mulig kvalitet per tilstand |
| Multi-enhet med sync | CRDTs + event store | Konfliktfri synkronisering |
| Kritisk infrastruktur | Azure IoT Edge extended offline | Uavhengig drift i uker |
| Klient-app pa PC | SQLite + ONNX RT + bakgrunns-sync | Enkel, palitelig arkitektur |
| Beredskapsapplikasjon | Full offline med manuell sync | Ingen skyavhengighet |
---
## For Cosmo
- **Offline-first er et designprinsipp, ikke en feilhaandterings-strategi** — applikasjonen MA designes for a fungere lokalt foerst, med sky som en berikelse nar tilgjengelig
- **Event sourcing er det foretrukne datamoensteeret** for offline-first AI — alle hendelser og AI-resultater lagres lokalt som uforanderlige events og synkroniseres inkrementelt
- **Progressiv enhancement gir graceful degradation** — definer tydelige AI-kapabilitetsnivaaer (offline/begrenset/tilkoblet/full) og kommuniser dette til brukeren
- **Konflikthondtering maa vaere deterministisk og sporbar** — bruk Last-Write-Wins som standard, med custom resolvers for doemenespesifikke regler (f.eks. hoeyest AI-confidence vinner)
- **For norsk offentlig sektor: Test offline-scenarioer som foersteklasses testcase** — ikke anta tilkobling, og sooerg for at felt-personell kan fullfoere sine oppgaver uavhengig av nettverksstatus

View file

@ -0,0 +1,453 @@
# On-Premises SLM and Phi Model Deployment
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Small Language Models (SLM) er kompakte AI-modeller med faerre enn 10 milliarder parametere som kan kjores effektivt pa lokal hardware uten skyavhengighet. Microsofts Phi-modellserie — fra Phi-2 (2.7B) til Phi-4 (14B) — representerer state-of-the-art for SLM, med ytelse som konkurrerer med modeller mange ganger storre.
For norsk offentlig sektor er lokal SLM-deployment sarlig attraktivt: full datakontroll uten at data forlater organisasjonens nettverk, forutsigbare kostnader uten per-token-prising, og mulighet for drift i miljoer med begrenset eller ingen internettilkobling. Phi-modellene er spesielt godt egnet fordi de er optimalisert for oppgaver som klassifisering, oppsummering, enhetstuttrekking og enkel sporsmalsbesvaring.
Microsoft tilbyr flere deploymentsveier for lokale SLM: Azure App Service sidecar, AKS Edge Essentials med KAITO, ONNX Runtime pa Windows/Linux, og Windows ML pa Copilot+ PC-er. Valget avhenger av skaleringsbehovet, tilgjengelig hardware og integrasjonskrav.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Phi-3 Mini | 3.8B parameter SLM for generelle oppgaver | MIT-lisens |
| Phi-3 Small | 7B parameter SLM for hoeyere kvalitet | MIT-lisens |
| Phi-3 Medium | 14B parameter SLM for komplekse oppgaver | MIT-lisens |
| Phi-3.5 Mini | Forbedret 3.8B med multilingual stoette | MIT-lisens |
| Phi-4 Mini | Nyeste 3.8B med forbedret resonnering | MIT-lisens |
| ONNX Runtime | Cross-platform inferensmotor | Open source |
| KAITO | Kubernetes AI Toolchain Operator | Azure Arc |
| Olive | Modelloptimalisering for deployment | Microsoft |
| Windows ML | Lokal inferens pa Windows | Windows SDK |
---
## Phi-3/Phi-4 Deployment
### Modelloversikt
| Modell | Parametere | Kontekst | RAM-krav | GPU-krav | Styrker |
|--------|-----------|----------|----------|----------|---------|
| Phi-3 Mini 4K | 3.8B | 4K tokens | 8 GB | Valgfritt | Enkel Q&A, klassifisering |
| Phi-3 Mini 128K | 3.8B | 128K tokens | 8 GB | Anbefalt | Lange dokumenter |
| Phi-3 Small | 7B | 128K tokens | 16 GB | Anbefalt | Flerspraklig, koding |
| Phi-3 Medium | 14B | 128K tokens | 32 GB | Pakrevd | Kompleks resonnering |
| Phi-3.5 Mini | 3.8B | 128K tokens | 8 GB | Valgfritt | Multilingual, forbedret |
| Phi-4 Mini | 3.8B | 128K tokens | 8 GB | Valgfritt | Beste resonnering i klassen |
### Deployment med Azure App Service Sidecar
```yaml
# App Service sidecar-konfigurasjon for Phi-3.5 Mini
apiVersion: apps/v1
kind: Deployment
metadata:
name: phi-slm-app
spec:
template:
spec:
containers:
# Hoved-applikasjon
- name: web-app
image: myregistry.azurecr.io/myapp:latest
ports:
- containerPort: 8080
env:
- name: SLM_ENDPOINT
value: "http://localhost:11434"
# SLM sidecar
- name: phi-sidecar
image: mcr.microsoft.com/oss/ollama/ollama:latest
ports:
- containerPort: 11434
resources:
requests:
memory: "8Gi"
cpu: "4"
limits:
memory: "16Gi"
cpu: "8"
command: ["ollama", "serve"]
lifecycle:
postStart:
exec:
command: ["ollama", "pull", "phi3.5"]
```
### Deployment med KAITO pa AKS Edge
```yaml
# KAITO Workspace for Phi-3 Mini pa edge
apiVersion: kaito.sh/v1alpha1
kind: Workspace
metadata:
name: phi3-edge
annotations:
kaito.sh/enablelb: "false" # Ikke ekstern lastbalansering pa edge
spec:
resource:
instanceType: "Standard_NC4as_T4_v3" # GPU-node
labelSelector:
matchLabels:
apps: phi3-edge
inference:
preset:
name: "phi-3-mini-128k-instruct"
adapters:
- source:
name: "custom-norwegian-adapter"
image: "myregistry/phi3-no-adapter:v1"
```
### ONNX Runtime deployment (CPU)
```python
# Phi-3 Mini deployment med ONNX Runtime (ingen GPU nodvendig)
import onnxruntime_genai as og
class PhiLocalDeployment:
def __init__(self, model_path: str):
"""
Initialiser Phi-3/4 lokal deployment.
model_path: Sti til ONNX-optimalisert Phi-modell
"""
self.model = og.Model(model_path)
self.tokenizer = og.Tokenizer(self.model)
self.search_options = {
"max_length": 2048,
"temperature": 0.7,
"top_p": 0.9,
"do_sample": True
}
def generate(self, prompt: str, system_message: str = None,
max_tokens: int = 1024) -> str:
"""Generer svar fra lokal Phi-modell"""
if system_message:
full_prompt = (
f"<|system|>\n{system_message}<|end|>\n"
f"<|user|>\n{prompt}<|end|>\n"
f"<|assistant|>\n"
)
else:
full_prompt = (
f"<|user|>\n{prompt}<|end|>\n"
f"<|assistant|>\n"
)
input_tokens = self.tokenizer.encode(full_prompt)
params = og.GeneratorParams(self.model)
params.set_search_options(**{
**self.search_options,
"max_length": max_tokens
})
params.input_ids = input_tokens
generator = og.Generator(self.model, params)
output_tokens = []
while not generator.is_done():
generator.compute_logits()
generator.generate_next_token()
new_token = generator.get_next_tokens()[0]
output_tokens.append(new_token)
return self.tokenizer.decode(output_tokens)
def generate_streaming(self, prompt: str, system_message: str = None):
"""Streaming-generering for lavere opplevd latens"""
full_prompt = self._format_prompt(prompt, system_message)
input_tokens = self.tokenizer.encode(full_prompt)
params = og.GeneratorParams(self.model)
params.set_search_options(**self.search_options)
params.input_ids = input_tokens
generator = og.Generator(self.model, params)
tokenizer_stream = self.tokenizer.create_stream()
while not generator.is_done():
generator.compute_logits()
generator.generate_next_token()
token = generator.get_next_tokens()[0]
yield tokenizer_stream.decode(token)
```
---
## Resource-Constrained Sizing
### Hardware-dimensjonering
| Scenario | Modell | CPU | RAM | GPU | Disk | Inferens-hastighet |
|----------|--------|-----|-----|-----|------|-------------------|
| Minimal (PC) | Phi-3 Mini INT4 | 4 kjerner | 8 GB | Ingen | 5 GB | ~10 tokens/s |
| Standard (server) | Phi-3 Mini FP16 | 8 kjerner | 16 GB | T4 16GB | 10 GB | ~40 tokens/s |
| Ytelse (GPU) | Phi-3 Small FP16 | 8 kjerner | 32 GB | A10G 24GB | 20 GB | ~50 tokens/s |
| Enterprise | Phi-3 Medium FP16 | 16 kjerner | 64 GB | A100 40GB | 40 GB | ~60 tokens/s |
| Edge (NPU) | Phi-3 Mini INT4 | Snapdragon X | 16 GB | NPU | 5 GB | ~20 tokens/s |
### Minnesoptimalisering
```python
# Konfigurasjon for ressursbegrensede miljoer
import onnxruntime as ort
def create_optimized_session(model_path: str, max_memory_gb: float = 4.0):
"""Opprett ONNX-session optimalisert for begrenset minne"""
session_options = ort.SessionOptions()
# Reduser minnebruk
session_options.enable_mem_pattern = True
session_options.enable_mem_reuse = True
# Begrens trader basert pa tilgjengelige kjerner
import os
available_cores = os.cpu_count() or 4
session_options.intra_op_num_threads = max(1, available_cores // 2)
session_options.inter_op_num_threads = max(1, available_cores // 4)
# Velg execution provider basert pa tilgjengelig hardware
providers = []
if ort.get_device() == "GPU":
providers.append(('CUDAExecutionProvider', {
'device_id': 0,
'arena_extend_strategy': 'kSameAsRequested',
'gpu_mem_limit': int(max_memory_gb * 1024 * 1024 * 1024),
'cudnn_conv_algo_search': 'HEURISTIC'
}))
providers.append('CPUExecutionProvider')
return ort.InferenceSession(
model_path,
sess_options=session_options,
providers=providers
)
```
---
## Prompt Optimization for SLM
### SLM-spesifikke prompt-teknikker
SLM-er har begrenset kontekstvindu og resonneringskapasitet sammenlignet med LLM-er. Prompt-optimalisering er kritisk:
| Teknikk | Beskrivelse | Effekt |
|---------|-------------|--------|
| Konsist system-melding | Kort, presis rolledefinisjon (< 100 tokens) | Bedre oppgavefokus |
| Strukturert output | Be om JSON/tabell-format | Mer palitelig parsing |
| Few-shot eksempler | 1-3 konkrete eksempler | Hoyere noyaktighet |
| Decomposition | Del opp komplekse oppgaver | Bedre resultater |
| Constraint-basert | Eksplisitte begrensninger | Unnga hallusinasjoner |
### Prompt-maler for norsk offentlig sektor
```python
# Optimaliserte prompt-maler for SLM i offentlig sektor
SLM_PROMPTS = {
"klassifisering": """<|system|>
Du klassifiserer dokumenter. Svar KUN med en av kategoriene.
Kategorier: {categories}
<|end|>
<|user|>
Klassifiser folgende tekst:
"{text}"
Kategori:<|end|>
<|assistant|>""",
"oppsummering": """<|system|>
Du oppsummerer tekst pa norsk. Maks {max_words} ord.
<|end|>
<|user|>
Oppsummer folgende:
"{text}"
<|end|>
<|assistant|>
Oppsummering:""",
"uttrekking": """<|system|>
Du trekker ut strukturert informasjon. Svar i JSON-format.
<|end|>
<|user|>
Trekk ut folgende felter fra teksten: {fields}
Tekst: "{text}"
JSON:<|end|>
<|assistant|>
{{""",
"qa_med_kontekst": """<|system|>
Du svarer pa sporsmal basert pa konteksten. Svar KUN basert pa informasjonen gitt.
Hvis svaret ikke finnes i konteksten, si "Ikke tilstrekkelig informasjon."
<|end|>
<|user|>
Kontekst:
{context}
Sporsmal: {question}
<|end|>
<|assistant|>"""
}
```
---
## Fine-Tuning at Edge
### Lokal fine-tuning av Phi-modeller
```python
# LoRA fine-tuning av Phi-3 for norsk offentlig sektor
from transformers import (
AutoModelForCausalLM, AutoTokenizer,
TrainingArguments, Trainer
)
from peft import LoraConfig, get_peft_model
import torch
def finetune_phi_lora(
base_model: str = "microsoft/phi-3-mini-4k-instruct",
dataset_path: str = "training_data.jsonl",
output_dir: str = "./phi3-finetuned"
):
"""Fine-tune Phi-3 med LoRA for norsk offentlig sektor"""
# Last modell med 4-bit kvantisering for a spare minne
model = AutoModelForCausalLM.from_pretrained(
base_model,
torch_dtype=torch.bfloat16,
load_in_4bit=True,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(base_model)
# LoRA-konfigurasjon (minimal for edge)
lora_config = LoraConfig(
r=16, # Lav rank for edge-deployment
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# Treningsargumenter optimalisert for begrenset hardware
training_args = TrainingArguments(
output_dir=output_dir,
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_strategy="epoch",
optim="paged_adamw_8bit", # Minneeffektiv optimizer
max_grad_norm=0.3,
warmup_ratio=0.03
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=load_dataset(dataset_path),
tokenizer=tokenizer
)
trainer.train()
# Lagre kun LoRA-adapteret (liten filstorrelse)
model.save_pretrained(output_dir)
# Adapter-storrelse: typisk 20-50 MB vs 7+ GB for full modell
```
### ONNX-eksport for deployment
```bash
# Konverter fine-tuned Phi-3 til ONNX for deployment
python -m olive run \
--config olive-config.json \
--model-id ./phi3-finetuned \
--output-dir ./phi3-onnx-optimized \
--precision int4 \
--target-device cpu
```
```json
// olive-config.json
{
"input_model": {
"type": "HfModel",
"model_path": "./phi3-finetuned"
},
"passes": [
{"type": "OnnxConversion"},
{"type": "OnnxQuantization", "quant_mode": "dynamic", "weight_type": "QInt4"},
{"type": "OrtTransformersOptimization", "model_type": "gpt2"}
],
"engine": {
"target": "onnxruntime",
"execution_providers": ["CPUExecutionProvider"]
}
}
```
---
## Norsk offentlig sektor
### Hvorfor lokal SLM for offentlig sektor?
- **Datakontroll**: Ingen data forlater organisasjonens nettverk — viktig for personopplysninger og gradert informasjon
- **Kostnadskontroll**: Fast infrastrukturkostnad uten per-token-prising — enklere budsjettforvaltning
- **Tilgjengelighet**: Fungerer uten internettilkobling — relevant for felt, krisesituasjoner, og isolerte miljoer
- **Etterlevelse**: Enklere a demonstrere compliance med Schrems II, GDPR, og NSM-krav
### Anbefalte bruksomrader for SLM
| Bruksomrade | Modell | Beskrivelse |
|-------------|--------|-------------|
| Dokumentklassifisering | Phi-3 Mini INT4 | Klassifiser innkommende post/henvendelser |
| Oppsummering | Phi-3.5 Mini | Oppsummer lange utredninger og hoeringsnotater |
| Informasjonsuttrekking | Phi-3 Mini | Trekk ut nokkeldata fra skjemaer |
| Intern Q&A | Phi-4 Mini + RAG | Svar pa sporsmal fra regelverk |
| Tekstgenerering | Phi-3 Small | Utkast til brev og standardsvar |
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Enkel klassifisering/uttrekking | Phi-3 Mini INT4 pa CPU | Minimal hardware, rask inferens |
| Norsk sprakbehandling | Phi-3.5 Mini eller Phi-4 Mini | Bedre multilingual stoette |
| Kompleks resonnering | Phi-3 Medium pa GPU | Nodvendig kapasitet |
| Edge-deployment (IoT) | Phi-3 Mini INT4 ONNX | Minst fotavtrykk |
| Windows-klient | Windows ML + Phi-4 Mini | Automatisk hardware-optimalisering |
| Copilot+ PC | Windows ML med NPU | Best ytelse/watt |
| Server-deployment | KAITO pa AKS Edge | Skalerbart, Kubernetes-managed |
---
## For Cosmo
- **Phi-3 Mini INT4 er det naturlige startpunktet** for de fleste offentlige sektors SLM-bruk — 3.8B parametere gir overraskende god kvalitet for klassifisering, uttrekking og enkel Q&A, og krever kun 8 GB RAM uten GPU
- **Fine-tuning med LoRA er nodvendig for doenmespesifikke oppgaver** — en LoRA-adapter pa 20-50 MB er mye enklere a distribuere til edge enn en full modell, og gir dramatisk forbedring for norsk fagsprak
- **Prompt-optimalisering er viktigere for SLM enn for LLM** — korte, strukturerte prompts med eksplisitte output-formater og 1-3 few-shot-eksempler oker kvaliteten betydelig
- **ONNX Runtime + Olive er den foretrukne deployment-pipeline** — konverter til ONNX, kvantiser til INT4, og deploy pa CPU for maksimal portabilitet og ytelse
- **For norsk offentlig sektor: Lokal SLM-deployment eliminerer de fleste Schrems II-utfordringer** — data forlater aldri nettverket, noe som forenkler personvernkonsekvensvurdering og compliance-dokumentasjon

View file

@ -0,0 +1,412 @@
# ONNX Runtime for Edge Deployment
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
ONNX Runtime er Microsofts open-source, hoyytelses inferensmotor for kjoring av maskinlaeringsmodeller i ONNX-format (Open Neural Network Exchange). Den er optimalisert for bade sky og edge, og stotter Linux, Windows og macOS pa tvers av CPU, GPU og NPU-akseleratorer. ONNX Runtime er innebygd i Windows som del av Windows ML og driver inferens i hoyskala Microsoft-tjenester som Bing, Office og Azure AI.
For edge-deployment er ONNX Runtime sarlig verdifull fordi den gir en enhetlig inferensmotor pa tvers av hardware-plattformer — fra kraftige edge-servere med GPU til ressursbegrensede IoT-enheter med kun CPU. Modeller fra PyTorch, TensorFlow, scikit-learn og andre rammeverk kan konverteres til ONNX-format og deretter optimaliseres for spesifikk target-hardware med Olive-verktoysettet.
For norsk offentlig sektor betyr ONNX Runtime at AI-modeller kan deployes lokalt uten skyavhengighet, noe som er viktig for datasuverenitetsscenarier, offline-drift, og miljoer med begrenset nettverkstilkobling.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| ONNX Runtime | Inferensmotor for ONNX-modeller | C++/Python/C#/JS |
| ONNX Runtime GenAI | Generativ AI-inferens (LLM) | Python/C# |
| Olive | Modelloptimalisering og kompilering | Python CLI |
| Windows ML | ONNX Runtime integrert i Windows | Windows SDK |
| DirectML | Hardware-akselerasjon pa Windows | GPU EP |
| Execution Providers | Hardware-spesifikke akseleratorer | CPU/GPU/NPU |
---
## ONNX Model Conversion
### Konvertering fra populaere rammeverk
```python
# PyTorch til ONNX-konvertering
import torch
import onnx
def convert_pytorch_to_onnx(model, sample_input, output_path: str):
"""Konverter PyTorch-modell til ONNX-format"""
model.eval()
torch.onnx.export(
model,
sample_input,
output_path,
export_params=True,
opset_version=17,
do_constant_folding=True,
input_names=['input'],
output_names=['output'],
dynamic_axes={
'input': {0: 'batch_size'},
'output': {0: 'batch_size'}
}
)
# Valider ONNX-modell
onnx_model = onnx.load(output_path)
onnx.checker.check_model(onnx_model)
print(f"ONNX-modell lagret: {output_path}")
print(f"Modellstorrelse: {os.path.getsize(output_path) / 1024 / 1024:.1f} MB")
```
```python
# TensorFlow/Keras til ONNX med tf2onnx
import tf2onnx
import tensorflow as tf
def convert_tensorflow_to_onnx(saved_model_path: str, output_path: str):
"""Konverter TensorFlow SavedModel til ONNX"""
model_proto, _ = tf2onnx.convert.from_saved_model(
saved_model_path,
output_path=output_path,
opset=17
)
print(f"Konvertert TensorFlow-modell til {output_path}")
```
```python
# scikit-learn til ONNX med skl2onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
def convert_sklearn_to_onnx(model, n_features: int, output_path: str):
"""Konverter scikit-learn-modell til ONNX"""
initial_type = [('input', FloatTensorType([None, n_features]))]
onnx_model = convert_sklearn(model, initial_types=initial_type)
with open(output_path, "wb") as f:
f.write(onnx_model.SerializeToString())
```
### Olive-basert optimalisering
```bash
# Olive: Automatisk modelloptimalisering for target-hardware
pip install olive-ai
# Konverter og optimaliser Phi-3 for CPU edge deployment
olive run \
--model microsoft/Phi-3-mini-4k-instruct \
--output-dir ./optimized-model \
--device cpu \
--precision int4 \
--passes onnx_conversion,onnx_quantization,ort_optimization
```
```json
// olive-config.json for edge-optimalisering
{
"input_model": {
"type": "HfModel",
"model_path": "microsoft/Phi-3-mini-4k-instruct"
},
"systems": {
"local": {
"type": "LocalSystem",
"accelerators": [{"device": "cpu"}]
}
},
"passes": {
"conversion": {
"type": "OnnxConversion",
"target_opset": 17
},
"quantization": {
"type": "OnnxQuantization",
"quant_mode": "dynamic",
"weight_type": "QInt4",
"calibration_data_config": {
"name": "my_calibration_dataset"
}
},
"optimization": {
"type": "OrtTransformersOptimization",
"model_type": "gpt2",
"opt_level": 2,
"float16": false
}
},
"engine": {
"evaluator": {
"metrics": [
{"name": "latency", "type": "latency", "priority": 1},
{"name": "accuracy", "type": "accuracy", "priority": 2}
]
}
}
}
```
---
## Hardware Acceleration (GPU/NPU)
### Execution Provider-oversikt
| Execution Provider | Hardware | Plattform | Brukstilfelle |
|-------------------|----------|-----------|---------------|
| CPUExecutionProvider | Alle CPU-er | Alle | Baseline, alltid tilgjengelig |
| CUDAExecutionProvider | NVIDIA GPU | Linux/Windows | Hoy-ytelse GPU-inferens |
| TensorrtExecutionProvider | NVIDIA GPU | Linux | Lavest latens GPU-inferens |
| DirectMLExecutionProvider | GPU/NPU | Windows | Windows-universal akselerasjon |
| OpenVINOExecutionProvider | Intel CPU/GPU/NPU | Linux/Windows | Intel-optimalisert |
| QNNExecutionProvider | Qualcomm NPU | Windows ARM64 | Snapdragon AI akselerasjon |
| AzureExecutionProvider | Azure AI | Cloud | Sky-basert inferens |
### GPU-akselerert inferens
```python
# NVIDIA GPU-akselerert inferens med ONNX Runtime
import onnxruntime as ort
def create_gpu_session(model_path: str) -> ort.InferenceSession:
"""Opprett GPU-akselerert ONNX Runtime-session"""
session_options = ort.SessionOptions()
session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
session_options.enable_mem_pattern = True
# Prioriter GPU, fall tilbake til CPU
providers = [
('CUDAExecutionProvider', {
'device_id': 0,
'arena_extend_strategy': 'kNextPowerOfTwo',
'gpu_mem_limit': 4 * 1024 * 1024 * 1024, # 4 GB
'cudnn_conv_algo_search': 'EXHAUSTIVE',
'do_copy_in_default_stream': True
}),
'CPUExecutionProvider'
]
session = ort.InferenceSession(
model_path,
sess_options=session_options,
providers=providers
)
# Verifiser at GPU brukes
active_provider = session.get_providers()[0]
print(f"Aktiv provider: {active_provider}")
return session
```
### Windows ML med automatisk EP-discovery
```csharp
// Windows ML med automatisk hardware-deteksjon
using Microsoft.ML.OnnxRuntime;
public class WindowsMLInference
{
private InferenceSession _session;
public async Task InitializeAsync(string modelPath)
{
var sessionOptions = new SessionOptions();
// Windows ML velger automatisk beste EP
// Qualcomm NPU → Intel OpenVINO → DirectML GPU → CPU
sessionOptions.AppendExecutionProvider_WindowsML();
_session = new InferenceSession(modelPath, sessionOptions);
// Logg valgt EP
var providers = _session.GetAvailableProviders();
Console.WriteLine($"Tilgjengelige providers: {string.Join(", ", providers)}");
}
public float[] RunInference(float[] input, int[] dimensions)
{
var inputTensor = new DenseTensor<float>(input, dimensions);
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input", inputTensor)
};
using var results = _session.Run(inputs);
return results.First().AsTensor<float>().ToArray();
}
}
```
---
## Cross-Platform Compatibility
### Deployment-matrise
| Plattform | OS | Arkitektur | Stoettede EP-er | Brukstilfelle |
|-----------|-----|------------|-----------------|---------------|
| Edge server | Linux | x64 | CUDA, TensorRT, CPU | Hoyytelse-inferens |
| Edge server | Windows | x64 | DirectML, CUDA, CPU | Windows-basert edge |
| IoT Gateway | Linux | ARM64 | CPU, OpenVINO | Lettvekt-inferens |
| Copilot+ PC | Windows | ARM64 | QNN (NPU), DirectML | Klient-AI |
| Azure Local | Linux | x64 | CUDA, CPU | On-premises |
| Raspberry Pi | Linux | ARM64 | CPU | Prototype/test |
### Cross-platform deployment med Docker
```dockerfile
# Multi-platform ONNX Runtime edge container
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/onnxruntime/server:latest
# Kopier optimalisert modell
COPY ./models/optimized_model.onnx /models/model.onnx
# Konfigurasjon
ENV MODEL_PATH=/models/model.onnx
ENV HTTP_PORT=8001
# Helsesjekkek
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -f http://localhost:${HTTP_PORT}/health || exit 1
EXPOSE ${HTTP_PORT}
CMD ["--model_path", "/models/model.onnx", "--http_port", "8001"]
```
```yaml
# Multi-arch build for edge deployment
# docker buildx build --platform linux/amd64,linux/arm64 -t myregistry/inference:v1 .
apiVersion: apps/v1
kind: Deployment
metadata:
name: onnx-inference-edge
spec:
replicas: 1
template:
spec:
containers:
- name: inference
image: myregistry/inference:v1
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
nvidia.com/gpu: "1" # Valgfritt, kun pa GPU-noder
ports:
- containerPort: 8001
```
---
## Performance Profiling
### Ytelsesanalyse-verktoy
| Verktoy | Formal | Plattform |
|---------|--------|-----------|
| ONNX Runtime Profiler | Session-level profilering | Alle |
| Windows Performance Analyzer | System-wide AI-analyse | Windows |
| Netron | Modellvisualisering og inspeksjon | Alle |
| Olive Benchmarking | Automatisert ytelses-benchmarking | Alle |
| NVIDIA Nsight | GPU-profilering | NVIDIA |
### Profilering med ONNX Runtime
```python
# Ytelsesprofilering av ONNX-modell
import onnxruntime as ort
import numpy as np
import time
class ONNXProfiler:
def __init__(self, model_path: str):
self.options = ort.SessionOptions()
self.options.enable_profiling = True
self.options.profile_file_prefix = "onnx_profile"
self.session = ort.InferenceSession(
model_path,
sess_options=self.options
)
def benchmark(self, input_data: dict, iterations: int = 100) -> dict:
"""Kjor benchmark med detaljerte tidsmalinger"""
# Warmup
for _ in range(10):
self.session.run(None, input_data)
# Benchmark
latencies = []
for _ in range(iterations):
start = time.perf_counter_ns()
self.session.run(None, input_data)
end = time.perf_counter_ns()
latencies.append((end - start) / 1e6) # ms
# Stopp profilering og lagre rapport
profile_path = self.session.end_profiling()
return {
"mean_latency_ms": np.mean(latencies),
"p50_latency_ms": np.percentile(latencies, 50),
"p95_latency_ms": np.percentile(latencies, 95),
"p99_latency_ms": np.percentile(latencies, 99),
"throughput_qps": 1000 / np.mean(latencies),
"iterations": iterations,
"profile_file": profile_path,
"providers": self.session.get_providers()
}
```
---
## Norsk offentlig sektor
### Fordeler med ONNX Runtime for offentlig sektor
- **Leverandoruavhengighet**: ONNX er et apent format — modeller er portable mellom plattformer og leverandorer
- **Lokal deployment**: Kjor modeller lokalt uten skyavhengighet, viktig for datasuverenitet
- **Cross-platform**: Samme modell kan kjores pa server, edge-gateway, og klientenhet
- **Kostnadseffektivt**: Ingen per-inferens-kostnader, kun infrastrukturkostnader
### Anbefalt deployment-pipeline
1. Tren modell i Azure ML (sky)
2. Konverter til ONNX med Olive
3. Kvantiser til INT4/INT8 for target-hardware
4. Valider ytelse med benchmarking
5. Deploy via container til AKS Edge eller direkte til device
6. Overvak med Application Insights
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Windows-klient med NPU | Windows ML (automatisk EP) | Enklest, best integrasjon |
| NVIDIA GPU edge server | CUDA EP + TensorRT | Lavest latens |
| Intel-basert edge | OpenVINO EP | Intel-optimalisert |
| ARM64 IoT gateway | CPU EP med INT4-kvantisering | Minst ressursbruk |
| Cross-platform deployment | Docker + CPU EP | Maksimal portabilitet |
| Azure Local | CUDA EP i Kubernetes | Enterprise-skalerbart |
---
## For Cosmo
- **ONNX Runtime er den universelle inferensmotoren** for Microsoft-okosystemet — anbefal det som standard for alle edge AI-deployments pa grunn av cross-platform-stoette og hardware-akselerasjon
- **Olive er det foretrukne verktoeyet for modelloptimalisering** — det automatiserer konvertering, kvantisering og optimalisering i en pipeline og sikrer at modellen er optimalisert for spesifikk target-hardware
- **Windows ML erstatter DirectML** som anbefalt tilnaerming pa Windows — det abstraherer EP-management og velger automatisk beste akselerator (NPU, GPU, CPU)
- **INT4-kvantisering via Olive gir 5-8x stoerrelses-reduksjon** med minimalt noyaktighetstap — dette er kritisk for edge-deployment der minne og lagring er begrenset
- **For norsk offentlig sektor: ONNX-format sikrer leverandoeruavhengighet** som kreves av Digdirs arkitekturprinsipper — modeller kan flyttes mellom Azure, on-premises, og andre skyleverandoerer uten endring

View file

@ -0,0 +1,551 @@
# Regulatory Compliance for Edge AI
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
AI-systemer deployed pa edge — pa lokale servere, gateways, enheter eller on-premises Kubernetes-klynger — ma oppfylle de samme regulatoriske kravene som skybaserte AI-systemer, men med tilleggsutfordringer knyttet til fysisk tilgangskontroll, distribuert logging, og vedlikehold av mange enheter. Norsk offentlig sektor opererer under et komplekst regulatorisk landskap: GDPR, EU AI Act, NSM Grunnprinsipper, Utredningsinstruksen, og sektorspesifikke krav.
For edge AI er utfordringen todelt: For det forste ma selve AI-systemet vaere compliant (ansvarlig AI, dataminimering, transparens). For det andre ma den distribuerte arkitekturen — med data og modeller pa mange fysiske lokasjoner — administreres slik at alle noder er oppdaterte, logget, og revisjonsvennlige. Manglende sentralisert kontroll gir okt risiko for konfigurasjonsavvik og compliance-brudd.
Microsoft tilbyr verktoy for a adressere dette: Azure Arc for sentralisert policy-haandheving, Microsoft Purview for dataklassifisering og -styring, Microsoft Defender for Cloud for sikkerhetsvurdering, og Compliance Manager for regulatorisk kartlegging. Disse verktoyene kan utvides til edge-miljoer gjennom Arc-integrasjon.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Azure Arc | Sentralisert policy-haandheving pa edge | Kubernetes/Server |
| Microsoft Purview | Dataklassifisering og -styring | Data governance |
| Compliance Manager | Regulatorisk kartlegging og kontroller | Assessment |
| Microsoft Defender for Cloud | Sikkerhetsvurdering | CSPM/CWPP |
| Azure Policy | Automatisert policy-haandheving | Policy engine |
| Azure Monitor | Sentralisert logging og overvaking | Observability |
| Microsoft Priva | Personvernkonsekvensvurdering | Privacy |
---
## Data Protection Impact Assessment (DPIA)
### Nar er DPIA pakrevd for edge AI?
Ifoolge GDPR Art. 35 er DPIA pakrevd nar databehandling sannsynligvis medforer hoey risiko for fysiske personers rettigheter. For edge AI gjelder dette sarlig:
| Trigger | Edge AI-eksempel | DPIA-krav |
|---------|-----------------|-----------|
| Automatiserte beslutninger | AI-basert triage pa sykehus | Pakrevd |
| Systematisk overvaking | Kamerabasert trafikkanalyse | Pakrevd |
| Sensitive data i stor skala | Helsedata-analyse pa lokale servere | Pakrevd |
| Ny teknologi | AI-modeller pa edge-enheter | Vurderes |
| Saerbare grupper | AI i barnevern/NAV | Pakrevd |
### DPIA-mal for edge AI-system
```markdown
## DPIA for Edge AI-system
### 1. Systembeskrivelse
- **Navn**: [System-navn]
- **Formal**: [Formal med AI-behandling]
- **Datatyper**: [Persondata som behandles]
- **Datasubjekter**: [Hvem gjelder det]
- **Edge-lokasjon**: [Hvor AI kjorer]
- **Modelltype**: [SLM/ONNX/Custom]
- **Dataminimering**: [Hvordan begrenses datainnsamling]
### 2. Nodvendighet og proporsjonalitet
- [ ] Er AI-behandling nodvendig for formalet?
- [ ] Kan formalet oppnas med mindre inngripende midler?
- [ ] Er datamengden begrenset til det nodvendige?
- [ ] Er lagringstid fastsatt og begrunnet?
### 3. Risikovurdering
| Risiko | Sannsynlighet | Konsekvens | Tiltak |
|--------|--------------|------------|--------|
| Data pa avveie fra edge-enhet | Medium | Hoey | Kryptering, fysisk sikring |
| Feilaktig AI-beslutning | Medium | Avhengig av kontekst | Menneskelig overstyring |
| Modell-bias | Lav-Medium | Hoey | Bias-testing, overvaking |
| Manglende logging | Lav | Hoey | Sentralisert audit via Arc |
| Uautorisert tilgang | Medium | Hoey | Tilgangskontroll, attestasjon |
### 4. Tiltak for a redusere risiko
- [ ] Kryptering av data pa edge-enhet (at rest, in transit, in use)
- [ ] Automatisert logging til sentralt system
- [ ] Menneskelig overstyring for kritiske beslutninger
- [ ] Regelmessig bias-evaluering av AI-modell
- [ ] Sletterutiner for persondata pa edge
- [ ] Fysisk sikring av edge-enheter
- [ ] Sentralisert policy-haandheving via Azure Arc
### 5. Konsultasjon
- [ ] Personvernombud konsultert
- [ ] Datatilsynet konsultert (om pakrevd)
- [ ] Beroorte parter informert
```
### Implementering av DPIA-kontroller
```python
# Automatisert DPIA-kontrollsjekk for edge AI
from dataclasses import dataclass
from typing import Optional
from datetime import datetime
@dataclass
class DPIAControl:
id: str
name: str
description: str
status: str # "implemented", "partial", "missing"
evidence: Optional[str] = None
last_verified: Optional[datetime] = None
class EdgeAIDPIAChecker:
def __init__(self):
self.controls = self._define_controls()
def _define_controls(self) -> list[DPIAControl]:
return [
DPIAControl(
id="DPIA-01",
name="Data minimering",
description="Kun nodvendige persondata samles inn pa edge",
status="missing"
),
DPIAControl(
id="DPIA-02",
name="Kryptering at rest",
description="All data pa edge-enhet er kryptert",
status="missing"
),
DPIAControl(
id="DPIA-03",
name="Kryptering in transit",
description="TLS 1.3 for all kommunikasjon",
status="missing"
),
DPIAControl(
id="DPIA-04",
name="Tilgangskontroll",
description="RBAC implementert pa edge-klynge",
status="missing"
),
DPIAControl(
id="DPIA-05",
name="Audit logging",
description="All AI-inferens og datatilgang logges",
status="missing"
),
DPIAControl(
id="DPIA-06",
name="Menneskelig overstyring",
description="AI-beslutninger kan overstyres av menneske",
status="missing"
),
DPIAControl(
id="DPIA-07",
name="Slettemekanisme",
description="Persondata kan slettes fra edge-enhet",
status="missing"
),
DPIAControl(
id="DPIA-08",
name="Bias-evaluering",
description="Modellen er testet for bias og diskriminering",
status="missing"
),
DPIAControl(
id="DPIA-09",
name="Transparens",
description="Bruker informeres om AI-behandling",
status="missing"
),
DPIAControl(
id="DPIA-10",
name="Policy-haandheving",
description="Azure Policy haandheves pa edge via Arc",
status="missing"
)
]
def assess(self) -> dict:
"""Vurder compliance-status"""
implemented = sum(1 for c in self.controls if c.status == "implemented")
total = len(self.controls)
return {
"score": f"{implemented}/{total}",
"percentage": f"{(implemented/total)*100:.0f}%",
"status": "COMPLIANT" if implemented == total else "NON_COMPLIANT",
"missing": [c.name for c in self.controls if c.status == "missing"],
"partial": [c.name for c in self.controls if c.status == "partial"]
}
```
---
## Risk Assessment Frameworks
### NSM Grunnprinsipper for edge AI
```python
# NSM Grunnprinsipper-basert risikovurdering for edge AI
class NSMRiskAssessment:
"""Risikovurdering basert pa NSMs grunnprinsipper for IKT-sikkerhet"""
CATEGORIES = {
"identifisere": [
"Kartlegge enheter, systemer og tjenester",
"Klassifisere informasjon og data",
"Identifisere saarbarheter",
"Vurdere risiko"
],
"beskytte": [
"Haandtere identiteter og tilganger",
"Beskytte data (kryptering)",
"Sikre plattformer og applikasjoner",
"Beskytte nettverk"
],
"oppdage": [
"Overvake sikkerhetstilstand",
"Logge og analysere hendelser",
"Oppdage uonsket aktivitet"
],
"haandtere": [
"Haandtere sikkerhetshendelser",
"Gjenopprette etter hendelser",
"Forbedre basert pa erfaring"
]
}
def assess_edge_ai_system(self, system_config: dict) -> dict:
"""Vurder et edge AI-system mot NSM Grunnprinsipper"""
results = {}
for category, principles in self.CATEGORIES.items():
category_results = []
for principle in principles:
score = self._evaluate_principle(principle, system_config)
category_results.append({
"principle": principle,
"score": score, # 1-5
"status": "OK" if score >= 3 else "MANGELFULL"
})
results[category] = {
"principles": category_results,
"avg_score": sum(r["score"] for r in category_results) / len(category_results)
}
overall = sum(r["avg_score"] for r in results.values()) / len(results)
return {
"categories": results,
"overall_score": round(overall, 1),
"overall_status": "AKSEPTABEL" if overall >= 3.0 else "UTILSTREKKELIG",
"recommendation": self._generate_recommendations(results)
}
```
### EU AI Act risikoklassifisering for edge AI
| Risikoniva | Eksempel edge AI | Krav | Konsekvens |
|------------|-----------------|------|------------|
| Uakseptabel | Sosial scoring pa edge | Forbudt | Kan ikke deployes |
| Hoey risiko | Biometrisk ID pa edge | Full compliance | DPIA + CE-merking + audit |
| Begrenset risiko | Chatbot pa klientenhet | Transparens | Bruker ma informeres |
| Minimal risiko | Spam-filter lokalt | Frivillig | Anbefalt god praksis |
---
## Audit Logging at Edge
### Sentralisert audit-logging arkitektur
```
┌─────────────┐ ┌──────────────┐ ┌───────────────┐
│ Edge Node 1 │ │ Edge Node 2 │ │ Edge Node N │
│ │ │ │ │ │
│ [AI-inferens]│ │ [AI-inferens] │ │ [AI-inferens] │
│ [Audit log] │ │ [Audit log] │ │ [Audit log] │
│ ↓ │ │ ↓ │ │ ↓ │
│ [OMS Agent] │ │ [OMS Agent] │ │ [OMS Agent] │
└──────┬──────┘ └──────┬───────┘ └──────┬────────┘
│ │ │
└───────────────────┼─────────────────────┘
┌────────────────────────┐
│ Log Analytics │
│ Workspace │
│ (Norway East) │
│ │
│ ┌────────────────┐ │
│ │ KQL-queries │ │
│ │ for compliance │ │
│ └────────────────┘ │
└────────────┬───────────┘
┌────────────────────────┐
│ Azure Sentinel / SIEM │
│ (Sikkerhetshendelser) │
└────────────────────────┘
```
### Implementering av edge audit logging
```python
# Strukturert audit logging for edge AI
import json
import logging
from datetime import datetime
from typing import Optional
class EdgeAIAuditLogger:
"""Audit logger for AI-inferens pa edge, kompatibel med Azure Monitor"""
def __init__(self, device_id: str, log_path: str):
self.device_id = device_id
self.logger = logging.getLogger("edge-ai-audit")
# Filbasert logging (lokal buffer)
handler = logging.FileHandler(log_path)
handler.setFormatter(logging.Formatter('%(message)s'))
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
def log_inference(self, model_name: str, model_version: str,
input_summary: str, output_summary: str,
confidence: float, latency_ms: float,
user_id: Optional[str] = None,
contains_pii: bool = False):
"""Logg AI-inferens for audit"""
audit_entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"event_type": "AI_INFERENCE",
"device_id": self.device_id,
"model": {
"name": model_name,
"version": model_version
},
"inference": {
"input_summary": input_summary if not contains_pii else "[PII_REDACTED]",
"output_summary": output_summary,
"confidence": confidence,
"latency_ms": latency_ms
},
"context": {
"user_id": user_id,
"contains_pii": contains_pii,
"processing_location": "edge",
"data_residency": "Norway"
}
}
self.logger.info(json.dumps(audit_entry))
def log_data_access(self, data_type: str, purpose: str,
legal_basis: str, user_id: Optional[str] = None):
"""Logg datatilgang for GDPR Art. 30"""
audit_entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"event_type": "DATA_ACCESS",
"device_id": self.device_id,
"data_access": {
"data_type": data_type,
"purpose": purpose,
"legal_basis": legal_basis,
"user_id": user_id
}
}
self.logger.info(json.dumps(audit_entry))
def log_model_update(self, old_version: str, new_version: str,
update_source: str, integrity_check: str):
"""Logg modell-oppdatering"""
audit_entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"event_type": "MODEL_UPDATE",
"device_id": self.device_id,
"model_update": {
"old_version": old_version,
"new_version": new_version,
"source": update_source,
"integrity_verified": integrity_check == "valid"
}
}
self.logger.info(json.dumps(audit_entry))
```
### KQL-queries for compliance-rapportering
```kusto
// KQL: AI-inferens audit-rapport siste 30 dager
EdgeAIAudit_CL
| where TimeGenerated > ago(30d)
| where event_type_s == "AI_INFERENCE"
| summarize
TotalInferences = count(),
AvgConfidence = avg(inference_confidence_d),
AvgLatency = avg(inference_latency_ms_d),
PIIInferences = countif(context_contains_pii_b == true),
UniqueModels = dcount(model_name_s),
UniqueDevices = dcount(device_id_s)
by bin(TimeGenerated, 1d)
| order by TimeGenerated desc
// KQL: Sjekk at alle edge-noder har oppdatert modell
EdgeAIAudit_CL
| where event_type_s == "MODEL_UPDATE"
| summarize LastUpdate = max(TimeGenerated), CurrentVersion = arg_max(TimeGenerated, model_update_new_version_s)
by device_id_s
| extend DaysSinceUpdate = datetime_diff('day', now(), LastUpdate)
| where DaysSinceUpdate > 7
| project device_id_s, CurrentVersion, DaysSinceUpdate
| order by DaysSinceUpdate desc
// KQL: PII-tilgangsrapport for GDPR Art. 30
EdgeAIAudit_CL
| where event_type_s == "DATA_ACCESS"
| summarize
AccessCount = count(),
UniqueUsers = dcount(data_access_user_id_s),
Purposes = make_set(data_access_purpose_s)
by data_access_data_type_s, data_access_legal_basis_s
| order by AccessCount desc
```
---
## Transparency and Explainability
### Forklarbarhets-krav for edge AI
| Krav | Kilde | Implementering |
|------|-------|----------------|
| Rett til forklaring | GDPR Art. 22 | Modell-forklaringsrapporter |
| Transparens | EU AI Act | Brukerinformasjon om AI-bruk |
| Dokumentasjon | EU AI Act (hoey-risiko) | Teknisk dokumentasjon |
| Menneskelig tilsyn | EU AI Act | Override-mekanisme |
| Etterproovbarhet | Forvaltningsloven | Audit trail + begrunnelse |
### Implementering av forklarbarhet pa edge
```python
# SHAP-basert forklarbarhet for edge AI-modeller
import shap
import numpy as np
class EdgeAIExplainer:
"""Lettvekts forklarbarhet for edge-deployde modeller"""
def __init__(self, model, feature_names: list[str]):
self.model = model
self.feature_names = feature_names
# Bruk pre-beregnet bakgrunnsdata for effektivitet
self.explainer = None
def initialize_with_background(self, background_data: np.ndarray):
"""Initialiser forklarer med representativt datasett"""
# Bruk kun 100 eksempler for minneeffektivitet pa edge
sample = background_data[:100] if len(background_data) > 100 else background_data
self.explainer = shap.KernelExplainer(
self.model.predict,
sample,
link="logit"
)
def explain_prediction(self, input_data: np.ndarray) -> dict:
"""Generer forklaring for en enkelt prediksjon"""
if self.explainer is None:
return {"error": "Forklarer ikke initialisert"}
shap_values = self.explainer.shap_values(input_data, nsamples=50)
# Sorter features etter viktighet
feature_importance = []
for i, name in enumerate(self.feature_names):
importance = abs(float(shap_values[0][i]))
direction = "oker" if shap_values[0][i] > 0 else "reduserer"
feature_importance.append({
"feature": name,
"importance": importance,
"direction": direction,
"value": float(input_data[0][i])
})
feature_importance.sort(key=lambda x: x["importance"], reverse=True)
# Generer menneskelesbar forklaring
top_features = feature_importance[:3]
explanation = self._generate_norwegian_explanation(top_features)
return {
"prediction": self.model.predict(input_data)[0],
"feature_importance": feature_importance,
"explanation_no": explanation,
"model_type": type(self.model).__name__,
"explainability_method": "SHAP (KernelExplainer)"
}
def _generate_norwegian_explanation(self, top_features: list[dict]) -> str:
"""Generer forklaring pa norsk"""
parts = []
for f in top_features:
parts.append(
f"'{f['feature']}' (verdi: {f['value']:.2f}) "
f"{f['direction']} sannsynligheten"
)
return "Beslutningen er hovedsakelig basert pa: " + ", ".join(parts) + "."
```
---
## Norsk offentlig sektor
### Regulatorisk sjekkliste for edge AI
| Regulering | Krav | Edge AI-implementering |
|-----------|------|----------------------|
| GDPR Art. 5 | Dataminimering | Prosesser pa edge, send kun aggregert |
| GDPR Art. 22 | Automatiserte beslutninger | Menneskelig overstyring pakrevd |
| GDPR Art. 30 | Behandlingsprotokoll | Audit logging pa alle noder |
| GDPR Art. 32 | Tekniske tiltak | Kryptering, tilgangskontroll |
| GDPR Art. 35 | DPIA | Dokumentert vurdering |
| EU AI Act | Risikoklassifisering | Kategoriser edge AI-systemer |
| NSM Grunnprinsipper | IKT-sikkerhet | Policy-haandheving via Arc |
| Forvaltningsloven | Begrunnelse | Forklarbarhetsmekanisme |
| Offentleglova | Innsyn | Tilgjengelig dokumentasjon |
| Arkivlova | Bevaring | Langsiktig audit-lagring |
### Datatilsynets anbefalinger for AI
- Gjennomfoer DPIA for alle AI-systemer som prosesserer persondata
- Implementer privacy by design og privacy by default
- Sikre rett til forklaring ved automatiserte beslutninger
- Dokumenter rettslig grunnlag for AI-behandling
- Gjennomfoer jevnlige revisjoner av AI-systemets funksjon
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Hoey-risiko AI (helse, justis) | Full DPIA + EU AI Act compliance + audit | Pakrevd, strengeste krav |
| Begrenset risiko (chatbot, hjelp) | Transparenserklaring + logging | Informasjonsplikt |
| Minimal risiko (spam, klassifisering) | God praksis + logging | Frivillig, men anbefalt |
| Multi-edge deployment | Azure Arc + Policy + sentralisert logging | Konsistent haandheving |
| Sensitive persondata | DPIA + kryptering + forklarbarhet | GDPR-krav |
| Offentlig forvaltning | Full compliance-stack + begrunnelse | Forvaltningsloven |
---
## For Cosmo
- **DPIA er pakrevd for de fleste edge AI-systemer** i offentlig sektor som prosesserer persondata — bruk den strukturerte malen og implementer automatiserte kontrollsjekker
- **Audit logging pa edge ma synkroniseres sentralt** — bruk Azure Monitor Agent (OMS) pa alle edge-noder og lagre logger i Log Analytics Workspace i Norway East med 7 ars retention
- **EU AI Act krever risikoklassifisering** — kategoriser edge AI-systemer tidlig i prosjektet og implementer kravene for riktig risikoniva for du deployer
- **Forklarbarhets er et lovkrav ved automatiserte beslutninger** — implementer lettvekts SHAP-basert forklarbarhet pa edge og generer norskspraklige forklaringer for brukere og saksbehandlere
- **Azure Arc + Policy er den eneste skalerbare maaten** a handheve compliance pa tvers av mange edge-noder — definer policies sentralt og la Arc sikre at alle noder er compliant

View file

@ -0,0 +1,375 @@
# Sovereign Cloud for Norwegian AI
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Microsoft Sovereign Cloud er en suite av kapabiliteter og deploymentmodeller designet for a hjelpe myndigheter og regulerte industrier med a oppfylle krav til dataresidency, compliance og operasjonell suverenitet — uten a gi avkall pa fordelene ved hyperscale sky-innovasjon. For norsk offentlig sektor er dette sarlig relevant gitt strenge krav fra NSM, Datatilsynet, og EU-regulering.
Sovereign Cloud tilbyr tre deploymentmodeller: Sovereign Public Cloud i Microsoft-drevne datasentre innenfor definerte geopolitiske grenser (f.eks. Norway East/West), Sovereign Private Cloud via Azure Local for customer-kontrollerte miljoer, og National Partner Clouds for lokaliserte suverene skyimplementeringer. Hver modell balanserer skyverdi mot suverenitetskontroller.
For AI-arbeidsbelastninger i norsk offentlig sektor kombinerer Sovereign Cloud dataresendens med konfidensielle beregningsteknologier, kundestyrte krypteringsnoekler, og policy-baserte guardrails — alt for a muliggjore avansert AI-bruk uten a kompromittere suverenitet.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Sovereign Landing Zone | Infrastruktur-as-code for suverene miljoer | Bicep/Terraform |
| Sovereignty Baseline Policies | Azure Policy for dataresidency og konfidensialitet | Azure Policy |
| Azure Confidential Computing | Beskyttelse av data under prosessering | AMD SEV-SNP, Intel TDX |
| Customer-Managed Keys (CMK) | Kundekontrollert kryptering | Azure Key Vault mHSM |
| Data Guardian | Datastyring for suverene arbeidsbelastninger | Preview |
| External Key Management | Kundekontrollert nokkelhandtering utenfor Azure | Integration |
| Transparency Logs | Innsyn i operatoerens aktiviteter | Audit |
| Azure Local | On-premises sky-infrastruktur | Sovereign Private Cloud |
---
## Data Sovereignty Architecture
### Sovereign Landing Zone (SLZ)
SLZ er en variant av Azure Landing Zone spesielt designet for digital suverenitet:
```
┌─────────────────────────────────────────────────┐
│ Sovereign Landing Zone │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Management Group Hierarchy │ │
│ │ Root → Sovereign → Production → AI │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ Sovereignty │ │ Workload Templates │ │
│ │ Baseline │ │ │ │
│ │ Policies │ │ - AI Foundry template │ │
│ │ │ │ - AKS template │ │
│ │ - Data │ │ - App Service template │ │
│ │ residency │ │ - Storage template │ │
│ │ - CMK │ │ │ │
│ │ - Network │ │ │ │
│ └──────────────┘ └───────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ Confidential │ │ Monitoring & Audit │ │
│ │ Computing │ │ │ │
│ │ Layer │ │ - Azure Monitor │ │
│ │ │ │ - Transparency Logs │ │
│ │ - CVMs │ │ - Compliance Manager │ │
│ │ - mHSM │ │ - Defender for Cloud │ │
│ └──────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────┘
```
### Deployment med Bicep
```bicep
// Sovereign Landing Zone for norsk offentlig AI
targetScope = 'managementGroup'
@description('Sovereignty Baseline Policy Assignment')
resource sovereigntyBaseline 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'sovereignty-baseline-norway'
properties: {
displayName: 'Sovereignty Baseline - Norway AI'
policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/sovereignty-baseline'
parameters: {
allowedLocations: {
value: ['norwayeast', 'norwaywest']
}
requireConfidentialComputing: {
value: true
}
requireCustomerManagedKeys: {
value: true
}
requirePrivateEndpoints: {
value: true
}
}
}
}
// Nektelsespolicy: Hindre data fra a forlate Norge
resource dataResidencyPolicy 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'data-residency-norway'
properties: {
displayName: 'Enforce Norway Data Residency'
policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c'
parameters: {
listOfAllowedLocations: {
value: ['norwayeast', 'norwaywest']
}
}
enforcementMode: 'Default'
}
}
// Customer-Managed Key krav for AI-lagring
resource cmkPolicy 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'cmk-encryption-ai'
properties: {
displayName: 'Require CMK for AI Storage'
policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/6fac406b-40ca-413b-bf8e-0bf964659c25'
enforcementMode: 'Default'
}
}
```
---
## Regional Deployment Constraints
### Azure-regioner i Norge
| Region | Lokasjon | Tjenester | GA-status |
|--------|----------|-----------|-----------|
| Norway East | Oslo | Fullt AI-spekter | GA |
| Norway West | Stavanger | DR og backup | GA |
### AI-tjenester tilgjengelig i Norway East
| Tjeneste | Tilgjengelig | Sovereign-kompatibel |
|----------|-------------|---------------------|
| Azure OpenAI | Ja | Ja (med CMK) |
| Azure AI Search | Ja | Ja |
| Azure ML | Ja | Ja |
| Azure AI Services | Ja | Ja |
| Azure AI Foundry | Ja | Ja |
| Confidential VMs | Ja | Ja |
| AKS | Ja | Ja |
### Deployment-begrensninger
```python
# Verifiser at AI-ressurser deployes i tillatte regioner
from azure.mgmt.resource import ResourceManagementClient
from azure.identity import DefaultAzureCredential
class SovereigntyChecker:
ALLOWED_REGIONS = ["norwayeast", "norwaywest"]
def __init__(self):
self.credential = DefaultAzureCredential()
def verify_resource_locations(self, subscription_id: str) -> list[dict]:
"""Verifiser at alle AI-ressurser er i tillatte regioner"""
client = ResourceManagementClient(self.credential, subscription_id)
violations = []
ai_resource_types = [
"Microsoft.CognitiveServices/accounts",
"Microsoft.MachineLearningServices/workspaces",
"Microsoft.Search/searchServices",
"Microsoft.OpenAI/accounts"
]
for resource in client.resources.list():
if resource.type in ai_resource_types:
if resource.location not in self.ALLOWED_REGIONS:
violations.append({
"resource_name": resource.name,
"resource_type": resource.type,
"location": resource.location,
"severity": "CRITICAL",
"remediation": f"Flytt til {self.ALLOWED_REGIONS}"
})
return violations
def verify_data_residency(self, subscription_id: str) -> dict:
"""Generer dataresidency-rapport"""
violations = self.verify_resource_locations(subscription_id)
return {
"status": "COMPLIANT" if not violations else "NON_COMPLIANT",
"allowed_regions": self.ALLOWED_REGIONS,
"total_ai_resources": self._count_ai_resources(subscription_id),
"violations": violations,
"recommendation": (
"Alle AI-ressurser er innenfor tillatte regioner"
if not violations
else f"{len(violations)} ressurser krever flytting"
)
}
```
---
## Compliance Audit Trails
### Logging-arkitektur for sovereign AI
```
┌─────────────────────────────────────────┐
│ AI-arbeidsbelastning │
│ │
│ [Inferens] → [Azure Monitor] │
│ [Trening] → [Log Analytics] │
│ [Datatilgang] → [Diagnostic Settings] │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Compliance Audit Layer │
│ │
│ [Transparency Logs] │
│ → Microsoft operator-aktiviteter │
│ │
│ [Azure Activity Log] │
│ → Ressurs-operasjoner │
│ │
│ [Key Vault Audit Log] │
│ → Nokkel-tilgang og -bruk │
│ │
│ [Purview Audit] │
│ → Data-tilgang og -klassifisering │
└────────────────┬────────────────────────┘
┌─────────────────────────────────────────┐
│ Long-term Retention (Norway) │
│ │
│ [Immutable Blob Storage] │
│ → 7 ars retention for compliance │
│ → WORM-policy (Write Once Read Many) │
│ → Norway East lokasjon │
└─────────────────────────────────────────┘
```
### Implementering av audit trail
```python
# Sovereign AI audit trail konfigurasjon
from azure.mgmt.monitor import MonitorManagementClient
class SovereignAuditConfiguration:
def configure_ai_diagnostics(self, resource_id: str, workspace_id: str):
"""Konfigurer diagnostikk for sovereign AI-ressurs"""
monitor_client = MonitorManagementClient(
self.credential, self.subscription_id
)
diagnostic_settings = {
"logs": [
{
"category": "RequestResponse",
"enabled": True,
"retentionPolicy": {"enabled": True, "days": 2555} # 7 ar
},
{
"category": "Audit",
"enabled": True,
"retentionPolicy": {"enabled": True, "days": 2555}
},
{
"category": "AllMetrics",
"enabled": True,
"retentionPolicy": {"enabled": True, "days": 365}
}
],
"workspaceId": workspace_id,
"storageAccountId": self.immutable_storage_id
}
monitor_client.diagnostic_settings.create_or_update(
resource_uri=resource_id,
name="sovereign-ai-diagnostics",
diagnostic_settings_resource=diagnostic_settings
)
```
---
## Vendor Lock-in Mitigation
### Strategier for a unnga avhengighet
| Strategi | Implementering | Effekt |
|----------|----------------|--------|
| ONNX-format | Bruk ONNX for alle modeller | Portabilitet mellom plattformer |
| Open-source SLM | Phi-modeller med MIT-lisens | Ingen leverandor-avhengighet |
| IaC (Bicep/Terraform) | Infrastruktur som kode | Reproduserbar deployment |
| Standard API-er | OpenAI-kompatible API-er | Bytt leverandor uten kodeendring |
| Multi-cloud exit plan | Dokumentert migrasjonsplan | Redusert risiko |
| Container-basert | Docker/Kubernetes | Platform-uavhengig |
### Exit-strategi-sjekkliste
```markdown
## Exit-strategi for sovereign AI-plattform
### Data
- [ ] Alle data kan eksporteres i standardformater (JSON, Parquet, CSV)
- [ ] Vektordatabase kan eksporteres (HNSW-indekser)
- [ ] Krypteringsnoekler lagret i customer-controlled HSM
- [ ] Backup-kopier i kundens kontroll
### Modeller
- [ ] Alle modeller i ONNX-format
- [ ] Fine-tuning-data og adaptere eksporterbare
- [ ] Evalueringsmetriker dokumentert for sammenligning
- [ ] Ingen proprietaere modellformater
### Infrastruktur
- [ ] All infrastruktur definert i Bicep/Terraform
- [ ] Kubernetes-arbeidsbelastninger med standard Helm charts
- [ ] Ingen Azure-spesifikke SDK-avhengigheter i forretningslogikk
- [ ] CI/CD-pipelines platform-uavhengige
### Kontrakt
- [ ] Dataportabilitet klausul i avtale
- [ ] Migrasjonsbistand klausul
- [ ] Oppsigelsesperiode tilstrekkelig for migrasjon
- [ ] Eierskap til data og modeller tydelig definert
```
---
## Norsk offentlig sektor
### Relevante krav og rammeverk
| Krav | Kilde | Sovereign Cloud-losning |
|------|-------|------------------------|
| Data ma lagres i Norge/EOS | Schrems II / Datatilsynet | Norway East/West regioner |
| Kryptering av data ved hvile og transport | NSM Grunnprinsipper | CMK + TLS 1.3 |
| Kryptering av data under prosessering | NSM / okt sikkerhet | Confidential Computing |
| Innsyn i operatoerens handlinger | Offentleglova / transparens | Transparency Logs |
| Dokumentert risikovurdering | Utredningsinstruksen | Compliance Manager |
| Leverandoruavhengighet | Arkitekturprinsippene (Digdir) | ONNX + open source + IaC |
| Universell utforming | Likestillingsloven | N/A (applikasjonsniva) |
### Anskaffelseshensyn
- **SSA-L/SSA-T**: Sovereign Cloud-kapabiliteter bor spesifiseres i kravspesifikasjonen
- **Databehandleravtale**: Ma dekke dataresidency, kryptering, og audit-rettigheter
- **Exit-klausul**: Kontrakten ma sikre rett til dataeksport og migrasjonsbistand
- **Gevinstrealisering**: Dokumenter besparelser vs. on-premises drift
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Standard AI-arbeidsbelastning | Sovereign Public Cloud (Norway East) | Enklest, fullt spekter av tjenester |
| Sensitive data med hoy sikkerhet | SPC + Confidential Computing + CMK | Maksimal beskyttelse |
| Forsvar/nasjonal sikkerhet | Sovereign Private Cloud (Azure Local) | Full kontroll, air-gapped |
| Flerparts-analyse mellom etater | Confidential Computing pa SPC | Verifiserbar isolasjon |
| Compliance-kritisk AI | SPC + Compliance Manager + Audit trails | Dokumenterbar etterlevelse |
| Hybrid sky + on-prem | Azure Arc + SLZ | Enhetlig forvaltning |
---
## For Cosmo
- **Sovereign Landing Zone er den korrekte startarkitekturen** for alle AI-prosjekter i norsk offentlig sektor — deploy SLZ med Sovereignty Baseline Policies som forste steg
- **Norway East er primary-regionen for AI** — alle Azure AI-tjenester inkludert OpenAI og Confidential VMs er tilgjengelig der, med Norway West for DR
- **Customer-Managed Keys (CMK) via Managed HSM er et minimum** for sovereign AI — dette gir kundekontrollert kryptering og tilfredsstiller NSM-krav
- **Vendor lock-in-mitigering er pabudt** ifoolge Digdirs arkitekturprinsipper — bruk ONNX, open-source SLM (Phi), og Infrastructure-as-Code for a sikre portabilitet
- **Transparency Logs og Compliance Manager er kritiske for revisjon** — norsk offentlig sektor ma kunne dokumentere operatoertilgang og compliance-status for Riksrevisjonen og Datatilsynet

View file

@ -0,0 +1,356 @@
# Windows AI and AI PC Capabilities
**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI
---
## Introduksjon
Windows AI-plattformen representerer Microsofts satsing pa lokal AI-inferens direkte pa klientenheter. Med Windows ML (Machine Learning), ONNX Runtime integrert i OS, og Neural Processing Units (NPU) i Copilot+ PC-er, kan AI-modeller kjores lokalt med full datakontroll, ingen nettverkslatens, og forutsigbar ytelse.
For norsk offentlig sektor er Windows AI relevant for klientbaserte AI-funksjoner som dokumentklassifisering, oppsummering, og informasjonsuttrekking — alt uten at data forlater enheten. Ansatte kan bruke AI-stoettede verktoy for daglige oppgaver uten bekymring for at sensitive data sendes til skytjenester. Phi-4 Mini, innebygd i Microsoft Edge som lokal SLM, demonstrerer denne tilnaermingen.
Windows ML er den anbefalte veien for a deploye ONNX-modeller pa Windows, med automatisk Execution Provider-discovery som velger beste tilgjengelige akselerator — NPU, GPU eller CPU — uten at utviklere trenger a kode for spesifikk hardware.
---
## Kjernekomponenter
| Komponent | Formal | Teknologi |
|-----------|--------|-----------|
| Windows ML | ONNX Runtime integrert i Windows | Windows App SDK |
| ONNX Runtime | Inferensmotor for AI-modeller | Open source |
| DirectML | GPU/NPU-akselerasjon (legacy) | Windows |
| Execution Providers | Hardware-spesifikke akseleratorer | QNN, OpenVINO, DML |
| Phi-4 Mini | Innebygd SLM i Microsoft Edge | Lokal inferens |
| AI Dev Gallery | Eksempler og modellkatalog | Open source |
| Foundry Local | Klare-til-bruk AI-modeller | Microsoft |
| Windows AI APIs | Innebygde AI-funksjoner | Windows SDK |
---
## Windows ML og ONNX Runtime
### Hvordan Windows ML fungerer
Windows ML inkluderer en kopi av ONNX Runtime og muliggjor dynamisk nedlasting av leverandorspesifikke Execution Providers (EP):
```
[ONNX-modell] → [Windows ML] → [EP Discovery] → [Inferens]
┌─────────────┼─────────────┐
↓ ↓ ↓
[Qualcomm QNN] [Intel OpenVINO] [DirectML]
(Snapdragon NPU) (Intel NPU) (GPU/CPU)
```
### Kodeeksempel: Windows ML-inferens i C#
```csharp
// Windows ML-inferens med automatisk EP-discovery
using Microsoft.ML.OnnxRuntime;
public class WindowsMLService
{
private InferenceSession _session;
public async Task<bool> InitializeAsync(string modelPath)
{
try
{
var options = new SessionOptions();
// Windows ML velger automatisk beste EP:
// 1. NPU (Qualcomm QNN / Intel OpenVINO) - lavest energibruk
// 2. GPU (DirectML) - hoeyest ytelse
// 3. CPU - alltid tilgjengelig fallback
// EP-er lastes ned automatisk via Windows Update
options.AppendExecutionProvider_WindowsML();
_session = new InferenceSession(modelPath, options);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Kunne ikke initialisere modell: {ex.Message}");
return false;
}
}
public float[] Classify(float[] input, int[] shape)
{
var tensor = new DenseTensor<float>(input, shape);
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input", tensor)
};
using var results = _session.Run(inputs);
return results.First().AsTensor<float>().ToArray();
}
}
```
### Kodeeksempel: Python med Windows ML
```python
# Windows ML-inferens i Python
import onnxruntime as ort
import numpy as np
# Opprett session med Windows ML EP
session_options = ort.SessionOptions()
session = ort.InferenceSession(
"model.onnx",
sess_options=session_options,
providers=["WindowsMLExecutionProvider", "CPUExecutionProvider"]
)
# Kjor inferens
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
result = session.run(None, {"input": input_data})
print(f"Output shape: {result[0].shape}")
```
### Modellkompilering for optimal ytelse
```csharp
// Kompiler modell for optimal EP-ytelse (forhands-optimalisering)
using Microsoft.ML.OnnxRuntime;
// Kompilering kan ta flere minutter — gjor dette i bakgrunnen
var compileOptions = new OrtModelCompilationOptions(sessionOptions);
compileOptions.SetInputModelPath(modelPath);
compileOptions.SetOutputModelPath(compiledModelPath);
// Kompiler modellen (optimal for enhetens hardware)
await Task.Run(() => compileOptions.CompileModel());
// Bruk kompilert modell for raskere inferens
var session = new InferenceSession(compiledModelPath, sessionOptions);
```
---
## Neural Processing Unit (NPU)
### Hva er en NPU?
En Neural Processing Unit er en dedikert AI-brikke designet spesifikt for a utfore AI-oppgaver som moenstergjenkjenning, klassifisering og naturlig sprakbehandling. I motsetning til CPU (generelle beregninger) og GPU (parallellprosessering for grafikk), er NPU-er optimalisert for nevrale nettverksoperasjoner med lavt energiforbruk.
### NPU-landskap i Copilot+ PC-er
| Leverandoer | Chip | TOPS | Prosess | Plattform |
|-------------|------|------|---------|-----------|
| Qualcomm | Snapdragon X Elite | 45 TOPS | 4nm | ARM64 |
| Qualcomm | Snapdragon X Plus | 45 TOPS | 4nm | ARM64 |
| Intel | Core Ultra 200V | 48 TOPS | Intel 4 | x64 |
| AMD | Ryzen AI 300 | 50 TOPS | 4nm | x64 |
### NPU vs GPU vs CPU for AI
| Aspekt | NPU | GPU | CPU |
|--------|-----|-----|-----|
| Energiforbruk | Lavest | Hoeyest | Medium |
| AI-ytelse | Hoey (spesialisert) | Hoeyest (generell) | Lavest |
| Tilgjengelighet | Nye PC-er | Diskret/integrert | Alle |
| Modellstoette | INT4/INT8 | FP16/FP32 | Alle formater |
| Best for | Alltid-pa AI | Tunge oppgaver | Fallback |
### Tilgang til NPU via Windows ML
```csharp
// Automatisk NPU-bruk via Windows ML
// Ingen eksplisitt NPU-koding nodvendig — Windows ML velger beste EP
// For avansert kontroll: Sjekk tilgjengelig hardware
public void CheckAICapabilities()
{
var session = new InferenceSession("model.onnx");
var providers = session.GetAvailableProviders();
foreach (var provider in providers)
{
Console.WriteLine($"Tilgjengelig EP: {provider}");
// Eksempel output:
// QNNExecutionProvider (Qualcomm NPU)
// OpenVINOExecutionProvider (Intel NPU)
// DmlExecutionProvider (GPU)
// CPUExecutionProvider (CPU)
}
}
```
---
## Copilot+ PC Specifications
### Minimumskrav for Copilot+ PC
| Krav | Spesifikasjon |
|------|---------------|
| NPU | Minimum 40 TOPS |
| RAM | 16 GB eller mer |
| Lagring | 256 GB SSD eller mer |
| OS | Windows 11 24H2 eller nyere |
### Windows AI APIs (innebygde funksjoner)
| API | Funksjon | Krav | Status |
|-----|----------|------|--------|
| OCR | Tekstgjenkjenning i bilder | Copilot+ PC | GA |
| Image Description | Bildebeskrivelese med AI | Copilot+ PC | GA |
| Text Summarization | Oppsummering av tekst | Copilot+ PC | GA |
| Object Erase | Fjern objekter fra bilder | Copilot+ PC | GA |
| Image Segmentation | Segmentering av bilder | Copilot+ PC | GA |
| Phi Silica | Innebygd SLM i Windows | Copilot+ PC | GA |
### Bruk av Windows AI APIs
```csharp
// Windows AI API: Tekstoppsummering
using Windows.AI;
public async Task<string> SummarizeText(string text)
{
var summarizer = await TextSummarizer.CreateAsync();
var result = await summarizer.SummarizeAsync(text, new SummarizerOptions
{
MaxSentences = 3,
Language = "no" // Norsk stoette
});
return result.Summary;
}
```
---
## Local LLM Inference on Device
### Phi-4 Mini i Microsoft Edge
Microsoft Edge inkluderer Phi-4 Mini som lokal SLM, tilgjengelig via Web AI API-er:
```javascript
// Prompt API i Microsoft Edge (Phi-4 Mini lokal inferens)
// Ingen nettverkskall — alt skjer pa enheten
async function localAIClassification(text) {
// Sjekk tilgjengelighet
const availability = await ai.languageModel.capabilities();
if (availability.available === 'no') {
console.log('Lokal AI ikke tilgjengelig pa denne enheten');
return null;
}
// Opprett session med system-prompt
const session = await ai.languageModel.create({
systemPrompt: `Du er en dokumentklassifiserer for norsk offentlig sektor.
Klassifiser dokumenter i en av disse kategoriene:
- Vedtak
- Klage
- Henvendelse
- Intern notat
- Hoeringssvar
Svar KUN med kategorinavnet.`
});
// Kjor lokal inferens
const result = await session.prompt(
`Klassifiser dette dokumentet: "${text.substring(0, 500)}"`
);
session.destroy();
return result.trim();
}
```
```javascript
// Writing Assistance API: Oppsummering i Edge
async function summarizeDocument(text) {
const summarizer = await ai.summarizer.create({
type: 'key-points',
length: 'short',
format: 'markdown'
});
const summary = await summarizer.summarize(text);
summarizer.destroy();
return summary;
}
```
### Foundry Local for rikere modeller
```bash
# Installer Foundry Local for lokale AI-modeller
# Gir tilgang til storre modeller enn de innebygde
# List tilgjengelige modeller
foundry model list
# Last ned Phi-3.5 for lokal bruk
foundry model download phi-3.5-mini
# Start inferens-server
foundry model serve phi-3.5-mini --port 11434
# Bruk via OpenAI-kompatibelt API
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "phi-3.5-mini",
"messages": [
{"role": "system", "content": "Du er en hjelpsom assistent for norsk offentlig sektor."},
{"role": "user", "content": "Oppsummer folgende utredning..."}
]
}'
```
---
## Norsk offentlig sektor
### Brukstilfeller for Windows AI i offentlig sektor
| Brukstilfelle | Windows AI-losning | Fordel |
|---------------|-------------------|--------|
| E-post-triage | Phi-4 Mini (Edge Prompt API) | Klassifiser innkommende post uten sky |
| Dokumentoppsummering | Windows AI Summarizer API | Rask oversikt over lange dokumenter |
| Skjema-lesing | Windows AI OCR | Digitalisering av papirskjemaer |
| Intern Q&A | Foundry Local + Phi-3.5 | Svar basert pa lokale dokumenter |
| Referat-skriving | Edge Writing Assistance | Utkast til moetereferater |
### Sikkerhetshensyn
- Alle data forblir pa enheten — ingen nettverkskall for AI-inferens
- Phi-4 Mini-modellen er innebygd i Edge, ikke nedlastet fra sky per session
- Windows ML-modeller lagres lokalt og krever ingen sky-autentisering
- IT-administratorer kan kontrollere AI-API-tilgjengelighet via Group Policy
---
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Enkel tekst-AI pa klient | Edge Prompt API (Phi-4 Mini) | Innebygd, ingen oppsett |
| Oppsummering/skriving | Edge Writing Assistance APIs | Spesialisert, hoey kvalitet |
| Custom ONNX-modell | Windows ML med automatisk EP | Best hardware-utnyttelse |
| Storre SLM lokalt | Foundry Local | OpenAI-kompatibelt API |
| Enterprise-utrulling | Windows ML + Intune-administrasjon | Sentralisert styring |
| NPU-optimalisert | Copilot+ PC med Windows ML | Best ytelse/watt |
---
## For Cosmo
- **Windows ML er den anbefalte veien for lokal AI pa Windows** — det erstatter DirectML og gir automatisk hardware-deteksjon og EP-nedlasting, noe som forenkler deployment dramatisk
- **Copilot+ PC-er med NPU muliggjor always-on AI** med lavt energiforbruk — anbefal dette for klientbaserte AI-oppgaver som dokumentklassifisering og oppsummering
- **Edge Prompt API (Phi-4 Mini) er den laveste terskelen for lokal AI** — utviklere kan bruke JavaScript-API-er for a integrere AI uten modellnedlasting eller kompleks oppsett
- **For norsk offentlig sektor: Lokal AI pa klientenheter eliminerer behovet for a sende sensitive data til sky** — dette forenkler DPIA og Schrems II-compliance for enklere AI-brukstilfeller
- **Modellkompilering er viktig for produksjonsytelse** — kompiler ONNX-modeller for target-hardware for a oppna opptil 2-3x forbedring i inferenshastighet etter forste kjoring