ms-ai-architect/skills/ms-ai-engineering/references/agent-orchestration/agent-latency-optimization.md
Kjell Tore Guttormsen 3a73eeafdc refactor(ms-ai-architect): R13 del 1 — nøytraliser Cosmo-personaen i ref-korpusets headinger, etter å ha rettet en gate som var målt usann to ganger
Ordre 20260912T193441Z-7358817909. Steg 1 var ikke transformen, men å rette
roadmapens R13-gate og få den ratifisert. Gaten `grep -rl "Cosmo"
skills/*/references -> 0` var usann på to uavhengige måter:

1. Ordren fanget den første: 451 av forekomstene er Azure Cosmos DB, ekte
   produktinnhold. Diskriminatoren er ikke bokstaven «s» — `Cosmos <norsk
   substantiv>` er genitiv av personaen (`### Cosmos tonalitet`), mens
   `Cosmos DB`/`CosmosClient`/`cosmos_ru` er produkt.
2. Denne økten fant den andre: 132 persona-forekomster ligger i prosa,
   tabeller, dialog-replikker og proveniens-linjer. Heading-nøytralisering
   kan ikke nå dem, så «0 persona» er uoppnåelig også under den ratifiserte
   formen. Operatøren ratifiserte alternativ A: gaten speiler formen, og de
   132 bokføres til R13b/R14.

Tre korreksjoner av premisser som sto i ordren og STATE:
  «ca 320 produkt»   -> 451 (case-sensitivt nett manglet 327 lowercase
                        TOC-ankre + 99 identifikatorer; sann nevner 1 638)
  «169 headinger»    -> 401. 169 var `^## For Cosmo`-prefikset (168) og var
                        internt inkonsistent med sin egen topp-variant (204)
  «417 matcher ingen
   populasjon»       -> 417 er cosmo-headinger utenfor kodefences; briefens
                        nevner var reell hele tiden

Fence-bevissthet er målt skadelig, ikke nødvendig: begge toggle-regler er
gale på dette korpuset (naiv toggle skjuler en ekte heading i
chain-of-thought-prompting.md, CommonMark-regelen ubalanserer
service-level-documentation-dr.md). Fence-agnostisk deteksjon finner 401
heading-linjer i nøyaktig de samme 40 variantene som fence-bevisst finner
400 i — ingen kodeblokk-linje er byte-identisk til en persona-heading. Derfor
nøkles transformen på 40 enumererte heading-tekster og ignorerer fences. En
ukjent variant kaster; en slug-kollisjon kaster. Ingenting auto-fikses.

TOC-en regenereres ikke, den rettes kirurgisk: alle 327 persona-lenker hadde
lenketekst lik én av de 40 heading-tekstene og anker lik slugify av den
(327/327, 0 avvik), så heading og TOC-entry skrives i samme operasjon og
ingen mellomtilstand etterlater en død lenke.

Ratifisert målform: `For Cosmo`, `For Cosmo Skyberg` og `For arkitekten
(Cosmo)` konvergerer på `For arkitekten`. To filer kolliderte og er adjudisert
ved å lese dem, ikke ved regel.

Verifisering (alle 7 kriterier fra ordren):
  G1 persona på heading-linjer   401 -> 0
  G2 døde fragmentlenker         1 -> 1 (pre-eksisterende, unntatt)
  G3 produkt-forekomster         451 -> 451; `Cosmos DB|Azure Cosmos` 308 = 308
  de 3 kun-produkt-filene        byte-identiske
  nettet validert begge veier    injisert persona feller G1; genitiv feller G1;
                                 produkt-heading og de 3 filene passerer
  hele diffen                    802 heading-linjer + 654 TOC-linjer, ANNET = 0
  linjeantall                    728 lagt til = 728 slettet
  suite                          1120/1120 (1097 + 23 nye)
  validate-plugin                250 PASS / 0 FAIL
  stikkprøve                     10 filer, alle 5 skills, inkl. de 3 mest
                                 produkt-tunge (26/20/19) — kun heading+TOC

Utenfor scope, urørt: de 4 SKILL.md, de 23 commands, CLAUDE.md, README.md,
NOTICE.md, docs/ (alt R14).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 22:12:28 +02:00

407 lines
14 KiB
Markdown

