# Agent Monitoring, Observability and Debugging

**Last updated:** 2026-02
**Status:** GA / Preview (Agent 365)
**Category:** Agent Orchestration & Automation

---

## Introduksjon

Observability for agentsystemer går utover tradisjonell applikasjonsovervåking. Agenter opererer probabilistisk, tar dynamiske beslutninger, og produserer ulike outputs for identiske inputs. Denne ikke-deterministiske naturen krever spesialiserte overvåkingsverktøy som fanger ikke bare ytelsesmetrikker, men også beslutningsprosesser, verktøybruk, prompt-respons-par og evalueringskvalitet.

Microsoft tilbyr en komplett observability-stack for agenter gjennom Microsoft Foundry Tracing, Application Insights, Azure Monitor og Microsoft Agent 365. Foundry-plattformen integrerer OpenTelemetry-basert tracing med AI-spesifikke semantiske konvensjoner, slik at hvert LLM-kall, tool-invokasjon og orkestreringsbeslutning fanges som spans i en distribuert trace.

Agent 365 er Microsofts unified plattform for agentobservability på tvers av Copilot Studio, Microsoft Foundry og tredjepartsruntimes. Den gir enterprise-grade governance med sikkerhet, compliance og business impact-metrikker for hele agentflåten.

## Kjernekomponenter

| Komponent | Formål | Teknologi |
|-----------|--------|-----------|
| Distributed Tracing | Capture full request lifecycle | OpenTelemetry, Microsoft Foundry Tracing |
| Agent Event Logging | Logg agentbeslutninger og handlinger | Application Insights, Log Analytics |
| Performance Profiling | Identifiser flaskehalser | Azure Monitor Metrics, custom spans |
| Error Categorization | Klassifiser og prioriter feil | Azure Monitor Alerts, Sentinel |
| Debugging Tools | Interaktiv feilsøking | Foundry Portal, Aspire Dashboard |
| Agent 365 | Unified agent governance og observability | Microsoft Agent 365 platform |

## Distributed Tracing for Agents

### OpenTelemetry-basert tracing med Microsoft Foundry

```python
from azure.ai.projects import AIProjectClient
from azure.monitor.opentelemetry import configure_azure_monitor
from azure.identity import DefaultAzureCredential
import os

# Aktiver content recording for full prompt/respons-logging
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"

# Koble til prosjekt
project_client = AIProjectClient(
    credential=DefaultAzureCredential(),
    endpoint=os.environ["PROJECT_ENDPOINT"]
)

# Hent Application Insights connection string fra prosjektet
connection_string = (
    project_client.telemetry
    .get_application_insights_connection_string()
)

# Konfigurer Azure Monitor telemetry
configure_azure_monitor(connection_string=connection_string)
```

### Trace-konsepter

| Konsept | Beskrivelse | Eksempel |
|---------|------------|---------|
| Trace | Fullstendig reise for en forespørsel gjennom systemet | Bruker-spørsmål → routing → RAG → LLM → respons |
| Span | Enkeloperasjon innenfor en trace | Ett LLM-kall, ett tool-kall |
| Attributes | Nøkkel-verdi metadata på spans | `gen_ai.prompt`, `gen_ai.completion`, `tool.name` |
| Semantic Conventions | Standardiserte attributtnavn | OpenTelemetry GenAI semantic conventions |

### Custom spans for agentorkestrering

```python
from opentelemetry import trace

tracer = trace.get_tracer("agent-orchestrator")

async def orchestrate_agent_request(query: str, user_id: str):
    with tracer.start_as_current_span("agent_orchestration") as root_span:
        root_span.set_attribute("user.id", user_id)
        root_span.set_attribute("query.text", query)

        # Routing span
        with tracer.start_as_current_span("intent_routing") as route_span:
            routing = await classify_intent(query)
            route_span.set_attribute("routing.target", routing.agent)
            route_span.set_attribute("routing.confidence", routing.confidence)
            route_span.set_attribute("routing.intent", routing.intent)

        # RAG retrieval span
        with tracer.start_as_current_span("rag_retrieval") as rag_span:
            documents = await retrieve_context(query, routing.agent)
            rag_span.set_attribute("rag.doc_count", len(documents))
            rag_span.set_attribute("rag.sources",
                [d.source for d in documents])

        # Agent invocation span
        with tracer.start_as_current_span("agent_invocation") as agent_span:
            agent_span.set_attribute("agent.name", routing.agent)
            response = await invoke_agent(routing.agent, query, documents)
            agent_span.set_attribute("response.token_count",
                response.usage.total_tokens)
            agent_span.set_attribute("response.model", response.model)

        root_span.set_attribute("total_tokens", response.usage.total_tokens)
        return response
```

## Agent Event Logging

### Strukturert hendelseslogging

