Add /ultraresearch-local for structured research combining local codebase analysis with external knowledge via parallel agent swarms. Produces research briefs with triangulation, confidence ratings, and source quality assessment. New command: /ultraresearch-local with modes --quick, --local, --external, --fg. New agents: research-orchestrator (opus), docs-researcher, community-researcher, security-researcher, contrarian-researcher, gemini-bridge (all sonnet). New template: research-brief-template.md. Integration: --research flag in /ultraplan-local accepts pre-built research briefs (up to 3), enriches the interview and exploration phases. Planning orchestrator cross-references brief findings during synthesis. Design principle: Context Engineering — right information to right agent at right time. Research briefs are structured artifacts in the pipeline: ultraresearch → brief → ultraplan --research → plan → ultraexecute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
802 lines
23 KiB
Markdown
802 lines
23 KiB
Markdown
# Application Insights for LLM Monitoring
|
|
|
|
**Kategori:** Monitoring & Observability
|
|
**Dato:** 2026-02-05
|
|
**Status:** Komplett
|
|
|
|
## Oversikt
|
|
|
|
Application Insights er Azures native observability-plattform for å overvåke LLM-applikasjoner med OpenTelemetry-kompatibel tracing. Denne guiden dekker LLM-spesifikk telemetri, custom events for AI-interaksjoner, distributed tracing for AI-pipelines, performance baselines, og error tracking.
|
|
|
|
Application Insights integrerer sømløst med Azure AI Foundry, Azure OpenAI Service, og alle større AI-rammeverk (LangChain, Semantic Kernel, Microsoft Agent Framework, OpenAI Agents SDK).
|
|
|
|
## Hvorfor Application Insights for LLM-applikasjoner?
|
|
|
|
### Utfordringer med LLM-observabilitet
|
|
|
|
LLM-applikasjoner introduserer unike overvåkingsutfordringer:
|
|
|
|
1. **Komplekse kjeder** — Agent kan kjøre 10+ steg med nøstede tool calls
|
|
2. **Varierende flows** — Execution path avhenger av user input og model reasoning
|
|
3. **Lange inputs/outputs** — Prompts og responses kan være 1000+ tokens
|
|
4. **Multi-agent orchestration** — Koordinering mellom flere agenter og tools
|
|
5. **Kostnadskontroll** — Token usage og latency må spores per operasjon
|
|
|
|
### Application Insights løsning
|
|
|
|
- **OpenTelemetry-standard** — Følger GenAI semantic conventions
|
|
- **Full trace tree** — Se nøstede spans for agent → tool → LLM calls
|
|
- **Token tracking** — Custom metrics for input/output tokens og cost
|
|
- **Performance baselines** — P50, P90, P95 latency per operasjon
|
|
- **Error correlation** — Link exceptions til spesifikk LLM call eller tool
|
|
- **Multi-framework** — Samme backend for LangChain, Semantic Kernel, etc.
|
|
|
|
## LLM-spesifikk telemetri i Application Insights
|
|
|
|
### Telemetri-typer for AI-applikasjoner
|
|
|
|
Application Insights lagrer LLM-telemetri i standardtabeller:
|
|
|
|
| Telemetri | Tabell | Bruk for LLM-applikasjoner |
|
|
|-----------|--------|----------------------------|
|
|
| **Request** | `AppRequests` | HTTP request til AI endpoint (chat completion, agent run) |
|
|
| **Dependency** | `AppDependencies` | Kall til Azure OpenAI, embeddings API, vector database |
|
|
| **Trace** | `AppTraces` | Agent reasoning steps, tool outputs, system messages |
|
|
| **Exception** | `AppExceptions` | Model errors (rate limit, content filter), tool failures |
|
|
| **Custom Event** | `AppEvents` | User feedback, agent decisions, evaluation results |
|
|
| **Custom Metric** | `AppMetrics` | Token count, cost per request, embedding dimensions |
|
|
|
|
### OpenTelemetry Spans for GenAI
|
|
|
|
Application Insights støtter OpenTelemetry Semantic Conventions for GenAI:
|
|
|
|
**Standard span-attributter:**
|
|
|
|
```json
|
|
{
|
|
"gen_ai.system": "azure_openai",
|
|
"gen_ai.request.model": "gpt-4o",
|
|
"gen_ai.request.max_tokens": 1000,
|
|
"gen_ai.request.temperature": 0.7,
|
|
"gen_ai.response.finish_reason": "stop",
|
|
"gen_ai.usage.input_tokens": 450,
|
|
"gen_ai.usage.output_tokens": 320,
|
|
"gen_ai.prompt": "[redacted]", // hvis content recording er enabled
|
|
"gen_ai.completion": "[redacted]"
|
|
}
|
|
```
|
|
|
|
**Multi-agent spans (Microsoft-utvidelse):**
|
|
|
|
| Span type | Attributt | Beskrivelse |
|
|
|-----------|-----------|-------------|
|
|
| `execute_task` | — | Task planning og event propagation |
|
|
| `invoke_agent` | `agent.name`, `agent.id` | Agent invocation |
|
|
| `agent_to_agent_interaction` | `source_agent`, `target_agent` | Inter-agent kommunikasjon |
|
|
| `agent.state.management` | `memory_type` | Memory og context management |
|
|
| `agent_planning` | `plan_steps` | Agent's internal planning |
|
|
| `execute_tool` | `tool.name`, `tool.call.arguments`, `tool.call.results` | Tool execution |
|
|
| `gen_ai.evaluation` | `evaluation.name`, `evaluation.score` | Agent performance evaluation |
|
|
|
|
## Custom Events for AI-interaksjoner
|
|
|
|
### Logg user feedback
|
|
|
|
User feedback er kritisk for å evaluere LLM-output kvalitet:
|
|
|
|
```python
|
|
from opentelemetry import trace
|
|
from opentelemetry.trace import Status, StatusCode
|
|
|
|
tracer = trace.get_tracer(__name__)
|
|
|
|
def log_user_feedback(response_id: str, rating: int, comment: str):
|
|
"""Log user feedback som OpenTelemetry event."""
|
|
with tracer.start_as_current_span("user_feedback") as span:
|
|
span.set_attribute("gen_ai.response.id", response_id)
|
|
span.set_attribute("user_feedback.rating", rating)
|
|
span.set_attribute("user_feedback.comment", comment)
|
|
|
|
# Link til parent span (LLM response)
|
|
span.set_attribute("parent_span_id", get_response_span_id(response_id))
|
|
```
|
|
|
|
**Query i Application Insights:**
|
|
|
|
```kusto
|
|
AppTraces
|
|
| where OperationName == "user_feedback"
|
|
| extend Rating = tolong(Properties.user_feedback_rating)
|
|
| summarize AvgRating = avg(Rating), Count = count() by bin(TimeGenerated, 1h)
|
|
```
|
|
|
|
### Agent decisions som events
|
|
|
|
Logg agent decisions for å forstå reasoning:
|
|
|
|
```python
|
|
def log_agent_decision(agent_name: str, decision: str, reasoning: str):
|
|
"""Log agent decision point."""
|
|
with tracer.start_as_current_span("agent_decision") as span:
|
|
span.set_attribute("agent.name", agent_name)
|
|
span.set_attribute("agent.decision", decision)
|
|
span.set_attribute("agent.reasoning", reasoning)
|
|
span.set_attribute("timestamp", datetime.utcnow().isoformat())
|
|
```
|
|
|
|
### Tool invocation tracking
|
|
|
|
Track tool usage patterns:
|
|
|
|
```python
|
|
def track_tool_usage(tool_name: str, success: bool, latency_ms: float):
|
|
"""Track tool execution metrics."""
|
|
with tracer.start_as_current_span(f"tool_{tool_name}") as span:
|
|
span.set_attribute("tool.name", tool_name)
|
|
span.set_attribute("tool.success", success)
|
|
span.set_attribute("tool.latency_ms", latency_ms)
|
|
|
|
if not success:
|
|
span.set_status(Status(StatusCode.ERROR))
|
|
```
|
|
|
|
## Distributed Tracing for AI Pipelines
|
|
|
|
### Trace hele agent execution
|
|
|
|
Application Insights viser full trace tree for agent execution:
|
|
|
|
```
|
|
[Request] POST /chat/completions (2.3s)
|
|
├─ [Span] agent_session (2.2s)
|
|
│ ├─ [Span] agent_planning (0.1s)
|
|
│ ├─ [Span] execute_task (2.0s)
|
|
│ │ ├─ [Span] invoke_agent: ResearchAgent (1.2s)
|
|
│ │ │ ├─ [Dependency] Azure OpenAI gpt-4o (0.8s)
|
|
│ │ │ └─ [Span] execute_tool: web_search (0.4s)
|
|
│ │ │ └─ [Dependency] Bing Search API (0.35s)
|
|
│ │ └─ [Span] invoke_agent: SummaryAgent (0.7s)
|
|
│ │ └─ [Dependency] Azure OpenAI gpt-4o-mini (0.6s)
|
|
│ └─ [Span] agent.state.management (0.1s)
|
|
└─ [Custom Event] user_feedback (rating: 5)
|
|
```
|
|
|
|
### Correlation IDs
|
|
|
|
Application Insights bruker W3C Trace Context for correlation:
|
|
|
|
- `operation_Id` — Unique ID for hele request (trace-id)
|
|
- `operation_ParentId` — Parent span ID (parent-id)
|
|
- `id` — Current span ID
|
|
|
|
**Query relaterte spans:**
|
|
|
|
```kusto
|
|
AppRequests
|
|
| where OperationId == "abc123..."
|
|
| union (AppDependencies | where OperationId == "abc123...")
|
|
| union (AppTraces | where OperationId == "abc123...")
|
|
| order by TimeGenerated asc
|
|
```
|
|
|
|
### Instrumentering med Azure AI Foundry SDK
|
|
|
|
**Python setup:**
|
|
|
|
```python
|
|
from azure.ai.projects import AIProjectClient
|
|
from azure.monitor.opentelemetry import configure_azure_monitor
|
|
from opentelemetry import trace
|
|
|
|
# Connect til AI Foundry project
|
|
project_client = AIProjectClient(
|
|
credential=DefaultAzureCredential(),
|
|
endpoint=os.environ["PROJECT_ENDPOINT"]
|
|
)
|
|
|
|
# Hent Application Insights connection string
|
|
connection_string = project_client.telemetry.get_application_insights_connection_string()
|
|
|
|
# Enable Azure Monitor tracing
|
|
configure_azure_monitor(connection_string=connection_string)
|
|
|
|
# Get tracer
|
|
tracer = trace.get_tracer(__name__)
|
|
|
|
# Trace agent execution
|
|
with tracer.start_as_current_span("my_agent_flow"):
|
|
agent = project_client.agents.create_agent(...)
|
|
run = project_client.agents.runs.create_and_process(...)
|
|
```
|
|
|
|
### Content recording (opt-in)
|
|
|
|
For å trace prompt/completion content (kan inneholde persondata):
|
|
|
|
```python
|
|
import os
|
|
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
|
|
```
|
|
|
|
**Alternativt via miljøvariabel:**
|
|
|
|
```bash
|
|
# PowerShell
|
|
$env:AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED = "true"
|
|
|
|
# Bash
|
|
export AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED=true
|
|
```
|
|
|
|
⚠️ **Sikkerhet:** Content recording logger prompts og responses. Vurder GDPR/privacy før aktivering.
|
|
|
|
## Performance Baselines for LLMs
|
|
|
|
### Definere baselines
|
|
|
|
LLM-applikasjoner har annen performance-profil enn tradisjonelle APIs:
|
|
|
|
| Metric | Baseline (gpt-4o) | Baseline (gpt-4o-mini) |
|
|
|--------|-------------------|------------------------|
|
|
| **Latency P50** | 1.2s | 0.6s |
|
|
| **Latency P95** | 3.5s | 1.8s |
|
|
| **Tokens/sec (output)** | 40-60 | 80-120 |
|
|
| **Time to first token** | 0.3s | 0.2s |
|
|
| **Tool call overhead** | +0.5s per tool | +0.3s per tool |
|
|
|
|
### Custom metrics for LLM performance
|
|
|
|
**Track token usage:**
|
|
|
|
```python
|
|
from opentelemetry import metrics
|
|
|
|
meter = metrics.get_meter(__name__)
|
|
|
|
# Create metrics
|
|
input_tokens = meter.create_counter(
|
|
"gen_ai.input_tokens",
|
|
description="Total input tokens consumed",
|
|
unit="tokens"
|
|
)
|
|
|
|
output_tokens = meter.create_counter(
|
|
"gen_ai.output_tokens",
|
|
description="Total output tokens generated",
|
|
unit="tokens"
|
|
)
|
|
|
|
cost_metric = meter.create_counter(
|
|
"gen_ai.cost_usd",
|
|
description="Estimated cost in USD",
|
|
unit="USD"
|
|
)
|
|
|
|
# Log metrics
|
|
def track_llm_call(model: str, input_tokens: int, output_tokens: int):
|
|
input_tokens.add(input_tokens, {"model": model})
|
|
output_tokens.add(output_tokens, {"model": model})
|
|
|
|
# Calculate cost (example rates)
|
|
cost = (input_tokens * 0.000005) + (output_tokens * 0.000015)
|
|
cost_metric.add(cost, {"model": model})
|
|
```
|
|
|
|
**Query metrics i Application Insights:**
|
|
|
|
```kusto
|
|
AppMetrics
|
|
| where Name == "gen_ai.input_tokens"
|
|
| summarize TotalTokens = sum(Sum) by Model = tostring(Properties.model), bin(TimeGenerated, 1h)
|
|
| render timechart
|
|
```
|
|
|
|
### Latency percentiles
|
|
|
|
**Query latency distribution:**
|
|
|
|
```kusto
|
|
AppDependencies
|
|
| where Target contains "openai.azure.com"
|
|
| extend Model = tostring(Properties["gen_ai.request.model"])
|
|
| summarize
|
|
P50 = percentile(DurationMs, 50),
|
|
P90 = percentile(DurationMs, 90),
|
|
P95 = percentile(DurationMs, 95),
|
|
P99 = percentile(DurationMs, 99),
|
|
Count = count()
|
|
by Model, bin(TimeGenerated, 1h)
|
|
```
|
|
|
|
### Time to first token (TTFT)
|
|
|
|
TTFT er kritisk metric for user experience:
|
|
|
|
```python
|
|
import time
|
|
|
|
def track_streaming_latency(model: str):
|
|
"""Track time to first token for streaming response."""
|
|
with tracer.start_as_current_span("streaming_call") as span:
|
|
start_time = time.time()
|
|
first_token_received = False
|
|
|
|
for chunk in stream_response:
|
|
if not first_token_received:
|
|
ttft = (time.time() - start_time) * 1000 # ms
|
|
span.set_attribute("gen_ai.ttft_ms", ttft)
|
|
first_token_received = True
|
|
```
|
|
|
|
**Query TTFT:**
|
|
|
|
```kusto
|
|
AppTraces
|
|
| where OperationName == "streaming_call"
|
|
| extend TTFT = todouble(Properties.gen_ai_ttft_ms)
|
|
| summarize avg(TTFT), percentile(TTFT, 95) by bin(TimeGenerated, 1h)
|
|
```
|
|
|
|
## Error Tracking og Alerting
|
|
|
|
### LLM-spesifikke errors
|
|
|
|
**Common error patterns:**
|
|
|
|
| Error type | Årsak | Mitigering |
|
|
|------------|-------|------------|
|
|
| **RateLimitError** | 429 Too Many Requests | Implement exponential backoff, øk TPM quota |
|
|
| **ContentFilterError** | Content policy violation | Sanitize prompts, adjust content filter settings |
|
|
| **TimeoutError** | Request > 10min timeout | Chunk inputs, bruk streaming |
|
|
| **TokenLimitExceeded** | Input > model context window | Truncate history, summarize context |
|
|
| **ModelNotFound** | Deployment name feil | Validate deployment names i config |
|
|
|
|
### Exception tracking
|
|
|
|
Application Insights fanger exceptions automatisk, men legg til context:
|
|
|
|
```python
|
|
def safe_llm_call(prompt: str):
|
|
"""LLM call with exception handling."""
|
|
with tracer.start_as_current_span("llm_call") as span:
|
|
try:
|
|
response = client.chat.completions.create(...)
|
|
span.set_attribute("gen_ai.success", True)
|
|
return response
|
|
except RateLimitError as e:
|
|
span.set_status(Status(StatusCode.ERROR, "Rate limit exceeded"))
|
|
span.record_exception(e)
|
|
span.set_attribute("gen_ai.error.type", "rate_limit")
|
|
span.set_attribute("gen_ai.retry_after", e.retry_after)
|
|
raise
|
|
except ContentFilterError as e:
|
|
span.set_status(Status(StatusCode.ERROR, "Content filtered"))
|
|
span.record_exception(e)
|
|
span.set_attribute("gen_ai.error.type", "content_filter")
|
|
span.set_attribute("gen_ai.filter.category", e.category)
|
|
raise
|
|
```
|
|
|
|
**Query error rates:**
|
|
|
|
```kusto
|
|
AppExceptions
|
|
| where Properties.gen_ai_error_type == "rate_limit"
|
|
| summarize ErrorCount = count() by bin(TimeGenerated, 5m)
|
|
| render timechart
|
|
```
|
|
|
|
### Smart alerting for LLM failures
|
|
|
|
**Alert rule examples:**
|
|
|
|
1. **High error rate:**
|
|
```kusto
|
|
AppDependencies
|
|
| where Target contains "openai.azure.com"
|
|
| where Success == false
|
|
| summarize ErrorRate = (count() * 100.0) / todouble(count()) by bin(TimeGenerated, 5m)
|
|
| where ErrorRate > 10 // > 10% errors
|
|
```
|
|
|
|
2. **Latency spike:**
|
|
```kusto
|
|
AppDependencies
|
|
| where Target contains "openai.azure.com"
|
|
| summarize P95 = percentile(DurationMs, 95) by bin(TimeGenerated, 5m)
|
|
| where P95 > 5000 // > 5 seconds P95
|
|
```
|
|
|
|
3. **Cost spike:**
|
|
```kusto
|
|
AppMetrics
|
|
| where Name == "gen_ai.cost_usd"
|
|
| summarize TotalCost = sum(Sum) by bin(TimeGenerated, 1h)
|
|
| where TotalCost > 100 // > $100/hour
|
|
```
|
|
|
|
### Anomaly detection for token usage
|
|
|
|
Application Insights har innebygd anomaly detection:
|
|
|
|
```kusto
|
|
AppMetrics
|
|
| where Name == "gen_ai.input_tokens"
|
|
| make-series TotalTokens = sum(Sum) default=0 on TimeGenerated step 1h
|
|
| extend Anomalies = series_decompose_anomalies(TotalTokens, 1.5)
|
|
| where Anomalies > 0 // Anomali detektert
|
|
```
|
|
|
|
## Framework-integrasjoner
|
|
|
|
### LangChain / LangGraph
|
|
|
|
**Instrumentering:**
|
|
|
|
```python
|
|
from langchain_azure_ai.callbacks.tracers import AzureAIOpenTelemetryTracer
|
|
|
|
azure_tracer = AzureAIOpenTelemetryTracer(
|
|
connection_string=os.environ["APPLICATION_INSIGHTS_CONNECTION_STRING"],
|
|
enable_content_recording=True,
|
|
name="My Agent",
|
|
id="agent_v1"
|
|
)
|
|
|
|
# Attach til LangChain model
|
|
llm = AzureChatOpenAI(..., callbacks=[azure_tracer])
|
|
|
|
# Eller til agent
|
|
agent = create_agent(model=llm, tools=tools, callbacks=[azure_tracer])
|
|
```
|
|
|
|
**Query LangChain traces:**
|
|
|
|
```kusto
|
|
AppTraces
|
|
| where Properties.framework == "langchain"
|
|
| extend ChainName = tostring(Properties.chain_name)
|
|
| summarize Count = count(), AvgDuration = avg(DurationMs) by ChainName
|
|
```
|
|
|
|
### Semantic Kernel
|
|
|
|
Semantic Kernel har native Application Insights støtte:
|
|
|
|
```csharp
|
|
using Microsoft.ApplicationInsights;
|
|
using Microsoft.SemanticKernel;
|
|
|
|
var telemetryClient = new TelemetryClient();
|
|
|
|
var kernel = Kernel.CreateBuilder()
|
|
.AddAzureOpenAIChatCompletion(...)
|
|
.Build();
|
|
|
|
// Tracing er automatisk enabled
|
|
var result = await kernel.InvokePromptAsync("...");
|
|
```
|
|
|
|
### Microsoft Agent Framework
|
|
|
|
Agent Framework sender automatisk telemetri til Application Insights:
|
|
|
|
```python
|
|
from azure.ai.agents.telemetry import AIAgentsInstrumentor
|
|
|
|
# Enable instrumentation
|
|
AIAgentsInstrumentor().instrument()
|
|
|
|
# All agent calls er automatisk tracet
|
|
agent = project_client.agents.create_agent(...)
|
|
```
|
|
|
|
### OpenAI Agents SDK
|
|
|
|
**Instrumentering:**
|
|
|
|
```python
|
|
from opentelemetry.instrumentation.openai_agents import OpenAIAgentsInstrumentor
|
|
|
|
# Instrument SDK
|
|
OpenAIAgentsInstrumentor().instrument(tracer_provider=trace.get_tracer_provider())
|
|
|
|
# All OpenAI agent calls er tracet
|
|
with tracer.start_as_current_span("agent_session"):
|
|
# ... run agent
|
|
pass
|
|
```
|
|
|
|
## Lokal debugging med Aspire Dashboard
|
|
|
|
For lokal utvikling uten Application Insights:
|
|
|
|
**Setup:**
|
|
|
|
```bash
|
|
pip install opentelemetry-exporter-otlp
|
|
```
|
|
|
|
**Code:**
|
|
|
|
```python
|
|
from opentelemetry import trace
|
|
from opentelemetry.sdk.trace import TracerProvider
|
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
|
|
# Setup OTLP exporter (Aspire Dashboard)
|
|
provider = TracerProvider()
|
|
provider.add_span_processor(
|
|
BatchSpanProcessor(
|
|
OTLPSpanExporter(endpoint="http://localhost:4317")
|
|
)
|
|
)
|
|
trace.set_tracer_provider(provider)
|
|
```
|
|
|
|
**Start Aspire Dashboard:**
|
|
|
|
```bash
|
|
docker run -p 4317:4317 -p 18888:18888 mcr.microsoft.com/dotnet/aspire-dashboard:latest
|
|
```
|
|
|
|
Åpne `http://localhost:18888` for å se traces lokalt.
|
|
|
|
## Best Practices
|
|
|
|
### 1. Service naming
|
|
|
|
Bruk `cloud_RoleName` for å skille tjenester:
|
|
|
|
```python
|
|
from opentelemetry.sdk.resources import Resource
|
|
|
|
resource = Resource.create({
|
|
"service.name": "chat-api",
|
|
"service.version": "1.2.0",
|
|
"deployment.environment": "production"
|
|
})
|
|
|
|
provider = TracerProvider(resource=resource)
|
|
```
|
|
|
|
**Query per service:**
|
|
|
|
```kusto
|
|
AppRequests
|
|
| where AppRoleName == "chat-api"
|
|
```
|
|
|
|
### 2. Redact sensitive data
|
|
|
|
**Ikke logg:**
|
|
- User PII (navn, epost, telefon)
|
|
- API keys eller secrets
|
|
- Sensitive business data
|
|
|
|
**Implementer redaction:**
|
|
|
|
```python
|
|
import re
|
|
|
|
def redact_pii(text: str) -> str:
|
|
"""Redact common PII patterns."""
|
|
# Email
|
|
text = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]', text)
|
|
# Phone (US)
|
|
text = re.sub(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b', '[PHONE]', text)
|
|
# SSN
|
|
text = re.sub(r'\b\d{3}-\d{2}-\d{4}\b', '[SSN]', text)
|
|
return text
|
|
|
|
# Bruk før logging
|
|
span.set_attribute("gen_ai.prompt", redact_pii(original_prompt))
|
|
```
|
|
|
|
### 3. Sampling for kostnadsoptimalisering
|
|
|
|
For high-volume applikasjoner, bruk adaptive sampling:
|
|
|
|
```python
|
|
from azure.monitor.opentelemetry import configure_azure_monitor
|
|
|
|
configure_azure_monitor(
|
|
connection_string=connection_string,
|
|
enable_adaptive_sampling=True,
|
|
sampling_rate=0.1 # 10% av requests
|
|
)
|
|
```
|
|
|
|
**Aldri sample:**
|
|
- Errors og exceptions
|
|
- High-value user sessions (premium users)
|
|
- Performance anomalies
|
|
|
|
### 4. Correlation med evaluations
|
|
|
|
Link tracing til offline evaluation runs:
|
|
|
|
```python
|
|
def log_evaluation_result(trace_id: str, metric_name: str, score: float):
|
|
"""Link evaluation score til original trace."""
|
|
with tracer.start_as_current_span("evaluation_result") as span:
|
|
span.set_attribute("evaluation.trace_id", trace_id)
|
|
span.set_attribute("evaluation.metric", metric_name)
|
|
span.set_attribute("evaluation.score", score)
|
|
```
|
|
|
|
**Query:**
|
|
|
|
```kusto
|
|
AppTraces
|
|
| where OperationName == "evaluation_result"
|
|
| join kind=inner (
|
|
AppRequests
|
|
| extend TraceId = OperationId
|
|
) on $left.Properties.evaluation_trace_id == $right.TraceId
|
|
| project TimeGenerated, RequestName, EvaluationScore = todouble(Properties.evaluation_score)
|
|
```
|
|
|
|
### 5. Cost allocation per customer
|
|
|
|
Tag requests med customer ID:
|
|
|
|
```python
|
|
with tracer.start_as_current_span("customer_request") as span:
|
|
span.set_attribute("customer.id", customer_id)
|
|
span.set_attribute("customer.tier", "enterprise")
|
|
```
|
|
|
|
**Query cost per customer:**
|
|
|
|
```kusto
|
|
AppMetrics
|
|
| where Name == "gen_ai.cost_usd"
|
|
| extend CustomerId = tostring(Properties.customer_id)
|
|
| summarize TotalCost = sum(Sum) by CustomerId
|
|
| order by TotalCost desc
|
|
```
|
|
|
|
## Visualisering i Azure Portal
|
|
|
|
### Transaction details view
|
|
|
|
Application Insights **End-to-end transaction details** viser:
|
|
|
|
1. Full trace timeline med alle spans
|
|
2. Dependencies sortert etter latency
|
|
3. Exceptions linket til parent spans
|
|
4. Custom properties per span
|
|
|
|
**Navigasjon:**
|
|
- Application Insights → **Investigate** → **Performance**
|
|
- Velg en request → Klikk **View all telemetry**
|
|
|
|
### Workbooks for LLM monitoring
|
|
|
|
**Pre-built workbook template:**
|
|
|
|
1. Token usage over time (per model)
|
|
2. Cost per hour/day/month
|
|
3. Latency percentiles (P50, P95, P99)
|
|
4. Error rate by error type
|
|
5. Top 10 slowest requests
|
|
6. User feedback distribution
|
|
|
|
**Opprett workbook:**
|
|
- Application Insights → **Monitoring** → **Workbooks** → **+ New**
|
|
|
|
### Dashboards
|
|
|
|
**Key metrics dashboard:**
|
|
|
|
- Total requests (trendline)
|
|
- Average latency (by model)
|
|
- Error rate (%)
|
|
- Total cost (daily/monthly)
|
|
- Token usage (input vs output)
|
|
- Active users
|
|
|
|
## For Cosmo Skyberg: Application Insights for LLM Monitoring
|
|
|
|
**Når anbefalte:**
|
|
|
|
Application Insights er riktig valg for LLM-observabilitet når:
|
|
|
|
1. **Azure-native setup** — Kunden bruker Azure AI Foundry eller Azure OpenAI
|
|
2. **Multi-framework miljø** — LangChain, Semantic Kernel, Agent Framework i samme system
|
|
3. **Enterprise compliance** — Trenger logging i Azure subscription med RBAC
|
|
4. **Cost tracking** — Viktig å korrelere token usage med fakturering
|
|
5. **Eksisterende Azure Monitor** — Allerede bruker App Insights for web APIs
|
|
|
|
**Når vurdere alternativer:**
|
|
|
|
- **LangSmith** — Hvis kun LangChain, og trenger dataset curation
|
|
- **Weights & Biases** — Hvis ML engineering team, trenger experiment tracking
|
|
- **Elastic APM** — Hvis eksisterende Elastic stack for logging
|
|
- **Aspire Dashboard** — Lokal dev/debugging (ikke produksjon)
|
|
|
|
**Key decision factors:**
|
|
|
|
| Faktor | Application Insights | LangSmith | W&B |
|
|
|--------|----------------------|-----------|-----|
|
|
| **OpenTelemetry native** | ✅ Ja | ⚠️ Partial | ❌ Nei |
|
|
| **Cost per GB** | ~$2.76/GB | $0 (gratis tier), $39+ | $0 (gratis tier), $50+ |
|
|
| **Retention** | 90 dager default | 14 dager (gratis), 400 dager (betalt) | Ubegrenset |
|
|
| **Multi-framework** | ✅ Alle | ⚠️ LangChain best | ⚠️ Custom integration |
|
|
| **Azure integration** | ✅ Native | ❌ Nei | ❌ Nei |
|
|
| **Offline evaluation** | ⚠️ Via custom code | ✅ Built-in | ✅ Built-in |
|
|
|
|
**Arkitekturrådgiving:**
|
|
|
|
1. **Start med Application Insights** — Enkleste setup for Azure-kunder
|
|
2. **Enable content recording selektivt** — Kun for debugging, ikke produksjon
|
|
3. **Implementer custom metrics** — Token cost, latency percentiles, TTFT
|
|
4. **Sett opp alerting** — Error rate, cost spikes, latency anomalies
|
|
5. **Kombiner med prompt evaluation** — Azure AI Foundry Evaluation + App Insights tracing
|
|
|
|
**Eksempel-arkitektur:**
|
|
|
|
```
|
|
[User] → [Azure API Management]
|
|
↓ (trace-id propagation)
|
|
[Chat API] → [Application Insights]
|
|
↓
|
|
[Agent Orchestrator]
|
|
├─ [LangChain Agent] → [Azure OpenAI] → [Token metrics]
|
|
├─ [Tool: Azure AI Search] → [Dependency trace]
|
|
└─ [Tool: Bing Search] → [Dependency trace]
|
|
```
|
|
|
|
**Kostnadsestimat:**
|
|
|
|
- **Ingestion:** 1M requests/måned ≈ 10 GB ≈ $27/mnd
|
|
- **Query:** 10 GB scanned/mnd ≈ $0.50/mnd
|
|
- **Retention:** 90 dager default (inkludert i pris)
|
|
- **Total:** ~$30-50/mnd for medium-scale produksjon
|
|
|
|
**Sett opp i 10 minutter:**
|
|
|
|
```bash
|
|
# 1. Connect Application Insights til AI Foundry project
|
|
az monitor app-insights component create \
|
|
--app my-ai-app \
|
|
--location norwayeast \
|
|
--resource-group my-rg
|
|
|
|
# 2. Link til Foundry project (via portal eller CLI)
|
|
# 3. Install SDK + configure
|
|
pip install azure-ai-projects azure-monitor-opentelemetry
|
|
|
|
# 4. Skriv 5 linjer code (se "Instrumentering med Azure AI Foundry SDK")
|
|
# 5. Deploy → Se traces i portal
|
|
```
|
|
|
|
**Første queries å kjøre:**
|
|
|
|
```kusto
|
|
// 1. Top 10 slowest requests
|
|
AppRequests
|
|
| top 10 by DurationMs desc
|
|
| project TimeGenerated, Name, DurationMs, Success
|
|
|
|
// 2. Error distribution
|
|
AppExceptions
|
|
| summarize Count = count() by Type
|
|
| order by Count desc
|
|
|
|
// 3. Cost per hour
|
|
AppMetrics
|
|
| where Name == "gen_ai.cost_usd"
|
|
| summarize Cost = sum(Sum) by bin(TimeGenerated, 1h)
|
|
| render timechart
|
|
```
|
|
|
|
---
|
|
|
|
**Relaterte referanser:**
|
|
- `token-usage-tracking.md` — Token metrics og cost calculation
|
|
- `azure-monitor-integration.md` — Full Azure Monitor setup
|
|
- `llm-performance-baselines.md` — Performance benchmarks per model
|
|
- `distributed-tracing-patterns.md` — Multi-service correlation
|