Meluncurkan integrasi LLM tanpa observabilitas seperti mendeploy aplikasi web tanpa log — Anda akan menemukan masalah dari keluhan pengguna, bukan dari pemantauan Anda. Tantangannya adalah bahwa aplikasi LLM gagal dengan cara kualitatif yang melewatkan metrik tradisional: panggilan API berhasil dengan 200 OK, tetapi responsnya salah secara faktual, di luar topik, atau mengabaikan instruksi kritis. Setelah menjalankan integrasi LLM produksi selama 18 bulan, ini adalah stack observabilitas yang telah saya standarkan.
Observabilitas tradisional (latensi, tingkat error, throughput) diperlukan tetapi tidak cukup untuk aplikasi LLM. Anda juga membutuhkan: logging prompt/respons (teks lengkap, bukan hanya metadata), konsumsi token per panggilan dan per pengguna, pelacakan versi model, sinyal kualitas (umpan balik pengguna, tingkat penyelesaian tugas, deteksi halusinasi), atribusi biaya, dan deteksi anomali untuk upaya injeksi.
Langfuse adalah platform observabilitas LLM open-source yang saya host sendiri di infrastruktur DigitalOcean kami. Ini menyediakan: logging trace (prompt lengkap, respons, model, latensi per panggilan), pelacakan biaya dengan kalkulasi harga token otomatis, pengelompokan sesi, alur kerja evaluasi, dan manajemen dataset untuk pengujian.
┌─────────────────────────────────────────────────────────────┐
│ LLM Observability Stack │
│ │
│ Application │
│ ┌─────────────────────────────────────────────┐ │
│ │ NestJS API → LLM Service → Langfuse │ │
│ │ │ SDK │ │
│ │ ▼ │ │
│ │ Anthropic / OpenAI │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Langfuse (self-hosted) │ │
│ │ - Traces: prompt + response + latency │ │
│ │ - Costs: tokens × price per model │ │
│ │ - Sessions: linked conversation turns │ │
│ │ - Evaluations: quality scores │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Grafana Dashboard + PagerDuty Alerts │ │
│ │ - Cost per feature per day │ │
│ │ - Latency p50/p95/p99 │ │
│ │ - Error rate by model │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Dari pengalaman saya memantau integrasi LLM: atur peringatan biaya dari hari pertama, bukan setelah mendapat tagihan kejutan. Biaya token dapat melonjak secara dramatis jika ada bug yang menyebabkan sistem mengirim prompt masif atau memasuki loop panggilan LLM. Saya pernah mengalami insiden di mana loop retry menyebabkan 400K token dalam 10 menit — tertangkap oleh peringatan sebelum menjadi insiden ratusan dolar.
Kuncinya adalah menginstrumentasi setiap panggilan LLM dengan metadata yang konsisten: user ID, session ID, nama fitur, parameter model, dan prompt/respons lengkap. Gunakan library tracing yang menangani serialisasi dan batching untuk menghindari penambahan latensi ke aplikasi Anda.
Implementasikan atribusi biaya di tingkat fitur, bukan hanya tingkat pengguna. Tandai setiap panggilan LLM dengan fitur yang memicunya (misalnya, invoice_summary, chat_support, document_extraction). Ini memungkinkan Anda mengidentifikasi fitur mana yang mendorong biaya dan mana yang tidak efisien.
import Anthropic from "@anthropic-ai/sdk"
import { Langfuse } from "langfuse"
const anthropic = new Anthropic()
const langfuse = new Langfuse({
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
secretKey: process.env.LANGFUSE_SECRET_KEY,
baseUrl: process.env.LANGFUSE_HOST, // your self-hosted instance
})
async function callLLM(params: {
userId: string
sessionId: string
feature: string
systemPrompt: string
userMessage: string
}) {
const trace = langfuse.trace({
id: crypto.randomUUID(),
userId: params.userId,
sessionId: params.sessionId,
tags: [params.feature],
metadata: { feature: params.feature },
})
const generation = trace.generation({
name: "claude-call",
model: "claude-opus-4-5",
input: [
{ role: "system", content: params.systemPrompt },
{ role: "user", content: params.userMessage },
],
startTime: new Date(),
})
try {
const response = await anthropic.messages.create({
model: "claude-opus-4-5",
max_tokens: 1024,
system: params.systemPrompt,
messages: [{ role: "user", content: params.userMessage }],
})
generation.end({
output: response.content,
usage: {
input: response.usage.input_tokens,
output: response.usage.output_tokens,
},
endTime: new Date(),
})
return response.content[0].type === "text" ? response.content[0].text : ""
} catch (err) {
generation.end({ level: "ERROR", statusMessage: String(err) })
throw err
} finally {
await langfuse.flushAsync()
}
}Metrik keberhasilan/kegagalan biner tidak menangkap kualitas output LLM. Bangun pipeline evaluasi kualitas: (1) Pengumpulan umpan balik pengguna — jempol atas/bawah pada respons. (2) Pemeriksaan format otomatis — verifikasi output terstruktur sesuai skema yang diharapkan. (3) Pemeriksaan kesetiaan untuk RAG. (4) Persentil latensi — lacak p50, p95, p99 secara terpisah.
Log trace LLM berisi teks lengkap input pengguna dan output model. Jika aplikasi Anda memproses data pribadi (nama, detail kontak, catatan keuangan), log trace Anda adalah data pribadi yang tunduk pada GDPR, UU PDP Indonesia, atau regulasi privasi lainnya. Implementasikan redaksi PII sebelum logging: gunakan regex atau model deteksi PII khusus untuk menghapus email, nomor telepon, nomor identitas, dan data keuangan dari trace.
Bangun detektor anomali khusus untuk mode kegagalan LLM: distribusi panjang output (perubahan mendadak dalam panjang output rata-rata mungkin mengindikasikan prompt injection), frekuensi tool call (lonjakan tool call per sesi menunjukkan loop atau injeksi), tingkat error per model, dan anomali spesifik pengguna.
Stack lengkap yang saya jalankan: Langfuse (self-hosted, Docker Compose, PostgreSQL) untuk trace LLM dan pelacakan biaya. Metrik Prometheus kustom untuk infrastruktur. Dashboard Grafana menggabungkan data Prometheus dan Langfuse melalui API. Peringatan PagerDuty untuk lonjakan biaya, peningkatan tingkat error, dan pelanggaran latensi p99. Retensi: 90 hari untuk trace lengkap, metrik agregat disimpan selamanya.