OpenTelemetry za LLM aplikacije u 2026: distribuirano praćenje s GenAI semantičkim konvencijama

Otvoreni standard za praćenje LLM aplikacija: GenAI semantičke konvencije, OpenLLMetry, tail sampling i produkcijski dashboardi s Python primjerima.

Ažurirano: 15. kolovoza 2026.

OpenTelemetry za LLM aplikacije je otvoreni standard za instrumentaciju, prikupljanje i izvoz telemetrijskih podataka (traceova, metrika i logova) iz aplikacija koje pozivaju velike jezične modele, gdje se svaki poziv modelu bilježi kao raspon (span) sa standardiziranim gen_ai.* atributima. U 2026. su GenAI Semantic Conventions konačno prešle iz experimental u stable, što znači da se atributi poput gen_ai.request.model ili gen_ai.usage.output_tokens mogu koristiti u dashboardima bez straha od preimenovanja. Ovaj vodič pokazuje kako to postaviti u produkciji, na temelju grabljima po kojima sam prešao gradeći ovakav stack za dva klijenta u posljednjih šest mjeseci.

  • GenAI Semantic Conventions v1.30+ (siječanj 2026.) stabiliziraju gen_ai.system, gen_ai.request.model, gen_ai.usage.* i gen_ai.operation.name, pa se dashboardi izgrađeni na tim ključevima više ne lome pri nadogradnji SDK-a.
  • Za brzu instrumentaciju bez ručnog pisanja spanova, OpenLLMetry (Traceloop) i OpenInference (Arize) pokrivaju OpenAI, Anthropic, Google Vertex, Bedrock, LangChain, LlamaIndex i CrewAI.
  • Head-based sampling po korisniku/tenantu daje čitljive traceove; tail-based sampling zadržava spore i skupe pozive. Kombinirajte oboje kroz OpenTelemetry Collector.
  • Izbor backend-a: Langfuse za produktne metrike i evaluaciju, Arize Phoenix za istraživanje traceova, Grafana Tempo za povezivanje s postojećom Grafana infrastrukturom.
  • Prvi dashboard koji biste trebali izgraditi: p95/p99 latencija po modelu, operaciji i tenantu, s podijeljenim „time to first token" i „time to completion". Otkriva 80% incidenata prije nego što korisnici prijave.

Što je OpenTelemetry za LLM aplikacije?

OpenTelemetry (OTel) je proizvođački-neutralni okvir za promatranje sustava koji definira API, SDK i žični protokol (OTLP) za slanje traceova, metrika i logova bilo kojem backend-u koji ih razumije. Za LLM aplikaciju to znači da se svaki poziv modelu, bez obzira ide li kroz OpenAI, Anthropic, Bedrock ili lokalni vLLM, bilježi kao span s dogovorenim skupom atributa. Kada agent poziva alat koji poziva drugi model, spanovi se povezuju u trace koji vidite kao vodopadni prikaz.

U 2024. i početkom 2025. ovo je bilo bolno. Svaka biblioteka je imala svoje ime za „model", „prompt tokens" i „latency to first token", pa je izgradnja dashboardova značila napisati sloj mapiranja koji se raspada svaki drugi tjedan. U siječnju 2026. su OpenTelemetry GenAI Semantic Conventions prešle u status stable, što znači tri stvari: imena atributa se više ne mijenjaju bez major verzije, backend-i garantiraju podršku, a vaši upiti u Grafani ili Langfuseu neće se raspasti pri sljedećoj nadogradnji SDK-a.

Zašto vam ovo treba iako već imate logove? Log linija „poziv modelu je trajao 2.4s" nema kontekst. Trace vam kaže koji korisnik, u kojem agentskom koraku, s kojim promptom, koristeći koji model, i koliko je od tih 2.4s bio mrežni time to first token, a koliko sam generation time. Bez toga, debug produkcijskog incidenta znači postavljati logove u proizvodnju, čekati da se problem ponovi, pa ponoviti sve. S traceovima, otvorite jedan zahtjev i vidite cijeli put.

GenAI semantičke konvencije u 2026.

