[Langfuse 2] UI에서 Trace·오류·Session·Score 읽기

심대용·3일 전

Langfuse 2 — Trace·오류·Session·Score 읽기

이번 글에서 해볼 일

  • 여러 실행 중 원하는 요청을 찾고, 어떤 단계에 시간이 걸렸는지 확인한다.
  • 모델 호출의 성공과 응답 검증 실패를 구분한다.
  • 대화 세 턴을 Session에서 읽고, 사람이 판단한 점수와 이유를 남긴다.

주요 단어 · Trace · Observation · Generation · Session · Score

1편에서 Docker로 Langfuse를 띄우고 Ollama의 qwen3:8b를 연결했다. 이제 확인할 것은 서버가 켜졌다는 사실보다, 원하는 실행을 찾고 문제가 생긴 위치를 설명할 수 있는가다.

이번에는 같은 대화에 속한 요청 세 개를 실제로 실행했다. 세 번째 응답에는 학습용 검증 실패를 하나 넣었다. 로컬 모델 호출은 성공했지만, 앱에서 정한 조건을 통과하지 못한 경우다. 화면에서 이 차이를 읽고 마지막에는 수동 평가까지 남겨본다.

Trace와 Score를 중심으로 보는 Langfuse 학습 지도

이번 편의 범위는 빨간 테두리로 표시한 Trace와 Score다. 점선은 학습 순서이며, 제품이 이 과정을 자동으로 수행한다는 의미는 아니다.

1. 실습 환경과 메뉴

화면은 Langfuse 4.50.0 로컬 Docker 환경이다. Python SDK langfuse 4.16.0, Ollama 모델 qwen3:8b를 사용했다. 예제 실행은 2026-10-04 밤, UI 확인은 10월 4~5일에 진행했다.

http://localhost:3300에 접속하고 Langfuse Local Lab 프로젝트를 선택한다. 설치와 로그인은 1편을 참고한다.

로컬 Langfuse의 Home과 기능 메뉴

로컬에서 직접 캡처한 Home과 왼쪽 메뉴. 이 화면은 설치 확인용 Trace 두 개가 남아 있을 때 찍었다. 뒤에서 보는 UI 실습용 세 요청의 집계 화면과는 구분한다.

처음에는 메뉴를 전부 외울 필요가 없다. 이번 글에서 사용할 위치는 다음과 같다.

위치이번에 확인할 내용
Home선택한 프로젝트와 데이터가 들어오는지 확인
Tracing개별 실행을 찾고 단계별 입력·출력·시간 확인
Sessions같은 대화에 속한 요청들을 묶어서 확인
Scores평가 결과 확인
Settings의 Scores Configs사람이 사용할 평가 이름·자료형·범위 정의

시간 범위도 검색 조건이다. 이 글의 데이터는 2026-10-04 23:58 KST 전후에 생성했다. 해당 시간을 포함하도록 조회 범위를 잡는다. 직접 실행했다면 자신의 실행 시각을 기준으로 한다.

2. 원하는 요청 찾기

2.1 Trace와 Observation

Trace는 한 요청의 실행을 묶는 단위이고, Observation은 그 안에서 기록한 개별 작업이다. 이 예제에서는 질문 하나에 Trace 하나를 만들고, 전처리·모델 호출·검증을 각각 Observation으로 기록했다. Generation은 모델 이름과 토큰 사용량 등을 담는 LLM 호출용 Observation 타입이다. 공식 데이터 모델

필터와 Session 탐색에는 다음 값을 사용한다.

Trace / root name: ui-guide-chat
Session ID: guide-ui-session
User ID: ui-guide-learner
Tags: ui-guide, local-llm, synthetic-study
Model: qwen3:8b

ui-guide는 이번 예제라는 분류, guide-ui-session은 하나의 대화 묶음을 나타낸다.

2.2 이름으로 필터링

