ms-ai-architect/skills/ms-ai-infrastructure/references/hybrid-edge/network-constrained-ai-deployment.md
Kjell Tore Guttormsen baa2d0220b feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase
analysis with external knowledge via parallel agent swarms. Produces research
briefs with triangulation, confidence ratings, and source quality assessment.

New command: /ultraresearch-local with modes --quick, --local, --external, --fg.
New agents: research-orchestrator (opus), docs-researcher, community-researcher,
security-researcher, contrarian-researcher, gemini-bridge (all sonnet).
New template: research-brief-template.md.

Integration: --research flag in /ultraplan-local accepts pre-built research
briefs (up to 3), enriches the interview and exploration phases. Planning
orchestrator cross-references brief findings during synthesis.

Design principle: Context Engineering — right information to right agent at
right time. Research briefs are structured artifacts in the pipeline:
ultraresearch → brief → ultraplan --research → plan → ultraexecute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 08:58:35 +02:00

470 lines
17 KiB
Markdown

# 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