Semantičke konvencije su ugovor između biblioteke koja emitira telemetriju i backend-a koji je prikazuje. Za GenAI, stabilizirani skup u 2026. uključuje sljedeće ključne atribute na svakom LLM spanu:

  • gen_ai.system: vrijednosti poput openai, anthropic, aws.bedrock, vertex_ai, azure.ai.openai.
  • gen_ai.operation.name: chat, text_completion, embeddings, execute_tool.
  • gen_ai.request.model: traženi model (npr. gpt-4.1, claude-opus-4-7).
  • gen_ai.response.model: model koji je zapravo poslužio (bitno kod aliasa i A/B testova).
  • gen_ai.usage.input_tokens, gen_ai.usage.output_tokens i noviji gen_ai.usage.cached_input_tokens. Ovaj potonji je dodan u v1.29 za praćenje uštede od prompt caching-a.
  • gen_ai.response.finish_reasons: polje razloga završetka po odabiru (stop, length, tool_calls, content_filter).
  • gen_ai.request.temperature, gen_ai.request.top_p, gen_ai.request.max_tokens: parametri poziva.

Uz spanove, konvencije definiraju i metrike: gen_ai.client.token.usage (histogram), gen_ai.client.operation.duration (histogram) i gen_ai.server.time_to_first_token za streaming odgovore. Metrike su jeftinije od spanova jer se agregiraju na strani klijenta, pa se vaš „koliko tokena mjesečno po tenantu" dashboard može temeljiti isključivo na njima, bez pretraživanja miliona spanova.

Postavljanje OpenTelemetry za OpenAI i Claude pozive

Minimalno postavljanje u Pythonu izgleda ovako. Instalirajte core SDK i OTLP exporter, pa jednu od auto-instrumentacijskih biblioteka za vašeg LLM providera.

# requirements.txt
opentelemetry-api==1.31.0
opentelemetry-sdk==1.31.0
opentelemetry-exporter-otlp-proto-http==1.31.0
opentelemetry-instrumentation-openai-v2==2.4.0
opentelemetry-instrumentation-anthropic==0.44.0

Zatim inicijalizirajte tracer provider jednom, pri pokretanju aplikacije:

# otel_setup.py
import os
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
    OTLPSpanExporter,
)
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor


def setup_tracing(service_name: str) -> None:
    resource = Resource.create(
        {
            "service.name": service_name,
            "service.version": os.getenv("APP_VERSION", "dev"),
            "deployment.environment": os.getenv("ENV", "development"),
        }
    )
    provider = TracerProvider(resource=resource)
    exporter = OTLPSpanExporter(
        endpoint=os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] + "/v1/traces",
        headers={"authorization": f"Bearer {os.environ['OTEL_EXPORTER_OTLP_HEADERS_TOKEN']}"},
    )
    # BatchSpanProcessor grupira spanove i šalje ih u pozadini. Nemojte koristiti
    # SimpleSpanProcessor u produkciji, blokira svaki poziv modela za mrežni I/O.
    provider.add_span_processor(BatchSpanProcessor(exporter, max_queue_size=4096))
    trace.set_tracer_provider(provider)

    # Auto-instrumentacija zamjenjuje klase klijenata pri uvozu.
    OpenAIInstrumentor().instrument()
    AnthropicInstrumentor().instrument()

Nakon toga, obični pozivi klijenata automatski proizvode spanove:

# app.py
from otel_setup import setup_tracing
setup_tracing("checkout-assistant")

from openai import OpenAI
from anthropic import Anthropic

openai_client = OpenAI()
anthropic_client = Anthropic()

# Ovaj poziv proizvodi span s gen_ai.system=openai,
# gen_ai.request.model=gpt-4.1, gen_ai.usage.input_tokens=... itd.
resp = openai_client.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Sažmi zadnji sastanak."}],
    temperature=0.2,
)

# Isto vrijedi za Anthropic, span dobiva gen_ai.system=anthropic.
resp = anthropic_client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Sažmi zadnji sastanak."}],
)

Instrumentacija s OpenLLMetry i OpenInference

