LLM Observability ด้วย Langfuse ปี 2026: Dashboard ที่ต้องมีก่อน Deploy Production

สร้าง LLM observability stack ด้วย Langfuse v3.0: metric ที่ต้อง track, ติดตั้ง self-hosted, instrument Python SDK, สร้าง dashboard และ alert สำหรับ production

อัปเดตล่าสุด: 15 สิงหาคม 2026

LLM Observability คือกระบวนการเก็บ trace, metric และ log ของทุกการเรียก LLM ในระบบ production เพื่อให้ทีมมองเห็น latency, cost, คุณภาพของ output และเส้นทางการทำงานของ agent ได้แบบ real-time ในปี 2026 Langfuse กลายเป็นตัวเลือกอันดับต้น ๆ ของทีม production LLM เพราะเป็น open source, self-hostable, รองรับ OpenTelemetry และมี integration กับ LangChain, LlamaIndex, LangGraph ครบทุกตัว บทความนี้จะพาสร้าง observability stack ที่คุณจะเสียใจถ้าไม่ได้ทำก่อน deploy (ผมเคยผ่านฝันร้ายนี้มาแล้ว จะเล่าให้ฟังในหัวข้อถัดไป)

  • Langfuse v3.0 (เปิดตัวต้นปี 2026) รองรับ OpenTelemetry protocol แบบ native ทำให้ integrate กับ Grafana, Datadog, Jaeger ได้โดยไม่ต้องเขียน adapter
  • Metric ที่ต้อง track ก่อน deploy production คือ latency p50/p95/p99, token usage per user, cost per session และ error rate จำแนกตาม provider
  • Langfuse self-hosted บน Docker Compose ใช้ทรัพยากรราว 2 vCPU + 4 GB RAM สำหรับ traffic 1M traces/เดือน ถูกกว่า LangSmith SaaS ประมาณ 8–10 เท่า
  • Prompt versioning ใน Langfuse ช่วยให้ทำ A/B test prompt ได้โดยไม่ต้อง redeploy โค้ด (เปลี่ยน prompt ผ่าน UI แล้ว SDK ดึงเวอร์ชันล่าสุดอัตโนมัติ)
  • Alert สำหรับ p99 latency ที่ทะลุ 8 วินาที และ error rate เกิน 2% ควรเชื่อมกับ PagerDuty หรือ Slack ผ่าน webhook ตั้งแต่วันแรก

LLM Observability คืออะไร และทำไมต้องทำก่อน deploy

ตอนที่ทีมผมเริ่ม ship LLM feature แรกในปี 2023 เราคิดว่า application logging แบบเดิมพอแล้ว พิมพ์ prompt กับ response ลง stdout แล้วส่งไป Loki ก็จบ. ผ่านไปสองสัปดาห์เรื่องบานปลาย: cost บิลจาก OpenAI พุ่งไป 40,000 บาทโดยไม่รู้ว่าใครใช้อะไร, latency p99 พุ่งไป 45 วินาทีตอนเช้าวันจันทร์, และ agent ที่ควรจะจบใน 3 ขั้น กลับ loop ไป 27 ขั้นก่อนหมด context window ตอนนั้นเลย.

บทเรียนแรกคือ LLM observability ต่างจาก application observability ตรงที่ต้องเก็บ trace แบบ nested เพราะการเรียก LLM หนึ่งครั้งจากมุมของผู้ใช้ อาจกลายเป็น 15 sub-call ภายใน (retrieval → rerank → LLM → tool call → LLM อีกรอบ). แต่ละ span ต้องบันทึก prompt เต็ม, response เต็ม, token count, cost คำนวณตาม pricing ณ เวลานั้น, และ metadata อย่าง user_id, session_id, model_version เพื่อให้ debug ได้ว่าทำไม conversation หนึ่งพัง