왼쪽 Tracing으로 이동한다. 이 버전의 표는 Observation을 기준으로 탐색하며, 이번 환경에서는 처음에 Is Root Observation = true가 적용돼 있었다. 루트만 보면 요청별 시작점이 한 행씩 보여 첫 탐색에 편하다. 공식 문서에서도 호환 SDK를 사용하는 프로젝트의 기본 동작을 설명하지만, 저장한 뷰나 이전 필터 상태가 있으면 초기 화면은 달라질 수 있다. v4 Observations 사용법

이번 UI에서 이름 조건은 다음 순서로 추가했다.

  1. 필터에서 Name을 선택한다.
  2. Text → contains 조건을 고른다.
  3. ui-guide-chat을 입력하고 Add를 누른다.

ui-guide-chat 이름 조건으로 세 요청을 찾은 화면

이름에 ui-guide-chat이 들어가는 루트 관측을 찾은 결과다. 이번 예제에서는 요청 세 개가 나온다.

행이 세 개라는 것과 내부 작업이 세 개라는 것은 다르다. 요청마다 루트와 자식 세 개가 있으므로 실제로 저장된 Observation은 총 12개다. 루트 필터를 제거하면 같은 실행에 속한 자식들이 행으로 드러난다.

결과가 비어 있으면 데이터를 다시 만들기 전에 프로젝트 → 시간 범위 → 환경 → 기존 필터 순서로 살펴본다. 특히 Generation이나 자식 ERROR를 찾으면서 루트 필터를 그대로 두면, 찾으려는 관측을 조건으로 제외한 셈이 된다.

3. Trace 상세 읽기

3.1 트리와 시간 순서

이름으로 찾은 요청을 열면 한 실행의 내부 단계들을 확인할 수 있다. 이번 예제의 구조는 다음과 같다.

ui-guide-chat            SPAN · 요청 전체
├─ preprocess            SPAN · 질문 정리, 대화 입력 준비
├─ ollama-answer         GENERATION · 로컬 모델 호출
└─ validate-answer       SPAN · 출력 길이 조건 확인

요청의 루트와 전처리·모델 호출·검증 관측

실제 저장된 Trace의 단계들. 모델 호출을 선택하면 그 호출에 보낸 입력과 돌아온 응답을 함께 읽을 수 있다.

여기서 트리는 포함 관계를 나타낸다. preprocess와 ollama-answer는 둘 다 ui-guide-chat이 실행한 작업이므로 형제다. 전처리 뒤에 모델을 호출했다고 해서 모델 호출을 전처리의 자식으로 넣지는 않았다.

반면 타임라인은 언제 시작하고 끝났는지 읽는 화면이다. 이 예제는 전처리 → 모델 호출 → 검증을 순서대로 수행했다. 트리에서 같은 깊이에 있는 작업이 항상 동시에 실행된다는 뜻은 아니다. 동시 호출을 만드는 앱이라면 시간 막대가 겹칠 수 있지만, 이번 코드는 그런 병렬 실행을 하지 않았다.

상세 화면의 Timeline을 선택하면 같은 관측을 시간축으로 볼 수 있다.

세 번째 요청의 관측을 시간축으로 본 Timeline

실제 세 번째 요청의 Timeline. 루트와 모델 호출이 약 1.21초로 겹치는 것은 루트가 모델 호출을 포함하기 때문이다. 전처리·검증의 0ms 표시는 UI에서 매우 짧은 시간을 표시한 값이며, 해당 작업이 실행되지 않았다는 뜻이 아니다.

코드에서는 요청 전체의 context 안에 세 작업의 context를 차례로 열었다. 아래는 실행한 코드의 구조를 줄여 보여주는 설명용 발췌다. preprocess, call_ollama, validate는 역할을 표시한 이름이며 그대로 복사해 실행하는 완성 프로그램은 아니다.

with lf.start_as_current_observation(
    as_type="span", name="ui-guide-chat"
):
    # 서로 형제인 세 관측을 순서대로 실행한다.
    preprocess()
    call_ollama()   # as_type="generation"
    validate()