Iako OpenTelemetry projekt sada održava vlastite instrumentacije za OpenAI i Anthropic, postoje dvije šire biblioteke koje pokrivaju znatno više okvira i imaju često dublju integraciju:

  • OpenLLMetry (Traceloop): jedan paket traceloop-sdk koji instrumentira OpenAI, Anthropic, Cohere, Bedrock, Vertex, Ollama, LangChain, LlamaIndex, Haystack, CrewAI i pretraživače vektora (Pinecone, Chroma, Qdrant, Weaviate). Emitira standardne gen_ai.* atribute plus proširenja traceloop.*.
  • OpenInference (Arize): modularniji, po-provideru paketi (openinference-instrumentation-openai, ...-anthropic, ...-langchain, ...-dspy, itd.). Naglašava hijerarhijski prikaz agenata (LLM span, tool span, retriever span) što se izvrsno vizualizira u Arize Phoenix.

Za većinu ekipa savjetujem sljedeće: krenite s OpenLLMetry ako želite jedan pip install, ili s OpenInference ako radite s više okvira (LangChain + DSPy + prilagođeni klijenti) i želite fino-zrnatu kontrolu. Oba emitiraju validne OTLP spanove pa možete promijeniti backend bez ponovnog pisanja instrumentacije. Ako već imate LiteLLM proxy kao LLM gateway, dobra vijest: LiteLLM od v1.60 emitira OTel spanove izravno iz proxy sloja. U tom slučaju možda ne trebate instrumentaciju u samoj aplikaciji, već samo propagirati trace kontekst kroz HTTP zaglavlja.

Izbor backend-a: Langfuse, Arize Phoenix, Grafana Tempo

OTel je standard, pa možete slati na bilo koji OTLP-kompatibilni backend. Za LLM traceove tri kandidata dominiraju u 2026., svaki s drugim naglaskom:

Značajka Langfuse Arize Phoenix Grafana Tempo
Primarni slučaj Produktne metrike, evaluacija, feedback Debug agenata, istraživanje traceova Ujedinjeno praćenje s ostatkom infrastrukture
Deployment Self-hosted (Docker/K8s) ili cloud Self-hosted, lokalno ili u kontejneru Self-hosted (K8s), dio Grafana LGTM stack-a
Native OTel prijem OTLP HTTP endpoint od v3.0 (2025.) OTLP HTTP/gRPC, stabilno od 5.x OTLP HTTP/gRPC, uvijek bio native
Evaluacija i feedback Ugrađeno (LLM-as-judge, ocjene korisnika) Ugrađeno (Phoenix Evals) Ne, samo prikaz traceova
Skalabilnost pohrane ClickHouse backend od v3, milijardi spanova DuckDB/Postgres, srednja skala Objektna pohrana (S3/GCS), tehnički neograničeno
Cijena samohostiranja Srednja (ClickHouse operativno) Niska (jedan proces za start) Visoka (Grafana + Tempo + kolektor)
Najbolje za Produktne timove, product managere ML/AI inženjere, istraživanje Platform ekipe s postojećim Grafana stack-om

U praksi mnoge ekipe voze dva backend-a paralelno: Grafana Tempo za sistemsku observabilnost (povezuje se s Prometheusom i Lokijem preko trace ID-a), a Langfuse ili Phoenix za LLM-specifične poglede: sadržaj promptova, evaluacije, korisnički feedback. OpenTelemetry Collector to čini trivijalnim, dodate dva exportera i isti trace ide na oba mjesta. Isti pristup smo detaljno opisali u vodiču za eval cjevovode s Langfuseom, koji nadograđuje ono što ovdje pokrivamo.

Distribuirano praćenje kroz agente i alate

Agentski sustav rijetko je jedan proces. Tipično imate: orkestratorski servis koji prima zahtjev, planer agent koji odlučuje o alatima, više tool servisa (svaki može sam pozivati LLM), a možda i drugi agent kao alat (A2A protokol). Distribuirano praćenje spaja sve to u jedan trace ako se pravilno propagira trace kontekst.

Standard za HTTP propagaciju je W3C Trace Context: dva zaglavlja, traceparent i tracestate. OpenTelemetry ih injektira i ekstraktira automatski ako koristite instrumentirani HTTP klijent:

# Servis A (orkestrator), automatska propagacija s requests instrumentacijom
from opentelemetry.instrumentation.requests import RequestsInstrumentor
RequestsInstrumentor().instrument()