Observability ต้องมาก่อน deploy ไม่ใช่หลัง. เมื่อ user report ว่า "AI ตอบผิด" คุณจะไม่มีทางย้อนดูได้เลยว่ามัน retrieve context ผิด, ใช้ prompt version ผิด, หรือ LLM hallucinate ถ้าไม่ได้เก็บ trace ไว้ตั้งแต่ต้น. การเพิ่ม instrumentation หลัง incident แรก แปลว่าคุณกำลังบินแบบตาบอดจนกว่าจะเกิด incident ครั้งที่สอง

Metrics 8 ตัวที่ต้อง track ในระบบ LLM production

นี่คือ metric list ที่ผมใช้เป็น checklist ทุกครั้งที่ onboard LLM app ใหม่เข้า production. ก่อนที่ SRE จะเซ็นอนุมัติ deploy ได้ metric ทั้ง 8 ตัวต้องมี dashboard และ alert threshold ที่ทีมเห็นพ้องแล้ว

  • End-to-end latency (p50/p95/p99): จากที่ user กด submit จนได้ response ครบ ไม่ใช่แค่เวลาเรียก LLM API
  • Time to first token (TTFT): สำหรับ streaming response, user perception ขึ้นกับ TTFT มากกว่า total latency
  • Token usage per request: input tokens, output tokens แยกกัน เพื่อคำนวณ cost ได้แม่นยำ
  • Cost per user / per session: เพื่อตรวจจับ abuse และวางแผน pricing tier
  • Error rate จำแนกตาม provider และ error type: rate limit, context length exceeded, safety refusal, timeout, 5xx
  • Retry count และ fallback trigger rate: เมื่อไหร่ที่ระบบต้อง fall back จาก Claude Opus ไป Sonnet หรือจาก OpenAI ไป Anthropic
  • Cache hit rate: ทั้ง prompt caching และ semantic cache. ถ้าอ่านเรื่อง Prompt Caching ด้วย Claude API แล้วยังไม่ได้ track hit rate ก็ไม่รู้ว่ามัน work จริงหรือเปล่า
  • Quality score จาก user feedback: thumbs up/down หรือ implicit signal (เช่น ผู้ใช้ regenerate)

Langfuse vs LangSmith vs Helicone: เลือกอะไรดีในปี 2026

สามตัวนี้เป็นตัวเลือกหลักในตลาด LLM observability ปี 2026 และแต่ละตัวมี trade-off ชัดเจน. ผมได้ใช้ทั้งสามในโปรเจกต์ต่างกัน สรุปสั้น ๆ คือ Langfuse สำหรับทีมที่ต้องการควบคุมข้อมูลและงบประมาณ, LangSmith สำหรับทีมที่ใช้ LangChain ecosystem หนัก, ส่วน Helicone สำหรับทีมที่ต้องการเร่งด่วนโดยไม่แตะโค้ด

คุณสมบัติLangfuse v3.0LangSmithHelicone
LicenseMIT (open source)ProprietaryApache 2.0
Self-hostingฟรี, Docker ComposeEnterprise plan เท่านั้นฟรี, Docker
ราคา cloud (1M traces/เดือน)~$59~$500+~$80
Integration methodSDK + OpenTelemetrySDK (LangChain-first)Proxy (base_url swap)
Prompt managementใช้ได้ผ่าน UI + APIใช้ได้Beta
Evals ในตัวรองรับ LLM-as-judgeรองรับเต็มBasic
OpenTelemetry nativeใช่ (v3.0)ไม่Partial
เหมาะกับทีม production ที่ควบคุมข้อมูลเองทีมที่ใช้ LangChain หนักทีมที่ต้อง instrument เร็ว

ในทีมผมเลือก Langfuse เพราะเราต้อง comply กับ PDPA ในไทยและ SOC 2 ของลูกค้าองค์กร. Self-host ใน VPC เดียวกับ application ทำให้ trace ไม่ออกนอก network เลย. นอกจากนี้ Langfuse v3.0 รองรับ OpenTelemetry trace spec เต็มรูปแบบ แปลว่าเราส่ง span เดียวกันเข้าทั้ง Langfuse และ Grafana Tempo ได้พร้อมกันโดยไม่ซ้ำซ้อน

