Çalıştırma bulma
Bir inceleme bir gösterge paneliyle değil, bir şikâyetle başlar. Bu sayfa, bir şikâyeti ona sebep olan tek çalıştırmaya nasıl çevireceğinizi anlatır.
Etiketi değil, payload'ı aramak
Çoğu izleme aracı yalnızca adları ve öznitelikleri dizine ekler. mcpd
prompt'u ve tamamlamayı da dizine ekler, çünkü bir insanın şikâyet ettiği
şey modelin söylediği bir şeydir ve size aktardığı sözcükler elinizdeki tek
tutamaktır. q parametresi işte o dizindir.
- 3 hits the run whose completion said it
- 12 hits every answer a scorer marked down
- 7 hits runs slower than five seconds
- 2 hits failures on one model
q tam olarak neyi kapsar
Dizin her span için üç alan tutar: adı, girdisi ve çıktısı. Girdi ve çıktı JSON'dur, bu yüzden mcpd dizine eklemeden önce payload'ı gezer ve yalnızca dizgilerini ve sayılarını tutar; süslü parantezleri, tırnakları ve anahtarları atar. Ham JSON'u dizine eklemek her noktalama işaretini bir belirteç yapar ve asıl aradığınız sözcükleri gömerdi.
Buna güvenmeden önce bilmeye değer altı sonuç:
- İçindeki tek bir span eşleştiğinde çalıştırma eşleşir. Geriye tek tek span'ler değil, çalıştırmalar bütün olarak döner; hangi adım olduğunu görmek için birini açın.
-
Sözcükler AND ile birleşir.
q=refund delayed, ikisini de dizine eklenmiş metnin herhangi bir yerinde, yan yana olmadan içeren span'leri bulur. -
Eşleşme tam sözcük üzerindendir, büyük küçük harf ayrımı gözetmez ve
aksanlar eşitlenir; yani
resume,résumésonucunu bulur. Kök bulma yoktur:refund,refundssonucunu bulmaz. - Girdinin ve çıktının her birinin ilk 64 KiB'ı dizine girer. Daha uzun payload'lar bütün olarak saklanır ve gösterilir; yalnızca çok büyük bir prompt'un kuyruğu aranamaz.
- Maskeleme depolamadan önce çalışır, yani maskeleyicinin kaldırdığı hiçbir şey dizine de girmemiştir. Maskeleme ve saklama sayfasına bakın.
- Birden çok kez yazılan bir span, uzun süren bir span'in bittiğinde yazıldığı gibi, kendi dizin satırının yerine geçer; böylece arama her zaman güncel sürümü yansıtır.
Kaçak bir tırnak hata değil, aramadır
Alttaki dizinin kendi sorgu dili vardır: çift tırnak bir öbek açar;
NEAR, OR ve * ise operatördür.
Metninizi olduğu gibi ona vermek, içinde tırnak işareti geçen bir müşteri
mesajının sonuç yerine sözdizimi hatası olarak dönmesi demek olurdu; hata
verebilen bir arama kutusu da insanların kullanmayı bıraktığı bir arama
kutusudur.
Bu yüzden metniniz o ayrıştırıcıya hiç ulaşmaz. mcpd yazdığınızı boşluklardan
böler, her sözcükten çift tırnakları çıkarır, her sözcüğü birebir bir terim
olarak sarar ve hepsini AND ile birleştirir. q=say "no",
say ve no sözcüklerini arar. q=NEAR, NEAR
sözcüğünü arar. Yazabileceğiniz hiçbir şey sözdizimi hatası değildir ve size
açık hiçbir operatör yoktur. Ödünleşim budur ve bilerek bu yönde yapılmıştır.
Yalnızca boşluktan oluşan bir q, hiçbir şeyle eşleşmek yerine
düşürülür; boş bir q= ise yok sayılır. Her parametre böyle
davranır, yani boş filtre kutularını da gönderen bir form yanlışlıkla boş
dizgiye göre filtrelemez.
Çalıştırma listesini daraltmak
GET /v1/api/traces şunları alır. Yazmadığınız hiçbir şey
uygulanmaz.
| Parametre | Neyi seçtiği |
|---|---|
q | Her span'in adı, girdisi ve çıktısı üzerinde tam metin. |
status | unset, ok, error ya da
cancelled. İçindeki herhangi bir span başarısız olduğunda,
hatası yakalanmış olsa bile çalıştırma error olur.
|
kind |
Çalıştırmanın kök span'inin türü; örneğin agent_run ya da
workflow_run.
|
name | Kök span'in adı, birebir. Ön ek değil, kalıp değil. |
tags | Virgülle ayrılır. Bir çalıştırma listelenen etiketlerin hepsini taşımalıdır, herhangi birini değil. |
session_id | Tek bir konuşmadaki her çalıştırma. |
thread_id | Tek bir thread'deki her çalıştırma. |
user_id | Tek bir son kullanıcı için her çalıştırma. |
environment | Tek bir ortam; örneğin production. |
model | Bu model kimliğine karşı en az bir span'i olan çalıştırmalar. |
from, to | Pencere; çalıştırmanın başlangıç zamanına göre ölçülür. |
min_duration_ms | En az bu kadar yavaş olan çalıştırmalar, milisaniye olarak. |
min_cost | En az bu kadar tutan çalıştırmalar. Fiyatlanmış span'i olmayan bir çalıştırma sıfır sayılır, yani pozitif her değer onu dışarıda bırakır. |
has_score | Bu puanlayıcı kimliğinden bir puan taşıyan çalıştırmalar. |
score_below | has_score ile: o puanlayıcının bu değerin altına koyduğu çalıştırmalar. |
sort | start_time, duration, cost,
span_count ya da score. Varsayılanı
start_time. score, çalıştırmaları herhangi bir
puanlayıcının verdiği en düşük puana göre sıralar, yani artan sıra
en kötü çalıştırmayı başa koyar; puanı olmayan bir çalıştırma sıfır
sayılır.
|
order | asc ya da desc. Varsayılanı desc. |
limit | Sayfa boyutu, 1 ile 500 arasına sıkıştırılır. Varsayılanı 50. |
cursor | Önceki sayfadan gelen opak imleç. |
project | Hangi projenin okunacağı. Varsayılanı daemon'un kendi varsayılan projesidir, yani tek projeli bir kurulumun bunu hiç göndermesi gerekmez. |
name, ilişkilendirme kimlikleri ve environment
eşitlik sınamalarıdır; onları ucuz kılan da budur. Bir adda kısmi eşleşme
istiyorsanız q kullanın: span adları da metin dizinindedir.
Bir liste satırının maliyeti
İkinci, daha hafif bir çalıştırma listesi yoktur, çünkü ilki zaten hafiftir. Saklanan trace izdüşümü hiç girdi, çıktı ve öznitelik taşımaz: adlar, sayılar, zamanlar, maliyet, etiketler ve ilişkilendirme vardır, modelin söylediği hiçbir şey yoktur. Payload'ı getiren şey bir çalıştırmayı açmaktır.
Gerçekçi tek bir çalıştırma üzerinde ölçüldü: üç politika belgesi getiren, iki araç çağıran ve gerçek prompt ile tamamlamalarla iki model çağrısı yapan sekiz adımlık bir destek ajanı. Liste satırı 577 bayt, çalıştırmanın tamamı ise 10.717 bayt, yani %94,6 azalma. Ölçüm, bu daemon üzerinde, o çalıştırmada ve gerçek HTTP yüzeyinden yapılmıştır. Sizin çalıştırmalarınız farklı olacaktır ve asıl önemlisi yön: prompt'lar büyüdükçe kazanç da büyür, çünkü büyüyen kısım listenin taşımadığı kısımdır.
Bu rakam buraya elle yazılmaz, bir test tarafından üretilir; böylece bir
yerde eskiyip diğerinde hata vermemesi mümkün değildir.
cargo test -p mcpd-server --test payload_size -- --nocapture
komutunu çalıştırın, iki bayt sayısını da yazar. İzdüşüme payload taşıyan bir
alan eklemek o testi başarısız kılar.
Geri kalan her şey, adıyla reddedilir
O tablo sözlüğün tamamıdır. Orada olmayan bir parametre, kabul edilip
düşürülmek yerine kendisini adlandıran bir 400 ile reddedilir. Bu değişti:
mcpd eskiden bilmediği bir parametreyi yok sayardı, yani
?statuss=error projedeki her çalıştırmayla birlikte 200 dönerdi
ve yanıtta filtrenin uygulanmadığını söyleyen hiçbir şey olmazdı. İnandırıcı
ama yanlış bir yanıt, bir hatadan kötüdür, çünkü fark edilecek bir şey yoktur.
Bunun getirdiği ödünleşimi anmakta yarar var. Daha yeni bir daemon'un anladığı bir filtreyi gönderen bir istemci, artık daha eski bir daemon'dan daha geniş bir sonuç kümesi yerine 400 alır. İki başarısızlıktan iyisi budur: 400 parametreyi adlandırır ve durur.
Bazı retler "bilinmiyor" demekten fazlasını söyler. mcpd her trace'te
service_name, service_version,
resource_id, run_id, request_id,
experiment_id ve release kaydeder, ama çalıştırma
listesi henüz bunlara göre filtreleyemez. Yedisinden yalnızca
service_name bir
keşif boyutudur; bu yüzden
keşif yalnızca onun değerlerini listeler, diğerlerininkini listelemez. Bu
yedisi durumu söyleyen bir mesajla reddedilir; çünkü
service_name=checkout-api bir yazım hatası değildir ve birine
bunun bilinmeyen bir parametre olduğunu söylemek, onu var olmayan bir yazım
hatasının peşine takar.
Bu kural yalnızca çalıştırma listesinin kuralı değildir.
GET /v1/api/spans, /v1/api/logs,
/v1/api/scores, /v1/api/metrics ve
/v1/api/feedback, her biri kendi sözlüğünü aynı şekilde uygular.
Karşılaşmadan önce bilmeye değer iki tanesi: puan listesi order
almaz, çünkü en yenisi baştadır ve imleci tek yönde karşılaştırır; geri
bildirim listesi ise bir trace_id ister, çünkü mcpd henüz bütün
bir projenin geri bildirimini listeleyemez ve boş bir liste hiç geri bildirim
yokmuş gibi okunurdu.
Bir filtre kutusunu doldurmak
Bir filtre ancak içine ne yazacağınızı biliyorsanız kullanılabilir ve değerler
sabit bir liste değildir: bir projeye rapor veren servisler ne gönderiyorsa
odur. GET /v1/api/discovery bunu yanıtlar. facet ile
her seferinde tek bir boyut isteyin; o boyutun projede aldığı değerleri,
değere göre sıralanmış olarak ve her birini bir sinyalin onu en son ne zaman
taşıdığı bilgisiyle döndürür.
| Facet | Neyi listelediği |
|---|---|
entity_kind | Ajan, iş akışı, araç, model, puanlayıcı, işleyici, boru hattı ya da servis. |
entity_name | Bu şeylerin taşıdığı adlar; örneğin çağrılan araçlar. |
service_name | Rapor vermiş olan servisler. |
environment | Görülen ortamlar; örneğin production. |
tag | Herhangi bir çalıştırmanın taşıdığı her etiket. |
metric_name | Kaydedilen metrik adları, mcpd'nin türettikleri dahil. |
metric_label_key | Projede görülen her etiket anahtarı. |
metric_label_value | Tek bir etiketin değerleri. key gerektirir. |
prefix, yazarken tamamlama için listeyi daraltır; birebir ve
büyük küçük harfe duyarlı eşleşir, çünkü bu değerler bir filtrenin
karşılaştırılacağı değerlerdir ve burada harfleri eşitlemek, sonra hiçbir
şeyle eşleşmeyen bir değer önermek olurdu. limit yanıtı 1000 ile
sınırlar. Değerler yeniden eskiye değil değere göre sıralanır, yani birinin
okumakta olduğu liste iki yoklama arasında altından yeniden dizilmez.
Buna karşı bir şey yazmadan önce bilmeye değer bir sınır var.
metric_label_key, adı verilen tek bir metriğin anahtarlarını
değil, projenin herhangi bir yerinde görülmüş her etiket anahtarını listeler:
benzersiz değer listesi, onu taşıyan metriğe göre değil etikete göre
anahtarlanır.
Bir listeyi yeniden sorgulamadan tazelemek
GET /v1/api/deltas, bir imleçten bu yana geleni, en eskisi başta
olacak şekilde traces, spans ya da
logs için döndürür. Son yoklamadan gelen imleci saklayın, geri
gönderin ve satırları elinizde tuttuğunuz listeye uygulayın.
Bir yoklama bir sayfa değildir ve iki imleç bilerek birbirinin yerine kullanılamaz. Sayfalama, seçtiğiniz sıralamada sabit bir sonuç kümesinde geriye doğru yürür; yoklama ise varış sırasında ileri yürür ve hiç bitmez. Watermark, bir satırın varış zamanı ile kimliğinden oluşur; çünkü bir yığın, içindeki her satırı tek bir zaman damgasıyla yazar ve yalnızca "bu nanosaniyeden sonra" diyebilen bir watermark ya bütün yığını baştan oynatır ya da kalanını atlar. Boş bir yoklama imleci değiştirmeden döndürür: imleci ileri almak, biraz sonra daha erken bir varış zamanıyla düşen bir satırı kaybetmek olurdu.
Puanlar ve geri bildirimler yoklanamaz. Çağıranın gönderdiği zaman damgasıyla
damgalanırlar ve sunucunun koyduğu bir varış zamanı taşımazlar, yani onların
üzerindeki bir watermark ya geç gelen bir satırı atlar ya da her satırı
sonsuza kadar tekrarlar. signal=scores, boş bir sayfayla
yanıtlanmak yerine reddedilir.
Bu dağıtım neler yapabilir
mcpd'nin tek bir depolama trait'i ve onun arkasında üç arka ucu vardır; bunlar
eşit değildir. ClickHouse'ta bir silme kabul edilir ve çağrı döndükten sonra
uygulanır; bu yüzden yanıttaki sayılar diskten neyin gittiğini değil, neyin
eşleştiğini söyler. SQLite ve Postgres'te satırlar çağrı döndüğünde gitmiş
olur.
GET /v1/api/capabilities bunun
yazılı olduğu yerdir: daemon'un arkasında hangi arka uç var, q
neyle eşleşir, ASCII dışında harfler eşitlenir mi, çağrı döndüğünde silme
diske işlenmiş olur mu ve keşif ile delta yoklaması yanıt veriyor mu.
Bağlanırken bir kez okuyun ve yalnızca dağıtımın yanıtladığını sunun. Yeteneği
kapalı olan bir endpoint 501 ile reddeder ve gövdesindeki
capability alanı, yetenek kümesinin yayımladığı sözcüğün
aynısını adlandırır; böylece ikisi asla çelişemez ve bu retle karşılaşan bir
istemci, hiç değişmeyecek bir şeyi yeniden denemek yerine bakıp öğrenebilir.
Bugün gelen üç arka uç da hem keşfe hem delta yoklamasına yanıt verir, yani aşağıdaki 501, SQLite, Postgres ya da ClickHouse'tan göreceğiniz bir yanıt değil, sonradan eklenen bir arka ucun reddederken kullanacağı biçimdir. Hangisi olduğunu size yetenek kümesi söyler ve şimdiden okumaya değer: üçü, çağrı döndüğünde silmenin diskte olup olmadığında ve saklamanın nasıl uygulandığında ayrışır. ASCII dışındaki harfleri üçü de eşitler.
neye göre filtreleyebilirim?
# Bu projenin bildirdiği her ortam, bir filtre kutusu için.
curl -s --get http://localhost:4317/v1/api/discovery \
--data-urlencode 'project=default' \
--data-urlencode 'facet=environment'
# Yazarken tamamlama: "check" ile başlayan servis adları.
curl -s --get http://localhost:4317/v1/api/discovery \
--data-urlencode 'facet=service_name' \
--data-urlencode 'prefix=check'
# Tek bir metrik etiketinin aldığı değerler.
curl -s --get http://localhost:4317/v1/api/discovery \
--data-urlencode 'facet=metric_label_value' \
--data-urlencode 'key=model' {
"facet": "environment",
"values": [
{ "value": "production", "last_seen": "2026-09-03T08:04:18Z" },
{ "value": "staging", "last_seen": "2026-09-02T17:41:02Z" }
]
} {
"error": "not_implemented",
"capability": "discovery",
"message": "this deployment stores signals in <backend>, which does not support discovery: ... GET /v1/api/capabilities lists what it does support."
} Zaman pencereleri
from ve to, bir RFC 3339 zaman damgası ya da göreli
bir kısaltma alır: bir eksi işareti, bir sayı ve s,
m, h veya d harflerinden biri.
-15m, -1h ve -7d, hepsi çalışır.
Kısaltma var, çünkü son bir saate bakmak için elle ISO zaman damgası yazmak
kimsenin ödemesi gerekmeyen bir sürtünmedir.
from dahildir, to hariçtir ve ikisi de çalıştırmanın
başladığı zamanla karşılaştırılır. Pencerenizden önce başlayıp
pencerenin içinde hâlâ süren uzun bir çalıştırma dönmez. Saat 09:00'da
çalışmakta olan bir şeyin peşindeyseniz, from değerini daraltmak
yerine geriye doğru genişletin.
Ne zaman damgası ne de süre olan bir değer, yok sayılmak yerine adıyla reddedilir. Her liste endpoint'indeki her parametre böyle davranır, çünkü sessizce düşürülen bir filtre inandırıcı ama yanlış bir yanıt döndürür ve inandırıcı ama yanlış bir yanıt bir hatadan kötüdür.
from=yesterday
{
"error": "bad_parameter",
"field": "from",
"message": "\"yesterday\" is not an RFC 3339 timestamp or a relative duration like -1h"
} Kötü yanıtları bulan sorgu
has_score=answer_relevance&score_below=0.5, o puanlayıcının
yarımın altına koyduğu her çalıştırmayı döndürür. Şikâyet bir arızayla değil
kaliteyle ilgiliyse başvuracağınız sorgu budur, çünkü kötü bir yanıt
genellikle başarılı olmuş bir çalıştırmadır.
has_score tek başına, o puanlayıcının baktığı her çalıştırmayı
döndürür. score_below tek başına hiçbir şey yapmaz: bir
puanlayıcıya bağlanmamış bir eşiğin anlamı yoktur ve mcpd hangi puanlayıcıyı
kastettiğinizi tahmin etmek zorunda kalırdı. Tahmin etmez. Bu değerlerin
nereden geldiğini
Puanlar ve geri bildirim
anlatır.
Çalıştırma yerine adım aramak
GET /v1/api/spans, tek tek adımları bütün çalıştırmalar boyunca
aynı anda arar. Bir oturumla değil bir adımla ilgili sorular için kullanın:
hangi araç çağrısı yavaş, tek bir modele yapılan hangi çağrılar hata verdi,
bir getirme ne kadar sürüyor.
q, kind, status, name,
model, trace_id, from,
to, min_duration_ms, sort,
order, limit ve cursor alır; aynı
anlamlar ve aynı varsayılanlarla. Burada tags,
session_id ya da user_id filtresi yoktur, çünkü
bunlar çalıştırmaya aittir; üçü de kabul edilip düşürülmek yerine adıyla
reddedilir.
sort burada çalıştırma listesindekinden daha az değer alır:
start_time, duration ya da cost, başka
bir şey değil. Bir span, span sayısı ve puan toplamı taşımaz, yani
span_count ve score sıralanacak bir şey bulamaz ve
yok sayılmak yerine adıyla reddedilir.
Bir çalıştırmada model, herhangi bir span'in onu kullanıp
kullanmadığını sorar. Bir span'de model, o span'in kendi
modelidir. Aynı sözcük, iki soru.
bul
# Son bir günde başarısız olan ve her iki sözcüğü de bir span adında, bir
# prompt'ta ya da bir tamamlamada geçiren çalıştırmalar.
curl -s --get http://localhost:4317/v1/api/traces \
--data-urlencode 'q=Leeds depot' \
--data-urlencode 'status=error' \
--data-urlencode 'from=-24h'
# Tek bir modeli çağırıp başarısız olan her adım, bütün çalıştırmalarda.
curl -s --get http://localhost:4317/v1/api/spans \
--data-urlencode 'model=gpt-4o-mini' \
--data-urlencode 'status=error' \
--data-urlencode 'min_duration_ms=2000' import { Mcpd } from '@mcpd/sdk'
const mcpd = new Mcpd()
const bad = await mcpd.listTraces({
q: 'Leeds depot',
status: 'error',
from: '-24h',
limit: 20,
})
for (const trace of bad.traces) {
console.log(trace.traceId, trace.name, trace.status)
} from mcpd import Mcpd
mcpd = Mcpd()
bad = mcpd.list_traces(q="Leeds depot", status="error", from_="-24h", limit=20)
for trace in bad.traces:
print(trace.trace_id, trace.name, trace.status.value) Sayfalama ve imlecin neden opak olduğu
Bir liste en çok limit kadar satır ve bir page
nesnesi döndürür. Devamı varsa page.next_cursor bulunur;
page.has_more aynı şeyi bir mantıksal değerle söyler.
Sayfalama offset ile değil keyset ile yapılır. İmleç size verilen son satırı adlandırır ve sonraki sayfa kesinlikle ondan sonra başlar. Offset sayfalama kayardı: trace'ler sürekli gelir, yani siz 50 ile 99 arasındaki satırları isteyene kadar pencere altınızdan kaymış olur ve bir çalıştırmayı iki kez okurken bir başkası hiç görünmez. Bir incelemede kaçırdığınız çalıştırma, aradığınız çalıştırmadır.
İmleç, sıralama değeri ile satır kimliğinin onaltılık kodlanmış çiftidir ve
onu anlamsız kabul etmelisiniz. Opaktır, böylece kodlama her çağıranı bozmadan
değişebilir; ayrıca içindeki değer her sıralamada başka bir niceliktir:
start_time ve duration için nanosaniye,
cost için ölçeklenmiş bir tam sayı, span_count için
düz bir sayı, score için çalıştırmanın en düşük puanı.
Buna bağlı tek bir kural var. Bir imleci, onu üreten filtrelerle, aynı
sort ve aynı order değeriyle geri gönderin.
mcpd bunu yapıp yapmadığınızı denetlemez ve farklı bir sıralamada
yeniden oynatılan bir imleç yanlış sütunla karşılaştırılır ve satır atlar.
Düzenlenmiş ya da kırpılmış bir imleç invalid_query ve
malformed cursor mesajıyla reddedilir.
Toplam yoktur. page.total, eşleşmeleri ucuza sayabilen bir arka
uç için ayrılmıştır ve yerleşik SQLite deposu onu doldurmaz, bu yüzden bir
sayıya doğru saymak yerine has_more üzerinde döngü kurun.
sayfalarda gezinmek
# İlk sayfa, page.next_cursor içinde bir imleç döndürür.
curl -s --get http://localhost:4317/v1/api/traces \
--data-urlencode 'q=refund' \
--data-urlencode 'sort=cost' \
--data-urlencode 'limit=100'
# Sonraki sayfa: aynı filtreler, aynı sıralama, artı imleç.
curl -s --get http://localhost:4317/v1/api/traces \
--data-urlencode 'q=refund' \
--data-urlencode 'sort=cost' \
--data-urlencode 'limit=100' \
--data-urlencode 'cursor=MzEzNzQyfDBhZjM...' let cursor: string | undefined
do {
const page = await mcpd.listTraces({
q: 'refund',
sort: 'cost',
order: 'desc',
limit: 100,
cursor,
})
for (const trace of page.traces) {
console.log(trace.traceId, trace.cost?.totalCost ?? 'not priced')
}
// hasMore üzerinde döngü kurun. Sayacağınız bir toplam yok.
cursor = page.page.hasMore ? page.page.nextCursor : undefined
} while (cursor) cursor = None
while True:
page = mcpd.list_traces(
q="refund", sort="cost", order="desc", limit=100, cursor=cursor
)
for trace in page.traces:
cost = trace.cost.total_cost if trace.cost else "not priced"
print(trace.trace_id, cost)
# has_more üzerinde döngü kurun. Sayacağınız bir toplam yok.
if not page.page.has_more:
break
cursor = page.page.next_cursor Çalıştırmayı bulduktan sonra
Bu sorguların herhangi birinden çıkan bir trace kimliği
GET /v1/api/traces/{trace_id} adresine gider; bu da
çalıştırmayı ve içindeki her span'i, bir şelale ikinci bir istek olmadan
çizilebilecek şekilde sıralanmış olarak döndürür. Ona bağlı her şeyi aynı
yanıtta almak için include=logs,scores,feedback ekleyin.
Oradan sonra Maliyet muhasebesi her adımdaki rakamları açıklar, HTTP API referansı ise her endpoint ve alanı belirtimin bildirdiği hâliyle listeler.