import requests
# Ova zaglavlja se automatski dodaju: traceparent, tracestate
resp = requests.post("http://tool-service/search", json={"q": "..."})

# Servis B (tool), automatska ekstrakcija u FastAPI-ju
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from fastapi import FastAPI
app = FastAPI()
FastAPIInstrumentor.instrument_app(app)
# Svaki span unutar rute nastavlja isti trace ID kao orkestrator.

Za agente koji komuniciraju porukama (Redis Streams, Kafka, gRPC), morate ručno serijalizirati kontekst u payload i deserijalizirati ga na drugoj strani. OTel to podržava kroz TraceContextTextMapPropagator. Ako radite s MCP serverima u Pythonu, MCP referenca za spec od siječnja 2026. uključuje polje _meta.traceparent upravo za tu svrhu; provjerite da vaš MCP klijent to popunjava.

Za agent-to-agent pozive (A2A protokol), propagacija je već ugrađena u referenced Python SDK. Svaki A2A zahtjev automatski nosi trace kontekst pa vidite jedan trace koji obuhvaća planera, izvršitelja i alate, bez obzira na to je li pola sustava napisano u Pythonu, a druga polovica u TypeScriptu.

Strategije uzorkovanja i kontrola troška

Ako emitirate span za svaki LLM poziv na produkcijskom prometu, brzo generirate desetke gigabajta traceova dnevno. Većina backend-a naplaćuje po volumenu, a i sam mrežni izvoz troši CPU. Rješenje je uzorkovanje, pametno, ne slijepo.

Head-based sampling

Odluka o zadržavanju traceova donosi se na početku, pri stvaranju root spana. Prednost: jeftino, deterministički. Nedostatak: možete izgubiti pojedinačne spore zahtjeve. Klasična konfiguracija koristi ParentBased(TraceIdRatioBased(0.1)), dakle 10% svih traceova, ali djeca uvijek prate roditelja pa je trace uvijek cjelovit. Za multi-tenant SaaS, prilagodite: 100% za internu ekipu i besplatni tier za debug, 5% za enterprise (gdje je promet velik, ali tipičan).

Tail-based sampling

Odluka se donosi nakon što je cijeli trace završio, u OpenTelemetry Collectoru. Skuplje (svaki trace se drži u memoriji kolektora ~30 sekundi), ali dopušta pravila poput „zadrži sve traceove s trajanjem > p95" ili „zadrži sve s gen_ai.response.finish_reasons=content_filter". Ovo je jedini način da pouzdano uhvatite rijetke, ali skupe kvarove.

# otel-collector-config.yaml (fragment)
processors:
  tail_sampling:
    decision_wait: 30s
    num_traces: 50000
    policies:
      - name: errors
        type: status_code
        status_code: { status_codes: [ERROR] }
      - name: slow
        type: latency
        latency: { threshold_ms: 5000 }
      - name: expensive
        type: numeric_attribute
        numeric_attribute:
          key: gen_ai.usage.output_tokens
          min_value: 4000
      - name: baseline
        type: probabilistic
        probabilistic: { sampling_percentage: 5 }

Kombinacija koju preporučujem u produkciji: head-based 100% u razvoju, head-based 20% u produkciji za standardnu vidljivost, plus tail-based koji na vrhu zadržava sve greške, sve traceove sporije od p95, i sve pozive s izlazom > 4000 tokena. To vam daje sam podatkovni volumen uz manje od 5% troška pune telemetrije.

Prvi dashboardi koje biste trebali izgraditi