ติดตั้ง Langfuse self-hosted ด้วย Docker Compose

วิธีที่เร็วที่สุดในการเริ่มคือใช้ docker-compose.yml จาก official repo. สำหรับ production ควรแยก PostgreSQL และ ClickHouse ออกไปใช้ managed database แต่สำหรับ dev หรือ traffic ต่ำกว่า 500k traces/เดือน stack ทั้งหมดใน compose file เดียวก็เพียงพอ (ตอนแรกผมก็รันแบบ single VM อยู่ 6 เดือน แล้วค่อยย้ายทีหลัง)

# โคลน repo และเริ่ม service
git clone https://github.com/langfuse/langfuse.git
cd langfuse
cp .env.prod.example .env

# แก้ค่าต่อไปนี้ใน .env ก่อน start
# NEXTAUTH_SECRET=$(openssl rand -base64 32)
# SALT=$(openssl rand -base64 32)
# ENCRYPTION_KEY=$(openssl rand -hex 32)
# CLICKHOUSE_PASSWORD=<strong-password>
# POSTGRES_PASSWORD=<strong-password>

# start stack: PostgreSQL + ClickHouse + Redis + Langfuse web/worker
docker compose up -d

# เช็คว่า service พร้อม
docker compose ps
curl http://localhost:3000/api/public/health

เมื่อ container ขึ้นครบแล้วเข้า http://localhost:3000 สร้าง admin account, สร้าง organization และ project แรก จากนั้นไปที่ Settings → API Keys เพื่อสร้าง public key (pk-lf-...) และ secret key (sk-lf-...) สำหรับ SDK

Instrument Python application ด้วย Langfuse SDK v3

SDK v3 ใช้ decorator-based tracing เป็นหลัก ทำให้ code เดิมเปลี่ยนน้อยที่สุด. ตัวอย่างต่อไปนี้เป็น RAG endpoint ที่ instrument ครบทุก span (retrieval, rerank, LLM call) ใช้กับ FastAPI ในปี 2026

from fastapi import FastAPI
from langfuse import Langfuse, observe
from langfuse.anthropic import AsyncAnthropic
from qdrant_client import AsyncQdrantClient
import os

# init client อ่านค่าจาก env: LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_HOST
langfuse = Langfuse()
anthropic = AsyncAnthropic()  # wrapper ที่ auto-trace ทุก call
qdrant = AsyncQdrantClient(url=os.environ["QDRANT_URL"])

app = FastAPI()

@observe(name="retrieve_context", capture_input=True, capture_output=True)
async def retrieve_context(query: str, top_k: int = 5) -> list[str]:
    # span นี้ auto-record latency, input, output ให้ Langfuse
    results = await qdrant.query_points(
        collection_name="knowledge_base",
        query=query,
        limit=top_k,
    )
    return [hit.payload["text"] for hit in results.points]

@observe(name="rerank", capture_input=True, capture_output=True)
async def rerank(query: str, docs: list[str]) -> list[str]:
    # ตัวอย่างการเรียก reranker และ trace score กลับมา
    scores = await call_reranker(query, docs)
    langfuse.update_current_span(metadata={"rerank_scores": scores[:3]})
    return [d for _, d in sorted(zip(scores, docs), reverse=True)[:3]]

@observe(name="rag_pipeline")
async def rag_answer(user_id: str, session_id: str, question: str) -> str:
    # ใส่ context ระดับ trace เพื่อให้ filter ตาม user ได้ใน UI
    langfuse.update_current_trace(
        user_id=user_id,
        session_id=session_id,
        tags=["rag", "prod"],
    )

    docs = await retrieve_context(question)
    top_docs = await rerank(question, docs)

    prompt = f"บริบท:\n{chr(10).join(top_docs)}\n\nคำถาม: {question}"
    response = await anthropic.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}],
    )
    return response.content[0].text

@app.post("/chat")
async def chat(user_id: str, session_id: str, question: str):
    answer = await rag_answer(user_id, session_id, question)
    return {"answer": answer}