# Agent Latency Optimization and Performance Tuning
**Last updated:** 2026-02
**Status:** GA
**Category:** Agent Orchestration & Automation
**Type:** reference
---
## Innhold
- [Introduksjon](#introduksjon)
- [Kjernekomponenter](#kjernekomponenter)
- [Latency-anatomi for agentsystemer](#latency-anatomi-for-agentsystemer)
- [Response Streaming](#response-streaming)
- [Request Batching](#request-batching)
- [Prefetching Strategies](#prefetching-strategies)
- [Semantic Caching med APIM](#semantic-caching-med-apim)
- [Async-Awaitable Patterns](#async-awaitable-patterns)
- [Model Selection for Latency](#model-selection-for-latency)
- [Norsk offentlig sektor](#norsk-offentlig-sektor)
- [Beslutningsrammeverk](#beslutningsrammeverk)
- [For arkitekten](#for-arkitekten)
## Introduksjon
Responstid er en kritisk kvalitetsfaktor for AI-agenter. Brukere forventer sub-sekund initial respons og fullstendige svar innen få sekunder. I multi-agent-systemer akkumuleres latency gjennom hver orkestreringsbeslutning, modellkall, verktøyinvokasjon og data-retrieval. Uten bevisst optimalisering kan en agent som involverer 3-4 LLM-kall raskt nå 15-30 sekunders total responstid, noe som er uakseptabelt for interaktive scenarier.
Azure OpenAI tilbyr flere mekanismer for latency-optimalisering: modellvalg (GPT-4o mini for lavest latency), streaming for opplevd rask respons, prompt-caching for gjentatte forespørsler, og batching for asynkrone workloads. I tillegg gir Azure API Management som AI Gateway mulighet for intelligent routing, semantic caching og request-deduplication.
For agentsystemer spesifikt er de største latency-driverne antall sekvensielle LLM-kall, størrelsen på kontekstvinduer, og ventetid på eksterne verktøy. Parallellisering av uavhengige operasjoner, prefetching av sannsynlige data-behov, og async-patterns for verktøybruk er de mest effektive optimaliseringene.
## Kjernekomponenter
| Komponent | Formål | Teknologi |
|-----------|--------|-----------|
| Streaming | Reduser opplevd latency | Azure OpenAI streaming, SSE |
| Prompt Caching | Reduser time-to-first-token for gjentatte prefixer | Azure OpenAI prompt caching |
| Request Batching | Samle bulk-operasjoner | Azure OpenAI Batch API |
| Semantic Caching | Cache semantisk like forespørsler | APIM semantic caching policy |
| Model Selection | Velg riktig modell for oppgavens krav | GPT-4o mini, GPT-4o, Model Router |
| Async Patterns | Parallelliser uavhengige operasjoner | C# async/await, Python asyncio |
## Latency-anatomi for agentsystemer
### Typisk breakdown
```
┌─────────────────────────────────────────────────────┐
│ Total agent response: ~8-15 sekunder (uten opt.) │
│ │
│ Router LLM-kall: ~0.5-1.5s │
│ RAG retrieval: ~0.3-1.0s │
│ Specialist LLM-kall: ~2-5s │
│ Tool invocation (API): ~0.5-3s │
│ Response formatting: ~0.5-1.5s │
│ Content filtering: ~0.1-0.3s │
│ │
│ Etter optimalisering: ~2-5 sekunder │
└─────────────────────────────────────────────────────┘
```
### Latency-metrikker
| Metrikk | Beskrivelse | Måling |
|---------|------------|--------|
| Time to First Token (TTFT) | Tid til første token i streamed respons | Azure Monitor, streaming |
| End-to-End Request Time | Total tid for komplett respons | API Gateway metrics |
| Token Generation Rate | Tokens per sekund under generering | Calculated metric |
| Tool Call Latency | Tid brukt på verktøyinvokasjoner | Custom spans |
| Orchestration Overhead | Tid brukt i routing/orkestrering | Custom spans |
## Response Streaming
### Implementering med Semantic Kernel
```csharp
// Streaming reduserer opplevd latency dramatisk
public async IAsyncEnumerable<string> StreamAgentResponse(
ChatCompletionAgent agent,
string userMessage,
AgentThread thread)
{
var message = new ChatMessageContent(
AuthorRole.User, userMessage);
await foreach (var chunk in
agent.InvokeStreamingAsync(message, thread))
{
if (!string.IsNullOrEmpty(chunk.Content))
{
yield return chunk.Content;
}
}
}
// Bruk med SignalR for web-klienter
public class AgentHub : Hub
{
public async Task SendMessage(string query)
{
await foreach (var token in
_orchestrator.StreamAgentResponse(query))
{
await Clients.Caller.SendAsync("ReceiveToken", token);
}
await Clients.Caller.SendAsync("ResponseComplete");
}
}
```
### Streaming med Azure OpenAI direkte
```python
import asyncio
from openai import AsyncAzureOpenAI
client = AsyncAzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_KEY"],
api_version="2024-12-01-preview"
)
async def stream_response(messages: list):
stream = await client.chat.completions.create(
model="gpt-4o",
messages=messages,
stream=True,
# Optimaliseringer
max_tokens=500, # Begrens generering
temperature=0.3, # Lavere = raskere konvergens
)
async for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
```
## Request Batching
### Azure OpenAI Batch API
For ikke-interaktive workloads tilbyr Batch API 50% kostnadsreduksjon og høyere throughput:
```python
# Batch API for bulk-operasjoner (24-timers SLA)
import json
from openai import AzureOpenAI
client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_KEY"],
api_version="2024-12-01-preview"
)
# Opprett batch-fil med JSONL
batch_requests = []
for i, query in enumerate(evaluation_queries):
batch_requests.append({
"custom_id": f"eval-{i}",
"method": "POST",
"url": "/chat/completions",
"body": {
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "Evaluer følgende..."},
{"role": "user", "content": query}
],
"max_tokens": 200
}
})
# Skriv JSONL-fil
with open("batch_input.jsonl", "w") as f:
for req in batch_requests:
f.write(json.dumps(req) + "\n")
# Last opp og start batch
file = client.files.create(
file=open("batch_input.jsonl", "rb"),
purpose="batch"
)
batch = client.batches.create(
input_file_id=file.id,
endpoint="/chat/completions",
completion_window="24h"
)
```
## Prefetching Strategies
### Proaktiv data-henting
```python
import asyncio
class PrefetchingOrchestrator:
"""Parallelliser data-henting med LLM-klassifisering"""
async def process_query(self, query: str) -> str:
# Start routing og data-henting PARALLELT
routing_task = asyncio.create_task(
self.classify_intent(query)
)
# Prefetch de mest sannsynlige datakildene
common_data_task = asyncio.create_task(
self.fetch_common_context(query)
)
# Vent på routing-resultat
routing = await routing_task
common_data = await common_data_task
# Hent spesialisert data basert på routing
specialized_data = await self.fetch_specialized_data(
routing.target_agent, query
)
# Kombiner kontekst og generer svar
context = {**common_data, **specialized_data}
return await self.generate_response(
routing.target_agent, query, context
)
async def fetch_common_context(self, query: str) -> dict:
"""Hent data som sannsynligvis trengs uansett agent"""
user_profile, recent_history = await asyncio.gather(
self.get_user_profile(),
self.get_recent_interactions(limit=3)
)
return {
"user_profile": user_profile,
"recent_history": recent_history
}
```
## Semantic Caching med APIM
### APIM Semantic Cache Policy
```xml
<!-- Azure API Management semantic caching for AI-forespørsler -->
<policies>
<inbound>
<!-- Sjekk semantic cache før videresending -->
<azure-openai-semantic-cache-lookup
score-threshold="0.90"
embeddings-backend-id="embedding-backend"
embeddings-backend-auth="system-assigned" />
</inbound>
<outbound>
<!-- Lagre respons i cache for fremtidige like forespørsler -->
<azure-openai-semantic-cache-store
duration="3600" />
</outbound>
</policies>
```
### Cache-invalidering
```python
class AgentCacheManager:
"""Håndter cache-invalidering for agent-systemer"""
def __init__(self, redis_client):
self.redis = redis_client
async def cache_response(
self, query_embedding: list, response: str, ttl: int = 3600
):
key = self._embedding_to_key(query_embedding)
await self.redis.setex(key, ttl, response)
async def invalidate_on_knowledge_update(
self, updated_sources: list
):
"""Når kunnskapsbase oppdateres, invalider relaterte cacher"""
# Fjern alle cache-entries som refererer til oppdaterte kilder
for source in updated_sources:
pattern = f"agent_cache:*:{source}:*"
keys = await self.redis.keys(pattern)
if keys:
await self.redis.delete(*keys)
async def invalidate_on_model_change(self):
"""Ved modellbytte, flush hele cachen"""
await self.redis.flushdb()
```
## Async-Awaitable Patterns
### Parallell verktøyinvokasjon
```csharp
// Parallelliser uavhengige tool calls
public class OptimizedAgentToolHandler
{
public async Task<ToolResults> ExecuteToolsParallel(
IEnumerable<ToolCall> toolCalls)
{
// Grupper verktøykall etter avhengigheter
var independentCalls = toolCalls
.Where(t => !t.HasDependencies)
.ToList();
var dependentCalls = toolCalls
.Where(t => t.HasDependencies)
.ToList();
// Kjør uavhengige kall parallelt
var parallelResults = await Task.WhenAll(
independentCalls.Select(tool =>
ExecuteToolWithTimeout(tool, TimeSpan.FromSeconds(5))
)
);
// Kjør avhengige kall sekvensielt
var sequentialResults = new List<ToolResult>();
foreach (var tool in dependentCalls)
{
var result = await ExecuteToolWithTimeout(
tool, TimeSpan.FromSeconds(5));
sequentialResults.Add(result);
}
return new ToolResults(parallelResults, sequentialResults);
}
private async Task<ToolResult> ExecuteToolWithTimeout(
ToolCall tool, TimeSpan timeout)
{
using var cts = new CancellationTokenSource(timeout);
try
{
return await tool.ExecuteAsync(cts.Token);
}
catch (OperationCanceledException)
{
return ToolResult.Timeout(tool.Name);
}
}
}
```
## Model Selection for Latency
### Modell-latency sammenligning
| Modell | TTFT (median) | Tokens/sek | Anbefalt bruk |
|--------|---------------|------------|----------------|
| gpt-4o-mini | ~200ms | ~120 | Routing, klassifisering, enkle svar |
| gpt-4o | ~400ms | ~80 | Komplekse resonneringer, RAG |
| gpt-4.1 | ~350ms | ~90 | Generell agent-bruk |
| gpt-4.1-mini | ~180ms | ~130 | Høyvolum, lav-latency |
| gpt-4.1-nano | ~100ms | ~150 | Ultra-lav latency, enkel klassifisering |
### Tiered Model Strategy
```python
# Velg modell basert på oppgavens kompleksitet
MODEL_TIERS = {
"routing": "gpt-4.1-nano", # Ultra-rask routing
"simple_qa": "gpt-4o-mini", # Enkle spørsmål
"rag_synthesis": "gpt-4o", # RAG med resonnering
"complex_analysis": "gpt-4.1", # Dype analyser
"evaluation": "gpt-4o-mini", # Batch-evaluering
}
async def select_model_for_task(task_type: str, context: dict) -> str:
base_model = MODEL_TIERS.get(task_type, "gpt-4o-mini")
# Override basert på kontekst
if context.get("token_count", 0) > 4000:
# Store kontekster trenger kraftigere modell
return "gpt-4o"
if context.get("requires_reasoning", False):
return "gpt-4.1"
return base_model
```
## Norsk offentlig sektor
| Aspekt | Krav | Latency-implikasjon |
|--------|------|---------------------|
| Data residency | Azure Norway East | +20-50ms vs. West Europe |
| Content filtering | Obligatorisk for offentlig sektor | +100-200ms per request |
| Audit logging | Full logging av alle kall | +10-30ms overhead |
| VNet isolation | Private endpoints | +5-15ms for DNS resolution |
| Token-grenser | Budget-begrensninger | Bruk mindre modeller der mulig |
## Beslutningsrammeverk
| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Chat-bot med < 2s krav | Streaming + gpt-4o-mini + semantic cache | Lavest opplevd latency |
| Multi-agent med 3+ steg | Parallelliser uavhengige steg + prefetching | Reduserer sekvensielle ventetider |
| Høy-volum asynkront | Batch API + gpt-4o-mini | 50% kostnadsreduksjon, 24h SLA |
| RAG med store dokumenter | Prompt caching + chunk-optimalisering | Reduser TTFT for store prompts |
| Global distribusjon | APIM multi-region + Front Door | Nearest-region routing |
## For arkitekten
- **Streaming er alltid-på for interaktive agenter** -- det er den enkelttiltaket som mest forbedrer brukeropplevelsen, selv om total tid forblir lik.
- **Model tiering er obligatorisk** for kostnadseffektiv latency-optimalisering -- bruk nano/mini for routing og klassifisering, gpt-4o for kompleks resonnering.
- **Parallelliser aggressivt**: Prefetch data mens routing pågår, kjør uavhengige verktøykall parallelt, og bruk async/await konsekvent.
- **Semantic caching i APIM** gir dramatisk forbedring for repetitive forespørsler -- spesielt i kundestøtte-scenarier der mange brukere stiller lignende spørsmål.
- **Mål alltid TTFT og total latency separat** -- TTFT driver brukeropplevelse, total latency driver kostnad. Optimaliser begge men prioriter TTFT for interaktive scenarier.