Operasyon
Telemetry & Trace
Luma her LLM isteğini, her tool çağrısını, her agent method'unu, her query'yi, her HTTP çağrısını ve her doğrulama kuralını ayrı bir event olarak kaydeder. Bu, bir çıkarımın neden başarısız olduğunu tahmin etmek yerine okuyabilmeni sağlar.
Event modeli
Her event aynı temel şekle sahiptir:
{
"type": "tool",
"name": "hesapla",
"start": 1785242769011,
"durationMs": 1,
"ok": true,
"error": null,
"metadata": { "cached": false }
}type | name ne olur | Ne zaman üretilir |
|---|---|---|
llm | Model kimliği (text-model-l) | Her provider isteği için — cache isabetleri dahil. |
tool | Tool adı | Her tool çalıştırması için — direct ve native fark etmez. |
agent | Agent.method | Her agent method çağrısı tamamlandığında. |
validation | Doğrulanan alan yolu | Her doğrulama kuralı için ayrı ayrı — başarılı olanlar dahil. |
http | http.<fn> (http.get, http.postJson …) | Her std/http çağrısı için — başarısız olanlar dahil. |
query | Query adı | Bir query döndüğünde. type, bildirim türünün kendisidir. |
command | Command adı | Bir command döndüğünde — aynı kural, kendi type değeri. |
parallel_over | Sonuç değişkeninin adı | Yalnızca on_error continue en az bir elemanı atladığında. |
error | Ölen operasyon | v0.9.0. Düşen bir koşuda, o ana kadar toplanan olayların ardına yazılan tek sentetik nihai olay. Diğer her olay gibi ok: false taşır. |
Dört değil, sekiz type değeri var. http, query, command ve parallel_over v0.4.0'ın veri yüzeyiyle geldi; dolayısıyla llm|tool|agent|validation üzerine yazılmış her filtre — v0.4.0 öncesinde yayımlanmış her tarif dahil — bu yüzeyin tamamını rapordan sessizce düşürür. Bildirimlerin kendisi Veri & Entegrasyon sayfasında; bu sayfa onların telemetry görünümünün sahibidir.
Her alan, her kural ve her array elemanı için bir event üretilir. Karmaşık bir schema'da tek bir ask yüzlerce validation event'i üretebilir. Bu bilinçlidir: hangi kuralın çalıştığını ve hangisinin patladığını tam olarak görürsün. Filtrelemek için aşağıdaki jq tariflerini kullan.
Event metadata'sı
llm
{
"agent": "Analist", "method": "incele", "instance": "analist",
"model": "text-model-l", "provider": "openai-compatible",
"logical_run_id": "run-8f3c…",
"attempt_id": "run-8f3c…:attempt-1",
"local_request_id": "request-1a2b…",
"provider_request_id": "req_abc123",
"cached": false,
"usage": {
"prompt_tokens": 812, "completion_tokens": 143,
"total_tokens": 955, "reasoning_tokens": 0
},
"cost": 0.00955,
"cost_per_1k_tokens": 0.01
}tool
| Alan | Anlamı |
|---|---|
logical_run_id · attempt_id | Tool'u tetikleyen LLM akışının kimlikleri. |
local_request_id · provider_request_id | Tool call'ı üreten HTTP isteği. |
provider_tool_call_id | Provider'ın verdiği tool call kimliği. |
logical_tool_call_id | attempt_id + provider_tool_call_id — retry'lar arasında tekil kalır. |
timeout | Uygulanan etkin timeout (ms). |
cached | Sonuç cache'ten geldi mi? |
single_flight | Eşzamanlı özdeş çağrıyla paylaşıldı mı? |
idempotent · deduplicated | Idempotency store'dan döndü mü? |
agent
{ "instance": "h", "deterministic": true }deterministic: true, method'un return ile bittiğini (LLM çağrısı yapmadığını) gösterir.
validation
rule alanı hangi kuralın çalıştığını söyler:
rule değeri | Anlamı |
|---|---|
type | Tip doğrulaması (ek alan: value_type — v0.9.0'a kadar type idi; olayın kendi type alanıyla çakışıyor ve OTel çıktısında tekrarlı luma.type attribute'u üretiyordu). |
schema | Nesne düzeyi schema doğrulaması (ek alan: schema). |
tagged_union | Tagged union üye doğrulaması (ek alanlar: schema, member). Yayımlanmış filtrelerin çoğunda yoktur — bunu atlayan bir kural listesi tüm discriminated union kontrollerini düşürür. |
field | Alan düzeyi başarısızlık (eksik alan, alias çakışması). |
optional | Opsiyonel alan mevcut değildi — atlandı. |
unknown_field | Schema'da tanımlı olmayan alan. |
format:email · match:^…$ | Format ve desen kuralları. |
minLength:2 · maxLength:40 | Uzunluk kuralları. |
min:0 · max:100 | Sayısal aralık kuralları. |
minItems:1 · maxItems:5 · unique · contains · uniqueBy:id | Koleksiyon kuralları. |
valid:x => … | @valid ifadesi (ifadenin kendisi kodda görünür). |
where | Invariant (ek alan: expression). |
conditional_required · conditional_forbidden | when kuralları (ek alan: condition). |
v0.8.0'dan beri doğrulama olayı metadata.severity da taşır. Düşen bir kuralın koşuyu durdurup durdurmadığını, yalnızca trace'e bakarak anlamanın tek yolu budur: "warn" değer bir şerhle kabul edildi demektir, diğer her şey o denemenin ölümcül düştüğü anlamına gelir.
jq -r '.events[] | select(.type=="validation" and .ok==false and .metadata.severity=="warn") | .metadata.rule' trace.json
O olmadan bir rapor, şerhli bir koşuyla düşen bir koşuyu aynı şey sayar.
http
{ "method": "GET", "status": 200, "headers": { "x-api-key": "[REDACTED]", "cookie": "session=…" } }Event adı http.<fn>'dir. Başarısız çağrı da event üretir: ok: false, sebep error alanında ve status yok — host'a hiç ulaşmamış bir isteğin raporlayacak status'ü yoktur. URL ve istek gövdesi trace'e hiç yazılmaz; header'lar redaksiyondan geçtikten sonra yazılır. Ayrıntılar: std/http.
query · command
{ "datasource": "TestDb", "rows": 3 }Event type'ı bildirim türünün kendisidir, name ise bildirim adıdır; yani bir query Orders şunu üretir: { "type": "query", "name": "Orders" }. rows, dönen listenin uzunluğudur — ama yalnızca bir liste döndüğünde. Tek satırlık query (Schema[] değil, -> Schema) ve her command rows: 1 raporlar; çünkü runtime array'leri sayar, array olmayan her şeyi tek satır kabul eder. Bir command'ın etkilediği satır sayısı telemetry'sinde değil, dönüş değerindedir. Başarısız çağrılar aynı event'i ok: false ve yalnızca datasource ile üretir.
parallel_over — span değil, düşme sayacı
{ "type": "parallel_over", "name": "kept", "ok": true, "metadata": { "total": 3, "skipped": 1 } }Bu event yalnızca skipped > 0 olduğunda ve yalnızca on_error continue altında üretilir. Bir parallel over'ın çalıştığını işaretlemez: hiçbir şey atlamayan bir fan-out hiç event bırakmaz, fail_fast bir fan-out ise hiçbir zaman üretmez. ok her zaman true'dur — fan-out'un kendisi başarılıdır; event, girdinin ne kadarını yolda düşürdüğünü raporlar.
Bu da onu, on_error continue'un bir sonuç listesini kısalttığını gösteren tek makine-okunur sinyal yapar. Başarısız eleman listede boşluk bırakmaz — liste yalnızca daha kısa döner ve bunu trace'te ya da stdout'ta söyleyen başka hiçbir şey yoktur. Aşağıdaki tarif bunu okur; semantiği parallel over'da.
Metrikler
{
"llmCalls": 3,
"toolCalls": 5,
"promptTokens": 2436,
"completionTokens": 419,
"reasoningTokens": 0,
"totalTokens": 2855,
"estimatedCost": 0.02855
}| Metrik | Nasıl hesaplanır |
|---|---|
llmCalls | Provider'a giden gerçek istek sayısı. Cache isabetleri ve paylaşılan single-flight istekleri sayılmaz. |
toolCalls | Tüm tool çağrıları (cache isabetleri dahil). |
promptTokens · completionTokens · totalTokens | Provider'ın usage alanından toplanır. |
reasoningTokens | reasoning_tokens ya da completion_tokens_details.reasoning_tokens. |
estimatedCost | totalTokens / 1000 × cost_per_1k_tokens. Agent'ta bu ayar yoksa 0 kalır. |
Bazı gateway'ler usage alanını hiç göndermez. Bu durumda token metrikleri 0 kalır, maliyet hesaplanamaz ve token_budget/cost_budget hiç tetiklenmez. Bütçe kontrolüne güveniyorsan gateway'inin usage döndürdüğünü doğrula.
Export biçimleri
| Bayrak | Çıktı |
|---|---|
--trace | Tam rapor stderr'e — stdout'taki program sonucunu kirletmez. |
--trace-file trace.json | { metrics, traces, events } tek JSON. |
--trace-jsonl trace.jsonl | Her event bir satır; son satır {"type":"metrics", …}. |
--otel-file otel.json | OTLP uyumlu resourceSpans yapısı. |
luma program.luma \ --trace-file /tmp/trace.json \ --trace-jsonl /tmp/trace.jsonl \ --otel-file /tmp/otel.json
Trace'in en değerli olduğu an düşen koşudur, bu yüzden trace yazımı finally konumundadır: hataya kadar biriken ne varsa diske iner, son hata da bir event olarak kaydedilir. Terminal mesajı yalnızca output schema failed validation demek yerine düşen kuralları ve kaç deneme harcandığını adıyla söyler.
Bu her zaman böyle değildi — v0.5.0'a kadar hatayla biten bir koşu hiç trace dosyası üretmiyordu, yani tanıya en çok ihtiyaç duyulan tek durum okunacak hiçbir şeyin olmadığı durumdu. Sahadan #100 olarak bildirildi.
Aggregate trace'ler
traces dizisi, event'leri logical_run_id'ye göre gruplar:
{
"traceId": "e19fbd6da18af2dc3b57561c073adf7a",
"logicalRunId": "run-8f3c…",
"start": 1785242769010,
"durationMs": 1843,
"ok": true,
"attempts": [
{ "attemptId": "run-8f3c…:attempt-1", "start": …, "end": …, "ok": false, "events": 12 },
{ "attemptId": "run-8f3c…:attempt-2", "start": …, "end": …, "ok": true, "events": 14 }
]
}logical_run_id taşımayan event'ler (deterministik agent method'ları, direct tool çağrıları) kendi tekil trace'lerine yerleştirilir.
OpenTelemetry
{
"resourceSpans": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "luma" } }] },
"scopeSpans": [{
"scope": { "name": "luma-runtime" },
"spans": [{
"traceId": "e19fbd6d…", "spanId": "a1b2c3d4…",
"name": "hesapla",
"startTimeUnixNano": "1785242769011000000",
"endTimeUnixNano": "1785242769012000000",
"attributes": [
{ "key": "luma.cached", "value": { "boolValue": false } },
{ "key": "luma.type", "value": { "stringValue": "tool" } }
],
"status": { "code": 1 }
}]
}]
}]
}Tüm event metadata'sı luma.<anahtar> önekiyle span attribute'una çevrilir. status.code: 1 başarılı, 2 hatalı. Dosya doğrudan bir OTLP collector'a POST edilebilir.
Correlation zinciri
logical_run_id bir mantıksal ask akışı — retry'lar boyunca AYNI kalır │ ├── attempt_id her retry denemesi / her run policy adayı │ │ run-8f3c…:attempt-2 │ │ run-8f3c…:fallback-2-Backup │ │ │ ├── local_request_id tekil HTTP isteği (Luma üretir) │ ├── provider_request_id gateway'in döndürdüğü kimlik │ │ │ └── provider_tool_call_id provider'ın tool call kimliği │ └── logical_tool_call_id attempt + tool call │ └── ikinci deneme, üçüncü deneme …
Bu kimlikler her isteğe HTTP başlığı olarak da gönderilir:
x-luma-run-id: run-8f3c… x-luma-attempt-id: run-8f3c…:attempt-2 x-luma-request-id: request-1a2b…
Böylece gateway loglarındaki bir kaydı Luma trace'indeki event'le birebir eşleştirebilirsin.
| attempt_id deseni | Ne zaman |
|---|---|
<run>:attempt-N | Normal retry denemeleri. |
<run>:fallback-N-<Model> | fallback policy adayları. |
<run>:parallel-N-<Model> · race-… · vote-… | İlgili policy adayları. |
<run>:fusion-<Model> | Fusion synthesizer çağrısı. |
<run>:combo-N-<Model> | Combo pipeline aşaması. |
Secret redaksiyonu
Trace dosyaları paylaşılabilir olmalıdır; runtime bunu iki katmanda sağlar:
| Katman | Ne yapar |
|---|---|
| Değer redaksiyonu | Bilinen tüm API key değerleri (api_key, OPENAI_API_KEY, ANTHROPIC_API_KEY ve tüm model profil key'leri) hata mesajlarında [REDACTED] ile değiştirilir. Authorization: Bearer … başlıkları da maskelenir. |
| Anahtar adı redaksiyonu | Metadata içinde adı api_key, password, secret, credential veya authorization desenine uyan her alanın — iç içe olsa bile — değerinin tamamı [REDACTED] ile değiştirilir. |
HTTP header'larını yöneten katman anahtar adı katmanıdır: header adı metadata'ya anahtar olarak girer, dolayısıyla authorization ve x-api-key değerinin tamamını kaybeder — değer katmanının bir hata mesajı içinde yaptığı gibi yalnızca Bearer sonrasındaki token'ı değil. Desene uymayan her header adı trace'e olduğu gibi gider: cookie, x-auth-token, x-session ve imza header'ları redakte edilmez. Yalnızca header değerleri ayrıca önce değer katmanından geçer; böylece bilinen bir model API key'ini taşıyan header, adı ne olursa olsun değerinden yakalanır. Ya credential'ı desenin yakaladığı bir header adında tut, ya da trace dosyasını credential kadar hassas kabul et.
Redaksiyon hata mesajlarını ve metadata'yı kapsar. Prompt metinleri trace event'lerine yazılmaz, ancak bir doğrulama hatası mesajında model çıktısının bir parçası görünebilir. Ayrıca LUMA_DEBUG_MODEL_RESPONSE=1 ayarı ham model yanıtının ilk 4000 karakterini hata detayına ekler — bunu üretimde açık bırakma.
jq tarifleri
Başarısız doğrulamalar
jq -r '.events[] | select(.type=="validation" and .ok==false)
| "\(.name) [\(.metadata.rule // "-")] \(.error // "-")"' trace.jsonLLM istekleri: model, süre, token, maliyet
jq -r '.events[] | select(.type=="llm")
| "\(.metadata.model) \(.durationMs)ms \(.metadata.usage.total_tokens // 0)tok $\(.metadata.cost // 0)"' trace.jsonEn yavaş 10 adım
jq -r '.events | sort_by(-.durationMs) | .[:10][]
| "\(.durationMs)ms \(.type) \(.name)"' trace.jsonTool çağrı özeti (cache isabetleriyle)
jq -r '.events[] | select(.type=="tool")
| "\(.name) ok=\(.ok) cached=\(.metadata.cached // false) \(.durationMs)ms"' trace.jsonHangi retry denemesi başarılı oldu?
jq -r '.traces[] | select(.attempts | length > 1)
| "\(.logicalRunId): " + ([.attempts[] | "\(.attemptId)=\(.ok)"] | join(" "))' trace.jsonEn sık başarısız olan doğrulama kuralları
jq -r '.events[] | select(.type=="validation" and .ok==false) | .metadata.rule' trace.json \ | sort | uniq -c | sort -rn
Sessizce düşen elemanlar (parallel over)
jq -r '.events[] | select(.type=="parallel_over")
| "\(.name): \(.metadata.total) elemandan \(.metadata.skipped) tanesi düştü"' trace.jsonÇıktı olmaması hiçbir şeyin atlanmadığı anlamına gelir: skipped 0 iken event hiç oluşmaz. Kısalmış bir on_error continue sonuç listesinin göründüğü tek yer burasıdır — içinde parallel over geçen her trace için bunu CI'da çalıştır.
Veri yüzeyi: query, command, HTTP
jq -r '.events[] | select(.type=="query" or .type=="command" or .type=="http")
| "\(.type) \(.name) ok=\(.ok) \(.durationMs)ms \(.metadata.rows // .metadata.status // "-")"' trace.jsonTüm hatalar (her tür)
jq -r '.events[] | select(.ok==false) | "\(.type) \(.name) \(.error)"' trace.json
JSONL akışını canlı izleme
tail -f trace.jsonl | jq -r 'select(.type=="llm" or .ok==false)
| "\(.type) \(.name) ok=\(.ok)"'Maliyet ve bütçe takibi
agent Kontrollu {
model: Primary
cost_per_1k_tokens: 0.01
token_budget: 20000
cost_budget: 0.50
}Bütçe kontrolü LLM yanıtının usage bilgisi okunduktan sonra yapılır: limiti aşan istek tamamlanır, ancak bir sonraki istek MODEL_BUDGET_EXCEEDED ile engellenir. Hata retryable: false olduğu için retry döngüsü de durur.
# Tek çalıştırmanın toplam maliyeti
jq '.metrics | { totalTokens, estimatedCost, llmCalls }' trace.json
# Agent bazında token dağılımı
jq -r '.events[] | select(.type=="llm")
| "\(.metadata.agent // "top-level")\t\(.metadata.usage.total_tokens // 0)"' trace.json \
| awk -F'\t' '{s[$1]+=$2} END {for (a in s) printf "%-24s %d\n", a, s[a]}' | sort -k2 -rn| Maliyeti düşüren ayar | Etkisi |
|---|---|
cache: true (model profili) | Aynı istek tekrar edilirse provider'a gidilmez. |
memory_limit | Geçmiş mesajların prompt token'ını büyütmesini sınırlar. |
max_tokens | Çıktı token'ını sınırlar. |
combo run policy | Ucuz modelle taslak, pahalı modelle rötuş. |
Ara dosyalar (pre.md) | Pahalı görsel adımını tekrarlamadan çıkarımı yeniden çalıştırmanı sağlar. |
| Dar schema'lar | JSON Schema system mesajına yazıldığı için her istekte token maliyeti vardır. |
Üretilen JSON Schema her istekte system mesajına yazılır. $defs yalnızca kök schema'dan erişilebilen tanımları taşır — kullanılmayan schema'lar maliyete girmez. Yine de derin bir schema grafiği her istekte ödenir; nadiren kullanılan dalları ayırmak bu sabit maliyeti düşürür.