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

  1. Wejdź na ai.azure.com
  2. Wybierz swój projekt
  3. W lewym menu: Tracing

Krok 2: Connect Application Insights

  1. Kliknij “Connect” (lub “Connect Application Insights”)
  2. Wybierz z listy swój resource: appi-{company}-user{N}-{suffix}
  3. 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:

  1. Pobierze connection string z Application Insights
  2. Skonfiguruje Azure Monitor exporter
  3. Włączy content recording
  4. Uruchomi agenta z tracingiem
  5. 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 Insights
  • opentelemetry-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 Insights
  • opentelemetry.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?

  1. tracer.start_as_current_span("krs-lookup-demo") - tworzy custom span (parent)
  2. WSZYSTKO wewnątrz with block jest child tego spana:
    • create_thread() - automatyczny span dla thread creation
    • create_message() - automatyczny span dla message creation
    • create_and_process_run() - automatyczny span dla agent run
    • list_messages() - automatyczny span dla pobierania messages
  3. 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 PRZED with block → będzie sibling, nie child!
  • Jeśli list_messages() jest PO with block → 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

  1. Wejdź na ai.azure.com
  2. Wybierz swój projekt
  3. 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_run wywoł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

  1. Wejdź na portal.azure.com
  2. Resource Group → appi-{company}-user{N}-{suffix}
  3. Kliknij na resource
  1. W lewym menu: Transaction search (pod “Investigate”)
  2. Filters:
    • Event types: Select “Dependency” (tool calls), “Request” (agent runs)
    • Time range: Last 30 minutes
    • Custom filters: operation_name = 'krs-lookup-demo'
  3. Zobaczysz listę transactions

  4. 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:

  1. Start: tracer.start_as_current_span("operation_name")
  2. Execution: Twój kod wykonuje operację
  3. Set attributes: span.set_attribute("key", value)
  4. Log events: span.add_event("something_happened")
  5. End: Span automatycznie kończy się gdy wychodzisz z with block

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 span
  • agent_to_agent_interaction - Delegacja między agentami
  • agent.state.management - Memory/state operations
  • agent_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ędzia
  • tool.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:

  1. Czekaj 2-5 minut - traces są wysyłane asynchronicznie
  2. Application Insights nie połączony - sprawdź w AI Foundry Portal → Tracing tab
  3. 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:

  1. 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
  1. Typo w nazwie - copy-paste poprawną nazwę:
os.environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true"

High storage costs

Rozwiązania:

  1. Wyłącz content recording w production: "false" zamiast "true"
  2. Zmniejsz retention: Azure Portal → App Insights → Data Retention → 30 days
  3. 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! 🎉

results matching ""

    No results matching ""