```python
import logging
import json
from datetime import datetime

class AgentEventLogger:
    """Strukturert logging for agent-hendelser"""

    def __init__(self, app_insights_handler):
        self.logger = logging.getLogger("agent-events")
        self.logger.addHandler(app_insights_handler)

    def log_agent_decision(self, event: dict):
        """Logg en agentbeslutning med full kontekst"""
        self.logger.info(json.dumps({
            "event_type": "agent_decision",
            "timestamp": datetime.utcnow().isoformat(),
            "agent_name": event["agent"],
            "decision_type": event["type"],  # routing, tool_selection, response
            "input_summary": event.get("input_summary", ""),
            "decision": event["decision"],
            "confidence": event.get("confidence", None),
            "reasoning": event.get("reasoning", ""),
            "tokens_used": event.get("tokens", 0),
            "latency_ms": event.get("latency_ms", 0),
            "metadata": event.get("metadata", {})
        }))

    def log_tool_invocation(self, tool_name: str, input_params: dict,
                            output: str, duration_ms: float, success: bool):
        self.logger.info(json.dumps({
            "event_type": "tool_invocation",
            "timestamp": datetime.utcnow().isoformat(),
            "tool_name": tool_name,
            "input_params": input_params,
            "output_preview": output[:200],
            "duration_ms": duration_ms,
            "success": success
        }))
```

## Performance Profiling

### KQL-spørringer for agentytelse

```kql
// Latency-breakdown per agent-komponent
traces
| where timestamp > ago(24h)
| where customDimensions.event_type == "agent_decision"
| extend
    agent = tostring(customDimensions.agent_name),
    decision_type = tostring(customDimensions.decision_type),
    latency = todouble(customDimensions.latency_ms),
    tokens = toint(customDimensions.tokens_used)
| summarize
    p50_latency = percentile(latency, 50),
    p95_latency = percentile(latency, 95),
    p99_latency = percentile(latency, 99),
    avg_tokens = avg(tokens),
    request_count = count()
    by agent, decision_type
| order by p95_latency desc
```

```kql
// Identifiser trege tool calls
traces
| where timestamp > ago(7d)
| where customDimensions.event_type == "tool_invocation"
| extend
    tool = tostring(customDimensions.tool_name),
    duration = todouble(customDimensions.duration_ms),
    success = tobool(customDimensions.success)
| summarize
    avg_duration = avg(duration),
    p95_duration = percentile(duration, 95),
    failure_rate = countif(success == false) * 100.0 / count(),
    total_calls = count()
    by tool
| where p95_duration > 2000 or failure_rate > 5
| order by p95_duration desc
```

### Azure Monitor dashboards

```kql
// Agent health dashboard - hoveddatakilder
let agent_health = traces
| where timestamp > ago(1h)
| where customDimensions.event_type in
    ("agent_decision", "tool_invocation")
| extend agent = tostring(customDimensions.agent_name)
| summarize
    requests = count(),
    errors = countif(customDimensions.success == "false"),
    avg_latency = avg(todouble(customDimensions.latency_ms)),
    avg_tokens = avg(todouble(customDimensions.tokens_used))
    by agent, bin(timestamp, 5m);

agent_health
| render timechart
```

## Error Categorization

### Feilkategorisering for agentsystemer

| Kategori | Eksempler | Alvorlighet | Handling |
|----------|-----------|-------------|---------|
| Model Errors | Rate limit, timeout, content filter | Medium | Retry med backoff |
| Tool Failures | API-feil, timeout, ugyldige params | Medium | Fallback til alternativt verktøy |
| Routing Errors | Feil agent valgt, lav confidence | Lav | Logg + iterér på router-prompt |
| Hallucination | Agent fabrikkerer fakta | Høy | Groundedness-evaluering + alert |
| Safety Violations | Upassende innhold generert | Kritisk | Umiddelbar blokkering + varsling |
| Data Quality | RAG returnerer irrelevante dokumenter | Medium | Indeks-kvalitetsjekk |

```python
# Automatisk feilkategorisering
class AgentErrorClassifier:
    ERROR_CATEGORIES = {
        "rate_limit": {"severity": "medium", "retry": True},
        "timeout": {"severity": "medium", "retry": True},
        "content_filter": {"severity": "high", "retry": False},
        "tool_failure": {"severity": "medium", "retry": True},
        "hallucination": {"severity": "high", "retry": False},
        "routing_error": {"severity": "low", "retry": True},
    }

    def classify(self, error: Exception, context: dict) -> dict:
        if "429" in str(error):
            return {**self.ERROR_CATEGORIES["rate_limit"],
                    "wait_seconds": self._extract_retry_after(error)}
        if "timeout" in str(error).lower():
            return self.ERROR_CATEGORIES["timeout"]
        if "content_filter" in str(error).lower():
            return self.ERROR_CATEGORIES["content_filter"]
        # Default
        return {"severity": "unknown", "retry": False}
```

## Debugging Tools

### Microsoft Foundry Portal

