Langfuse 연동

Langfuse 연동

Langfuse와 LiteLLM을 연결하면 모든 프로바이더의 LLM 호출을 Langfuse로 보내 추적(tracing)하고, 프롬프트를 관리하고, 애플리케이션을 평가(evaluation)할 수 있어요. LiteLLM 콜백을 한 줄만 추가하면 다중 모델 호출까지 한 트레이스로 묶이는 구조라, 팀이 함께 LLM 애플리케이션을 디버깅·분석·반복 개선하는 데 아주 유용합니다.

출처: 공식문서

Langfuse란?

Langfuse(GitHub)는 오픈소스 LLM 엔지니어링 플랫폼으로, 모델 추적, 프롬프트 관리, 애플리케이션 평가를 제공해요. 팀이 LLM 애플리케이션을 함께 디버깅하고 분석하며 반복 개선하도록 돕는 도구죠.

:::tip 권장: OpenTelemetry v2 사용 Langfuse v3와 v4에서는 OpenTelemetry v2 가이드langfuse_otel 프리셋을 권장해요. 스팬 품질이 더 좋고 지연이 낮으며 네이티브 OpenTelemetry 의미론을 따르기 때문이에요. 아래의 langfuse SDK 콜백은 레거시 v2 SDK 연동으로, 하위 호환을 위해 유지되고 있습니다. :::

LiteLLM Proxy(LLM 게이트웨이)에서 쓰기

LiteLLM 프록시 서버에서 Langfuse로 로그를 보내려면 프록시 로깅 가이드를 따라가면 돼요.

다른 팀이나 가상 키를 서로 다른 Langfuse 프로젝트로 라우팅하려면 팀/키 기반 로깅을 참고하세요. 팀 기본값은 신뢰할 수 있는 config.yaml에 두고(os.environ/... 참조는 게이트웨이가 해석), 키별 콜백은 /key/generate·/key/update로 해석된 자격 증명 값과 함께 프로비저닝해요. 이것은 키에 저장된 설정이지 요청별 자격 증명이 아니에요.

LiteLLM Python SDK에서 쓰기

:::note 레거시 SDK 연동 이 섹션은 레거시 Langfuse v2 SDK 연동이에요. Langfuse v3+에서는 성능과 호환성이 더 나은 OpenTelemetry v2 연동을 선호하세요. :::

사전 준비

이 연동을 쓰려면 langfuse 패키지를 설치해야 해요.

uv add langfuse==2.59.7 litellm

빠른 시작

아주 간단하게, 코드 두 줄로 모든 프로바이더의 응답을 Langfuse(레거시 v2 SDK)에 기록할 수 있어요. API 키는 https://cloud.langfuse.com/ 에서 받아요.

litellm.success_callback = ["langfuse"]
litellm.failure_callback = ["langfuse"] # logs errors to langfuse
# uv add langfuse
import litellm
import os

# from https://cloud.langfuse.com/
os.environ["LANGFUSE_PUBLIC_KEY"] = ""
os.environ["LANGFUSE_SECRET_KEY"] = ""
# Optional, defaults to https://cloud.langfuse.com
os.environ["LANGFUSE_HOST"] # optional

# LLM API Keys
os.environ['OPENAI_API_KEY']=""

# set langfuse as a callback, litellm will send the data to langfuse
litellm.success_callback = ["langfuse"]

# openai call
response = litellm.completion(
  model="{{openai_small}}",
  messages=[
    {"role": "user", "content": "Hi 👋 - i'm openai"}
  ]
)

커스텀 생성 이름과 메타데이터

metadatageneration_name을 넘기면 Langfuse 생성(generation) 이름을 지정할 수 있고, 프로젝트·태그 등 커스텀 필드도 함께 보낼 수 있어요.

import litellm
from litellm import completion
import os

# from https://cloud.langfuse.com/
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-..."

# OpenAI and Cohere keys
os.environ['OPENAI_API_KEY']="sk-..."

# set langfuse as a callback, litellm will send the data to langfuse
litellm.success_callback = ["langfuse"]

# openai call
response = completion(
  model="{{openai_small}}",
  messages=[
    {"role": "user", "content": "Hi 👋 - i'm openai"}
  ],
  metadata = {
    "generation_name": "litellm-ishaan-gen", # set langfuse generation name
    # custom metadata fields
    "project": "litellm-proxy"
  }
)

print(response)

커스텀 Trace ID, Trace User ID, Trace Metadata, Version, Release, Tags

metadatatrace_id, trace_user_id, trace_metadata, trace_version, trace_release, tags를 넘겨 트레이스 단위 속성을 제어할 수 있어요.

import litellm
from litellm import completion
import os

# from https://cloud.langfuse.com/
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-..."

os.environ['OPENAI_API_KEY']="sk-..."

# set langfuse as a callback, litellm will send the data to langfuse
litellm.success_callback = ["langfuse"]