เมื่อเรียก endpoint นี้ 1 ครั้ง คุณจะเห็น trace หนึ่งต้นใน Langfuse UI มี 3 span nested อยู่ (retrieve → rerank → anthropic.messages.create) พร้อม token count, cost, latency แยกทุก node เผยให้เห็นว่า bottleneck อยู่ที่ไหน. สำหรับผู้ที่ใช้ agent framework อย่าง LangGraph ดู LangGraph 0.4 ฉบับปฏิบัติสำหรับ Multi-Agent Workflow ที่มีตัวอย่างการ trace state transition

สร้าง dashboard สำหรับ latency p99 และ cost tracking

Langfuse UI มี dashboard สำเร็จรูปให้ใช้ทันที แต่ทีม production ควรสร้าง custom dashboard ที่ตรงกับ SLO ของตัวเอง. ตัวอย่างต่อไปนี้คือ layout ที่ผมใช้จริง แบ่งเป็น 4 quadrant บนหน้าเดียว

Quadrant 1: Latency SLO tracker

Line chart แสดง p50, p95, p99 latency ในช่วง 24 ชั่วโมงย้อนหลัง มี horizontal line ที่ SLO target (เช่น p99 = 8000ms) และเปลี่ยนสีเมื่อ metric ทะลุ threshold นาน 5 นาทีขึ้นไป. ใช้ Langfuse metrics API หรือส่ง span ต่อไปยัง Grafana ได้ทั้งสองทาง

Quadrant 2: Cost burndown

Bar chart รายวัน + trendline ที่ project cost สิ้นเดือน แยกตาม model (Sonnet, Opus, Haiku, GPT-4.1) เพื่อให้ finance ตัดสินใจได้ว่าควร shift traffic ไปโมเดลถูกกว่าได้หรือไม่ query ผ่าน /api/public/metrics endpoint

Quadrant 3: Error rate breakdown

Stacked area chart แสดง error rate แยกตาม type: rate_limit, context_length, safety_refusal, timeout, 5xx. ทำให้เห็นทันทีว่าถ้า error พุ่งเป็นเพราะโดน rate limit หรือ prompt ยาวเกิน

Quadrant 4: User feedback signal

Ratio chart ของ thumbs up vs thumbs down จำแนกตาม prompt version เชื่อมกับ prompt versioning ใน section ถัดไป เพื่อให้เห็นได้ทันทีว่า prompt เวอร์ชันใหม่ทำให้ user happy ขึ้นหรือแย่ลง

ตั้ง SLO ให้ตรงกับ user perception

ทีมส่วนใหญ่ตั้ง SLO ผิดตั้งแต่แรกโดยเอา latency ของ LLM API มาตั้งตรง ๆ (เช่น "p99 ต้องต่ำกว่า 5 วินาที") ซึ่งไม่สะท้อน user experience เลย. User รู้สึกว่า "เร็ว" หรือ "ช้า" จาก time to first token (TTFT) มากกว่า total time โดยเฉพาะเมื่อ stream response กลับมาเป็น token-by-token, user เริ่มอ่านตั้งแต่ token แรกออก แม้ทั้ง response จะยาว 30 วินาที ก็ยังรู้สึกโอเคถ้า TTFT อยู่ที่ 800ms

SLO ที่แนะนำสำหรับ conversational AI: TTFT p95 ≤ 1500ms, throughput ≥ 40 tokens/second, และ end-to-end p99 ≤ 20 วินาทีสำหรับ agent ที่มี multi-step tool call. ค่า p99 ของ conversational บาง product ผมเคยเห็นสูงถึง 45 วินาทีในกรณี agent workflow แต่ user ก็ยังพอใจเพราะมี progress indicator ที่บอกว่า "กำลังค้นหา..." → "กำลังวิเคราะห์...". Observability ช่วยตอบว่าควรใส่ progress indicator ตอนไหน

