Verifisert mot offisiell MS-doc (juni 2026): «Microsoft Foundry» er det gjeldende produkt-/portalnavnet; «Foundry (classic)» = gamle «Azure AI Foundry» (/azure/foundry/ vs /azure/foundry-classic/). Premiss bekreftet før sveip. Multi-regel, IKKE naiv s/Azure AI Foundry/Microsoft Foundry/ — MS dropper «Azure AI» (legger IKKE til «Microsoft») for to produktvarianter: - «Azure AI Foundry Agent[ Service|s]» → «Foundry Agent Service/Agents» (MS-form) - «Azure AI Foundry Models» → «Foundry Models» (i «Azure OpenAI in Foundry Models») - «Azure AI Foundry SDK» → «Microsoft Foundry SDK» (operatør-valg) - «Azure AI Foundry portal/project» + generisk → «Microsoft Foundry» - Pre-eksisterende «Microsoft Foundry Models» (4) normalisert → «Foundry Models» Bevart: «Azure OpenAI», «Azure AI Inference SDK», «Azure AI Search», «Azure AI Services», kode-IDer. Historisk ref «(tidligere Azure AI Foundry)» i model-catalog-2026.md beskyttet via lookbehind. URL /azure/ai-foundry/→ /azure/foundry/ kun i owasp-llm-top10 (KB-ref); docs/-filer deferred. Scope: skills (inkl. 3 SKILL.md) + commands + agents + README + CLAUDE. Ekskludert: docs/ (interne), playground/+tests/ fixtures (testdata), CHANGELOG.md (historisk logg), STATE.md (gitignored). 3 SKILL.md endret (advisor/engineering/security) → judge-cache teknisk invalidert for disse, men scorer uendret: advisor 91, eng/gov/infra/sec 96 (alle ≥90). validate 239/0. 0 «Azure AI Foundry» igjen (utenom bevart ref). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
362 lines
14 KiB
Markdown
362 lines
14 KiB
Markdown
# 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.
|