출력 후처리가 실패했는데 모델 호출 하나만 기록해 두면 둘을 구분하기 어렵다. 화면에서 조사하고 싶은 작업을 별도 관측으로 남긴다.

3.2 입력·출력·토큰

ollama-answer를 선택하면 다음 순서로 읽는다.

ollama-answer의 실제 system 지시와 질문·응답

세 번째 Generation의 Preview. 위쪽에 모델 qwen3:8b, 약 1.21초, 총 241토큰이 표시된다. 가운데는 실제 입력과 모델 응답이다. 이전 메시지가 접혀 있으면 Show 4 more로 펼쳐 확인한다.

확인할 항목이 예제에서 읽을 내용
Inputsystem 지시, 현재 질문, 이전 대화가 실제로 전달됐는가
Output모델이 반환한 문장이 무엇인가
Modelqwen3:8b를 호출했는가
Usage입력·출력·총 토큰이 얼마인가
시간해당 모델 요청에 얼마나 걸렸는가

실제 호출 결과는 아래와 같다. 시간은 클라이언트에서 API 호출 앞뒤를 재어 기록한 요청 시간이며, 모델 내부 계산 시간만 분리한 값은 아니다.

요청입력 토큰출력 토큰총 토큰요청 시간
170371072.546초
2129361651.010초
3193482411.213초

뒤로 갈수록 입력 토큰이 늘었다. 이번 코드가 앞선 사용자 질문과 모델 응답을 messages 목록에 유지하고, 다음 호출에 함께 보냈기 때문이다. 출력 글자 수와 토큰 수는 다른 단위이므로 한글 65자를 65토큰으로 해석하지 않는다.

세 번의 요청으로 모델 속도를 평가하지는 않는다. 모델 로드 상태·입력 길이의 영향을 통제한 반복 측정이 아니라 UI 읽기 실습이다.

비용이 0이거나 비어 있어도 로컬 추론의 총비용이 0이라는 뜻은 아니다. 모델 가격 설정에 따른 토큰 비용과 내 Mac의 장비·전력 비용은 다르다. 이 실습은 유료 모델 API를 호출하지 않았고 로컬 모델의 과금 단가를 등록하지 않았으므로, 우선 토큰과 지연을 읽는다. 공식 토큰·비용 추적 설명

4. ERROR 위치 찾기

세 번째 요청은 모델 응답을 받았지만 validate-answer에 ERROR가 있다. 원인은 일부러 만든 조건이다.

규칙: 답변 길이가 1자 이하여야 한다.
실제 응답: 65자
결과: 검증 실패

의도적으로 만든 validate-answer의 ERROR와 이유

학습용 앱 검증 실패. 모델 호출이 실패했다는 화면이 아니다. 상태 메시지에도 의도한 실습 실패임을 명시했다.

관측에는 수준과 이유를 함께 남겼다. 아래는 실제 실행한 코드의 해당 부분이다.

validation.update(
    level="ERROR",
    status_message=(
        "INTENTIONAL STUDY FAILURE: "
        "max_characters=1; Ollama request succeeded."
    ),
)

Langfuse의 관측 수준에는 DEBUG, DEFAULT, WARNING, ERROR가 있고, statusMessage로 이유를 보충할 수 있다. 오류를 기록하는 것과 프로세스를 강제로 종료하는 것은 별개다. 이 코드는 검증 실패를 기록한 뒤 결과를 저장했다. 공식 Log Levels 문서

루트가 DEFAULT여도 자식에는 ERROR가 있을 수 있다. 예제는 검증 관측만 ERROR로 표시했다. 루트 상태만 보고 모든 단계가 정상이라고 판단하지 않는다.

전체 표에서 이 오류만 찾으려면 루트 필터를 해제하고 level = ERROR 조건으로 좁힌다. 이름을 더 지정한다면 validate-answer가 대상이다. ui-guide-chat이라는 이름 조건을 남겨두고 자식 이름의 오류를 찾으면 조건이 충돌한다.

