Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
418 lines
16 KiB
Markdown
418 lines
16 KiB
Markdown
# Agent Routing and Task Specialization
|
|
|
|
**Last updated:** 2026-02
|
|
**Status:** GA
|
|
**Category:** Agent Orchestration & Automation
|
|
**Type:** reference
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Kjernekomponenter](#kjernekomponenter)
|
|
- [Intent Classification Routing](#intent-classification-routing)
|
|
- [Agent Capability Matching](#agent-capability-matching)
|
|
- [Semantic Kernel Handoff Pattern](#semantic-kernel-handoff-pattern)
|
|
- [Load Balancing Strategies](#load-balancing-strategies)
|
|
- [Fallback Routing](#fallback-routing)
|
|
- [Specialization Hierarchies](#specialization-hierarchies)
|
|
- [Norsk offentlig sektor](#norsk-offentlig-sektor)
|
|
- [Beslutningsrammeverk](#beslutningsrammeverk)
|
|
- [For Cosmo](#for-cosmo)
|
|
|
|
## Introduksjon
|
|
|
|
Intelligent routing mellom spesialiserte agenter er en av de mest kritiske arkitekturbeslutningene i multi-agent-systemer. Istedenfor å bygge en "god nok til alt"-agent, deler man ansvarsområder mellom spesialiserte agenter som hver mestrer sitt domene. En router-agent eller orkestrator analyserer innkommende forespørsler og dirigerer dem til riktig spesialist basert på intent-klassifisering, kontekstuell matching og kapabilitets-deklarasjoner.
|
|
|
|
Microsoft Agent Framework og Semantic Kernel tilbyr flere routing-mekanismer gjennom orkestreringsmønstrene Handoff, Group Chat og Magentic. Handoff-mønsteret er spesielt designet for agent-til-agent delegering, der en agent kan overføre en samtale til en mer kvalifisert agent basert på brukerens behov. Group Chat bruker en manager-agent til å dirigere samtaler, mens Magentic bruker en planbasert tilnærming med dynamisk oppgavefordeling.
|
|
|
|
For komplekse enterprise-scenarier er routing-strategien avgjørende for brukeropplevelse, kostnadseffektivitet og systemets evne til å skalere. Feil routing betyr enten at brukeren møter en agent som ikke kan svare godt nok, eller at en dyr premium-modell brukes på enkle oppgaver. Riktig routing balanserer kvalitet, kostnad og responstid.
|
|
|
|
## Kjernekomponenter
|
|
|
|
| Komponent | Formål | Teknologi |
|
|
|-----------|--------|-----------|
|
|
| Intent Classifier | Klassifiser brukerens hensikt | Azure AI Language, LLM-basert klassifisering |
|
|
| Capability Registry | Registrer agent-kapabiliteter | Agent manifest, Semantic Kernel plugins |
|
|
| Router Agent | Dirigér forespørsler til rett agent | Handoff orchestration, custom routing logic |
|
|
| Load Balancer | Fordel last mellom agentinstanser | Azure APIM, Azure Load Balancer |
|
|
| Fallback Handler | Håndtér situasjoner der ingen agent matcher | Default agent, human escalation |
|
|
| Skill Matcher | Match oppgave-krav til agent-ferdigheter | Semantic matching, capability scoring |
|
|
|
|
## Intent Classification Routing
|
|
|
|
### LLM-basert intent-klassifisering
|
|
|
|
```python
|
|
from semantic_kernel import Kernel
|
|
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
|
|
|
|
# Router-agent som klassifiserer intent og velger spesialist
|
|
ROUTER_PROMPT = """
|
|
Du er en routing-agent. Analyser brukerens forespørsel og klassifiser den.
|
|
|
|
Tilgjengelige agenter:
|
|
1. HR-Agent: Spørsmål om ansettelse, ferie, lønn, personalhåndbok
|
|
2. IT-Support-Agent: Tekniske problemer, tilganger, programvare
|
|
3. Økonomi-Agent: Faktura, budsjett, reiseregning, innkjøp
|
|
4. Juridisk-Agent: Kontrakter, personvern, compliance, anskaffelser
|
|
5. General-Agent: Alt annet
|
|
|
|
Svar med JSON:
|
|
{
|
|
"intent": "<kort beskrivelse>",
|
|
"target_agent": "<agent-navn>",
|
|
"confidence": <0.0-1.0>,
|
|
"reasoning": "<kort begrunnelse>"
|
|
}
|
|
|
|
Brukerforespørsel: {{$query}}
|
|
"""
|
|
|
|
async def route_query(kernel: Kernel, query: str) -> dict:
|
|
result = await kernel.invoke_prompt(
|
|
ROUTER_PROMPT,
|
|
input_vars={"query": query}
|
|
)
|
|
routing = json.loads(str(result))
|
|
|
|
# Fallback hvis confidence er lav
|
|
if routing["confidence"] < 0.6:
|
|
routing["target_agent"] = "General-Agent"
|
|
routing["needs_clarification"] = True
|
|
|
|
return routing
|
|
```
|
|
|
|
### Azure AI Language for intent-klassifisering
|
|
|
|
```python
|
|
# CLU (Conversational Language Understanding) for deterministisk routing
|
|
from azure.ai.language.conversations import ConversationAnalysisClient
|
|
from azure.core.credentials import AzureKeyCredential
|
|
|
|
client = ConversationAnalysisClient(
|
|
endpoint=os.environ["LANGUAGE_ENDPOINT"],
|
|
credential=AzureKeyCredential(os.environ["LANGUAGE_KEY"])
|
|
)
|
|
|
|
def classify_intent(query: str) -> dict:
|
|
result = client.analyze_conversation(
|
|
task={
|
|
"kind": "Conversation",
|
|
"analysisInput": {
|
|
"conversationItem": {
|
|
"id": "1",
|
|
"participantId": "user",
|
|
"text": query
|
|
}
|
|
},
|
|
"parameters": {
|
|
"projectName": "agent-routing",
|
|
"deploymentName": "production"
|
|
}
|
|
}
|
|
)
|
|
|
|
prediction = result["result"]["prediction"]
|
|
return {
|
|
"intent": prediction["topIntent"],
|
|
"confidence": prediction["intents"][0]["confidenceScore"],
|
|
"entities": prediction.get("entities", [])
|
|
}
|
|
```
|
|
|
|
## Agent Capability Matching
|
|
|
|
### Capability Registry Pattern
|
|
|
|
```csharp
|
|
// Definer agent-kapabiliteter som et registrer
|
|
public class AgentCapabilityRegistry
|
|
{
|
|
private readonly List<AgentCapability> _capabilities = new();
|
|
|
|
public void Register(AgentCapability capability)
|
|
{
|
|
_capabilities.Add(capability);
|
|
}
|
|
|
|
public AgentCapability FindBestMatch(
|
|
string intent,
|
|
Dictionary<string, string> context)
|
|
{
|
|
return _capabilities
|
|
.Where(c => c.CanHandle(intent))
|
|
.OrderByDescending(c => c.CalculateScore(intent, context))
|
|
.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
public class AgentCapability
|
|
{
|
|
public string AgentName { get; set; }
|
|
public string[] SupportedIntents { get; set; }
|
|
public string[] RequiredEntities { get; set; }
|
|
public string[] SupportedLanguages { get; set; }
|
|
public int MaxComplexity { get; set; } // 1-5
|
|
public decimal CostPerRequest { get; set; }
|
|
|
|
public bool CanHandle(string intent)
|
|
=> SupportedIntents.Any(i =>
|
|
intent.Contains(i, StringComparison.OrdinalIgnoreCase));
|
|
|
|
public double CalculateScore(
|
|
string intent, Dictionary<string, string> context)
|
|
{
|
|
double score = 0;
|
|
// Eksakt intent-match gir høy score
|
|
if (SupportedIntents.Contains(intent)) score += 10;
|
|
// Språkmatch
|
|
if (SupportedLanguages.Contains(context.GetValueOrDefault("lang", "no")))
|
|
score += 5;
|
|
// Lavere kostnad gir bonus (for like-kapable agenter)
|
|
score += (1.0 / (double)(CostPerRequest + 0.01));
|
|
return score;
|
|
}
|
|
}
|
|
|
|
// Registrering
|
|
var registry = new AgentCapabilityRegistry();
|
|
registry.Register(new AgentCapability
|
|
{
|
|
AgentName = "HR-Specialist",
|
|
SupportedIntents = new[] { "ferie", "lønn", "ansettelse", "permisjon" },
|
|
RequiredEntities = new[] { "ansatt-id" },
|
|
SupportedLanguages = new[] { "no", "en" },
|
|
MaxComplexity = 3,
|
|
CostPerRequest = 0.02m
|
|
});
|
|
```
|
|
|
|
## Semantic Kernel Handoff Pattern
|
|
|
|
Handoff-mønsteret i Semantic Kernel er designet for agent-til-agent delegering:
|
|
|
|
```python
|
|
from semantic_kernel.agents import ChatCompletionAgent, HandoffOrchestration
|
|
from semantic_kernel.agents.orchestration.handoffs import HandoffBuilder
|
|
|
|
# Definer spesialiserte agenter
|
|
triage_agent = ChatCompletionAgent(
|
|
name="Triage",
|
|
instructions="""
|
|
Du er en triage-agent. Analyser brukerens forespørsel og
|
|
overfør til riktig spesialist:
|
|
- HR-spørsmål → transfer_to_hr
|
|
- IT-problemer → transfer_to_it_support
|
|
- Økonomi → transfer_to_finance
|
|
""",
|
|
kernel=kernel
|
|
)
|
|
|
|
hr_agent = ChatCompletionAgent(
|
|
name="HR-Specialist",
|
|
instructions="Du er en HR-ekspert. Svar på HR-relaterte spørsmål.",
|
|
kernel=kernel
|
|
)
|
|
|
|
it_agent = ChatCompletionAgent(
|
|
name="IT-Support",
|
|
instructions="Du er IT-support. Hjelp med tekniske problemer.",
|
|
kernel=kernel
|
|
)
|
|
|
|
# Konfigurer handoff-regler
|
|
handoffs = (
|
|
HandoffBuilder()
|
|
.add(source=triage_agent, target=hr_agent, description="HR-spørsmål")
|
|
.add(source=triage_agent, target=it_agent, description="IT-problemer")
|
|
.add(source=hr_agent, target=triage_agent, description="Ikke HR-relatert")
|
|
.add(source=it_agent, target=triage_agent, description="Ikke IT-relatert")
|
|
.build()
|
|
)
|
|
|
|
# Opprett orkestrering
|
|
orchestration = HandoffOrchestration(
|
|
members=[triage_agent, hr_agent, it_agent],
|
|
handoffs=handoffs
|
|
)
|
|
|
|
# Kjør
|
|
result = await orchestration.invoke(
|
|
task="Hvordan søker jeg om foreldrepermisjon?",
|
|
runtime=runtime
|
|
)
|
|
```
|
|
|
|
## Load Balancing Strategies
|
|
|
|
### Multi-instans agent routing via APIM
|
|
|
|
```xml
|
|
<!-- APIM policy for intelligent agent routing -->
|
|
<policies>
|
|
<inbound>
|
|
<!-- Klassifiser intent basert på header eller body -->
|
|
<set-variable name="agentType"
|
|
value="@{
|
|
var body = context.Request.Body.As<JObject>();
|
|
var query = body["query"]?.ToString() ?? "";
|
|
if (query.Contains("HR") || query.Contains("ferie"))
|
|
return "hr-agent";
|
|
if (query.Contains("IT") || query.Contains("tilgang"))
|
|
return "it-agent";
|
|
return "general-agent";
|
|
}" />
|
|
|
|
<!-- Route til riktig backend basert på agent-type -->
|
|
<choose>
|
|
<when condition="@(context.Variables.GetValueOrDefault<string>("agentType") == "hr-agent")">
|
|
<set-backend-service
|
|
backend-id="hr-agent-pool" />
|
|
</when>
|
|
<when condition="@(context.Variables.GetValueOrDefault<string>("agentType") == "it-agent")">
|
|
<set-backend-service
|
|
backend-id="it-agent-pool" />
|
|
</when>
|
|
<otherwise>
|
|
<set-backend-service
|
|
backend-id="general-agent-pool" />
|
|
</otherwise>
|
|
</choose>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
## Fallback Routing
|
|
|
|
### Graceful degradation ved routing-feil
|
|
|
|
```python
|
|
class FallbackRouter:
|
|
"""Router med multi-level fallback"""
|
|
|
|
def __init__(self, agents: dict, default_agent: str):
|
|
self.agents = agents
|
|
self.default = default_agent
|
|
self.escalation_threshold = 2 # Maks antall re-routes
|
|
|
|
async def route(self, query: str, context: dict) -> AgentResponse:
|
|
attempts = 0
|
|
current_agent = self._classify_and_select(query)
|
|
|
|
while attempts < self.escalation_threshold:
|
|
try:
|
|
response = await self.agents[current_agent].invoke(query)
|
|
|
|
# Sjekk om agenten selv indikerer at den ikke kan svare
|
|
if response.confidence < 0.4:
|
|
attempts += 1
|
|
current_agent = self._get_fallback(current_agent)
|
|
continue
|
|
|
|
return response
|
|
|
|
except AgentUnavailableError:
|
|
attempts += 1
|
|
current_agent = self._get_fallback(current_agent)
|
|
|
|
# Ultimat fallback: default agent eller menneskelig eskalering
|
|
return await self.agents[self.default].invoke(query)
|
|
|
|
def _get_fallback(self, current: str) -> str:
|
|
fallback_chain = {
|
|
"HR-Specialist": "General-Agent",
|
|
"IT-Support": "General-Agent",
|
|
"Økonomi-Agent": "General-Agent",
|
|
"General-Agent": "Human-Escalation"
|
|
}
|
|
return fallback_chain.get(current, self.default)
|
|
```
|
|
|
|
## Specialization Hierarchies
|
|
|
|
### Tre-nivå spesialiseringshierarki
|
|
|
|
```
|
|
┌──────────────┐
|
|
│ Triage │ L0: Intent classification
|
|
│ Router │
|
|
└──────┬───────┘
|
|
│
|
|
┌──────────────┼──────────────┐
|
|
│ │ │
|
|
┌───────▼──────┐ ┌────▼────┐ ┌──────▼───────┐
|
|
│ HR Domain │ │ IT │ │ Finance │ L1: Domain
|
|
│ Agent │ │ Agent │ │ Agent │
|
|
└───────┬──────┘ └────┬────┘ └──────┬───────┘
|
|
│ │ │
|
|
┌───────▼──────┐ │ ┌──────▼───────┐
|
|
│ Rekruttering │ │ │ Faktura │ L2: Specialist
|
|
│ Onboarding │ │ │ Budsjett │
|
|
│ Permisjon │ │ │ Innkjøp │
|
|
└──────────────┘ │ └──────────────┘
|
|
│
|
|
┌──────▼───────┐
|
|
│ Nettverks- │ L2: Specialist
|
|
│ Applikasjons-│
|
|
│ Tilgangs- │
|
|
└──────────────┘
|
|
```
|
|
|
|
## Norsk offentlig sektor
|
|
|
|
### Routing-hensyn for offentlig sektor
|
|
|
|
| Aspekt | Krav | Implementering |
|
|
|--------|------|----------------|
|
|
| Sakstype-basert routing | Ulike sakstyper krever ulik behandling | Map sakstyper til agent-spesialister |
|
|
| Sikkerhetsnivå | Gradert informasjon krever spesielle agenter | Rout gradert info til isolerte agenter |
|
|
| Språk | Bokmål, nynorsk, samisk | Språkdeteksjon i router + dedikerte agenter |
|
|
| Arkivering | Alle agent-interaksjoner skal journalføres | Logging av routing-beslutninger |
|
|
| Innsynsrett | Borgere har rett til innsyn i saksbehandling | Dokumentér hvilken agent som behandlet saken |
|
|
|
|
### Eksempel: Norsk kommune agent-routing
|
|
|
|
```python
|
|
KOMMUNE_AGENT_MAP = {
|
|
"byggesak": {
|
|
"agent": "byggesak-agent",
|
|
"model": "gpt-4o", # Kompleks regulering
|
|
"knowledge": ["plan-og-bygningsloven", "kommuneplan"],
|
|
"requires_human_review": True
|
|
},
|
|
"barnehageplass": {
|
|
"agent": "barnehage-agent",
|
|
"model": "gpt-4o-mini", # Enklere forespørsler
|
|
"knowledge": ["barnehageloven", "lokale-vedtekter"],
|
|
"requires_human_review": False
|
|
},
|
|
"sosialtjenester": {
|
|
"agent": "sosial-agent",
|
|
"model": "gpt-4o", # Sensitive opplysninger
|
|
"knowledge": ["sosialtjenesteloven", "NAV-retningslinjer"],
|
|
"requires_human_review": True,
|
|
"data_classification": "fortrolig"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Beslutningsrammeverk
|
|
|
|
| Scenario | Anbefaling | Begrunnelse |
|
|
|----------|------------|-------------|
|
|
| < 5 agenttyper, lavt volum | Enkel LLM-basert router | Lav kompleksitet, rask å implementere |
|
|
| 5-20 agenttyper, middels volum | CLU + capability registry | Deterministisk + skalerbar |
|
|
| > 20 agenttyper, høyt volum | APIM-basert routing + hierarkisk | Ytelse + kostnadseffektivitet |
|
|
| Sensitive domener med compliance | Handoff med human-in-the-loop | Sikkerhet + etterprøvbarhet |
|
|
| Dynamisk agentøkosystem | Capability advertisement + discovery | Agenter kan registreres/fjernes uten kodeendring |
|
|
|
|
## For Cosmo
|
|
|
|
- **Handoff-mønsteret** i Semantic Kernel er den mest naturlige routing-mekanismen for multi-agent-systemer -- triage-agenten klassifiserer og delegerer, spesialistene behandler.
|
|
- **Kombiner LLM-basert og deterministisk routing** for best resultat: Bruk CLU for kjente intenter med høy volum, LLM for edge cases og nye scenarier.
|
|
- **Capability Registry** er nøkkelen til skalerbar arkitektur -- nye agenter registrerer sine kapabiliteter, og routeren oppdager dem automatisk uten kodeendringer.
|
|
- **Fallback er like viktig som routing** -- design alltid en graceful degradation-kjede fra spesialist via generalist til menneskelig eskalering.
|
|
- **For norsk offentlig sektor**: Map sakstyper til agenter, respekter sikkerhetsnivåer i routing, og sørg for at alle routing-beslutninger logges for etterprøvbarhet.
|