Observability aplikasi AI — logging, tracing, metrik
Observability aplikasi biasa sudah cukup rumit — request, response, latency. Aplikasi AI menambah dimensi baru: isi prompt, jumlah token, langkah-langkah internal agent, dan biaya yang menumpuk per panggilan.
Intisari
- Log HTTP standar (status code, latency) tidak cukup untuk debug aplikasi AI — kamu perlu tahu prompt apa yang dikirim, model apa yang dipakai, dan berapa token masuk/keluar.
- OpenTelemetry GenAI semantic conventions adalah standar terbuka (vendor-neutral) untuk atribut yang harus dicatat di setiap panggilan LLM — supaya tooling observability apa pun bisa membacanya konsisten.
- Untuk sistem dengan banyak langkah (agent Fase 3, RAG Fase 5, tool use Fase 2), tracing — bukan cuma logging — dibutuhkan untuk melihat urutan & durasi tiap langkah dalam satu request.
- Metrik kunci yang wajib dipantau: latency (termasuk time-to-first-token untuk streaming), token terpakai (input/output terpisah), error rate, dan tingkat penolakan/fallback (berapa sering guardrails Fase 6 terpicu).
- Simpan prompt & respons untuk debugging, tapi sadari implikasi privasi — data sensitif yang masuk prompt (Fase 2) juga ikut masuk log kalau tidak disaring.
Kenapa observability biasa tidak cukup
Log tradisional ("GET /api/chat 200 OK 340ms") memberitahu request berhasil dan berapa lama — tapi tidak menjawab pertanyaan yang paling sering muncul saat debug aplikasi AI: prompt apa persisnya yang dikirim? model mana yang dipakai? kenapa jawabannya aneh untuk input ini? Aplikasi AI butuh dimensi tambahan yang tidak ada di observability HTTP biasa.
Atribut standar: OpenTelemetry GenAI conventions
Alih-alih setiap tim menciptakan format logging sendiri, OpenTelemetry — standar observability vendor-neutral yang sudah dipakai luas untuk tracing/metrics aplikasi biasa — punya konvensi khusus untuk panggilan LLM. Memakainya berarti tooling observability apa pun (Grafana, Datadog, dll.) bisa membaca data ini secara konsisten.
import logging
def catat_panggilan_llm(model: str, response, durasi_ms: float, konteks: dict):
logging.info("llm_call", extra={
"gen_ai.request.model": model,
"gen_ai.usage.input_tokens": response.usage.input_tokens,
"gen_ai.usage.output_tokens": response.usage.output_tokens,
"gen_ai.response.finish_reason": response.stop_reason,
"duration_ms": durasi_ms,
"conversation_id": konteks.get("conversation_id"),
})
| Atribut | Kenapa penting |
|---|---|
gen_ai.request.model | Model routing (materi berikutnya) berarti model bisa berbeda tiap request — perlu tahu mana yang dipakai |
gen_ai.usage.input_tokens / output_tokens | Dasar perhitungan biaya per request — input dan output biasanya beda harga per token |
gen_ai.response.finish_reason | Membedakan respons selesai normal vs terpotong (max_tokens kena) vs berhenti karena tool call |
Tracing: melihat urutan langkah, bukan cuma hasil akhir
Untuk sistem dengan banyak langkah internal — agent loop (Fase 3), RAG (Fase 5), tool use (Fase 2) — satu "request" pengguna sebenarnya terdiri dari beberapa panggilan LLM & operasi lain yang berurutan. Logging datar (satu baris per event) membuat sulit melihat hubungan antar langkah. Tracing menyelesaikan ini dengan struktur span bersarang.
Trace: satu percakapan chatbot (trace_id: abc123)
├── Span: retrieval RAG (120ms)
│ └── query pgvector, 3 chunk ditemukan
├── Span: panggilan LLM #1 (890ms)
│ └── model minta tool "cek_stok"
├── Span: eksekusi tool cek_stok (45ms)
├── Span: panggilan LLM #2 — jawaban final (650ms)
└── Total: 1705ms, 1240 token, $0.008
Ini persis kebutuhan yang sama seperti debugging microservice — satu request yang melintasi
banyak service butuh trace_id yang sama di semua log-nya supaya bisa direkonstruksi
urutannya. Bedanya, di sini "service"-nya adalah langkah-langkah internal agent/RAG, dan setiap span
membawa metrik yang khas AI: token, model, biaya.
Metrik yang wajib dipantau
| Metrik | Kenapa dipantau |
|---|---|
| Latency (p50, p95, p99) | Rata-rata bisa terlihat baik padahal sebagian kecil pengguna mengalami timeout — p95/p99 menangkap ini |
| Time-to-first-token (untuk streaming, Fase 1) | Metrik pengalaman pengguna yang sebenarnya, beda dari total durasi |
| Token input/output per request | Dasar pemantauan biaya (materi berikutnya) dan deteksi anomali (prompt yang membengkak tanpa sadar) |
| Error rate per jenis error | Bedakan rate limit (Fase 2), timeout, vs error validasi — masing-masing butuh respons berbeda |
| Tingkat fallback/eskalasi (Fase 6) | Naik tiba-tiba bisa menandakan regresi kualitas (mis. setelah ubah prompt) sebelum pengguna ramai komplain |
Simpan prompt & respons — dengan hati-hati
Menyimpan isi prompt & respons lengkap sangat membantu debugging ("kenapa user ini dapat jawaban aneh?"), tapi ingat dari Fase 2: data sensitif yang masuk prompt ikut masuk log. Terapkan kebijakan retensi & akses yang sama ketatnya dengan log yang berisi data pengguna lain di sistemmu.
Latihan: tambahkan logging gen_ai.* ke fungsi kirim_pesan dari Fase 6,
catat durasi tiap panggilan dengan time.perf_counter(). Jalankan 5 percakapan berbeda, lalu
dari log yang terkumpul, hitung manual: total token terpakai, dan latency rata-rata per panggilan.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.