Ollama 요청 오류라면 연결·시간 제한·모델 상태를 살펴보겠지만, 지금은 응답을 정상 수신했다. 확인할 곳은 앱의 한 글자 제한 규칙이다. 의도한 실습 실패를 운영 장애로 해석하지 않는다.

5. Session으로 대화 읽기

Session은 관련된 여러 Trace를 묶는 식별자다. 한 질문의 내부 단계는 Trace에서 보고, 여러 질문으로 이어진 흐름은 Session에서 확인한다. 이번 예제에서는 세 요청 모두에 guide-ui-session을 전달했다. 공식 Sessions 문서

왼쪽 Sessions에서 guide-ui-session을 찾아 연다. 이 실습에서는 상단 보기에서 Last LLM Call per Trace를 선택했다. 각 Trace의 마지막 LLM 호출을 모아 같은 대화의 입력·출력을 이어 읽는 보기다.

Total traces 3과 앞선 두 턴이 보이는 Session 화면

상단의 Total traces: 3으로 세 요청이 묶였음을 확인한다. 이 캡처에는 앞선 두 턴만 보이며, 세 번째 턴은 아래로 스크롤해 확인한다. 화면에 카드 두 개가 보인다고 Session 전체가 두 요청이라고 해석하지 않는다.

SDK에서는 요청을 시작하기 전에 속성을 전파했다. 다음은 실행 코드의 발췌다.

with propagate_attributes(
    user_id="ui-guide-learner",
    session_id="guide-ui-session",
    tags=["ui-guide", "local-llm", "synthetic-study"],
    trace_name="ui-guide-chat",
):
    # 이 context 안에서 요청과 자식 관측을 생성한다.
    ...

여기서 모델 응답을 그대로 정답으로 읽으면 놓치는 부분이 있다. 세 번째 실제 출력은 다음과 같았다.

대화 턴을 session으로 묶으면 대화 맥락을 유지하고, 유저의 의도를 정확히 파악해 응답 품질을 높일 수 있습니다.

일반적인 대화 세션에 대한 설명처럼 들리지만, Langfuse의 session_id를 지정하는 것만으로 모델에 대화 기억이 생기지는 않는다. Langfuse는 관측 기록을 묶는다. 이 실습에서 과거 대화를 다음 추론에 전달한 것은 앱의 messages 목록이다.

따라서 Session은 “대화가 실제로 어떤 순서로 진행됐고 무엇을 전달했는가”를 조사하는 데 쓴다. 품질을 자동 보장하는 기능으로 설명하면 안 된다. 관측 데이터가 잘 수집됐다는 사실과, 모델이 정확한 설명을 했다는 사실은 각각 확인해야 한다.

6. 사람이 점수 남기기

6.1 평가 기준 만들기

실행 성공 여부 외에 “이 답변을 학습자가 이해하기 쉬운가?”라는 판단을 남기고 싶다. 이때 Score는 평가 이름과 값 등을 담는 결과 기록이다. 사람이 UI에서 입력할 수도 있고 코드나 모델 평가로 만들 수도 있다. 이번에는 수동 평가만 진행한다. 공식 평가 개념

먼저 Settings → Scores Configs → Add new score config에서 기준을 만든다. 실제 입력한 설정은 다음과 같다.

항목값
Nameui-readability
Data typeNUMERIC
Minimum0
Maximum2
Description학습용 수동 평가. 0=이해하기 어려움, 1=수정 필요, 2=간결하고 이해 가능. 사실 정확성 전체를 보장하는 점수가 아님.

ui-readability의 점수 범위와 설명 설정

실제 입력한 Score Config. 범위만 만들지 말고, 0·1·2를 언제 사용하는지 함께 적는다.

이 화면에서 Submit으로 설정을 생성한다. Score Config는 이후 평가가 사용할 이름·자료형·값 범위를 정하는 곳이다. 설정을 생성했다고 기존 모든 Trace에 점수가 자동으로 생기는 것은 아니다. 수동 평가를 위해 설정을 사용하는 방식은 공식 Score Config 문서에서도 확인할 수 있다.

