# 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 Cosmo](#for-cosmo) ## 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 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 ``` ### 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 ExecuteToolsParallel( IEnumerable 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(); foreach (var tool in dependentCalls) { var result = await ExecuteToolWithTimeout( tool, TimeSpan.FromSeconds(5)); sequentialResults.Add(result); } return new ToolResults(parallelResults, sequentialResults); } private async Task 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 Cosmo - **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.