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>
This commit is contained in:
commit
baa2d0220b
488 changed files with 213221 additions and 0 deletions
|
|
@ -0,0 +1,391 @@
|
|||
# Agent Latency Optimization and Performance Tuning
|
||||
|
||||
**Last updated:** 2026-02
|
||||
**Status:** GA
|
||||
**Category:** Agent Orchestration & Automation
|
||||
|
||||
---
|
||||
|
||||
## 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 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue