콘텐츠로 이동
Study NoteLangfuse

3. 좋은 trace 설계

좋은 trace는 많이 기록한 trace가 아니라 질문 하나에 안정된 filter로 답할 수 있는 trace다

이 장에서 처음 나오는 말4개
naming contract
같은 역할의 observation이 release가 바뀌어도 같은 name과 type을 쓰는 약속이다.
correlation id
Langfuse trace를 application log·LiteLLM·일반 OTel trace와 연결하는 식별자다.
cardinality
속성 값 조합의 수. request id 같은 고유값을 집계 차원에 쓰면 폭증한다.
sampling
모든 실행 대신 정해진 비율이나 조건의 실행만 상세 수집·평가하는 정책이다.

계측 코드를 쓰기 전에 dashboard와 incident에서 답할 질문을 적는다.

  • 어느 release부터 final answer score가 떨어졌는가
  • retrieval이 느린가, 첫 generation이 느린가
  • 어떤 tool이 가장 자주 실패하고 agent가 몇 번 재시도하는가
  • 어떤 prompt version이 더 비싸지만 품질 차이는 없는가
  • LiteLLM이 선택한 deployment별 품질과 latency가 다른가

질문에 쓰지 않을 payload와 span을 무조건 모으면 storage와 UI noise만 늘어난다.

agent-run 루트 아래 plan이 오고, plan 아래 판단 generation과 도구 호출 둘, 최종 답변 generation이 붙어 score로 이어지는 trace 설계

agent observation은 흐름을 결정하는 범위, generation은 실제 model 호출, tool은 외부 행동이다. agent의 반복 loop가 있으면 iteration을 metadata나 child span으로 드러내되 agent-loop-173821 같은 동적 name을 만들지 않는다.

단계type/name남길 것그대로 남기지 않을 것
질문 정규화span: normalize-query변환 종류·시간원문 중 PII
검색retriever: retrieve-contextquery hash·document id·score·top-k문서 전문 전체의 중복
rerankspan: rerank-contextcandidate 수·선택 id·model거대한 embedding vector
답변generation: final-answerprompt link·model·usage·outputprovider secret
평가scorefaithfulness·relevancerubric 없는 quality 한 값

retrieval input/output을 generation prompt에 다시 포함하더라도 분석용 metadata에는 document id와 rank처럼 작은 구조를 둔다. 전문이 필요하면 media/object storage 정책과 masking을 먼저 정한다.

좋은 name은 코드 함수명이 아니라 제품 역할이다. refactor 후에도 final-answer는 유지할 수 있다.

좋음: chat-turn / retrieve-policy / final-answer / lookup-order
나쁨: answer_20260818 / user-924-final / POST-/api/chat/84e3...

request·user·session id는 검색 속성으로 필요하지만 name이나 metric dimension처럼 반복 집계하는 자리에 넣지 않는다. 개별 실행은 trace id로 찾고, 비교는 environment·release·version·stable name으로 한다.

식별자만든 곳Langfuse에 둘 자리
application request idedge/applicationroot metadata
W3C trace idOTel tracertrace context 또는 metadata
Langfuse trace idSDK/OTeltrace 자체
LiteLLM call idLiteLLMgeneration metadata
session idchat/workflow servicepropagated session_id
user ididentity 계층가명화한 user_id

이 값들을 log에도 구조화해 남기면 “사용자 ticket → app log → Langfuse generation → LiteLLM provider attempt”로 건널 수 있다. secret key나 원본 API key는 correlation id가 아니다.

exception을 catch한 뒤 정상 output처럼 반환하면 observation은 성공으로 보일 수 있다. error type·message의 안전한 요약과 status를 남기고 stack trace의 secret·payload 노출을 검토한다.

streaming은 다음 시간을 구분한다.

  • root 시작부터 generation 시작까지: retrieval·queue·application overhead
  • generation 시작부터 첫 token까지: TTFT
  • 첫 token부터 끝까지: stream duration
  • client disconnect 뒤: model call을 취소했는지, usage·output이 어디까지 확정됐는지

응답 조각마다 observation을 만들지 않는다. generation 하나에 최종 사용량과 완결 상태를 남기고, 꼭 필요한 stream event만 제한적으로 기록한다.

tier대상내용
기본모든 production 요청name·timing·model·usage·status·가명 id
sampled content승인된 비율masking된 input/output·tool arguments
incident debug짧은 기간·특정 workload더 상세한 metadata, 명시적 만료
금지secret·원문 credential·규제 데이터수집 전에 제거

sampling은 실패만 제외하지 않는다. 정상 baseline이 없으면 실패가 얼마나 특이한지 비교할 수 없다. error 100%, 정상 일부, 고가 요청과 낮은 score를 조건부로 더 많이 모으는 식으로 설계한다.

  • root가 한 user-visible operation을 대표하는가
  • 같은 역할이 release 사이에 같은 name과 type인가
  • child가 parent보다 긴 비정상 timing을 보이지 않는가
  • generation에 model·usage·prompt version이 있는가
  • production과 staging이 environment로 분리되는가
  • user/session/metadata가 필요한 child에 전파되는가
  • payload가 policy보다 더 많이 복제되지 않는가
  • 일반 APM과 LiteLLM으로 건너갈 id가 있는가