เก็บ metric TTFT ผ่าน SDK ทำได้โดยใช้ streaming callback ที่ mark เวลาตอน token แรกออก แล้วส่งเป็น custom metric เข้า Langfuse ผ่าน langfuse.update_current_span(metadata={"ttft_ms": ttft}) จากนั้น aggregate ใน dashboard เป็น percentile ต่อ endpoint

Alerting: เมื่อไหร่ควร page on-call

Dashboard ที่ไม่มีใครดูก็เท่ากับไม่มี dashboard. กฎที่ผมใช้ในทีม (ปรับตาม context ของแต่ละองค์กรได้ตามสมควร):

  • Page on-call ทันที: p99 latency > 15s ต่อเนื่อง 5 นาที, error rate > 5% ต่อเนื่อง 3 นาที, หรือ cost วันนี้เกิน budget รายวัน 200%
  • Slack alert ในเวลาทำการ: p95 > SLO นาน 15 นาที, cache hit rate ลดลง > 20% เทียบกับสัปดาห์ก่อน, retry rate > 10%
  • Weekly report: top 20 session ที่แพงที่สุด, top 10 conversation ที่มี user feedback ลบ, drift ของ cost per session เทียบเดือนที่แล้ว

Langfuse v3.0 มี webhook integration แบบ native ส่ง event ตรงไป PagerDuty, Slack, OpsGenie ได้โดยไม่ต้องทำ polling script เอง. ตั้ง alert rule ผ่าน UI ที่ Project Settings → Alerts และเลือก metric, threshold, evaluation window ได้ในหน้าเดียว

Runbook สำหรับ incident LLM ระดับพื้นฐาน

เมื่อ page เด้ง on-call ต้องมี runbook ที่ตอบคำถาม 3 ข้อภายใน 5 นาที: (1) provider ไหนที่ล่ม (Anthropic/OpenAI/Azure), (2) เป็น incident ของ provider หรือของเราเอง, (3) มี fallback plan พร้อม execute หรือยัง. Langfuse trace ช่วยตอบข้อ 1 และ 2 ได้เร็วเพราะ filter ตาม model, error_type, region ได้ใน 2 คลิก

Runbook ที่ผมใช้จริงมี 4 ขั้น. (1) เช็ค status page ของ provider ก่อน ถ้า provider ล่มให้ toggle feature flag เปลี่ยนไป fallback provider ทันที. (2) ถ้าเป็น rate limit ให้ scale ขึ้น tier หรือใช้ traffic shaping ที่ gateway. (3) ถ้าเป็น context length exceeded แสดงว่า retrieval คืน chunk ยาวเกิน ให้ fallback ไป summarization mode. (4) ถ้าเป็น 5xx สุ่มจาก provider เดียว ให้เปิด circuit breaker 60 วินาที. Decision tree นี้ครอบคลุมประมาณ 90% ของ incident ที่เกิดในปีที่ผ่านมา (อย่างน้อยก็ในโปรเจกต์ที่ผมดูแลอยู่)

Prompt versioning และ A/B testing ผ่าน UI

ฟีเจอร์ที่ทำให้ Langfuse ต่างจาก logging tool ทั่วไปคือ prompt management. Langfuse เก็บ prompt template เป็น first-class object ใน database มี version history และ SDK ดึงเวอร์ชันล่าสุดโดย fetch จาก server ทุกครั้งที่โปรเซสรีสตาร์ท (หรือทุก N นาทีตาม cache TTL)

from langfuse import Langfuse

langfuse = Langfuse()

# ดึง prompt เวอร์ชัน production ล่าสุดจาก Langfuse
prompt = langfuse.get_prompt("rag-answer", label="production")

# compile ด้วย variable แบบ Mustache
compiled = prompt.compile(
    context="\n".join(top_docs),
    question=user_question,
)

# call LLM โดย pass prompt object ไปด้วย
# เพื่อให้ trace ผูก generation กับ prompt version อัตโนมัติ
response = await anthropic.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": compiled}],
    langfuse_prompt=prompt,  # link generation ↔ prompt version
)