Foundry-portalen gir visuell trace-inspeksjon:

1. **Traces-visning**: Filter traces etter tidsrom, agent, bruker eller status
2. **Span-detaljer**: Se inputs, outputs og attributter for hver operasjon
3. **Call tree**: Visualiser hierarkisk relasjon mellom spans
4. **Evaluering**: Se evalueringsresultater direkte på traces

### Aspire Dashboard for lokal debugging

```python
# Lokal debugging med Aspire Dashboard
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
    OTLPSpanExporter
)

# Eksporter til Aspire Dashboard (localhost:4317)
exporter = OTLPSpanExporter(endpoint="http://localhost:4317")
tracer_provider = TracerProvider()
tracer_provider.add_span_processor(SimpleSpanProcessor(exporter))
trace.set_tracer_provider(tracer_provider)

# Alle agent-operasjoner vises nå i Aspire Dashboard
# Start med: docker run --rm -p 18888:18888 -p 4317:18889 \
#   mcr.microsoft.com/dotnet/aspire-dashboard:latest
```

### Debugging-strategi for agenter

```
1. Reprodusér → Finn den spesifikke tracen i Foundry/AppInsights
2. Isolér    → Identifiser hvilken span som forårsaket problemet
3. Inspiser  → Se prompt, kontekst og respons for den spannen
4. Hypotese  → Er det routing? RAG? Modell? Verktøy?
5. Test      → Kjør isolert test med samme input
6. Fiks      → Oppdater prompt/config/verktøy
7. Verifiser → Sammenlign metrikker før/etter
```

## Observability SDK Integration

### Agent Framework observability

```csharp
// Microsoft Agent Framework med full observability
var builder = WebApplication.CreateBuilder(args);

// Aktiver agent observability
builder.Services.AddAgentObservability(options =>
{
    options.EnableSensitiveData = true; // Full prompt logging
    options.ServiceName = "customer-support-agent";
    options.ExportToApplicationInsights(
        connectionString: builder.Configuration["AppInsights:ConnectionString"]
    );
});
```

## Norsk offentlig sektor

| Aspekt | Krav | Implementering |
|--------|------|----------------|
| Logging av AI-beslutninger | EU AI Act Art. 12 | Full trace med decision reasoning |
| Personvern i logger | GDPR Art. 5 | Redact PII fra traces, eller disable content recording |
| Arkivering | Arkivloven | Retensjon av agent-traces minimum 5 år |
| Innsyn | Offentlighetsloven | Tilgjengeliggjør agent-beslutningslogger for innsyn |
| Sikkerhetshendelser | NSM Grunnprinsipper | Azure Sentinel-integrasjon for anomali-deteksjon |

### Personvern i observability

```python
# Sensitive data redaction for offentlig sektor
import re

class PIIRedactor:
    PATTERNS = {
        "fnr": r"\b\d{11}\b",  # Fødselsnummer
        "email": r"\b[\w.-]+@[\w.-]+\.\w+\b",
        "phone": r"\b(?:\+47|0047)?\s*\d{8}\b",
    }

    def redact(self, text: str) -> str:
        for pii_type, pattern in self.PATTERNS.items():
            text = re.sub(pattern, f"[REDACTED_{pii_type.upper()}]", text)
        return text

# Bruk i tracing
redactor = PIIRedactor()
span.set_attribute("query.text", redactor.redact(query))
```

## Beslutningsrammeverk

| Scenario | Anbefaling | Begrunnelse |
|----------|------------|-------------|
| Utvikling/testing | Aspire Dashboard + full content recording | Maksimal synlighet for debugging |
| Pre-produksjon | Foundry Tracing + evaluatorer | Kvalitetssikring før lansering |
| Produksjon standard | Application Insights + 10% sampling | Balanse mellom synlighet og kostnad |
| Produksjon høy-risiko | 100% tracing + Sentinel + Agent 365 | Full compliance og sikkerhet |
| Multi-team organisasjon | Agent 365 + sentralisert Log Analytics | Unified governance på tvers av team |

## For Cosmo

- **OpenTelemetry-basert tracing er fundamentet** -- all agent-observability bygger på traces med spans. Implementer fra dag 1, ikke legg til etterpå.
- **Agent 365** er veien fremover for enterprise-scale agent governance -- det gir unified synlighet på tvers av Copilot Studio, Foundry og tredjepartsagenter.
- **Redact PII i traces for offentlig sektor** -- bruk `AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED=false` i produksjon med persondata, eller implementer custom redaction.
- **KQL er ditt viktigste verktøy** for å analysere agent-atferd i produksjon -- bygg dashboards for latency, feilrater, token-bruk og kvalitetsmetrikker per agent.
- **Debugging-workflow**: Start alltid med å finne tracen, deretter isolér den problematiske spannen -- 90% av agent-feil kan diagnostiseres ved å inspisere prompt, kontekst og respons.
