İçeriğe geç
mcpd
daemon yok
Kaynak

Ç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.

Search
shipped on Tuesdayscore_below=0.5min_duration_ms=5000model=gpt-4o AND status=error
  • 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, refunds sonucunu 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.

ParametreNeyi 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_idTek bir konuşmadaki her çalıştırma.
thread_idTek bir thread'deki her çalıştırma.
user_idTek bir son kullanıcı için her çalıştırma.
environmentTek 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.

FacetNeyi listelediği
entity_kindAjan, iş akışı, araç, model, puanlayıcı, işleyici, boru hattı ya da servis.
entity_nameBu şeylerin taşıdığı adlar; örneğin çağrılan araçlar.
service_nameRapor vermiş olan servisler.
environmentGörülen ortamlar; örneğin production.
tagHerhangi bir çalıştırmanın taşıdığı her etiket.
metric_nameKaydedilen metrik adları, mcpd'nin türettikleri dahil.
metric_label_keyProjede görülen her etiket anahtarı.
metric_label_valueTek 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'

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'

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...'

Ç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.