6.2 값과 이유 기록하기

세 번째 Trace를 열고 ollama-answer Generation을 선택한 뒤 Annotate를 누른다. 이번 점수의 대상은 요청 전체인 루트가 아니라 해당 모델 호출 관측이다. 어느 대상을 선택했는지 먼저 확인한다.

  1. Add score → ui-readability (Numeric)를 선택한다.
  2. 값에 1을 입력하고 Enter로 확정한다.
  3. Saved 표시를 확인한다.
  4. 점수 이름 옆 말풍선 버튼 Add or view score comment로 이유를 입력하고 Save Changes를 누른다.

이 예제에 1을 준 이유는 문장이 짧더라도 Langfuse의 Session을 이해하는 데 필요한 설명 수정이 있기 때문이다.

수동 점수와 판단 이유를 남긴 화면

실제로 저장한 Generation 점수 1과 댓글. 댓글을 닫고 다시 열어 저장된 내용을 확인했다. 이 수동 평가는 앱이 자동으로 남긴 검증 ERROR와 별개다.

실제 댓글은 다음과 같다.

문장은 짧지만 Langfuse session과 대화 기억을 혼동할 수 있어 수정 필요. session_id는 관측 기록을 묶고, 대화 이력은 애플리케이션이 messages로 전달한다.

이처럼 어떤 수정이 필요한지 적는다. 숫자 1만 남아 있으면 길이 때문에 낮게 평가했는지, 개념이 모호해서 낮게 평가했는지 알기 어렵다.

ui-readability=2가 모든 사실의 검증 완료를 뜻하지 않고, 1이 전체 품질 50%를 뜻하지도 않는다. 정확성·안전성·형식 준수는 별도의 목적과 기준으로 평가해야 한다.

7. 재조회와 적용 과제

이번 실습은 SDK 전송이 끝난 시점에서 멈추지 않았다. Public API로 각 Trace의 관측을 다시 읽고 부모 관계, Session 전파, 모델, 입력·출력 토큰, 응답 저장, ERROR 이유, 종료 시각을 확인했다. Trace 3개·Observation 12개에 대해 45개 조건이 통과했다. 이는 관측 저장 검증이며 모델 답변의 사실 정확성 검사와는 다르다.

실습 폴더에서는 다음 명령으로 기존 결과를 다시 검증할 수 있다. 경로는 앞선 설치 작업에서 만든 ~/ws/study 기준이다.

cd ~/ws/study

docs/langfuse-study-2026-10-04/lab/.venv/bin/python \
  docs/langfuse-ui-guide-2026-10-04/lab/observability_demo.py \
  --verify-only

생성 스크립트는 기존 결과가 있으면 중복 생성을 거절한다. 같은 세 요청으로 UI를 계속 연습할 수 있고, 검증 증거는 evidence/observability.json에 남겼다.

화면을 닫기 전에 다음 작업을 스스로 해보자.

  1. 루트만 보이는 표와 모든 관측이 보이는 표의 행 수가 다른 이유를 설명한다.
  2. 세 번째 요청에서 모델 호출 성공과 앱 검증 실패를 각각 어떤 정보로 판단했는지 찾는다.
  3. 두 번째 모델 호출의 Input에서 이전 대화가 실제로 들어갔는지 확인한다.
  4. ui-readability의 값과 댓글을 읽고, 점수만으로 알 수 없는 정보를 하나 적는다.

로컬 환경과 실습 데이터는 이어서 공부할 수 있도록 유지했다. 다음 편에서는 화면에서 Prompt 버전을 관리하고, 실제 호출에 어떤 버전을 사용했는지 연결해서 확인한다.


자료 기준: Langfuse 4.50.0 로컬 실습, 2026-10-04~05. 본문의 화면은 실제 로컬 UI, 대화는 학습용 합성 질문과 실제 로컬 모델 응답이다. 공식 개념·API 문서는 2026-10-04~05에 확인했다.

profile
어제보다 더 성장하는 나

0개의 댓글