Lab 10: Observability - Tracing agentów
Opis
W tym laboratorium nauczysz się włączać i analizować tracing dla Azure AI Agents Service. Tracing pozwala obserwować każdy krok wykonania agenta: jakie narzędzia wywołał, ile tokenów zużył, jak długo trwała każda operacja.
Dlaczego tracing jest krytyczny dla agentów?
Agenci to nie proste wywołania API - to wieloetapowe orkiestracje:
- Agent może wywołać 5 narzędzi zanim odpowie
- Każde narzędzie ma swoją latencję i koszt (tokeny)
- Błędy mogą wystąpić na różnych poziomach (model, narzędzie, network)
- W produkcji potrzebujesz monitoringu kosztów i wydajności
Bez tracingu:
User: "Podaj dane KRS 0000028860"
Agent: "Oto dane: ..."
❓ Ile tokenów zużyto?
❓ Które narzędzie wywołano?
❓ Jak długo trwało zapytanie do API?
❓ Czy były błędy retry?
Z tracingiem:
Trace ID: abc123
├─ Span: agent_run (4.2s, 1250 tokens)
│ ├─ Span: model_call (0.8s, 150 tokens)
│ ├─ Span: tool_call: krs_search (2.1s)
│ │ └─ HTTP GET https://api-krs.ms.gov.pl/...
│ └─ Span: model_call (1.3s, 1100 tokens)
Widzisz dokładnie co się działo, w jakiej kolejności, ile to kosztowało.
Czym jest tracing?
OpenTelemetry to otwarty standard dla observability. Komponenty:
1. Traces
Journey of a request - pełna ścieżka żądania przez system.
W kontekście agentów: od pytania użytkownika → przez model → narzędzia → do finalnej odpowiedzi.
2. Spans
Building blocks of traces - pojedyncze operacje.
Każdy span ma:
- Name:
agent_run,tool_call,model_call - Start/End time: kiedy rozpoczęło się i zakończyło
- Duration: jak długo trwało
- Attributes: metadata (tokeny, model, status)
- Parent span: hierarchia (span w spanie)
3. Attributes
Key-value pairs - kontekstowe metadane:
{
"gen_ai.system": "azure_ai_agents",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": 150,
"gen_ai.usage.output_tokens": 1100,
"tool.name": "krs_search",
"tool.arguments": '{"krs": "0000028860"}'
}
4. Azure Monitor Integration
Azure AI Foundry natywnie wspiera tracing do Application Insights:
- Automatyczna instrumentacja agentów
- Semantic conventions dla multi-agent
- Integracja z AI Foundry Portal (Tracing tab)
Co jest tracowane?
Gdy włączysz tracing, automatycznie zbierane są:
✅ Agent operations
create_agent,create_thread,create_message,create_and_process_run- Status transitions: queued → in_progress → completed
✅ Model calls
- Model name (np.
gpt-4o) - Tokeny: input, output, total
- Latencja (response time)
✅ Tool calls
- Function name (np.
krs_search) - Arguments (jeśli content recording = true)
- Results (jeśli content recording = true)
- Execution time
✅ Multi-agent interactions
- Delegacje między orkiestratorem a sub-agentami
- Semantic conventions:
agent_to_agent_interaction
❌ Content recording (domyślnie WYŁĄCZONE)
- User messages
- Agent responses
- Tool arguments/results
Dlaczego content recording jest domyślnie wyłączone?
Privacy & Security:
- Wiadomości mogą zawierać PII (Personal Identifiable Information)
- Argumenty narzędzi mogą zawierać wrażliwe dane
- GDPR/compliance considerations
Włączasz je świadomie przez:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
Wymagania wstępne
1. Agent z poprzednich laboratoriów
Będziemy używać KRS-Agent z Lab 07 (Function Calling).
Potrzebujesz:
PROJECT_ENDPOINT(z AI Foundry Portal)AGENT_ID(twój KRS-Agent)
2. Application Insights
Terraform automatycznie utworzył dla Ciebie:
- Application Insights resource
- Log Analytics Workspace (backend dla App Insights)
- Contributor role assignment (możesz zarządzać)
Sprawdź nazwę swojego resource:
# W Azure Portal → Resource Group → poszukaj:
appi-{company}-user{N}-{suffix}
3. Pakiety Python
pip install azure-monitor-opentelemetry opentelemetry-sdk
Setup w Azure AI Foundry Portal (KRÓTKO!)
Zanim zaczniesz kodować, musisz połączyć Application Insights z projektem AI Foundry.
Krok 1: Otwórz Tracing tab
- Wejdź na ai.azure.com
- Wybierz swój projekt
- W lewym menu: Tracing
Krok 2: Connect Application Insights
- Kliknij “Connect” (lub “Connect Application Insights”)
- Wybierz z listy swój resource:
appi-{company}-user{N}-{suffix} - Kliknij “Connect”
Krok 3: Gotowe!
Portal pokazuje: ✅ Connected
Connection string będziemy pobierać programatycznie w kodzie (nie musisz go kopiować).
Budowanie skryptu krok po kroku
Teraz napiszemy kod, który:
- Pobierze connection string z Application Insights
- Skonfiguruje Azure Monitor exporter
- Włączy content recording
- Uruchomi agenta z tracingiem
- Pokaże gdzie znaleźć traces w portalu
Krok 1: Instalacja wymaganych pakietów
Zanim zaczniesz, zainstaluj wymagane biblioteki:
pip install azure-monitor-opentelemetry opentelemetry-sdk
Co instalujemy?
azure-monitor-opentelemetry- integracja OpenTelemetry z Application Insightsopentelemetry-sdk- OpenTelemetry SDK do tworzenia custom spans
Te pakiety są dodatkiem do azure-ai-projects (który już masz z Lab 07).
Krok 2: Utworzenie pliku i importy
Stwórz agent_tracing.py:
#!/usr/bin/env python3
"""
Azure AI Agents - Observability with Tracing
"""
import os
# Najpierw ustaw content recording (PRZED importami Azure!)
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
from azure.ai.projects import AIProjectClient
from azure.identity import ChainedTokenCredential, EnvironmentCredential, AzureCliCredential
from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import trace
print("✓ Importy załadowane")
Kluczowe:
os.environ[...]PRZED importem Azure SDK!azure.monitor.opentelemetry- integracja z Application Insightsopentelemetry.trace- tworzenie custom spanów
Uruchom:
python agent_tracing.py
Oczekiwany output:
✓ Importy załadowane
Krok 3: Konfiguracja klienta
Dodaj konfigurację (dane z Lab 07):
# Konfiguracja (zmień na swoje!)
PROJECT_ENDPOINT = "https://agent.services.ai.azure.com/api/projects/twojProjekt"
# ⚠️ WAŻNE: Użyj agenta z Lab 07 który MA krs_search tool!
# Jeśli użyjesz agenta bez tools, traces będą "płaskie" (brak tool calls)
AGENT_ID = "asst_XYZ..." # Zmień na swojego KRS-Agent!
print("\n🔧 Tworzenie klienta...")
# Credential chain
credential = ChainedTokenCredential(
EnvironmentCredential(),
AzureCliCredential()
)
# (Opcjonalne) Jeżeli masz rozszyfrowywanie ruchu na proxy (SSL interception),
# możesz wyłączyć weryfikowanie certyfikatu:
# from azure.core.pipeline.transport import RequestsTransport
# transport = RequestsTransport(connection_verify=False)
# Klient AI Foundry
project_client = AIProjectClient(
endpoint=PROJECT_ENDPOINT,
credential=credential
# transport=transport # Odkomentuj jeżeli używasz proxy
)
print("✓ Klient utworzony")
Uruchom:
python agent_tracing.py
Oczekiwany output:
✓ Importy załadowane
🔧 Tworzenie klienta...
✓ Klient utworzony
Krok 4: Pobranie connection string z Application Insights
Zamiast kopiować connection string ręcznie z portalu, pobierzemy go programatycznie:
print("\n🔌 Pobieranie Application Insights connection string...")
# Pobranie connection string z podłączonego App Insights
connection_string = project_client.telemetry.get_application_insights_connection_string()
print("✓ Connection string pobrany")
print(f" (długość: {len(connection_string)} znaków)")
Co się dzieje?
project_client.telemetry.get_application_insights_connection_string()pobiera connection string z zasobu, który połączyłeś w portalu (Krok 2 Setup)- Connection string zawiera instrumentation key i endpoint do wysyłania telemetrii
Uruchom:
python agent_tracing.py
Oczekiwany output:
🔌 Pobieranie Application Insights connection string...
✓ Connection string pobrany
(długość: 180 znaków)
Troubleshooting:
- ❌ Error: “Application Insights not connected” → Wróć do Setup w portalu, połącz App Insights
- ❌ Error: “Unauthorized” → Sprawdź czy masz Contributor role na App Insights
Krok 5: Konfiguracja Azure Monitor exporter
Teraz skonfigurujemy OpenTelemetry do wysyłania traces do Application Insights:
print("\n📊 Konfiguracja Azure Monitor...")
# Konfiguruj Azure Monitor jako backend dla OpenTelemetry
configure_azure_monitor(connection_string=connection_string)
print("✓ Azure Monitor skonfigurowany")
print(" Traces będą wysyłane do Application Insights")
Co robi configure_azure_monitor()?
- Rejestruje Azure Monitor exporter w OpenTelemetry
- Automatycznie instrumentuje Azure SDK (w tym
AIProjectClient) - Traces z agentów będą wysyłane do Application Insights
Uruchom:
python agent_tracing.py
Oczekiwany output:
📊 Konfiguracja Azure Monitor...
✓ Azure Monitor skonfigurowany
Traces będą wysyłane do Application Insights
Krok 6: Wyjaśnienie content recording
Dodaj info o tym co będzie logowane:
print("\n🔍 Content recording:")
content_recording = os.environ.get("AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED", "false")
print(f" Status: {content_recording}")
if content_recording == "true":
print(" ✓ Będą logowane:")
print(" - User messages (pytania)")
print(" - Agent responses (odpowiedzi)")
print(" - Tool arguments (np. KRS number)")
print(" - Tool results (dane z API)")
print("\n ⚠️ Privacy warning:")
print(" - Może zawierać PII (Personal Identifiable Information)")
print(" - W produkcji rozważ wyłączenie (false)")
else:
print(" ✗ Content NIE będzie logowany")
print(" - Tylko metadata: tokeny, latencja, tool names")
print(" - Bezpieczniejsze dla privacy/GDPR")
Uruchom:
python agent_tracing.py
Oczekiwany output:
🔍 Content recording:
Status: true
✓ Będą logowane:
- User messages (pytania)
- Agent responses (odpowiedzi)
- Tool arguments (np. KRS number)
- Tool results (dane z API)
⚠️ Privacy warning:
- Może zawierać PII (Personal Identifiable Information)
- W produkcji rozważ wyłączenie (false)
Kiedy ustawić true?
- ✅ Development/debugging
- ✅ Testing
- ✅ Troubleshooting konkretnych issues
Kiedy ustawić false?
- ✅ Production (dane klientów)
- ✅ Compliance requirements (GDPR)
- ✅ Optymalizacja kosztów (mniej danych w storage)
Krok 7: Utworzenie tracer i wrappera dla agent run
Teraz utworzymy custom span, który ogarnie całe wykonanie agenta:
print("\n🎯 Tworzenie tracer...")
# Pobierz tracer (OpenTelemetry)
tracer = trace.get_tracer(__name__)
print("✓ Tracer utworzony")
print(" Możemy teraz tworzyć custom spans")
Tracer to obiekt, który pozwala tworzyć custom spans dla własnych operacji.
Dlaczego to potrzebne?
- Azure SDK automatycznie tworzy spany dla
create_and_process_run(),tool calls, etc. - Ale możesz chcieć dodać swoje spany dla business logic (np. “preprocessing”, “validation”)
Uruchom:
python agent_tracing.py
Oczekiwany output:
🎯 Tworzenie tracer...
✓ Tracer utworzony
Możemy teraz tworzyć custom spans
Krok 8: Uruchomienie agenta z tracingiem
Teraz uruchomimy KRS-Agent w custom spanie.
⚠️ WAŻNE: Wszystkie operacje (create_thread, create_message, create_and_process_run) muszą być WEWNĄTRZ custom spana, żeby miały poprawną hierarchię parent-child!
print("\n▶️ Uruchamianie agenta z tracingiem...")
# Pytanie do agenta
question = "Podaj dane firmy KRS 0000028860"
# KLUCZOWE: Opakowujemy CAŁĄ interakcję w custom span
with tracer.start_as_current_span("krs-lookup-demo") as span:
span.set_attribute("demo.question", question)
span.set_attribute("demo.agent_id", AGENT_ID)
print(f" Pytanie: {question}")
print(f" Span name: krs-lookup-demo")
# Utwórz thread (WEWNĄTRZ spana)
thread = project_client.agents.threads.create()
span.set_attribute("demo.thread_id", thread.id)
print(f" Thread ID: {thread.id}")
# Dodaj message (WEWNĄTRZ spana)
message = project_client.agents.messages.create(
thread_id=thread.id,
role="user",
content=question
)
# Uruchom agenta (WEWNĄTRZ spana)
run = project_client.agents.runs.create_and_process(
thread_id=thread.id,
agent_id=AGENT_ID,
# Metadata dla business context (max 16 key-value pairs)
metadata={
"user_id": "workshop_user",
"session_id": "demo_session_001",
"environment": "development"
}
)
# Dodaj info o run do spana
span.set_attribute("demo.run_id", run.id)
span.set_attribute("demo.run_status", run.status)
print(f"\n✓ Agent wykonał zadanie")
print(f" Run ID: {run.id}")
print(f" Status: {run.status}")
⚠️ UWAGA: print() jest wewnątrz with block, ale to tylko wyświetlanie (nie API call), więc nie tworzy spana. Możemy je mieć tam lub poza - bez znaczenia dla traces.
Co się dzieje?
tracer.start_as_current_span("krs-lookup-demo")- tworzy custom span (parent)- WSZYSTKO wewnątrz
withblock jest child tego spana:create_thread()- automatyczny span dla thread creationcreate_message()- automatyczny span dla message creationcreate_and_process_run()- automatyczny span dla agent runlist_messages()- automatyczny span dla pobierania messages
- Hierarchia spanów:
krs-lookup-demo (custom parent span) ├─ create_thread (automatic child) ├─ create_message (automatic child) ├─ start_thread_run (automatic child) │ ├─ polling: get_thread_run (automatic) │ └─ ... └─ list_messages (automatic child)
Dlaczego wszystko musi być wewnątrz?
- Jeśli
create_thread()jest PRZEDwithblock → będzie sibling, nie child! - Jeśli
list_messages()jest POwithblock → będzie sibling, nie child! - Poprawna hierarchia parent-child = łatwiejsza analiza w trace viewer
- Zasada: Wszystkie API calls związane z tą interakcją = wewnątrz custom spana
Uruchom:
python agent_tracing.py
Oczekiwany output:
▶️ Uruchamianie agenta z tracingiem...
Thread ID: thread_abc123...
Pytanie: Podaj dane firmy KRS 0000028860
Span name: krs-lookup-demo
✓ Agent wykonał zadanie
Run ID: run_xyz789...
Status: completed
💡 Co to jest metadata?
W kodzie powyżej dodaliśmy metadata={"user_id": "workshop_user", ...} do create_and_process_run().
Metadata vs Custom Spans:
- Metadata: Business context (user ID, session, environment) - max 16 key-value pairs
- Custom Spans: Technical operations (krs-lookup-demo) - hierarchia parent-child
Metadata pojawi się w trace jako custom attributes - możesz filtrować traces po user_id lub environment w Application Insights.
Krok 9: Odczyt odpowiedzi i wyświetlenie danych
Dodajmy odczyt odpowiedzi agenta.
⚠️ WAŻNE: list_messages() musi być WEWNĄTRZ custom spana (przed zamknięciem with), bo to API call!
Dodaj na końcu with bloku (przed zamknięciem):
# (... poprzedni kod wewnątrz with block ...)
print("\n📄 Odpowiedź agenta:")
print("-" * 60)
# Pobierz messages z thread (WEWNĄTRZ spana!)
messages = project_client.agents.messages.list(thread_id=thread.id)
# Poza with block - span zamknięty, messages już pobrane
# Znajdź odpowiedź asystenta
for msg in messages:
if msg.role == "assistant" and msg.run_id == run.id:
response = msg.content[0].text.value
print(response[:300]) # Pierwsze 300 znaków
break
print("-" * 60)
Uruchom:
python agent_tracing.py
Oczekiwany output:
📄 Odpowiedź agenta:
------------------------------------------------------------
Oto dane firmy KRS 0000028860:
**Nazwa:** COMP Spółka Akcyjna
**KRS:** 0000028860
**NIP:** 5261040567
**REGON:** 017207559
**Adres:** ul. Legnicka 57, 54-203 Wrocław
...
------------------------------------------------------------
Krok 10: Token usage i tracing summary
Dodajmy statystyki:
print("\n📊 Statystyki wykonania:")
if run.usage:
print(f" Tokeny (input): {run.usage.prompt_tokens:5}")
print(f" Tokeny (output): {run.usage.completion_tokens:5}")
print(f" Tokeny (total): {run.usage.total_tokens:5}")
else:
print(" Tokeny: N/A")
print(f"\n🔍 Gdzie znaleźć traces?")
print(f" 1. AI Foundry Portal:")
print(f" https://ai.azure.com → Twój projekt → Tracing tab")
print(f" Filtruj po: Thread ID = {thread.id}")
print(f" ")
print(f" 2. Azure Portal:")
print(f" Application Insights → Transaction search")
print(f" Szukaj: operation_name = 'krs-lookup-demo'")
print(f" ")
print(f" ⏱️ Traces pojawiają się w portalu po 2-5 minutach")
Uruchom:
python agent_tracing.py
Oczekiwany output:
📊 Statystyki wykonania:
Tokeny (input): 450
Tokeny (output): 1100
Tokeny (total): 1550
🔍 Gdzie znaleźć traces?
1. AI Foundry Portal:
https://ai.azure.com → Twój projekt → Tracing tab
Filtruj po: Thread ID = thread_abc123...
2. Azure Portal:
Application Insights → Transaction search
Szukaj: operation_name = 'krs-lookup-demo'
⏱️ Traces pojawiają się w portalu po 2-5 minutach
Krok 11: Cleanup i custom span demo
Na koniec dodajmy demo custom spana dla własnej funkcji:
print("\n🧪 Demo: Custom span dla własnej funkcji")
def analyze_krs_data(krs_number):
"""Custom function z własnym spanem"""
with tracer.start_as_current_span("analyze_krs_data") as span:
span.set_attribute("krs.number", krs_number)
# Symulacja analizy
print(f" Analizuję KRS: {krs_number}")
# Dodaj wynik do spana
is_valid = len(krs_number) == 10
span.set_attribute("krs.is_valid", is_valid)
return is_valid
# Wywołaj funkcję (zostanie stracowana!)
result = analyze_krs_data("0000028860")
print(f" Wynik: {'✓ Poprawny' if result else '✗ Niepoprawny'}")
print(f" Span 'analyze_krs_data' został dodany do trace!")
# NIE usuwamy thread - traces muszą być wysłane!
print(f"\n💡 Thread ID zachowany: {thread.id}")
print(" (możesz go później usunąć ręcznie w portalu lub przez kod)")
print("\n" + "=" * 60)
print("ZAKOŃCZONO")
print("=" * 60)
print("\nCzekaj 2-5 minut, potem sprawdź traces w portalu:")
print(" → AI Foundry Portal → Tracing tab")
print(" → Filtruj po: Span name = 'krs-lookup-demo'")
print("\nZobaczysz pełną hierarchię:")
print(" krs-lookup-demo")
print(" ├─ agent_run")
print(" │ ├─ model_call")
print(" │ ├─ tool_call: krs_search")
print(" │ └─ model_call")
print(" └─ analyze_krs_data (custom!)")
print("=" * 60)
Uruchom pełny skrypt:
python agent_tracing.py
Pełny output:
✓ Importy załadowane
🔧 Tworzenie klienta...
✓ Klient utworzony
🔌 Pobieranie Application Insights connection string...
✓ Connection string pobrany
(długość: 180 znaków)
📊 Konfiguracja Azure Monitor...
✓ Azure Monitor skonfigurowany
Traces będą wysyłane do Application Insights
🔍 Content recording:
Status: true
✓ Będą logowane:
- User messages (pytania)
- Agent responses (odpowiedzi)
- Tool arguments (np. KRS number)
- Tool results (dane z API)
⚠️ Privacy warning:
- Może zawierać PII
- W produkcji rozważ wyłączenie (false)
🎯 Tworzenie tracer...
✓ Tracer utworzony
Możemy teraz tworzyć custom spans
▶️ Uruchamianie agenta z tracingiem...
Thread ID: thread_abc123...
Pytanie: Podaj dane firmy KRS 0000028860
Span name: krs-lookup-demo
✓ Agent wykonał zadanie
Run ID: run_xyz789...
Status: completed
📄 Odpowiedź agenta:
------------------------------------------------------------
Oto dane firmy KRS 0000028860:
**Nazwa:** COMP Spółka Akcyjna
**KRS:** 0000028860
...
------------------------------------------------------------
📊 Statystyki wykonania:
Tokeny (input): 450
Tokeny (output): 1100
Tokeny (total): 1550
🔍 Gdzie znaleźć traces?
1. AI Foundry Portal:
https://ai.azure.com → Twój projekt → Tracing tab
Filtruj po: Thread ID = thread_abc123...
2. Azure Portal:
Application Insights → Transaction search
Szukaj: operation_name = 'krs-lookup-demo'
⏱️ Traces pojawiają się w portalu po 2-5 minutach
🧪 Demo: Custom span dla własnej funkcji
Analizuję KRS: 0000028860
Wynik: ✓ Poprawny
Span 'analyze_krs_data' został dodany do trace!
🧹 Usuwanie thread...
✓ Thread usunięty
============================================================
ZAKOŃCZONO
============================================================
Czekaj 2-5 minut, potem sprawdź traces w portalu:
→ AI Foundry Portal → Tracing tab
→ Filtruj po: Span name = 'krs-lookup-demo'
Zobaczysz hierarchię:
krs-lookup-demo (custom parent)
├─ create_thread
├─ create_message
├─ start_thread_run
│ ├─ get_thread_run (polling)
│ └─ ...
├─ list_messages
└─ analyze_krs_data (custom!)
============================================================
✅ Skrypt gotowy! Wszystkie kroki od 1 do 10 działają.
Przeglądanie traces w Azure AI Foundry Portal
Po 2-5 minutach traces pojawią się w portalu. Oto jak je znaleźć i analizować.
Krok 1: Otwórz Tracing tab
- Wejdź na ai.azure.com
- Wybierz swój projekt
- W lewym menu: Tracing
Krok 2: Filtrowanie traces
W interfejsie Tracing zobaczysz listę traces. Możesz filtrować po:
Filters dostępne:
- Time range: Last 24 hours, Last 7 days, Custom
- Thread ID: Wklej swój Thread ID z outputu skryptu
- Span name: Np.
krs-lookup-demo - Status: Success, Failed, Running
Przykład:
Filters:
Time range: Last 24 hours
Span name: krs-lookup-demo
Results: 1 trace found
Krok 3: Kliknij na trace
Zobaczysz hierarchię spanów (span waterfall):
krs-lookup-demo [11.5s] ████████████████████
├─ create_thread [0.5s] █
├─ create_message [0.3s]
├─ start_thread_run [10.2s] ███████████████████
│ ├─ get_thread_run (poll) [0.3s]
│ ├─ get_thread_run (poll) [0.3s]
│ └─ ...
├─ list_messages [0.4s] █
└─ analyze_krs_data [0.01s]
Co widzisz:
- Duration bars: Wizualizacja czasu trwania (dłuższe = wolniejsze)
- Parent-child: Indentacja pokazuje zagnieżdżenie (wszystko pod
krs-lookup-demo) - Timeline: Kiedy każdy span się rozpoczął i zakończył
- Polling:
get_thread_runwywołane wielokrotnie (agent czeka na completion)
Krok 4: Inspect span details
Kliknij na start_thread_run span:
Attributes (metadata):
{
"http.method": "POST",
"http.url": "https://agent.services.ai.azure.com/api/projects/...",
"http.status_code": 200,
"span.kind": "client",
"duration_ms": 10200
}
Co widać:
- HTTP POST do Azure AI Foundry Service
- Status 200 OK
- Duration ~10s (agent czeka na completion)
Krok 5: Inspect metadata
W prawym panelu znajdź custom attributes z metadata:
{
"user_id": "workshop_user",
"session_id": "demo_session_001",
"environment": "development",
"demo.thread_id": "thread_M9AEZ...",
"demo.run_id": "run_xyz...",
"demo.question": "Podaj dane firmy KRS 0000028860"
}
Metadata jest queryable! Możesz filtrować traces po user_id lub environment w Application Insights.
Krok 6: End-to-end analysis
Spojrzyj na cały trace i analizuj:
Performance:
- 🐌 Najwolniejszy span:
start_thread_run(10.2s) → agent execution (server-side) - ⚡ Najszybsze spany:
create_thread,create_message(~0.3s) → API calls - 📊 Custom span:
analyze_krs_data(0.01s) → local computation
Reliability:
- ✅ Status: All spans succeeded
- ⏱️ Total duration: 11.1s (większość to agent execution)
- 🔁 Polling: Wielokrotne
get_thread_run(checking completion status)
Optimization ideas:
- Async mode zamiast
create_and_process_run()(nie blokuj na polling) - Reuse threads dla tego samego użytkownika (zamiast tworzyć nowe)
Analiza w Application Insights (Azure Portal)
AI Foundry Portal jest super dla ad-hoc debugging, ale dla production monitoring potrzebujesz Application Insights dashboards.
Krok 1: Otwórz Application Insights
- Wejdź na portal.azure.com
- Resource Group →
appi-{company}-user{N}-{suffix} - Kliknij na resource
Krok 2: Transaction search
- W lewym menu: Transaction search (pod “Investigate”)
- Filters:
- Event types: Select “Dependency” (tool calls), “Request” (agent runs)
- Time range: Last 30 minutes
- Custom filters:
operation_name = 'krs-lookup-demo'
-
Zobaczysz listę transactions
- Kliknij na transaction → End-to-end transaction details (taki sam waterfall jak w AI Foundry!)
Krok 3: Zaawansowana analiza (opcjonalnie)
Application Insights oferuje zaawansowane narzędzia:
Logs (Log Analytics):
- KQL queries do analizy tool statistics, token usage, failed runs
- Filtrowanie i agregacje po custom attributes
Dashboards:
- Custom dashboards z token usage over time
- Tool call latency charts
- Success rate monitoring
Alerting:
- Automatic notifications dla anomalii (high latency, failed runs)
- Thresholds dla kosztów (token usage limits)
Uwaga: Zaawansowana analiza wykracza poza ten lab. Skupiamy się na podstawach tracingu w UI.
Content Recording: TRUE vs FALSE
Porównajmy co jest tracowane w obu trybach.
Test: Content recording = TRUE
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
Span model_call attributes:
{
"gen_ai.system": "azure_ai_agents",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": 150,
"gen_ai.usage.output_tokens": 80,
// ✅ CONTENT (bo TRUE):
"gen_ai.prompt": "You are a helpful assistant with access to KRS database. User: Podaj dane KRS 0000028860",
"gen_ai.completion": "I'll search for that KRS number using the krs_search tool."
}
Span tool_call attributes:
{
"tool.name": "krs_search",
// ✅ ARGUMENTS & RESULTS (bo TRUE):
"tool.arguments": "{\"krs\": \"0000028860\"}",
"tool.result": "{\"nazwa\": \"COMP S.A.\", \"krs\": \"0000028860\", \"nip\": \"5261040567\", ...}"
}
Zalety:
- ✅ Widzisz dokładnie co agent powiedział i co dostał z narzędzi
- ✅ Debugging jest łatwy (nie musisz zgadywać)
- ✅ Możesz zreprodukować issue (masz pełny context)
Wady:
- ❌ Privacy risk: Messages mogą zawierać PII
- ❌ Storage cost: Więcej danych w Application Insights
- ❌ Compliance: GDPR może wymagać redaction
Test: Content recording = FALSE
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "false"
Span model_call attributes:
{
"gen_ai.system": "azure_ai_agents",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": 150,
"gen_ai.usage.output_tokens": 80,
// ❌ NO CONTENT (bo FALSE):
// "gen_ai.prompt" - BRAK
// "gen_ai.completion" - BRAK
}
Span tool_call attributes:
{
"tool.name": "krs_search",
// ❌ NO ARGUMENTS & RESULTS (bo FALSE):
// "tool.arguments" - BRAK
// "tool.result" - BRAK
}
Zalety:
- ✅ Privacy safe: Brak PII w traces
- ✅ Compliance: GDPR/SOC2 friendly
- ✅ Storage cost: Mniej danych
Wady:
- ❌ Debugging trudniejszy: Nie widzisz co agent powiedział
- ❌ Musisz zgadywać: “Dlaczego agent wywołał to narzędzie?”
Rekomendacja
Development/Testing:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
- Potrzebujesz pełnej widoczności
- Privacy nie jest concern (test data)
Production:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "false"
- Privacy i compliance są krytyczne
- Metadata (tokeny, latencja, tool names) wystarczają do monitoringu
- Dla debugging konkretnych issues, możesz temporary włączyć content recording
Hybrid approach:
# Włącz tylko dla specific users/sessions
if user.is_test_user or session.debug_mode:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
else:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "false"
Kluczowe koncepty
1. Traces = Journey of a request
Trace to pełna ścieżka żądania przez system.
W kontekście agentów:
User question → Agent orchestration → Model calls → Tool calls → Final response
Każdy trace ma:
- Trace ID: Unikalne ID (np.
abc123...) - Root span: Top-level operation (np.
krs-lookup-demo) - Child spans: Nested operations (model calls, tool calls)
Distributed tracing: Jeśli twój agent wywołuje inny serwis (np. microservice, external API), trace może span across services:
Trace ID: abc123
├─ Service A: Agent (AI Foundry)
│ └─ tool_call: webhook
│ └─ HTTP POST https://myservice.com/webhook
└─ Service B: Webhook handler (twój serwis)
├─ process_webhook
└─ database_query
Ten sam Trace ID propaguje się między serwisami (via HTTP headers: traceparent).
2. Spans = Building blocks
Span to pojedyncza operacja w trace.
Span lifecycle:
- Start:
tracer.start_as_current_span("operation_name") - Execution: Twój kod wykonuje operację
- Set attributes:
span.set_attribute("key", value) - Log events:
span.add_event("something_happened") - End: Span automatycznie kończy się gdy wychodzisz z
withblock
Span hierarchy:
with tracer.start_as_current_span("parent"):
# Parent span
with tracer.start_as_current_span("child1"):
# Child span 1
pass
with tracer.start_as_current_span("child2"):
# Child span 2
pass
Output w traces:
parent
├─ child1
└─ child2
3. Attributes = Metadata
Attributes to key-value pairs na spanie.
Standard attributes (OpenTelemetry semantic conventions):
# HTTP attributes
span.set_attribute("http.method", "GET")
span.set_attribute("http.url", "https://api.example.com/data")
span.set_attribute("http.status_code", 200)
# Database attributes
span.set_attribute("db.system", "postgresql")
span.set_attribute("db.statement", "SELECT * FROM users")
# GenAI attributes (new!)
span.set_attribute("gen_ai.system", "azure_ai_agents")
span.set_attribute("gen_ai.request.model", "gpt-4o")
span.set_attribute("gen_ai.usage.input_tokens", 150)
Custom attributes:
# Business logic metadata
span.set_attribute("user.id", "user123")
span.set_attribute("session.id", "session456")
span.set_attribute("feature.flag", "new_ui_enabled")
Dlaczego attributes są ważne?
- Możesz filtrować traces po attributes w portalu
- Możesz query traces w Log Analytics po attributes
- Możesz group by attributes w dashboardach
4. OpenTelemetry Semantic Conventions dla Multi-Agent
Microsoft rozszerza OpenTelemetry o semantic conventions dla agentów.
Nowe span kinds:
execute_task- Task planning spanagent_to_agent_interaction- Delegacja między agentamiagent.state.management- Memory/state operationsagent_planning- Planning/reasoning steps
Nowe attributes:
agent.name- Nazwa agenta (np. “orchestrator”, “krs-agent”)agent.role- Rola (np. “coordinator”, “specialist”)agent.state- Stan agenta (np. “idle”, “thinking”, “acting”)tool.name- Nazwa narzędziatool.arguments- Argumenty (jeśli content recording)tool.result- Wynik (jeśli content recording)
Przykład multi-agent trace:
orchestrator_agent (span)
├─ agent_planning (span: "decide which sub-agent to call")
├─ agent_to_agent_interaction (span: "delegate to krs-agent")
│ └─ krs_agent (span)
│ ├─ tool_call: krs_search
│ └─ model_call
└─ model_call (span: "synthesize final answer")
Attributes na agent_to_agent_interaction span:
{
"agent.source": "orchestrator",
"agent.target": "krs-agent",
"agent.delegation.task": "Retrieve KRS data for 0000028860",
"agent.delegation.result": "success"
}
5. Privacy Best Practices
Redact secrets w attributes:
# ❌ ZŁE (loguje secret!)
span.set_attribute("api.key", "sk-abc123...")
# ✅ DOBRE (redact)
span.set_attribute("api.key", "sk-***")
Redact PII w tool arguments:
# Jeśli content recording = true, ale tool ma PII:
import re
def redact_email(text):
return re.sub(r'\S+@\S+', '***@***', text)
# W tool function:
email = "user@example.com"
span.set_attribute("tool.arguments.email", redact_email(email))
Conditional content recording:
# Tylko dla test users
if not user.is_production:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
Data retention: W Application Insights, ustaw retention na 30 dni (default 90):
- Azure Portal → App Insights → Usage and estimated costs → Data Retention
- Shorter retention = less privacy risk + lower cost
Troubleshooting
Traces nie pojawiają się w portalu
Najczęstsze przyczyny:
- Czekaj 2-5 minut - traces są wysyłane asynchronicznie
- Application Insights nie połączony - sprawdź w AI Foundry Portal → Tracing tab
- Brak
configure_azure_monitor()- upewnij się że wywołałeś przed uruchomieniem agenta
Debug:
# Sprawdź connection string
connection_string = project_client.telemetry.get_application_insights_connection_string()
print(f"Connection string: {connection_string[:50]}...")
# Powinno zawierać: InstrumentationKey=...;IngestionEndpoint=...
Content nie jest logowany (mimo true)
Najczęstsze przyczyny:
- Zmienna ustawiona za późno - musisz ustawić PRZED importem:
import os
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
# POTEM dopiero:
from azure.ai.projects import AIProjectClient
- Typo w nazwie - copy-paste poprawną nazwę:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"
High storage costs
Rozwiązania:
- Wyłącz content recording w production:
"false"zamiast"true" - Zmniejsz retention: Azure Portal → App Insights → Data Retention → 30 days
- Daily cap: Usage and estimated costs → Daily cap → 1 GB/day
Podsumowanie
Czego się nauczyłeś?
✅ Tracing fundamentals
- Czym są traces, spans, attributes
- OpenTelemetry standard
- Azure Monitor integration
✅ Setup tracingu
- Połączenie Application Insights w portalu
- Programatyczne pobranie connection string
- Konfiguracja
configure_azure_monitor()
✅ Content recording
AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED(true/false)- Privacy considerations
- Kiedy włączyć, kiedy wyłączyć
✅ Custom instrumentation
- Tworzenie custom spanów (
tracer.start_as_current_span()) - Dodawanie attributes (
span.set_attribute()) - Span hierarchy (parent-child)
✅ Viewing traces
- AI Foundry Portal → Tracing tab (ad-hoc debugging)
- Azure Portal → Application Insights (production monitoring)
- Log Analytics queries (KQL)
✅ Analysis
- Tool call latency
- Token usage per operation
- Cost estimation
- Performance bottlenecks
✅ Best practices
- Redact secrets w attributes
- Conditional content recording
- Data retention policies
- Sampling dla high volume
Key Takeaways
🔑 Tracing jest krytyczny dla production agents
- Multi-step executions są niewidoczne bez tracingu
- Debugging “why agent did X?” wymaga traces
- Cost monitoring (tokeny) jest niemożliwy bez traces
🔑 Content recording = trade-off
- TRUE = pełna widoczność, ale privacy risk
- FALSE = bezpieczne, ale trudniejszy debugging
- Choose wisely based on environment
🔑 OpenTelemetry = standard
- Nie tylko Azure (OTLP exporter działa z Datadog, New Relic, etc.)
- Semantic conventions dla multi-agent (future-proof)
- Custom instrumentation = flexibility
🔑 Application Insights = powerful
- Transaction search dla ad-hoc debugging
- Log Analytics (KQL) dla advanced queries
- Dashboards dla production monitoring
- Alerting dla critical issues (np. failed runs)
Next Steps
Lab 11 (jeśli istnieje) może cover:
- Evaluations - jak mierzyć quality agenta (accuracy, relevance)
- A/B testing - porównywanie różnych promptów/modeli
- Alerting - automatic notifications dla failed runs, high latency
- Custom metrics - business KPIs (np. user satisfaction score)
Dalsza praktyka:
- Stwórz dashboard w Application Insights z key metrics dla swojego agenta
- Ustaw alerts dla anomalii (np. latency > 10s, failed runs)
- Dodaj custom spans dla wszystkich business functions
- Eksperymentuj z sampling ratio dla high-volume scenarios
Gratulacje! Znasz teraz observability dla Azure AI Agents! 🎉