ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • LangGraph 에이전트에 Langfuse 붙이기 — LLM 앱의 블랙박스를 유리상자로
    IT 2026. 4. 16. 22:00
    LangGraph 에이전트에 Langfuse 붙이기 — LLM 앱의 블랙박스를 유리상자로

    LLM 에이전트를 만들었다. 질문하면 답이 온다. 그런데 왜 그런 답을 했는지, 어디서 시간이 걸렸는지, 어떤 도구를 호출했는지 전혀 보이지 않는다. 에이전트는 블랙박스다. 이 블랙박스에 유리창을 달아주는 도구가 Langfuse다.

    Langfuse란 무엇인가

    Langfuse(Lang + Fuse)는 LLM 애플리케이션을 위한 오픈소스 관측성(observability) 플랫폼이다. 전기 퓨즈가 회로를 보호하고 모니터링하듯, LLM 앱 내부의 흐름을 추적하고 시각화한다.

    어떤 문제를 해결하는가

    LLM 앱은 전통적인 소프트웨어와 디버깅 방식이 다르다. 입력이 같아도 출력이 달라지고, 에이전트가 도구를 여러 번 호출하면서 내부 상태가 계속 바뀐다. 기존 로그만으로는 "에이전트가 왜 이런 판단을 내렸는지" 추적하기 어렵다.

    Langfuse는 이 문제를 Trace라는 개념으로 해결한다. 하나의 대화가 시작되면 trace가 생성되고, 그 안에서 LLM 호출, 도구 실행, 검색 결과 등 모든 단계가 타임라인으로 기록된다.

    어떤 정보를 제공하는가

    • 실행 흐름 — 에이전트가 어떤 순서로 도구를 호출했는지
    • 입출력 전문 — 각 단계에서 무엇을 받고 무엇을 내보냈는지
    • 소요 시간 — 단계별 latency, 어디가 병목인지
    • 토큰 사용량 — LLM 호출마다 소비한 토큰 수
    • 세션 추적 — 같은 사용자의 대화 흐름을 이어서 볼 수 있음

    실제 구현 — LangGraph 에이전트에 Langfuse 연동하기

    기존에 vLLM + LangGraph ReAct 에이전트를 운영하고 있는 환경에 Langfuse를 추가한 과정이다.

    1단계: Langfuse 서버 배포 (Docker)

    Langfuse는 셀프 호스팅을 지원한다. PostgreSQL과 함께 Docker Compose로 띄운다.

    services:
      langfuse-db:
        image: postgres:16-alpine
        ports:
          - "5433:5432"
        environment:
          POSTGRES_USER: langfuse
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          POSTGRES_DB: langfuse
    
      langfuse-server:
        image: langfuse/langfuse:2
        ports:
          - "3100:3000"
        environment:
          DATABASE_URL: postgresql://langfuse:${POSTGRES_PASSWORD}@langfuse-db:5432/langfuse
          NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
          SALT: ${SALT}
          NEXTAUTH_URL: http://localhost:3100
        depends_on:
          langfuse-db:
            condition: service_healthy

    docker compose up -d 한 번이면 http://localhost:3100에서 대시보드를 쓸 수 있다.

    2단계: Python SDK 설치

    pip install langfuse

    3단계: 에이전트 코드에 트레이싱 삽입

    핵심은 단 하나 — 에이전트가 실행될 때 Langfuse trace를 생성하고, 각 이벤트를 span으로 기록하는 것이다. 기존 LangGraph 코드를 건드리지 않고, 에이전트를 호출하는 프록시 레이어에서 감싸는 방식이다.

    from langfuse import Langfuse
    
    # Langfuse 클라이언트 초기화
    lf = Langfuse(
        secret_key=LANGFUSE_SECRET_KEY,
        public_key=LANGFUSE_PUBLIC_KEY,
        host="http://localhost:3100",
    )
    
    # 대화 시작 시 trace 생성
    trace = lf.trace(
        name="agent-chat",
        session_id=session_id,
        user_id="dashboard",
        input=user_message,
    )

    에이전트가 도구를 호출할 때마다 span을 열고 닫는다:

    # 도구 실행 시작
    span = trace.span(
        name=f"tool:{tool_name}",
        input=tool_input,
    )
    
    # 도구 실행 완료
    span.end(output=tool_output)

    대화가 끝나면 trace를 마무리한다:

    # 최종 응답 기록 + 서버로 전송
    trace.update(output=final_response)
    lf.flush()

    이 세 조각이 전부다. 에이전트를 정의하는 graph.py나 개별 도구 코드는 한 줄도 수정하지 않았다. Langfuse 코드는 에이전트를 호출하는 프록시 레이어(proxy.py)에만 추가했다. 프록시는 원래 에이전트의 이벤트 스트림을 받아서 브라우저로 중계하는 역할을 하는데, 그 이벤트 루프 안에 Langfuse 기록 코드를 끼워넣은 것이다. 에이전트 로직과 관측 로직이 서로 다른 파일에 완전히 분리되어 있으므로, Langfuse를 빼더라도 에이전트는 그대로 동작한다.

    4단계: 대시보드에서 확인

    챗봇에 메시지를 보내면 Langfuse 대시보드의 Traces 메뉴에 실시간으로 기록이 쌓인다. 각 trace를 클릭하면 에이전트의 실행 과정이 타임라인으로 펼쳐진다.

    Langfuse를 쓰면 뭐가 좋은가

    디버깅이 쉬워진다

    챗봇이 이상한 답을 할 때, 로그를 뒤지는 대신 해당 trace를 열면 된다. 에이전트가 어떤 도구를 호출했고, RAG 검색 결과가 무엇이었고, 최종 프롬프트가 어떻게 조립되었는지 한눈에 보인다.

    병목을 잡을 수 있다

    "응답이 느리다"고 느껴질 때, trace의 latency를 보면 LLM 추론이 느린 건지, 특정 도구 호출이 느린 건지 바로 구분된다.

    프롬프트 개선의 근거가 생긴다

    실제 사용 패턴을 trace로 관찰하면서, 시스템 프롬프트나 도구 설명을 어떻게 바꿔야 할지 데이터 기반으로 판단할 수 있다. 감으로 튜닝하던 것에서 벗어난다.

    정리

    LLM 에이전트를 만드는 것과 잘 운영하는 것은 다른 문제다. Langfuse는 운영 단계에서 필요한 "눈"을 제공한다. 셀프 호스팅이 가능하니 프라이빗 환경에서도 부담 없이 쓸 수 있고, 기존 코드를 거의 건드리지 않고 붙일 수 있다는 점이 가장 큰 장점이다.

    LLM 앱을 만들고 있다면, 로그 대신 trace를 남기는 것을 추천한다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.