Mengapa Aplikasi LLM Sulit Di-debug
Pahami alasan logging tradisional tidak memadai untuk aplikasi LLM, informasi yang Anda perlukan untuk mendiagnosis kegagalan dalam alur pemrosesan RAG dan agen, serta model data pelacakan.
Mengapa Aplikasi LLM Sulit Di-debug adalah pelajaran AI Engineering Academy gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Engineering Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Engineering Academy mencakup 4 pelajaran total.
Tantangan Unik dalam Men-debug LLM
Perangkat lunak tradisional gagal secara deterministik: dengan input yang sama, perangkat lunak selalu menghasilkan output yang sama, dan pelacakan tumpukan menunjukkan secara langsung baris yang gagal. Aplikasi LLM melanggar asumsi-asumsi ini. Perintah yang sama dapat menghasilkan output yang berbeda pada panggilan yang berbeda, kegagalan sering kali tidak terlihat (jawaban salah alih-alih pengecualian), dan penyebabnya mungkin tersembunyi dalam perintah yang digunakan 5 langkah sebelumnya dalam suatu rangkaian. Alat logging dan pen-debug-an standar memang tidak dirancang untuk hal ini.
Nondeterminisme Menyulitkan Reproduksi
Secara bawaan, output LLM bersifat nondeterministik. Bahkan dengan temperature=0, perintah yang sama dapat menghasilkan output yang sedikit berbeda akibat pemrosesan batch dan presisi numerik. Artinya, bug bersifat intermiten: perintah yang gagal 20% dari waktu yang ada akan lolos dari rangkaian pengujian Anda jika hanya dijalankan sekali. Untuk mereproduksi kegagalan tertentu, Anda harus mencatat input yang tepat, parameter model, dan output pada saat kegagalan — bukan hanya inputnya.
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 responseKegagalan Diam-Diam: Salah, Bukan Rusak
Kegagalan LLM yang paling sulit dideteksi adalah kegagalan diam-diam: panggilan API berhasil (HTTP 200, tanpa pengecualian), tetapi jawabannya salah, berhalusinasi, tidak lengkap, atau tidak relevan. Aplikasi Anda dengan senang hati memproses jawaban yang salah dan mengembalikannya kepada pengguna tanpa menunjukkan bahwa ada sesuatu yang gagal. Pemantauan tradisional yang hanya mengawasi pengecualian dan kode kesalahan tidak akan pernah mendeteksi hal ini — Anda memerlukan pemantauan semantik terhadap kualitas output.
# 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 sophisticatedRangkaian Multi-Langkah: Di Mana Kesalahannya?
Dalam pipeline RAG atau rangkaian agen, kegagalan pada respons akhir dapat ditelusuri kembali ke langkah pengambilan yang mengembalikan potongan-potongan tidak relevan. Langkah itu sendiri dapat ditelusuri kembali ke strategi pemotongan yang membagi kalimat penting menjadi dua potongan, yang kemudian dapat ditelusuri kembali ke model embedding yang tidak menangani jargon teknis dengan baik. Tanpa penelusuran per langkah, Anda hanya melihat jawaban akhir yang salah tanpa cara untuk menentukan langkah mana yang memperkenalkan kesalahan.
# 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?Kejutan Jumlah Token dan Biaya
Tanpa instrumentasi, jumlah token dan biaya tidak terlihat sampai tagihan bulanan tiba. Perintah sistem yang membesar dari 500 menjadi 5000 token karena kelalaian, fungsi pengambilan yang mengembalikan 20 potongan alih-alih 5, atau perulangan yang memanggil LLM 100 kali alih-alih 10 — semuanya secara diam-diam melipatgandakan biaya Anda. Instrumentasikan setiap panggilan LLM untuk mencatat token perintah, token penyelesaian, dan perkiraan biaya agar anomali terlihat secara waktu nyata.
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 responseLatensi: Langkah Mana yang Lambat?
Pengguna merasakan latensi LLM sebagai satu waktu tunggu, tetapi sebenarnya latensi merupakan jumlah dari banyak langkah individual: kueri basis data vektor, pengambilan dokumen, penyusunan perintah, panggilan jaringan API, pembuatan token, dan penguraian respons. Tanpa pengukuran waktu per langkah, Anda tidak dapat mengetahui apakah respons yang lambat disebabkan oleh pengambil yang lambat atau panggilan LLM yang lambat. Instrumentasikan setiap langkah dengan pengukuran latensi untuk mengidentifikasi hambatan utama yang sebenarnya.
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 answerInformasi yang Sebenarnya Anda Perlukan
Untuk mendiagnosis kegagalan aplikasi LLM apa pun, Anda perlu menangkap dan menyimpan: perintah input lengkap (sistem + semua pesan), model dan parameter yang digunakan (temperature, max_tokens), output lengkap, jumlah token dan perkiraan biaya, latensi per langkah, setiap panggilan alat beserta hasilnya, serta ID sesi atau permintaan yang menghubungkan semua langkah dalam satu permintaan pengguna. Inilah kumpulan data penelusuran minimum yang layak digunakan.
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
)Korelasi Penelusuran dengan ID Permintaan
Satu permintaan pengguna dapat memicu 10 panggilan LLM di berbagai layanan. Tanpa ID korelasi yang mengalir melalui semuanya, Anda tidak dapat mengelompokkan panggilan-panggilan tersebut ke dalam satu penelusuran. Sisipkan ID permintaan unik di titik masuk setiap permintaan pengguna, lalu teruskan ID tersebut dalam setiap panggilan LLM hilir, kueri basis data, dan pesan log. Dengan demikian, Anda dapat merekonstruksi jalur eksekusi lengkap untuk permintaan pengguna tertentu.
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)
})Tumpukan Observabilitas LLM
Tumpukan observabilitas LLM memiliki tiga lapisan: Logging menangkap catatan terstruktur dari setiap panggilan LLM (LangSmith, Langfuse, log khusus). Metrik melacak angka gabungan dari waktu ke waktu: jumlah permintaan, latensi rata-rata, tingkat kesalahan, dan biaya harian. Penelusuran mencatat rantai sebab-akibat dari berbagai langkah dalam satu permintaan. Bersama-sama, ketiga pilar ini memberi Anda visibilitas untuk mendiagnosis kegagalan, mendeteksi regresi, dan mengoptimalkan kinerja.
Peringatan atas Penurunan Kualitas
Berbeda dari perangkat lunak tradisional yang kesalahannya bersifat biner (berfungsi/rusak), kualitas LLM menurun secara bertahap. Perubahan perintah dapat menurunkan kualitas jawaban dari 85% menjadi 70% tanpa memunculkan pengecualian apa pun. Pantau kualitas dengan menjalankan evaluasi otomatis (penilaian LLM sebagai juri) setiap hari pada sampel respons produksi. Berikan peringatan ketika skor kualitas rata-rata bergerak turun di bawah ambang batas, sebelum pengguna mulai mengeluh.
Memulai Praktik Observabilitas Anda
Jangan menunggu hingga terjadi insiden produksi untuk menambahkan observabilitas. Mulailah dengan tiga langkah minimum: (1) catat setiap panggilan LLM beserta input lengkap, output, dan jumlah tokennya ke basis data atau file, (2) tetapkan ID permintaan untuk setiap interaksi pengguna dan sertakan ID tersebut dalam semua entri log, serta (3) tambahkan pengukuran waktu per langkah ke pipeline Anda. Ketiga hal ini saja akan membuat 80% tugas pen-debug-an dapat diselesaikan dalam hitungan menit, bukan jam.
Pemeriksaan Singkat
Ujilah pemahaman Anda tentang alasan aplikasi LLM sulit di-debug dari pelajaran ini.
Rangkuman Pelajaran
Dalam pelajaran ini, Anda mempelajari bahwa nondeterminisme membuat bug LLM bersifat intermiten dan sulit direproduksi tanpa menangkap konteks permintaan secara lengkap; kegagalan diam-diam (jawaban salah dari panggilan API yang berhasil) melewati pemantauan kesalahan tradisional dan memerlukan pemeriksaan kualitas semantik; serta penelusuran per langkah dengan korelasi ID permintaan merupakan hal minimum yang diperlukan untuk mendiagnosis kegagalan dalam pipeline RAG dan agen multi-langkah. Selanjutnya, kita akan mengimplementasikan penelusuran dengan LangSmith.
Belajar Python dengan tutor AI — gratis
Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.
- Kursus
- 30
- Pelajaran
- 120
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Mengapa Aplikasi LLM Sulit Di-debug” gratis?
Ya — teks lengkap “Mengapa Aplikasi LLM Sulit Di-debug” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Engineering Academy, upgrade ke CoddyKit PRO. Kursus AI Engineering Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Mengapa Aplikasi LLM Sulit Di-debug”?
Pahami alasan logging tradisional tidak memadai untuk aplikasi LLM, informasi yang Anda perlukan untuk mendiagnosis kegagalan dalam alur pemrosesan RAG dan agen, serta model data pelacakan. Kamu berlatih AI Engineering Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai AI Engineering Academy?
Tidak diperlukan pengalaman sebelumnya. AI Engineering Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.
Berapa lama pelajaran “Mengapa Aplikasi LLM Sulit Di-debug” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Engineering Academy ini?
Ya. Setiap pelajaran AI Engineering Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Mengapa Aplikasi LLM Sulit Di-debug
- Pelacakan dengan LangSmith
- Langfuse untuk Observabilitas yang Tidak Bergantung pada Model
- Peringatan untuk Latensi, Biaya, dan Penurunan Kualitas