# set custom langfuse trace params and generation params
response = completion(
  model="{{openai_small}}",
  messages=[
    {"role": "user", "content": "Hi 👋 - i'm openai"}
  ],
  metadata={
      "generation_name": "ishaan-test-generation",  # set langfuse Generation Name
      "generation_id": "gen-id22",                  # set langfuse Generation ID
      "parent_observation_id": "obs-id9",           # set langfuse Parent Observation ID
      "version":  "test-generation-version",        # set langfuse Generation Version
      "trace_user_id": "user-id2",                  # set langfuse Trace User ID
      "session_id": "session-1",                    # set langfuse Session ID
      "tags": ["tag1", "tag2"],                     # set langfuse Tags
      "trace_name": "new-trace-name",               # set langfuse Trace Name
      "trace_id": "trace-id22",                     # set langfuse Trace ID
      "trace_metadata": {"key": "value"},           # set langfuse Trace Metadata
      "trace_version": "test-trace-version",        # set langfuse Trace Version (if not set, defaults to Generation Version)
      "trace_release": "test-trace-release",        # set langfuse Trace Release
      ### OR ###
      "existing_trace_id": "trace-id22",            # if generation is continuation of past trace. This prevents default behaviour of setting a trace name
      ### OR enforce that certain fields are trace overwritten in the trace during the continuation ###
      "existing_trace_id": "trace-id22",
      "trace_metadata": {"key": "updated_trace_value"},            # The new value to use for the langfuse Trace Metadata
      "update_trace_keys": ["input", "output", "trace_metadata"],  # Updates the trace input & output to be this generations input & output also updates the Trace Metadata to match the passed in value. Requires `langfuse_enable_update_trace_keys: true`
      "debug_langfuse": True,                                      # Will log the scalar metadata sent to litellm for the trace/generation as `metadata_passed_to_litellm`
  },
)

print(response)

메타데이터를 요청 헤더로 보낼 수도 있어요. langfuse_* 프리픽스 헤더로 트레이스 속성을 지정하는 방식이에요.

curl --location --request POST 'http://0.0.0.0:4000/chat/completions' \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer ***' \
    --header 'langfuse_trace_id: trace-id2' \
    --header 'langfuse_trace_user_id: user-id2' \
    --header 'langfuse_trace_metadata: {"key":"value"}' \
    --data '{
    "model": "{{openai_small}}",
    "messages": [
        {
        "role": "user",
        "content": "what llm are you"
        }
    ]
}'

여러 Langfuse 프로젝트 (요청별 자격 증명)

completion()이나 acompletion()에 자격 증명을 직접 넘겨 요청별로 다른 Langfuse 프로젝트에 트레이스를 보낼 수 있어요. 전역 환경 변수와 함께 쓰거나 대신 쓸 수 있고, 각기 다른 팀이나 비즈니스 프로세스가 다른 Langfuse 프로젝트를 쓸 때 유용해요.

langfuse_public_keylangfuse_secret_key(또는 langfuse_secret), 필요하면 langfuse_host를 키워드 인자로 넘기세요.

import litellm
from litellm import completion

# Optional: set a default via env for requests that don't pass credentials
# os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-default..."
# os.environ["LANGFUSE_SECRET_KEY"] = "sk-default..."

litellm.success_callback = ["langfuse"]
litellm.failure_callback = ["langfuse"]

# Request 1 → Langfuse Project A
response_a = completion(
    model="{{openai_small}}",
    messages=[{"role": "user", "content": "Hello from team A"}],
    langfuse_public_key="«redacted:pk-lf-…»...",
    langfuse_secret_key="«redacted:sk-…»...",
    langfuse_host="https://us.cloud.langfuse.com",  # optional
)

# Request 2 → Langfuse Project B (different project)
response_b = completion(
    model="{{openai_small}}",
    messages=[{"role": "user", "content": "Hello from team B"}],
    langfuse_public_key="«redacted:pk-lf-…»...",
    langfuse_secret_key="«redacted:sk-…»...",
    langfuse_host="https://eu.cloud.langfuse.com",  # optional, can differ per project
)

이 인자들이 넘어오면 해당 요청은 그 프로젝트(와 호스트)로 Langfuse 콜백을 보내고, 생략하면 전역 Langfuse 클라이언트(환경 변수)를 사용해요. LiteLLM은 자격 증명 세트별로 Langfuse 클라이언트를 캐시해서 요청마다 새 클라이언트를 만드는 걸 피해요.

특정 호출 로깅 끄기

특정 호출만 로깅에서 제외하고 싶다면 no-log 플래그를 쓰면 돼요.

completion(messages = ..., model = ..., **{"no-log": True})

Langfuse 로깅에서 메시지·응답 가리기

전역으로 가리려면 litellm.turn_off_message_logging=True를 설정해요. 그럼 메시지와 응답은 Langfuse에 기록되지 않지만 요청 메타데이터는 계속 기록돼요.

특정 호출만 가리려면 해당 호출의 metadata에서 mask_inputTrue로 하면 입력이, mask_outputTrue로 하면 출력이 로깅에서 제외돼요.

:::note 이미 존재하는 트레이스를 이어갈 때 update_trace_keysinput이나 output을 포함시키고 동시에 mask_input/mask_output을 켜면, 해당 트레이스의 기존 입력/출력이 가려진 메시지로 교체돼요. langfuse_enable_update_trace_keys가 켜져 있을 때만 적용됩니다. :::

문제 해결

Langfuse에 데이터가 기록되지 않아요?

  • langfuse를 최신 버전으로 올려보세요. uv add langfuse -U — 최신 버전은 LLM이 JSON 입출력을 Langfuse에 기록할 수 있게 해줘요.
  • 트레이스가 하나도 안 보인다면 Langfuse 체크리스트를 따라가 보세요.

더 알아보기