Traceovi su neprocjenjivi za debug pojedinačnog incidenta, ali za dnevni operativni pregled trebate metrike i unaprijed pripremljene poglede. Evo prioritizirane liste koju smo tijekom vremena naučili graditi:

  1. Latencija po modelu, operaciji i tenantu. Heatmap p50/p95/p99 s prekidnicama. Kad p99 skoči, već znate ide li o jednom modelu, jednoj operaciji, ili jednom klijentu.
  2. Time to first token (TTFT) za streaming. Mjeri se posebno od ukupnog trajanja. Skok TTFT-a bez skoka ukupnog trajanja znači usporavanje na strani providera, ne u vašem kodu.
  3. Utrošak tokena po tenantu, danu i modelu. Sirovi input i output tokeni plus cached_input_tokens. Ovo je i budžetski i tehnički dashboard: pokazuje ROI prompt cachinga i identificira runaway agente.
  4. Stopa grešaka po tipu. Razlikujte 429 rate limit, 5xx, timeout, content_filter. Svaki traži drugu reakciju (backoff, failover, dizajn prompta).
  5. Fallback stopa. Kada primarni model padne pa idete na sekundarni, to je crvena zastavica. Sirov postotak zahtjeva koji su uspjeli tek na drugom pokušaju. Ako pređe 1%, primarni provider ima problem.
  6. Distribucija finish_reasons. Porast length znači da su korisnički prompti dulji od max_tokens (loše korisničko iskustvo), porast content_filter znači jailbreak pokušaje ili preosjetljivi filter.
  7. Trošak po zahtjevu, po značajki. Spojite gen_ai.usage.* s cjenicima providera u derivenoj metrici llm.cost_usd. Onda podijelite po značajki (dodajte feature atribut na spanovima).

Ne pokušavajte izgraditi svih sedam prvi dan. Napravite prvo latenciju po modelu (br. 1) i stopu grešaka (br. 4); pokrivaju otprilike 80% incidenata. Ostatak dodajte kad zatreba. Za produkcijski agentski sustav vrijedi i vodič za orkestraciju višeagentnih sustava, gdje smo pokazali kako iste ove metrike koristiti kao ulaz u odluke o rutiranju.

Česta pitanja

Koja je razlika između OpenLLMetry i Langfusea?

OpenLLMetry je instrumentacijska biblioteka koja generira OpenTelemetry spanove iz vaše aplikacije. Langfuse je backend koji te spanove prima, pohranjuje i prikazuje. Koristite ih zajedno: OpenLLMetry emitira, Langfuse prikazuje. Alternativno, Langfuse ima i vlastiti native SDK koji ne prolazi kroz OTel, ali OTel put je preporučen jer možete isti stream slati na više backend-a.

Utječe li OpenTelemetry instrumentacija na latenciju LLM poziva?

Uz BatchSpanProcessor, dodavanje spana na aplikacijskoj strani je manje od 100 mikrosekundi, praktički nemjerljivo naspram 500-2500 ms tipičnog LLM poziva. Mrežni izvoz ide asinkrono u pozadinskoj niti pa ne blokira zahtjev. Ako koristite SimpleSpanProcessor, on blokira svaki poziv za mrežni I/O; nikad ga ne koristite u produkciji.

Kako uhvatiti sadržaj promptova i odgovora u traceovima?

Postavite varijablu okoline OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true. Sadržaj se tada emitira kao odvojeni log record povezan s spanom preko trace ID-a, ne kao atribut na spanu. Budite oprezni: prompti često sadrže PII, pa razmislite o odvojenoj pipeline za taj stream s kraćim retentionom i strožom kontrolom pristupa.

Podržava li OpenTelemetry praćenje troška po LLM pozivu?

Direktno ne, konvencije opisuju samo tokene (gen_ai.usage.input_tokens, gen_ai.usage.output_tokens). Trošak se izračunava u backend-u ili kolektoru množenjem tokena s cjenikom providera. Većina backend-a (Langfuse, Phoenix, Helicone) imaju ugrađene tablice cijena i derivirane metrike gen_ai.cost. Za prilagođene modele (fine-tuned, self-hosted) morate sami dodati mapiranje.

Trebam li i traceove i metrike ili samo jedno?

Trebate oboje, ali radi različitih razloga. Metrike (histogrami, brojači) daju pregled u realnom vremenu za alerting i dashboardove; jeftine su za pohranu. Traceovi daju debug pojedinačnog zahtjeva i uzročnu analizu; skupi su, ali neprocjenjivi kad se nešto pokvari. Uzorkujte traceove agresivno (5-20%), a metrike držite 100%.

Cara Donovan
O Autoru Cara Donovan

AI operations lead at a B2B SaaS. Builds the unglamorous infrastructure that keeps prod LLM apps from melting.