LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu
Geleneksel günlük kaydının LLM uygulamaları için neden yetersiz olduğunu, RAG ve ajan iş akışlarındaki hataları tanılamak için hangi bilgilere ihtiyaç duyduğunuzu ve izleme veri modelini anlayın.
LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu, CoddyKit'te ücretsiz bir AI Engineering Academy dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Engineering Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Engineering Academy kursu toplamda 4 dersten oluşur.
LLM Hatalarını Ayıklamanın Benzersiz Zorlukları
Geleneksel yazılımlar belirlenimci biçimde başarısız olur: aynı girdi verildiğinde her zaman aynı çıktıyı üretir ve yığın izi doğrudan başarısız olan satırı gösterir. LLM uygulamaları bu varsayımları bozar. Aynı istem farklı çağrılarda farklı çıktılar üretebilir, başarısızlıklar çoğu zaman sessizdir (istisna yerine yanlış yanıt oluşur) ve neden, zincirin 5 adım önce kullanılan bir isteminin içine gömülmüş olabilir. Standart günlük kaydı ve hata ayıklama araçları bunun için tasarlanmamıştır.
Belirlenimci Olmama Yeniden Üretimi Zorlaştırır
LLM çıktıları varsayılan olarak belirlenimci değildir. temperature=0 olsa bile aynı istem; toplu işleme ve sayısal kesinlik nedeniyle biraz farklı çıktılar üretebilir. Bu, hataların aralıklı olduğu anlamına gelir: zamanın %20'sinde başarısız olan bir istemi yalnızca bir kez çalıştırırsanız sınama paketinizden geçer. Belirli bir başarısızlığı yeniden üretmek için yalnızca girdiyi değil, başarısızlık anındaki tam girdiyi, model parametrelerini ve çıktıyı günlüğe kaydetmeniz gerekir.
import json
import time
def logged_llm_call(client, messages, model, temperature, **kwargs):
request_id = f'{int(time.time() * 1000)}-{id(messages)}'
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
**kwargs
)
# Log EVERYTHING needed to reproduce this exact call
log_entry = {
'request_id': request_id,
'model': model,
'temperature': temperature,
'messages': messages,
'response': response.choices[0].message.content,
'finish_reason': response.choices[0].finish_reason,
'usage': response.usage.model_dump(),
'timestamp': time.time()
}
write_to_trace_store(log_entry)
return responseSessiz Başarısızlıklar: Bozuk Değil, Yanlış
En sinsi LLM başarısızlıkları sessiz başarısızlıklardır: API çağrısı başarılı olur (HTTP 200, istisna yoktur), ancak yanıt yanlış, uydurma, eksik veya konu dışıdır. Uygulamanız yanlış yanıtı sorunsuzca işler ve herhangi bir şeyin başarısız olduğuna dair belirti göstermeden kullanıcıya iletir. Yalnızca istisnaları ve hata kodlarını izleyen geleneksel izleme bunu asla yakalayamaz; çıktı kalitesini anlamsal olarak izlemeniz gerekir.
# This succeeds with HTTP 200 but returns wrong information
response = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': 'What is the boiling point of water at sea level?'}]
)
output = response.choices[0].message.content
# response.status_code: None (not relevant - always 200 if we got here)
# No exception thrown
# But if output is '90 degrees Celsius', it is WRONG and your app will serve bad data
# You need semantic validation:
def validate_boiling_point_answer(text: str) -> bool:
return '100' in text # Rough check - real validation is more sophisticatedÇok Adımlı Zincirler: Nerede Yanlış Gitti?
Bir RAG işlem hattında veya araç zincirinde son yanıttaki bir başarısızlık, ilgisiz parçalar döndüren bir getirme adımına kadar izlenebilir. Bu durum da önemli bir cümleyi iki parça arasında bölen parçalama stratejisinden, o da teknik jargonla iyi başa çıkamayan bir gömme modelinden kaynaklanabilir. Adım adım izleme olmadan yalnızca yanlış son yanıtı görür ve hatayı hangi adımın ortaya çıkardığını belirleyemezsiniz.
# Without tracing: you see only the final wrong answer
def rag_pipeline_naive(query):
chunks = retrieve(query) # step 1 - might return bad chunks
context = format_context(chunks) # step 2 - might truncate key info
answer = generate(query, context) # step 3 - LLM gets bad context
return answer # WRONG - but why?
# With tracing: you can see each step's input and output
def rag_pipeline_traced(query):
with trace_span('retrieve') as span:
chunks = retrieve(query)
span.set_attribute('num_chunks', len(chunks))
span.set_attribute('top_chunk_score', chunks[0]['score'] if chunks else 0)
with trace_span('format_context') as span:
context = format_context(chunks)
span.set_attribute('context_length', len(context))
with trace_span('generate') as span:
answer = generate(query, context)
span.set_attribute('answer_length', len(answer))
return answer # Now you can diagnose: was retrieve the problem?Belirteç Sayısı ve Maliyet Sürprizleri
Enstrümantasyon olmadan belirteç sayıları ve maliyetler aylık fatura gelene kadar görünmez kalır. Bir ihmal nedeniyle 500'den 5000 belirtece çıkan sistem istemi, 5 yerine 20 parça döndüren bir getirme işlevi veya LLM'yi 10 yerine 100 kez çağıran bir döngü maliyetlerinizi sessizce katlar. Anomalilerin gerçek zamanlı olarak görülebilmesi için her LLM çağrısını enstrümante edin; istem belirteçlerini, tamamlama belirteçlerini ve tahmini maliyeti günlüğe kaydedin.
COST_PER_1K = {'gpt-4o': {'input': 0.005, 'output': 0.015},
'gpt-4o-mini': {'input': 0.000150, 'output': 0.000600}}
def compute_cost(model: str, usage) -> float:
pricing = COST_PER_1K.get(model, {'input': 0.005, 'output': 0.015})
input_cost = (usage.prompt_tokens / 1000) * pricing['input']
output_cost = (usage.completion_tokens / 1000) * pricing['output']
return input_cost + output_cost
def instrumented_call(client, model, messages):
response = client.chat.completions.create(model=model, messages=messages)
cost = compute_cost(model, response.usage)
# Alert if single call is unexpectedly expensive
if cost > 0.10: # more than 10 cents for one call
print(f'WARNING: Expensive LLM call: ${cost:.4f} ({response.usage.prompt_tokens} prompt tokens)')
metrics.record('llm_cost_usd', cost, tags={'model': model})
metrics.record('llm_prompt_tokens', response.usage.prompt_tokens)
return responseGecikme: Hangi Adım Yavaş?
Kullanıcılar LLM gecikmesini tek bir bekleme süresi olarak deneyimler, ancak bu süre aslında birçok ayrı adımın toplamıdır: vektör veritabanı sorgusu, belge getirme, istem oluşturma, API ağ çağrısı, belirteç üretimi ve yanıt ayrıştırma. Adım bazında zaman ölçümü olmadan yavaş yanıtın yavaş bir getiriciden mi yoksa yavaş bir LLM çağrısından mı kaynaklandığını anlayamazsınız. Gerçek darboğazınızı belirlemek için her adımı gecikme ölçümleriyle enstrümante edin.
import time
from contextlib import contextmanager
@contextmanager
def timed(name: str, metrics_client):
start = time.monotonic()
try:
yield
finally:
elapsed_ms = (time.monotonic() - start) * 1000
metrics_client.histogram(f'step_latency_ms', elapsed_ms, tags={'step': name})
if elapsed_ms > 2000: # flag steps taking more than 2 seconds
print(f'SLOW STEP [{name}]: {elapsed_ms:.0f}ms')
# Usage
def rag_with_timing(query, metrics):
with timed('embed_query', metrics):
query_embedding = embed(query)
with timed('vector_search', metrics):
chunks = vector_db.search(query_embedding, top_k=5)
with timed('llm_generate', metrics):
answer = generate(query, chunks)
return answerGerçekte İhtiyacınız Olan Bilgiler
Herhangi bir LLM uygulaması başarısızlığını teşhis etmek için şunları yakalayıp saklamanız gerekir: tam istem girdisi (sistem ve tüm iletiler), kullanılan model ve parametreler (temperature, max_tokens), tam çıktı, belirteç sayıları ve tahmini maliyet, adım başına gecikme, tüm araç çağrıları ve bunların sonuçları ve tek bir kullanıcı isteğinin tüm adımlarını birbirine bağlayan bir oturum veya istek kimliği. Bu, kullanılabilir en küçük izleme veri kümesidir.
from dataclasses import dataclass, field
from typing import Optional
import time
@dataclass
class LLMTrace:
request_id: str
session_id: str
step_name: str
model: str
temperature: float
system_prompt: str
user_messages: list[dict]
response: str
finish_reason: str
prompt_tokens: int
completion_tokens: int
cost_usd: float
latency_ms: float
tool_calls: list[dict] = field(default_factory=list)
error: Optional[str] = None
timestamp: float = field(default_factory=time.time)
def is_anomalous(self) -> bool:
return (
self.cost_usd > 0.10 or
self.latency_ms > 10000 or
self.finish_reason == 'length' or # was cut off
self.error is not None
)İstek Kimlikleriyle İzleri İlişkilendirme
Tek bir kullanıcı isteği, farklı hizmetlerde 10 LLM çağrısını tetikleyebilir. Bunların tümünden geçen bir ilişkilendirme kimliği olmadan bu çağrıları tek bir iz altında gruplayamazsınız. Her kullanıcı isteğinin giriş noktasında benzersiz bir istek kimliği ekleyin ve bunu sonraki her LLM çağrısında, veritabanı sorgusunda ve günlük iletisinde aktarın. Böylece belirli bir kullanıcı isteğinin yürütme yolunun tamamını yeniden oluşturabilirsiniz.
import uuid
from contextvars import ContextVar
# Thread-safe request ID propagation using context variables
request_id_var: ContextVar[str] = ContextVar('request_id', default='unknown')
def handle_user_request(query: str):
# Set request ID at the entry point
req_id = str(uuid.uuid4())[:8]
request_id_var.set(req_id)
return rag_pipeline(query)
def get_current_request_id() -> str:
return request_id_var.get()
# Every LLM call logs with the same request_id
def log_llm_call(model, prompt, response):
logger.info('LLM call', extra={
'request_id': get_current_request_id(), # automatically correlates all calls
'model': model,
'prompt_length': len(prompt),
'response_length': len(response)
})LLM Gözlemlenebilirlik Yığını
LLM gözlemlenebilirlik yığınının üç katmanı vardır: Günlüğe kaydetme, her LLM çağrısının yapılandırılmış kayıtlarını yakalar (LangSmith, Langfuse, özel günlükler). Ölçümler, zaman içindeki toplu sayıları izler: istek sayısı, ortalama gecikme, hata oranı ve günlük maliyet. İzleme, tek bir istek içindeki adımların nedensel zincirini kaydeder. Bu üç temel unsur birlikte, başarısızlıkları teşhis etmeniz, gerilemeleri yakalamanız ve performansı iyileştirmeniz için gereken görünürlüğü sağlar.
Kalite Düşüşlerinde Uyarı Verme
Hataların ikili olduğu (çalışıyor/bozuk) geleneksel yazılımların aksine LLM kalitesi kademeli olarak düşer. Bir istem değişikliği, hiçbir istisna oluşmadan yanıt kalitesini %85'ten %70'e düşürebilir. Üretim yanıtlarından her gün bir örneklem üzerinde otomatik değerlendirme (LLM değerlendirici olarak puanlama) çalıştırarak kaliteyi izleyin. Kullanıcılar şikâyet etmeye başlamadan önce, hareketli ortalama kalite puanı bir eşik değerinin altına düştüğünde uyarı verin.
Gözlemlenebilirlik Uygulamanıza Başlama
Gözlemlenebilirlik eklemek için üretimde bir olay yaşanmasını beklemeyin. En azından şu üç adımla başlayın: (1) her LLM çağrısının tam girdisini, çıktısını ve belirteç sayılarını bir veritabanına veya dosyaya günlüğe kaydedin, (2) her kullanıcı etkileşimine bir istek kimliği atayın ve bunu tüm günlük girdilerine ekleyin, (3) işlem hattınıza adım bazında zaman ölçümü ekleyin. Yalnızca bu üç unsur bile hata ayıklama görevlerinin %80'ini saatler yerine dakikalar içinde çözülebilir hâle getirir.
Kısa Sınama
Bu dersten, LLM uygulamalarında hata ayıklamanın neden zor olduğunu ne ölçüde anladığınızı sınayın.
Ders Özeti
Bu derste şunları öğrendiniz: belirlenimci olmama, LLM hatalarını aralıklı ve tam istek bağlamı yakalanmadan yeniden üretilmesi zor hâle getirir; sessiz başarısızlıklar (başarılı API çağrılarından dönen yanlış yanıtlar) geleneksel hata izlemeyi atlar ve anlamsal kalite denetimleri gerektirir; istek kimliği ilişkilendirmesiyle adım adım izleme, çok adımlı RAG ve araç işlem hatalarındaki başarısızlıkları teşhis etmek için gereken en az unsurdur. Sırada LangSmith ile izleme uygulayacağız.
Yapay zeka eğitmeniyle Python öğren — ücretsiz
Tarayıcında gerçek kod yaz ve çalıştır, 7/24 yapay zeka eğitmeninden anında yardım al; web'de ya da uygulamada kaldığın yerden devam et.
- Kurslar
- 30
- Dersler
- 120
Sıkça Sorulan Sorular
“LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu” dersi ücretsiz mi?
Evet — “LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Engineering Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Engineering Academy kursu toplamda 4 dersten oluşur.
“LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu” dersinde ne öğreneceğim?
Geleneksel günlük kaydının LLM uygulamaları için neden yetersiz olduğunu, RAG ve ajan iş akışlarındaki hataları tanılamak için hangi bilgilere ihtiyaç duyduğunuzu ve izleme veri modelini anlayın. AI Engineering Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
AI Engineering Academy öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te AI Engineering Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.
“LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu AI Engineering Academy dersinde kod yazıp çalıştırabilir miyim?
Evet. Her AI Engineering Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- LLM Uygulamalarında Hata Ayıklamanın Neden Zor Olduğu
- LangSmith ile İzleme
- Modelden Bağımsız Gözlemlenebilirlik İçin Langfuse
- Gecikme, Maliyet ve Kalite Düşüşleri İçin Uyarı