ขั้นตอน A/B test คือ deploy prompt version ใหม่ด้วย label experiment-a และเปลี่ยน routing rule ใน SDK ให้ 10% ของ traffic ดึง label นี้. จากนั้นดู metric quality score, latency, token usage แยกระหว่าง production และ experiment-a ใน dashboard ที่กรองด้วย prompt.version

วิธีนี้ทำให้ product manager หรือ prompt engineer แก้ prompt ได้เองผ่าน UI โดยไม่ต้องรอ engineer deploy คล้ายกับที่ทีมใช้ feature flag แต่กับ prompt. สำหรับ workflow evaluation ที่ครบขั้นตอนอ่านต่อที่ LLM Evaluation ปี 2026 ด้วย DeepEval และ Promptfoo และถ้ายังไม่ได้ต่อ agent เข้ากับ tool calling ให้อ่าน Claude Tool Use ปี 2026 ก่อนเป็นพื้นฐาน

คำถามที่พบบ่อย

Langfuse ใช้ทรัพยากรเท่าไหร่สำหรับ traffic 1M traces ต่อเดือน?

Setup แบบ Docker Compose บน single VM ที่ 2 vCPU, 4 GB RAM, 50 GB SSD รองรับได้ 1M traces/เดือนสบาย ๆ (ราว 40 traces/นาที peak). ถ้าเกิน 5M แนะนำแยก ClickHouse ออกเป็น managed service เช่น ClickHouse Cloud หรือใช้ Aiven เพราะ query time-series บน trace หลายสิบล้าน record ต้องการ RAM มากขึ้น

Langfuse ต่างจาก OpenTelemetry ทั่วไปยังไง ในเมื่อ v3.0 support OTel แล้ว?

OpenTelemetry เป็น protocol และ collector สำหรับ trace/metric/log ทั่วไป แต่ไม่มี UI ที่เข้าใจ semantic ของ LLM (prompt, completion, token, cost). Langfuse คือ backend + UI ที่ specialize กับ LLM data. คุณส่ง OTel span เข้า Langfuse ได้ และ Langfuse จะ enrich ข้อมูลด้วย cost calculation, prompt linking และ eval integration ที่ Jaeger หรือ Tempo ทำไม่ได้

ต้องใช้ LangChain ไหมถึงจะใช้ Langfuse ได้?

ไม่ต้อง. Langfuse SDK มี native integration สำหรับ Anthropic, OpenAI, LiteLLM, LlamaIndex, LangGraph และ Vercel AI SDK แยกต่างหาก รวมทั้งมี generic @observe decorator สำหรับ instrument code ตัวเองได้ทุก framework (ต่างจาก LangSmith ที่ออกแบบมาสำหรับ LangChain ecosystem เป็นหลัก)

Langfuse cloud vs self-hosted ต่างกันตรงไหนเชิงฟีเจอร์?

ฟีเจอร์เหมือนกัน 100% ตั้งแต่ Langfuse v2.5 เป็นต้นมา. ต่างเฉพาะ operational คือ cloud มี SSO, SOC 2, backup, upgrade อัตโนมัติ เหมาะกับ startup ที่ยังไม่มี DevOps ส่วน self-hosted เหมาะกับองค์กรที่มีข้อจำกัดด้านข้อมูล (PDPA, HIPAA) หรือต้องการควบคุมต้นทุนที่ scale ใหญ่

ควรเก็บ trace ไว้นานแค่ไหน?

สำหรับ debug และ incident review เก็บ 30–90 วันพอ. สำหรับ dataset ที่จะเอาไป fine-tune หรือทำ eval ให้ export ออกไปเก็บใน object storage (S3, GCS) แยก แล้วตั้ง TTL ใน ClickHouse ที่ 90 วันเพื่อคุม storage cost. Langfuse v3.0 มี built-in data retention policy ตั้งได้ต่อ project

Cara Donovan
เกี่ยวกับผู้เขียน Cara Donovan

AI operations lead at a B2B SaaS. Builds the unglamorous infrastructure that keeps prod LLM apps from melting.