# 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 Azure AI 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, Azure AI 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, Azure AI 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 Azure AI 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 ### Azure AI 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.