cd ../
AI·2026-04-14·5 min read·# entry/033

Langfuse — LLM 어플리케이션을 위한 관측 플랫폼

LLM 기반 서비스의 내부를 들여다보기 어렵다는 문제에서 출발한 Langfuse. Trace 구조, 데이터 적재 방식, 커스터마이징 전략까지 정리합니다.

LLM을 서비스에 붙이고 나면 이상한 일들이 생깁니다. 답변이 왜 이렇게 나왔는지 알 수 없고, 비용이 얼마나 나가는지 파악이 안 되며, 어디서 느려지는지 찾기 어렵습니다.

기존의 APM 도구로는 LLM 내부를 들여다볼 수 없다.

Langfuse는 바로 이 문제에서 출발합니다.


탄생 배경

Langfuse는 2023년 초에 등장했습니다. ChatGPT가 LLM 붐을 불러온 직후, 개발자들이 LLM을 활용해 체인과 에이전트를 빠르게 구축하기 시작하던 시기입니다.

그런데 프로덕션에 올리고 나면 세 가지 문제가 반복됐습니다.

기대했던 것
  • 어떤 프롬프트가 나가는지 확인 가능 - 토큰 사용량과 비용 추적 - 응답 품질 모니터링
실제로 맞닥뜨린 것
  • LLM 내부는 블랙박스 — 일반 로그로는 인과관계 추적 불가 - 토큰 비용을 체계적으로 집계할 도구 부재 - 답변 품질을 평가할 수 있는 전용 워크플로우 없음
Langfuse가 해결하려는 핵심

단순한 로그 수집이 아닙니다. LLM 어플리케이션의 개발 생명주기 전체를 지원하는 엔지니어링 플랫폼을 목표로 합니다.


주요 기능

전체 요청 흐름을 하나의 Trace로 시각화합니다.

중첩된 LLM 호출, 데이터베이스 조회, 도구 실행이 각각 어느 시점에 얼마나 걸렸는지 한눈에 볼 수 있습니다.

[Trace] 사용자 질문 수신
  ├── [Span]       RAG 문서 검색 (120ms)
  ├── [Generation] GPT-4o 호출 (1.2s)
  │     ├── input:  "검색된 컨텍스트 + 질문"
  │     └── output: "최종 답변"
  └── [Event]      응답 전송 완료

데이터 구조: Trace와 Observation

Langfuse의 데이터 계층은 임의로 설계된 것이 아닙니다. 분산 시스템 모니터링의 표준인 OpenTelemetry 개념을 차용했습니다.

Trace  ─────────────────────────────────────────────────
│  사용자가 질문을 던지고 답변을 받기까지의 전체 과정
│
├── Observation (Generation)
│     LLM 호출 — 입출력 텍스트, 토큰 수, 모델명
│
├── Observation (Span)
│     특정 로직의 실행 구간 — DB 조회, 도구 실행, 외부 API
│
└── Observation (Event)
      특정 시점의 기록 — 클릭, 에러 발생, 상태 변경
Log vs Trace

일반 Log는 단발적인 사건의 기록입니다. Trace는 하나의 요청이 시스템을 거쳐가는 전체 경로와 인과관계의 기록입니다.

사용자 질문 → 에이전트 판단 → 도구 실행 → LLM 답변 으로 이어지는 흐름을 추적하기 때문에 Trace라고 부릅니다.


데이터 집계 방식

집계 항목은 세 가지 경로로 결정됩니다.

1. SDK 자동 집계

Langfuse SDK를 붙이면 별도 설정 없이 아래 항목이 자동으로 수집됩니다.

  • 모델 이름, 입출력 텍스트
  • 토큰 사용량 (입력/출력 분리)
  • 응답 시간

2. 개발자 커스터마이징 (metadata)

서비스 특성에 맞는 데이터는 metadata 필드에 자유롭게 추가합니다.

trace = langfuse.trace(
    name="chat-response",
    metadata={
        "userId": "user_123",
        "organizationId": "org_456",
        "featureFlag": "new-rag-v2",
        "sessionId": "sess_789"
    }
)

이 값들은 Langfuse 대시보드에서 필터링과 그룹화의 기준이 됩니다. "조직별 평균 비용"이나 "기능 플래그 A와 B의 품질 비교" 같은 분석이 가능해집니다.

3. Score (평가 데이터)

LLM 답변 품질은 기계적으로 알 수 없습니다. 사용자 피드백이나 평가 LLM의 결과를 Score로 전송합니다.

langfuse.score(
    trace_id=trace.id,
    name="user-feedback",
    value=1,       # 1: 좋아요, -1: 싫어요
    comment="답변이 정확했습니다"
)

비동기 적재 메커니즘

Langfuse는 서비스 성능에 영향을 주지 않기 위해 비동기 방식으로 데이터를 전송합니다.

[요청 시작]
  └── trace 객체 생성 → 시작 시간 + 입력값 전송

[단계별 실행 — Incremental Updates]
  └── Tool 호출, 추론 루프마다 개별 Observation 전송

[실행 완료]
  └── 종료 시간 + 출력값 + 토큰 사용량 묶어서 전송
내부적으로 어떻게 동작하는가

SDK 내부에는 인메모리 큐가 존재합니다. 데이터가 일정량 쌓이면 백그라운드 스레드가 배치로 Langfuse 서버에 API를 호출합니다.

애플리케이션 스레드        백그라운드 스레드
─────────────────          ────────────────────
trace 생성      →  큐 push
span 종료       →  큐 push   →  배치 API 호출
generation 완료 →  큐 push   →  배치 API 호출

이 덕에 LLM 응답 시간에 Langfuse 전송 지연이 더해지지 않습니다. 단, 프로세스가 갑작스럽게 종료되면 큐에 남은 데이터는 유실될 수 있으므로 종료 시점에 langfuse.flush()를 명시적으로 호출하는 것이 권장됩니다.


정리

Langfuse가 필요한 시점
  • LLM 답변이 왜 이렇게 나왔는지 디버깅이 어려울 때
  • 프롬프트를 수정했을 때 품질이 나아졌는지 정량적으로 확인하고 싶을 때
  • 토큰 비용이 어디서 얼마나 나가는지 파악이 안 될 때
  • 에이전트의 도구 실행 순서와 소요 시간을 시각화하고 싶을 때

기존 APM이나 단순 로깅으로는 LLM 어플리케이션의 내부를 충분히 들여다볼 수 없습니다. Langfuse는 OpenTelemetry 기반의 Trace 구조를 LLM 도메인에 맞게 재해석하여, 관측 가능성(Observability)의 공백을 채웁니다.