# Agent Routing and Task Specialization

**Last updated:** 2026-02
**Status:** GA
**Category:** Agent Orchestration & Automation

---

## 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.
