콘텐츠로 이동

LLM 관측성과 평가

분류: Layer 12 - AI 시스템 & LLM 애플리케이션 | 선수지식: L6-60 (Logs/Metrics/Traces), L11-80 (모델 평가), L12-60 (Agent), L12-70 (비용/지연), L12-80 (보안)

LLM 관측성과 평가 - Trace, Metric, Production Eval, Drift

섹션 제목: “LLM 관측성과 평가 - Trace, Metric, Production Eval, Drift”

LLM 관측성과 평가는 production LLM 애플리케이션이 실제로 어떻게 실행됐는지 관측하고, 그 결과가 충분히 좋은지 반복 평가해, 다음 배포 결정을 바꾸는 체계다. 요청 하나의 실행 경로는 trace로, 전체 트래픽의 상태는 metric으로, 출력의 품질과 정책 준수는 eval로 본다. 세 신호를 같은 버전과 식별자로 연결해야 비용 폭주, 품질 회귀, 보안 우회처럼 HTTP 성공 여부만으로는 보이지 않는 실패를 설명할 수 있다.

이 문서에 별도의 통계학·BLEU·ROUGE 선수지식은 필요하지 않다. 비율, 백분위, 표본이 많을수록 우연한 흔들림을 구분하기 쉬워진다는 감각이면 충분하다. 텍스트 자동 지표와 분포 비교에 필요한 최소 개념은 해당 절에서 다시 설명한다.

도구 이름보다 아래 용어의 역할을 먼저 구분해야 한다. 같은 대시보드에 표시되더라도 서로 다른 질문에 답한다.

용어첫 정의이 문서에서 맡는 역할
Observability(관측 가능성)시스템 내부를 직접 열어 보지 않고, 외부로 나온 신호로 내부 상태와 실패 원인을 추론할 수 있는 성질미리 정한 알람뿐 아니라 예상하지 못한 실패도 탐색하게 한다
Telemetry(텔레메트리, 원격 측정 데이터)애플리케이션이 실행 중 내보내는 log, metric, trace, event와 그 속성관측 가능성을 만드는 원재료다. 많이 모으는 것보다 연결 가능한 형태가 중요하다
Trace(트레이스, 추적)사용자 요청이나 agent 작업 한 건의 시작부터 끝까지 이어진 실행 경로어떤 단계가 느렸고 어떤 도구가 호출됐는지 한 건의 맥락을 보여준다
Span(스팬, 구간)trace를 구성하는 개별 작업 단위retrieval, LLM call, tool call, validation처럼 실패 위치를 나눈다
Metric(메트릭, 지표)실행 중 직접 기록한 measurement(측정값)를 시간창과 dimension별 stream으로 집계한 신호비용·지연·오류·품질이 얼마나 넓게 변했는지 감지한다
Evaluation / eval(평가)입력, 출력, 기대 조건을 비교해 품질·정확성·안전성·형식 준수를 판정하는 과정”실행됐다”를 넘어 “결과가 쓸 만했는가”를 판단한다
Grader(그레이더, 채점기)schema 검사, 실행 테스트, LLM, 사람처럼 eval 판정을 만드는 구현무엇이 어떤 기준으로 pass·fail·score를 만들었는지 식별한다
Rubric(루브릭, 채점 기준표)grader가 판정할 품질 축, 점수 단계, 예시와 경계 사례를 명시한 기준같은 score가 버전이 달라도 같은 뜻인지 검증한다
gold dataset사람이 기대 결과와 중요한 실패 조건을 검수해 둔 버전 있는 기준 평가 데이터셋변경 전후를 같은 시험지로 비교하고 production 실패를 회귀 테스트로 남긴다
Drift(드리프트, 분포 변화)시간에 따라 입력, 출력, 사용자군, 모델·검색 환경의 분포가 기준선과 달라지는 현상평가 데이터와 운영 현실이 멀어지는 조짐을 찾는다
PII(개인 식별 정보)Personally Identifiable Information, 개인을 직접 또는 간접 식별할 수 있는 정보prompt·tool argument·trace의 수집, 접근, 보존 범위를 제한한다

이 문서에서는 gold dataset을 대표 용어로 사용한다. 이름보다 더 중요한 계약은 누가 검수했고, 어느 버전이며, 어떤 production 분포와 실패를 대표하는가다.

Monitoring(모니터링)은 미리 고른 지표와 임계값을 계속 확인하는 활동이다. 예를 들어 p95 TTFT가 route의 SLO를 넘었는가를 묻는다. 여기서 p95(95th percentile, 95번째 백분위)는 요청을 빠른 순서로 세웠을 때 95%가 그 값 이하임을, TTFT(Time to First Token, 첫 토큰 도착 시간)는 streaming 응답을 처음 보기까지의 지연을 뜻한다. SLO(Service Level Objective, 서비스 수준 목표)는 특정 시간창에 서비스가 달성하기로 정한 신뢰성 목표다. Observability는 그 목표가 깨졌을 때 prompt_version, retrieval, tool retry, model route 중 무엇이 원인이었는지 새 질문을 던질 수 있게 하는 시스템 성질이다.

텍스트 외 modality까지 다루는 SDK에서는 첫 산출물이 token이 아니라 chunk일 수 있다. 2026-07-10 현재 OpenTelemetry GenAI 규약도 span attribute와 metric 이름에 time_to_first_chunk를 사용한다. 이 문서에서는 업계에서 익숙한 TTFT를 설명 용어로 쓰되, 실제 계측 schema에서는 SDK가 내보내는 first tokenfirst chunk를 섞지 않는다.

따라서 대시보드가 많다고 관측 가능성이 높은 것은 아니다. 모든 차트가 route=chat 전체 평균만 보여주고 trace에는 prompt·model·retriever 버전이 없다면, 이상은 감지해도 원인을 국소화하지 못한다. 반대로 trace 한 건이 상세하더라도 전체 사용자 중 몇 퍼센트가 영향을 받았는지는 metric이 필요하다.

Eval 결과도 집계하면 metric stream의 measurement가 될 수 있지만, 둘의 출발점은 다르다.

  • 애플리케이션의 histogram·counter가 요청 종료 시 기록한 지연·token·오류는 직접 계측한 runtime measurement다.
  • Collector나 backend가 span에서 만든 duration·error count는 trace 파생 measurement다. 유용하지만 metric이 trace 파생값으로 한정되는 것은 아니다.
  • faithfulness, policy_compliance, task_success는 기대 조건과 비교하는 grader가 판정하고, 그 결과를 시간대·route별 metric stream으로 집계할 수 있다.

즉 metric은 숫자의 저장 형태가 아니라 실행 중 생긴 측정값을 동일한 이름·단위·dimension 집합으로 묶어 시간에 따라 집계한 신호이고, eval은 출력에 품질 의미를 부여하는 판정 과정이다. helpfulness=0.82라는 숫자만 저장하고 rubric, grader, dataset 버전을 남기지 않으면 같은 0.82가 다음 달에도 같은 뜻인지 알 수 없다.

3. 선행 기술의 한계 - 왜 관측과 평가를 합쳐야 하나

섹션 제목: “3. 선행 기술의 한계 - 왜 관측과 평가를 합쳐야 하나”

APM(Application Performance Monitoring, 애플리케이션 성능 모니터링)은 요청 수, 오류율, 지연, DB·외부 API 구간을 보는 데 강하다. LLM 애플리케이션도 이 기반이 필요하지만, HTTP 200 OK가 성공을 뜻하지 않는 경우가 많다.

전통 APM의 가정과 LLM에서 깨지는 지점

한 요청의 비용이 대체로 고정된다

LLM 요청은 input/output token, reasoning, tool 횟수, retry, model route에 따라 비용이 달라진다.

token usage와 model·tool span을 같은 trace에 연결한다.

같은 입력은 비슷한 출력을 낸다

sampling과 provider 변경 때문에 같은 질문도 정답, 거절, 환각으로 갈릴 수 있다.

한 건의 trace와 반복 eval, 표본 집계를 함께 본다.

오류는 status code로 드러난다

문법상 정상인 답이 근거를 꾸미거나 잘못된 tool argument를 제안할 수 있다.

schema·정책·품질 grader와 사용자 피드백을 붙인다.

요청 경로는 HTTP와 DB 호출이다

RAG, reranker, LLM, tool, approval, critic loop가 한 작업 안에서 중첩된다.

agent trajectory를 parent-child span으로 표현한다.

가상의 환불 agent가 200 OK를 반환했다고 하자. 실제로는 검색이 오래된 정책을 골랐고, 모델이 잘못된 환불액을 만들었으며, 승인 tool이 권한 밖 요청을 거절했을 수 있다. APM은 지연과 tool의 403을 보여줄 수 있지만, 검색 실패인지 모델 판단 실패인지, 보안 통제가 피해를 막은 정상 거절인지까지는 eval과 업무 맥락이 필요하다.

3.2 오프라인 평가가 놓치는 production 변화

섹션 제목: “3.2 오프라인 평가가 놓치는 production 변화”

L11의 모델 평가는 고정된 데이터로 모델·prompt 후보를 비교하는 기반을 제공했다. 하지만 배포 뒤에는 다음 조건이 바뀐다.

  • 사용자가 묻는 언어, 길이, 주제와 난도가 달라진다.
  • RAG corpus가 갱신되고 embedding·reranker 버전이 바뀐다.
  • agent가 호출하는 tool schema와 권한 정책이 바뀐다.
  • provider가 rate limit, model route, 응답 동작을 바꿀 수 있다.
  • 비용을 줄이려고 caching·routing·fallback을 추가하면 실행 경로가 달라진다.

그래서 production eval은 오프라인 시험을 버리는 것이 아니라, 실패한 production trace를 다시 gold dataset으로 가져와 오프라인 회귀 검증을 갱신하는 닫힌 루프를 만든다.

앞선 L12 토픽은 각각 실패를 예방하는 메커니즘을 다뤘다.

  • Agent 문서는 stuck loop, wrong tool, goal drift, memory contamination을 설명했다.
  • 비용·지연 문서는 prompt 누적, cache miss, retry storm, tail latency를 설명했다.
  • 보안 문서는 prompt injection, 권한 밖 tool, PII 노출, RAG permission leak을 설명했다.

이 문서의 책임은 그 설명을 반복하는 것이 아니다. 그 실패가 실제 production에서 일어났는지 통합 trace로 증거를 남기고, 영향 범위를 metric으로 재며, 재발 여부를 production eval과 gold dataset으로 검증하는 계약을 소유한다.

4. Trace, metric, eval은 어떻게 결합되는가

섹션 제목: “4. Trace, metric, eval은 어떻게 결합되는가”

4.1 Trace와 span - 한 요청의 실행을 보존한다

섹션 제목: “4.1 Trace와 span - 한 요청의 실행을 보존한다”

Trace는 하나의 요청 또는 agent task를 뿌리로 하고, span은 그 안의 작업을 부모-자식 관계로 잇는다. 아래 숫자는 구조를 읽기 위한 가상 예시다.

Trace: refund_agent / request-7f3a / app v42
├─ Span: prompt_construction (78ms)
│ ├─ Span: query_embedding (11ms)
│ ├─ Span: vector_search (16ms, top_k=8)
│ ├─ Span: rerank (49ms, policy_doc=v17)
│ └─ Span: prompt_assembly (2ms, prompt=refund-v13)
├─ Span: llm_call (1,240ms, model_route=balanced)
│ ├─ TTFT: 260ms
│ ├─ input_tokens: 1,850
│ ├─ output_tokens: 286
│ └─ finish_reason: tool_call
├─ Span: tool_call/refund_preview (185ms, retry_count=0)
├─ Span: policy_check (7ms, decision=approval_required)
└─ Span: output_filter (5ms, pii_detected=false)

이 trace는 1,240ms가 느리다보다 많은 것을 말한다. 어떤 정책 문서와 prompt 버전을 썼는지, 모델이 답변 대신 tool을 제안했는지, 실제 write 전에 승인이 요구됐는지 연결한다. 하지만 이 한 건만으로 시스템 전체가 느려졌다고 결론 내릴 수는 없다. 전체 분포는 metric으로 돌아가야 한다.

Span 경계는 독립적으로 느려지거나 실패하고, 버전·소유자·재시도 정책이 다른 작업에 둔다.

후보별도 span이 유용한 이유너무 잘게 나눈 신호
Retrieval / rerank검색 실패와 생성 실패를 분리한다각 배열 변환까지 span으로 만들어 탐색이 어려움
LLM callmodel, token, finish reason, provider 지연을 묶는다streaming chunk마다 span을 만들어 저장량이 폭증
Tool call권한, argument, retry, side effect를 추적한다내부 helper마다 span을 만들어 업무 경로가 묻힘
Guardrail / approval차단이 모델 실패인지 정상 통제인지 구분한다모든 단순 조건문을 독립 span으로 기록
Eval grader어떤 trace에 어떤 rubric 판정이 붙었는지 잇는다grader 내부 토큰마다 span을 생성

Span 수가 많다고 자동으로 좋지 않다. 질문에 답하는 경계가 아니라 구현 세부를 그대로 노출하면 telemetry 비용과 탐색 시간이 늘어난다.

4.2 Metric - 여러 요청의 규모와 추세를 본다

섹션 제목: “4.2 Metric - 여러 요청의 규모와 추세를 본다”

Metric은 애플리케이션이 실행 중 직접 기록한 measurement, trace에서 파생한 measurement, eval 결과를 같은 metric 이름·단위·저카디널리티 dimension으로 구성된 집계 stream에 담는다. Counter는 요청·오류처럼 누적되는 사건을, histogram은 요청별 지연·token처럼 분포가 중요한 값을, gauge는 현재 queue depth처럼 시점 상태를 표현한다. LLM 앱에서는 네 축을 함께 보는 편이 안전하다.

예시 metric답하는 질문
지연TTFT p50/p95/p99, total latency, tool latency, queue depth사용자가 어디서 얼마나 기다리는가
비용input/output/reasoning token, cost/request, cost/successful task, cache hit ratio성공 한 건을 만드는 데 얼마가 드는가
품질task success, faithfulness, refusal, human override, negative feedback정상 응답 중 실제로 쓸 만한 결과가 얼마나 되는가
신뢰성·안전timeout, retry, schema violation, guardrail hit, unauthorized tool attempt시스템 또는 통제가 어떤 실패를 막거나 놓치는가

평균이 1초여도 일부 요청이 15초라면 평균은 사용자 피해를 숨긴다. 다만 TTFT < 1초, cache hit 50% 이상 같은 숫자를 모든 route의 보편 목표로 쓰면 안 된다. 짧은 대화, 장문 분석, batch eval은 기대 지연과 캐시 가능성이 다르므로 route·사용자군·작업 위험도별 기준선과 SLO를 먼저 만든다.

Metric을 해석할 때는 집계·판단 시간창을 함께 고정한다. 예를 들어 gen_ai.client.operation.duration의 5분 histogram과 같은 이름의 24시간 histogram은 원본 measurement가 같아도 답하는 질문이 다르다. 짧은 창은 급격한 회귀를 빨리 보여주지만 흔들림이 크고, 긴 창은 SLO 추세를 안정적으로 보여주지만 incident 시작 시점을 늦게 드러낼 수 있다. Dashboard에는 5분 alert / 1시간 triage / 28일 SLO처럼 사용 목적과 창을 함께 표기한다.

Metric을 route, model_route, status, environment, app_version처럼 값 집합이 작고 통제 가능한 dimension으로 나누면 원인을 좁히기 쉽다. Version도 무제한 commit SHA를 영구 label로 쌓기보다 current, candidate, 최근 배포 몇 개처럼 분석 기간에 필요한 집합으로 제한한다. 반면 user_id, trace_id, span_id, 원문 prompt를 label로 넣으면 값 종류가 요청 수만큼 늘어 SDK 집계 상태·저장 비용·조회 부담이 커진다. 특히 trace_id label은 금지에 가까운 설계다. 요청별 식별자를 붙인 순간 metric stream이 사실상 request log로 변한다.

개별 요청으로 내려가야 할 때는 고카디널리티 label 대신 **exemplar(집계 구간을 대표하는 원본 measurement와 trace context 표본)**를 사용한다. 예를 들어 route=refund, app_version=v42의 p95 latency bucket에서 exemplar의 trace_id를 따라가 느린 한 건의 span을 연다. 이 연결이 성립하려면 measurement를 active span context 안에서 기록하고 SDK와 backend가 그 context를 exemplar로 보존해야 하며, 기본 TraceBased exemplar filter에서는 parent span이 sampled 상태여야 한다. Metric은 population의 규모를 유지하고 trace는 개별 원인을 보존하므로, 둘의 데이터 모델을 억지로 합치지 않고도 이동할 수 있다.

4.3 Eval - 실행 결과에 품질 의미를 붙인다

섹션 제목: “4.3 Eval - 실행 결과에 품질 의미를 붙인다”

Eval은 하나 이상의 grader를 사용해 결과를 판정한다.

  • 결정적 grader: JSON schema, exact match, 금지 tool, 권한 밖 source, 코드 테스트처럼 같은 입력에 같은 판정을 낸다.
  • 작업 특화 grader: RAG faithfulness, citation coverage, tool trajectory, 환불 정책 준수처럼 도메인 조건을 본다.
  • LLM-as-judge: 자유 텍스트의 유용성·명료성·근거 충실도를 rubric으로 채점한다.
  • 사람 평가: 법무·의료·결제, 새 실패 유형, judge 불일치처럼 자동 판정의 피해가 큰 구간을 검수한다.
  • 사용자 신호: 명시적 평가, 재질문, 수정, 취소, 사람 상담 전환을 결과 품질의 간접 증거로 쓴다.

한 grader가 모든 품질을 대표하지 않는다. JSON schema를 통과한 답이 사실과 다를 수 있고, 사용자 만족도가 높은 답이 보안 정책을 어길 수 있다. 그래서 품질 축과 안전 축을 하나의 평균 점수로 너무 빨리 합치지 않는다.

Trace, metric, eval은 다음 키로 연결한다.

역할누락되면 생기는 문제
trace_id, span_id한 실행과 세부 단계를 찾는다점수가 낮아도 어느 단계가 원인인지 모름
eval_run_id, dataset_version어떤 평가 실행과 시험지를 썼는지 고정한다같은 이름의 평가를 재현할 수 없음
app_version, git_sha배포된 코드와 설정을 찾는다회귀 시작 시점을 배포와 연결하지 못함
prompt_version, model_routeprompt와 model 변경을 분리한다모델 전체를 과잉 rollback하게 됨
retriever_version, document_id검색 경로와 근거를 재현한다생성 실패와 검색 실패를 구분하지 못함
tool_schema_version, guardrail_policy_version실행 계약과 보안 정책을 찾는다정상 차단과 계약 회귀를 혼동함
grader_name, grader_version, rubric_versiongrader 구현과 점수의 의미를 보존한다grader 변경을 앱 품질 변화로 오해함
judge_model, judge_prompt_version 또는 hashjudge model과 실제 채점 prompt를 식별한다judge 변경 뒤 score를 같은 척도로 오해함

Trace와 eval run은 보통 1:1이 아니다. 하나의 production trace를 여러 grader로 다시 평가할 수 있고, 같은 gold dataset item을 여러 app 후보로 실행할 수 있다. 따라서 trace_id는 원인 분석 키, eval_run_id는 비교·의사결정 키로 분리한다.

각 score와 eval result에는 이 키를 참조만 하지 말고 provenance snapshot을 함께 남긴다. LLM judge라면 grader_version, judge_model, judge_prompt_version 또는 immutable prompt hash와 함께 temperature, top_p, max_output_tokens, 지원되는 경우 seed처럼 결과 분포를 바꾸는 주요 decoding parameter를 기록한다. 결정적 grader에는 해당하지 않는 judge 필드를 비워 두되 grader_version은 유지한다.

4.5 Worked example - 평균 품질 하락에서 원인 span까지

섹션 제목: “4.5 Worked example - 평균 품질 하락에서 원인 span까지”

가상의 환불 agent 배포 전후를 비교해 보자.

전체 10,000 요청 metric:
task success 91% -> 87% (-4%p)
cost/request $0.018 -> $0.024 (+33%)
p95 total latency 2.8s -> 4.1s
HTTP error rate 0.7% -> 0.8% (거의 동일)
Slice:
일반 환불 route 93% -> 92%
고액 환불 route 88% -> 70%

HTTP 오류만 보면 거의 정상이다. 하지만 eval metric을 route=high_value_refund로 나누자 회귀가 집중된 곳이 드러났다. 실패 trace 40건의 span을 비교했더니 다음 차이가 있었다.

retrieval_top_k: 5 -> 20
prompt_version: refund-v12 -> refund-v13
tool retry_count p95: 0 -> 3
approval_required: 18% -> 61%

여기서 approval_required 증가는 곧바로 보안 실패가 아니다. 새 prompt가 불확실한 요청까지 write tool로 보내 보안 통제가 정상적으로 더 많이 개입한 결과일 수 있다. 원인 가설은 top_k 증가 -> 긴 context와 오래된 정책 혼입 -> 잘못된 tool 제안 -> retry와 approval 증가다.

검증은 한 번 더 필요하다. 실패 trace의 입력과 근거 문서를 gold dataset 후보로 저장하고, top_k, prompt, tool schema를 한 축씩 되돌려 offline eval을 실행한다. prompt v12와 top_k=8 조합에서 고액 환불 success가 회복되고 비용도 기준선으로 돌아오면 rollback 범위를 좁힐 수 있다. 이 예시는 metric이 영향 범위를 찾고, trace가 가설을 만들며, eval이 변경 결정을 검증하는 순서를 보여준다.

5.1 OpenTelemetry와 GenAI semantic convention

섹션 제목: “5.1 OpenTelemetry와 GenAI semantic convention”

OpenTelemetry(OTel)는 trace, metric, log를 수집·전송하는 벤더 중립 계측 체계다. OTLP(OpenTelemetry Protocol, OpenTelemetry 프로토콜)는 계측 애플리케이션·Collector·backend 사이에서 telemetry의 encoding, transport, delivery를 정의한다. Semantic convention은 그 데이터 안에서 같은 개념을 같은 attribute와 metric 이름으로 표현하기 위한 규약이다.

상태와 확인 시점이 중요하다. 2026-07-10 현재 GenAI semantic conventions는 기존 core semantic-conventions 문서에서 OpenTelemetry 공식 GenAI 전용 저장소로 이동했고, GenAI model spanGenAI metric은 모두 Development 상태다. 반면 OTLP의 trace·metric·log signal은 같은 시점 공식 spec에서 Stable이다. 따라서 아래 이름을 영구 불변 계약으로 복사하지 말고, instrumentation package와 semantic convention revision을 함께 고정한 뒤 upgrade diff를 검토한다.

아래 예시는 그 시점의 공식 model inference span 규약에 맞춘다.

gen_ai.provider.name: "openai"
gen_ai.operation.name: "chat"
gen_ai.request.model: "gpt-4.1"
gen_ai.request.temperature: 0.2
gen_ai.usage.input_tokens: 1850
gen_ai.usage.output_tokens: 286
gen_ai.response.finish_reasons: ["tool_calls"]
gen_ai.response.model: "provider-returned-model-id"
gen_ai.prompt.name: "refund-decision"
gen_ai.prompt.version: "refund-v13"
app.model_route: "balanced"
app.retriever.version: "policy-search-v7"
app.tool_schema.version: "refund-tools-v4"
app.guardrail.version: "refund-policy-v9"

여기서 gen_ai.request.modelbalanced 같은 애플리케이션 route가 아니라 provider에 실제로 요청한 정확한 model 이름이다. Provider가 응답에서 실제 처리 model을 돌려주면 그것은 gen_ai.response.model에 기록한다. 비용·품질 정책인 앱 route는 custom app.model_route로 분리해야 “route는 그대로인데 provider model만 바뀐” 사건을 찾을 수 있다. gen_ai.response.finish_reasons는 선택지마다 종료 이유가 있을 수 있으므로 scalar가 아니라 string[]이며, 한 응답도 ["stop"]처럼 배열로 남긴다.

gen_ai.*에는 도구 간 이동 가치가 큰 공통 필드를 우선한다. Prompt·output·tool definition의 content attribute는 현재 규약에서도 opt-in이고 민감 정보를 포함할 수 있다고 경고한다. 원문, retrieved chunk, tool argument를 편의 때문에 기본 수집하지 말고, 수집 목적·접근 권한·보존 기간이 승인된 별도 artifact 경로로 제한한다.

다음 코드는 특정 관측 제품의 사용법이 아니라 span 수명과 속성 경계를 보여주는 pseudo instrumentation이다.

import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
const tracer = trace.getTracer("refund-agent");
function getErrorType(error: unknown): string {
if (!(error instanceof Error)) return "unknown";
return (error as Error & { code?: string }).code ?? error.name;
}
async function runLlmStep(input: RedactedInput) {
return tracer.startActiveSpan(
"chat gpt-4.1",
{
kind: SpanKind.CLIENT,
attributes: {
"gen_ai.operation.name": "chat",
"gen_ai.provider.name": "openai",
"gen_ai.request.model": "gpt-4.1",
"gen_ai.prompt.name": "refund-decision",
"gen_ai.prompt.version": "refund-v13",
"app.model_route": "balanced",
"app.input.hmac": input.hmac,
"app.input.ref": input.artifactRef,
},
},
async (span) => {
try {
const result = await llm.chat(input.messages);
span.setAttributes({
"gen_ai.usage.input_tokens": result.usage.inputTokens,
"gen_ai.usage.output_tokens": result.usage.outputTokens,
"gen_ai.response.finish_reasons": [result.finishReason],
"gen_ai.response.model": result.model,
});
return result;
} catch (error) {
span.setAttribute("error.type", getErrorType(error));
span.setStatus({ code: SpanStatusCode.ERROR });
if (error instanceof Error) span.recordException(error);
throw error;
} finally {
span.end();
}
},
);
}

핵심은 세 가지다.

  1. span은 성공·실패 모두 끝나야 한다. 끝나지 않은 span은 지연 분포와 trace 구조를 왜곡한다.
  2. Trace에는 원문 대신 keyed HMAC과 접근 통제된 immutable artifact reference를 남긴다. HMAC만으로 원문이 복구되지는 않는다.
  3. 자동 계측으로 잡히지 않는 retrieval, 정책 판정, 업무 tool은 질문에 필요한 범위에서 수동 span을 보강한다.

Prompt와 tool result는 이메일, 전화번호, 계정 ID, 결제 정보, 내부 문서를 포함할 수 있다. 관측 시스템은 디버깅을 위해 데이터를 모으지만, 잘못 설계하면 새로운 유출 경로가 된다.

선택얻는 것잃는 것·보완
원문 전체 저장빠른 재현과 사람이 읽는 분석PII·secret 유출, 보존 비용. 고위험 route에는 부적합
수집 전 redaction민감 정보 노출 위험 감소문맥 일부 손실. 승인된 fixture·document ID로 보완
원문 미저장 + 참조 키최소 수집, 접근 통제 단순화원본 저장소의 버전·보존 계약이 없으면 replay 불가
제한된 샘플 원문대표 trace를 깊게 분석sampling 편향과 별도 접근 권한 관리 필요

Redaction은 exporter 뒤가 아니라 가능한 한 수집 앞단에서 수행한다. 이미 외부 backend로 전송된 뒤 마스킹하면 전송·임시 저장 구간의 노출을 막지 못한다. 단순 hash는 동일 입력 여부를 비교하는 fingerprint일 뿐 원문을 복구하지 못하고, 이메일·짧은 ID처럼 후보 공간이 작은 값은 사전 대입에도 약하다. HMAC(Hash-based Message Authentication Code, 비밀 키를 사용하는 메시지 인증 코드)을 회전 가능한 key로 만들거나 tokenization service가 발급한 opaque token을 쓰면 무키 hash보다 추측 공격과 키 연결 범위를 줄일 수 있지만, 이것도 원본 저장소나 복호화 권한을 대신하지는 않는다.

재현이 필요한 route는 trace 밖에 접근 통제된 immutable 원본 또는 승인된 canonical fixture를 둔다. Artifact reference에는 object version과 content HMAC을 연결하고, 저장소는 encryption, 최소 권한, 접근 감사, 삭제 정책을 적용한다. 예를 들어 raw artifact는 incident 탐지·조사 목표에 맞춰 14일, redacted trace metadata는 90일, 검수되어 gold dataset이 된 fixture는 dataset lifecycle 동안 보존할 수 있다. 이 숫자는 가상 정책이며 법적 의무·사용자 동의·사고 발견 지연에 맞춰 더 짧거나 길게 정해야 한다.

Replay manifest에는 입력만이 아니라 app_version, 정확한 request/response model, prompt·retriever·document·tool schema·guardrail version, sampling parameter, provider endpoint, 허용된 tool fixture를 남긴다. LLM은 비결정적이므로 성공 기준도 byte-for-byte 동일 출력으로 두지 않는다. 예를 들어 schema 통과, 같은 정책 문서 인용, 권한 밖 write 0건, 환불액 허용 오차 0원, 비용·지연 budget 충족처럼 업무 invariant와 허용 범위를 먼저 고정한다.

원본 보존이 허용되지 않으면 그 trace는 완전 replay 불가라고 명시하고, redacted synthetic fixture로 재현 가능한 부분만 시험한다. input_hash만 남겨 놓고 “replay 가능” 또는 “안전하게 익명화됨”이라고 부르면 실패다.

모든 trace를 영구 저장하면 비용이 빠르게 늘 수 있다. Sampling(샘플링)은 일부 trace만 보존하는 정책이다.

Trace sampling의 중심은 대표성과 진단이며, 감사 보존은 다시 별도 목적이다.

목적선택 방식답할 수 있는 질문
대표 무작위 samplingroute·version 같은 작은 strata 안에서 일관된 확률로 무작위 선택전체 pass rate, latency·cost 분포, slice 간 비율을 추정한다
Tail·diagnostic retention완료 trace의 error·고지연을, 채점 뒤 judge disagreement·새 실패를 표적 보존드문 실패 경로와 grader·rubric 문제를 깊게 분석한다
감사 보존결제·법무·외부 write 같은 route를 별도 정책으로 보존규제·분쟁·승인 이력을 확인한다. 통계 표본과 목적이 다르다

OTel의 head sampling은 확률 방식 자체가 아니라 trace 전체 결과를 보기 전에 초기에 보존 여부를 결정하는 방식이며, consistent probability sampling은 그 대표적인 구현일 뿐이다. Tail sampling은 대부분의 span이 끝난 뒤 error·overall latency·attribute를 보고 결정한다. Judge disagreement는 보통 trace 종료 뒤 eval에서 생기므로 Collector tail sampler만의 책임이 아니라 eval artifact retention 정책과 연결해야 한다.

표적 표본으로 population pass rate를 계산하면 생기는 편향

섹션 제목: “표적 표본으로 population pass rate를 계산하면 생기는 편향”

일 100,000건 중 대표 무작위 1%인 1,000건을 채점해 900건이 통과했다면 population pass rate의 관측 추정치는 90%다. 같은 날 error·고지연·judge disagreement 200건을 표적으로 모았더니 80건만 통과해 40%였다고 하자. 진단 표본은 실패가 많도록 설계했으므로 40%를 전체 품질이라고 보고하면 선택 편향이다. 대표 1,000건과 진단 200건을 단순 합쳐 980 / 1,200 = 81.7%라고 해도 같은 사건의 중복과 서로 다른 포함 확률 때문에 잘못된 추정이 된다.

각 artifact에는 표준 속성이라고 오해하지 않도록 app-owned metadata로 sampling.policy_version, sampling.reason, sampling.inclusion_probability를 남긴다. Strata마다 확률이 다르면 분석 시 sampling.analysis_weight = 1 / inclusion_probability 같은 가중치를 사용하되, 확률을 알 수 없는 표적 queue는 population estimator에서 제외한다. 정책 변경 전후를 비교할 때도 같은 policy version의 대표 표본끼리 먼저 비교한다.

span coverage 90% 같은 고정 목표는 출발점일 수 있지만 보편 기준은 아니다. 더 중요한 질문은 실패 가설을 검증하는 핵심 단계가 빠지지 않았는가다. retrieval span이 전부 빠진 RAG 앱은 전체 span 수가 많아도 원인을 설명하지 못한다.

6.1 gold dataset - 기준 시험지를 버전으로 관리한다

섹션 제목: “6.1 gold dataset - 기준 시험지를 버전으로 관리한다”

gold dataset은 단순한 질문 목록이 아니다. 최소한 다음을 포함한다.

필드이유
dataset_item_id, dataset_version같은 사례와 평가셋 버전을 재현한다
입력 또는 안전한 입력 참조모델·앱 후보를 다시 실행한다
기대 결과·허용 범위정답 하나가 아닌 유효한 변형을 표현한다
slice_tags언어, route, 위험도, 길이, retrieval hit/miss를 나눈다
provenanceproduction feedback, incident, 전문가 작성 등 출처를 안다
owner·reviewed_at누가 언제 기대 조건을 검수했는지 안다
grader·rubric version어떤 판정으로 통과했는지 고정한다

크기는 목적에 따라 달라진다. 초기 50개는 파이프라인과 명백한 회귀를 확인하는 데 유용할 수 있지만, 작은 품질 차이를 안정적으로 판정하기에는 부족할 수 있다. 500개도 production의 언어·권한·긴 대화 slice가 빠지면 대표성이 없다. 항목 수보다 실패 다양성, slice coverage, 라벨 신뢰도, 갱신 경로가 먼저다.

같은 200개 gold dataset item에 기존 prompt A와 후보 B를 실행했다고 하자.

A pass: 164 / 200 = 82%
B pass: 158 / 200 = 79%
차이: -3%p

이 숫자만으로 B를 폐기하면 성급하다. 같은 item을 비교했으므로 다음처럼 뒤집힌 사례를 본다.

둘 다 pass: 150
둘 다 fail: 28
A만 pass: 14
B만 pass: 8

핵심 정보는 A만 pass 14B만 pass 8이다. 표본이 작아 우연한 차이일 수 있고, B가 비용·지연을 크게 줄였을 수도 있다. 반대로 A만 통과한 14개가 권한·결제·법무 같은 high-risk slice라면 평균 차이가 작아도 배포를 막을 근거가 된다. 통계는 위험 우선순위를 대신하지 않는다.

신뢰구간은 “관측한 79%가 무한 반복에서도 정확히 79%인가”가 아니라, 제한된 표본 때문에 가능한 범위를 보여준다. Wilson interval은 pass/fail 비율의 불확실성을 다루는 한 방법이다. 공식을 외울 필요는 없지만, 표본이 작고 50% 근처일수록 구간이 넓어져 작은 차이를 구분하기 어렵다는 감각은 필요하다.

6.2 Offline, online, shadow, rollout을 분리한다

섹션 제목: “6.2 Offline, online, shadow, rollout을 분리한다”

Online eval은 production에서 실제 생성된 trace·output을 grader나 사람·사용자 신호로 채점하는 활동이다. 현재 version 하나만 운영 중이어도 online eval은 가능하다. 반면 A/B test와 canary는 어떤 candidate를 누구에게 노출할지 정하는 실험·배포 방식이다. A/B·canary가 online eval 데이터를 만들 수는 있지만 둘은 동의어가 아니다.

방식무엇을 하는가강점놓치는 것·위험
Offline eval고정 gold dataset에 baseline과 candidate를 재실행배포 전 빠른 회귀 검증, 반복 가능실제 traffic 변화와 사용자 행동
Online eval노출된 production trace·output을 배포 뒤 또는 실시간 채점현재 품질·정책 준수와 새 실패를 관찰정답 라벨 지연, 표본·사용자 신호의 selection bias
Shadow eval실제 입력을 candidate에도 복사하되 결과는 사용자에게 미노출현재 입력에서 candidate 차이를 비교실제 사용자 반응, side effect를 격리하지 않으면 중복 실행
A/B test·canary사용자군·traffic 일부에 서로 다른 version을 실제 노출candidate의 실제 사용자·업무 영향사용자 피해, 혼란 변수, 낮은 traffic에서의 표본 부족

Shadow는 production 입력을 candidate에 복제 실행하되 candidate 결과를 사용자에게 반환하지 않는 방식이다. Side effect 격리는 이 정의와 별개의 안전 조건이다. Shadow agent가 refund, send_email, delete_record 같은 실제 write tool을 호출해도 shadow가 아닌 것이 아니라 unsafe shadow이며, production을 중복 변경한다. 따라서 tool adapter를 stub으로 바꿔 호출 의도만 기록하거나, production credential이 없는 read-only sandbox와 격리된 fixture를 사용해야 한다. Read tool도 tenant data 접근과 rate limit을 건드릴 수 있으므로 shadow 전용 권한·quota를 둔다.

네 방식은 경쟁 관계가 아니다.

Offline: 명백한 schema·정책·품질 회귀를 먼저 차단
-> Shadow: side effect를 격리하고 현재 traffic에서 비용·지연·출력 차이 확인
-> Canary/A-B: 제한된 범위에서 실제 사용자 영향 측정
-> Online eval: 노출된 production trace를 계속 채점
-> Full rollout: drift와 feedback을 계속 관찰

canary 1%도 보편값이 아니다. 일 요청 100건인 route의 1%는 판단할 표본이 거의 없고, 일 요청 백만 건인 고위험 route의 1%는 피해가 클 수 있다. 비율보다 필요한 표본 수, 최대 허용 피해, rollback 속도를 함께 본다.

값싼 grader로 넓게 걸러내고, 비싸거나 느린 grader를 불확실하고 위험한 구간에 집중한다.

평가 대상먼저 쓸 grader보강할 grader경계
JSON·tool argumentschema validation, business rule사람 검수문법 통과가 업무 정합성을 보장하지 않음
코드·수학실행 테스트, exact matchLLM 설명 평가, 사람표현 유사도보다 실행 결과가 직접적
RAG 답변source 존재, citation 연결faithfulness judge, 사람검색 문서 자체가 틀리면 충실한 오답 가능
자유 텍스트 도움됨rubric 기반 LLM-as-judge사용자·전문가 평가길이·톤·judge 지식에 편향될 수 있음
보안 정책금지 action·권한 invariantred-team, 사람평균 점수로 치명적 단일 위반을 숨기면 안 됨
Agent task최종 task successtrajectory·recovery eval정답만 보면 불필요한 tool·비용·위험 경로를 놓침

BLEU(Bilingual Evaluation Understudy)는 번역 후보와 기준 문장의 n-gram 겹침을 보는 지표이고, ROUGE(Recall-Oriented Understudy for Gisting Evaluation)는 요약에서 기준 문구를 얼마나 회수했는지 본다. 둘 다 빠르지만 의미가 같은 다른 표현을 낮게 평가할 수 있다. 따라서 이 문서에서는 필수 선수지식으로 두지 않고, 표현 겹침이 실제 성공 조건인 작업에만 1차 필터로 선택한다.

Faithfulness(근거 충실도)는 RAG 답변의 주장 중 retrieved context로 뒷받침되는 정도를 본다. 이것도 정답률과 같지 않다. 오래된 정책 문서에 충실한 답은 faithfulness가 높아도 현재 업무 기준에는 틀릴 수 있다. 그래서 document_version, freshness, 권한을 trace에 남기고 task correctness와 분리한다.

6.4 LLM-as-judge의 한계와 안전장치

섹션 제목: “6.4 LLM-as-judge의 한계와 안전장치”

LLM-as-judge는 강한 LLM이 다른 출력의 유용성·정확성·스타일을 rubric으로 평가하는 방식이다. 자유 텍스트를 빠르게 평가할 수 있지만 judge도 하나의 비결정적 production dependency다.

기존 문서가 인용한 Judging the Judges 연구는 15개 judge, 22개 task, 약 15만 평가 instance에서 position bias를 비교했다. 여기서 가져올 결론은 특정 모델의 순위가 아니라, 순서 편향이 단순한 무작위 잡음이 아니며 model·task별로 달라질 수 있다는 점이다. 따라서 다른 workload에 연구 수치를 그대로 임계값으로 복사하지 않고 자기 calibration set에서 다시 측정한다.

한계작은 관찰 예시완화 방향
Position biasA/B 순서를 바꾸자 승자가 바뀜순서를 바꾼 swap test, tie 허용
Verbosity bias핵심은 같은데 긴 답을 더 높게 평가길이 제약, 핵심 기준을 rubric에 명시
Self-enhancementjudge와 같은 model family 답을 선호cross-family·사람 calibration 비교
Limited reasoning코드·수학·전문 정책의 오류를 놓침실행 가능한 verifier와 전문가 검수
Rubric driftrubric 문구 변경 뒤 점수 분포가 이동rubric_version, calibration set, 재채점
Judge drift앱은 그대로인데 provider의 judge 동작이 변함고정 calibration set의 주기적 재실행

Swap test를 수치로 보자. 200개 A/B pair를 두 순서로 평가했는데 18개가 순서만 바꾼 뒤 승패가 뒤집혔다면 flip rate는 18 / 200 = 9%다. 9%라는 값 자체가 보편 차단선은 아니다. 다만 후보의 예상 품질 차이가 2%p인데 judge 순서 민감도가 9%라면, 그 judge만으로 배포 결정을 내리기 어렵다는 뜻이다.

Calibration set은 사람이 이미 판정한 고정 사례 묶음이다. judge model, prompt, rubric을 바꿀 때 같은 묶음을 재평가해 사람과의 agreement와 slice별 오류를 비교한다. 사람이 판정한 모든 데이터를 judge 튜닝에 사용하면 다시 과적합될 수 있으므로, 개발용과 최종 점검용을 분리하는 편이 낫다.

Production 모든 trace를 judge에 보내는 것도 기본값이 아니다. 고위험 route, 새로운 app version, 사용자 부정 피드백, grader disagreement에는 높은 평가 비율을 적용하고, 안정된 저위험 route는 샘플링한다. 비용은 평가 건수 x judge input/output token x 단가 x 반복 횟수로 계산한다. Swap·다중 judge를 켜면 호출 수가 배수로 늘어난다.

Hallucination(환각)은 모델이 근거 없거나 사실과 다른 내용을 그럴듯하게 만드는 실패다. 하나의 detector가 모든 환각을 잡는다고 가정하지 않는다.

방법잘 맞는 조건반례·경계
FaithfulnessRAG처럼 답변이 의존해야 할 retrieved context가 있음Context가 오래됐거나 틀리면 근거에 충실한 오답을 통과시킴
Self-consistency같은 질문을 반복했을 때 답이 불안정한지 탐색반복해서 같은 오답을 내면 일관성은 높지만 사실성은 낮음
Reference-free judge정답 문서가 없고 의심 답을 넓게 선별Judge가 모르는 전문 사실과 자기 확신 편향을 놓칠 수 있음
Log probability·confidence모델 내부 불확실성의 보조 신호가 제공됨높은 확신은 사실성 보장이 아니고 provider마다 노출 계약이 다름
Citation validation출처 ID·문장 연결을 제품 계약으로 강제Citation이 존재해도 실제 주장을 뒷받침하지 않거나 source가 틀릴 수 있음

따라서 RAG route는 retrieval hit → source freshness·권한 → claim-source 연결 → task correctness를 나눠 평가한다. 근거 없는 일반 대화는 reference-free judge와 사람 검수를 사용할 수 있지만, 고위험 사실 판단은 별도 권위 source나 결정적 verifier가 필요하다. Detector disagreement 자체도 사람이 볼 failure slice다.

Production eval은 테스트 파일 하나보다 작은 CI 시스템에 가깝다.

[Dataset Registry]
-> dataset_version, item, slice, owner
[Runner]
-> app/prompt/model 실행, trace_id 생성
[Run Store]
-> output, latency, cost, artifact를 append-only 저장
[Graders]
-> deterministic + task-specific + judge + human sample
[Report]
-> baseline/candidate delta와 실패 slice
[Decision Gate]
-> block / manual review / shadow / canary / rollout

최소 데이터 모델은 trace와 decision을 join할 수 있을 정도면 된다.

레코드핵심 필드목적
datasetsname, version, owner, schema hash평가 기준을 고정한다
dataset_itemsitem ID, version, immutable input ref·HMAC, expected ref, slice입력과 기대 결과의 승인된 원본을 찾고 무결성을 확인한다
eval_runsrun ID, baseline run, app·prompt·model version, git SHA후보 실행 단위를 정의한다
eval_resultsrun ID, item ID, trace ID, output ref, latency, cost, status실행과 trace를 연결한다
scoresrun ID, item ID, score name, rubric version, grader, value점수의 의미를 보존한다

Run store는 append-only가 유리하다. Candidate가 baseline 결과를 덮어쓰면 나중에 “같은 dataset에서 왜 결정이 바뀌었는가”를 추적할 수 없다. Replay가 필요한 원문은 run row에 복제하지 않고 접근 통제된 immutable artifact store에 두며, run에는 object version·HMAC·retention class를 포함한 reference를 남긴다. 원문 보존이 금지된 item은 synthetic fixture인지 부분 replay만 가능한지 명시한다.

Eval은 점수표가 아니라 배포 위험을 줄이는 결정 입력이다.

신호예시 결정주의할 경계
Schema·PII·권한 invariant 위반hard block치명적 단일 위반을 평균으로 상쇄하지 않음
Helpfulness 소폭 하락, judge 불확실manual review 또는 shadow고정 -2%p 같은 보편 임계 대신 noise·slice·위험 확인
비용 상승과 품질 상승route별 canary단위 비용과 사용자 가치 함께 평가
특정 언어·고객군만 회귀해당 slice rollout 중지전체 평균이 통과해도 부분 실패를 숨기지 않음
Tool override·approval 증가정책·prompt 원인 분석보안 통제가 정상 작동한 결과일 수 있음

Rollback 단위는 LLM 앱 전체 하나보다 prompt, model route, retriever config, tool schema, guardrail policy로 나눈다. 그래야 비용 회귀 때문에 보안 수정까지 되돌리는 과잉 rollback을 피할 수 있다.

7. Drift와 품질 저하는 같은 말이 아니다

섹션 제목: “7. Drift와 품질 저하는 같은 말이 아니다”

Drift는 기준 시점과 현재의 분포가 달라진 현상이다.

  • Input drift: 질문 길이, 언어, 주제, 사용자군, 첨부 형식이 달라진다.
  • Retrieval drift: 검색 corpus, embedding, chunking, reranker, source freshness가 달라진다.
  • Output drift: 답변 길이, refusal, citation, tool 선택, token 사용 분포가 달라진다.
  • Label·rubric drift: 사람이 기대하는 정책이나 평가 기준이 달라진다.

분포 변화는 원인 또는 조짐이지 곧바로 품질 실패가 아니다. 새 FAQ가 유입되어 질문 주제가 바뀌어도 모델이 잘 대응하면 drift는 있지만 품질은 유지된다.

품질 저하는 task success, 정책 준수, 사용자 결과처럼 우리가 중요하다고 정의한 결과가 나빠진 현상이다. 입력 분포가 그대로여도 prompt 버그, provider 회귀, 잘못된 corpus 배포 때문에 품질이 떨어질 수 있다.

상태Drift품질 저하예시판단
A있음없음한국어 질문 비율이 늘었지만 success 유지경보보다 gold dataset 대표성 갱신 검토
B없음있음같은 replay에서 prompt v13만 tool 선택 실패배포 회귀로 보고 version rollback 검토
C있음있음새 정책 질문 유입 뒤 stale 문서 답변 증가corpus·검색·평가셋을 함께 갱신
D감지 안 됨있음detector job이 멈췄는데 점수만 하락telemetry freshness부터 복구

이 구분 때문에 drift alert -> 재학습을 자동 규칙으로 두면 위험하다. 먼저 실제 품질 metric과 실패 trace를 확인하고, 데이터·검색·prompt·model·정책 중 어느 경계가 바뀌었는지 찾는다.

KS(Kolmogorov-Smirnov) test는 두 연속값 표본의 누적 분포가 얼마나 다른지 본다. P-value는 “분포가 같다”는 가정 아래 지금 같은 차이가 얼마나 드문지를 나타낸다. 표본이 매우 크면 실무상 작은 차이도 통계적으로 유의해질 수 있으므로 effect size와 품질 영향을 함께 본다.

PSI(Population Stability Index)는 기준 구간과 현재 구간을 여러 bucket으로 나누고, 각 bucket 비율이 얼마나 달라졌는지 합산한다. 신용평가 등에서 알려진 0.1, 0.2, 0.25 같은 휴리스틱이 있지만 LLM query·embedding·route의 보편 임계는 아니다. 자기 workload의 seasonality, 표본 수, bucket 방식으로 burn-in한 뒤 alert 수준을 보정한다.

Embedding처럼 고차원인 입력은 축 하나의 PSI로 충분하지 않을 수 있다. MMD(Maximum Mean Discrepancy)나 cluster 비율처럼 결합 분포를 보는 방법을 보강할 수 있지만, 첫 회독에서는 “한 숫자가 모든 분포 변화를 대표하지 않는다”는 경계만 잡아도 된다.

7.4 Drift detector가 거짓말하는 반례

섹션 제목: “7.4 Drift detector가 거짓말하는 반례”
  1. Seasonality: 주말 질문이 평일과 정상적으로 다르다. 평일 하루를 baseline으로 잡으면 매주 drift가 울린다. 같은 요일·캠페인·업무 주기를 비교한다.
  2. Embedding version 변경: 사용자 질문은 그대로인데 encoder를 바꾸면 벡터 좌표가 달라져 큰 drift처럼 보인다. embedder_version을 분리하고 baseline을 다시 만든다.
  3. 단변량 false negative: 언어 비율과 길이는 각각 비슷하지만 “짧은 영어 질문” 조합만 사라질 수 있다. slice·cluster·결합 분포로 본다.
  4. Detector freshness 실패: 계산 job이 멈추면 값이 평온하게 고정된다. 마지막 계산 시각과 input row 수 자체를 metric으로 둔다.
  5. Sampling 정책 변경: 부정 피드백 trace만 과표집하면 output 분포가 갑자기 나빠진 것처럼 보인다. 수집 정책 버전을 함께 비교한다.

7.5 Worked example - drift는 울렸지만 품질은 유지된 경우

섹션 제목: “7.5 Worked example - drift는 울렸지만 품질은 유지된 경우”

가상의 고객지원 챗봇에서 프로모션 주간에 평균 질문 길이가 42 token에서 71 token으로 늘고, 배송 질문 비율이 20%에서 48%로 늘었다. Input drift detector는 route 기준선을 벗어났다고 알렸다.

그러나 배송 slice의 gold dataset replay와 online sample을 확인하니 task success는 90%에서 91%, negative feedback은 4.1%에서 4.0%로 거의 유지됐다. 비용은 긴 입력 때문에 요청당 18% 올랐다. 이 경우 “모델 품질 저하”로 rollback할 근거는 약하다. 대신 배송 질문을 gold dataset에 더 반영하고, prompt caching·context 길이·route 예산을 점검한다.

반대로 drift 값은 안정적인데 같은 고정 replay의 faithfulness가 88%에서 72%로 떨어졌다면 입력 변화가 아니라 app·retriever·provider version 회귀를 먼저 의심한다.

[Production traffic]
-> telemetry + 사용자 피드백 수집
[Metric]
-> 영향 범위와 변화 시점 감지
[Trace]
-> 실패 경로와 version 차이로 가설 생성
[Triage + human review]
-> 실제 실패·정상 통제·label 문제 구분
[gold dataset candidate]
-> provenance와 slice를 붙여 검수·versioning
[Offline eval]
-> 수정 후보의 회귀와 비용·지연 비교
[Shadow]
-> side effect를 격리하고 현재 입력에서 후보 비교
[Canary / A-B]
-> 제한된 노출로 실제 사용자 영향 확인
[Online eval]
-> 노출된 production trace를 계속 채점
[Rollout]
-> 다시 drift·feedback 관찰

루프가 닫혔다는 말은 부정 피드백을 데이터베이스에 저장했다는 뜻이 아니다. 실패가 검수된 평가 item이 되고, 수정 후보의 배포 gate에서 실제로 재실행되어야 한다.

8.2 Production feedback를 바로 정답으로 쓰지 않는 이유

섹션 제목: “8.2 Production feedback를 바로 정답으로 쓰지 않는 이유”

사용자 피드백에는 selection bias가 있다. 매우 만족하거나 불만인 사용자가 더 자주 평가하고, 재질문은 모델 실패가 아니라 요구사항 변화일 수 있다. 상담원 override도 모델 오답, 정책 변경, 상담원 개인 선호를 섞는다.

따라서 다음 단계를 둔다.

  1. Trace, 원문 접근 정책, 사용자 결과를 묶어 candidate로 저장한다.
  2. 중복·PII·권한을 처리하고 실패 유형을 라벨링한다.
  3. 전문가 또는 owner가 기대 결과와 허용 범위를 검수한다.
  4. 기존 slice와 비교해 과대표집을 조정한다.
  5. dataset_version을 만들고 baseline도 같은 버전으로 다시 평가한다.

“부정 피드백만 계속 추가”하면 gold dataset이 실패 사례에 과도하게 치우쳐 production pass rate를 대표하지 못할 수 있다. 회귀 방지용 challenge slice와 대표성 측정용 traffic slice를 분리하면 목적이 선명해진다.

8.3 Agent·Cost·Security 실패를 평가 자산으로 바꾸기

섹션 제목: “8.3 Agent·Cost·Security 실패를 평가 자산으로 바꾸기”
앞 토픽의 실패Trace에 남길 것Metric으로 볼 것Eval asset으로 남길 것
Agent stuck loopstep, tool fingerprint, termination reasonstep/request, repeated tool rate, recovery rate반복 tool trajectory와 종료 기대 조건
Goal driftoriginal goal hash, plan·observation spantask success, human override목표를 유지해야 하는 multi-step 사례
Prompt 누적·비용 폭주input token/step, retry, cache key versioncost/success, token growth, cache hitlong conversation과 retry budget 사례
Tail latencyretrieval·LLM·tool·queue spanTTFT·total latency p95/p99latency budget을 포함한 route 사례
Prompt injectioninput/source ID, proposed tool, policy decisionattack pass, unauthorized attempt, guardrail hit정상·공격 pair와 허용/차단 기대 결과
RAG permission leaktenant/ACL decision, retrieved chunk IDunauthorized source rate권한 밖 source가 0이어야 하는 fixture
PII trace 노출redaction policy version, detector resultPII finding rate, telemetry export failureredaction fixture와 보존 정책 검사

이 표의 마지막 열이 이 문서의 핵심 산출물이다. Incident를 post-mortem 문장으로만 끝내지 않고, 다음 변경에서 자동 또는 사람 검수로 다시 실패를 잡는 평가 자산으로 바꾼다.

아래 수치는 고정 임계가 아니라 자기 기준선을 만들 때 확인할 신호다.

증상의미할 수 있는 것먼저 확인할 연결
Trace는 많지만 원인 분석 불가핵심 span·version 누락, 구현 세부 span 과다retrieval/LLM/tool/policy 경계와 version key
Metric은 나빠졌는데 관련 trace가 없음sampling 편향, exporter 누락, trace 보존 실패metric 시간창과 trace sampling policy
Eval score만 있고 trace_id가 없음품질 알림은 가능하지만 실행 원인 국소화 불가eval_result -> trace -> span join
Judge 점수 상승, 사용자 결과 하락rubric·judge bias, proxy metric 불일치calibration set, disagreement, task outcome
같은 app을 재평가할 때 점수가 이동dataset·rubric·judge version 미고정eval run metadata와 immutable baseline
gold dataset은 통과, production 실패 증가dataset stale, slice 누락, traffic drift최근 실패 provenance와 slice coverage
Drift alert가 반복되지만 품질 정상seasonality, version change, 과민한 detector비교 window, embedder·sampling version
Drift 값이 계속 같음실제 안정 또는 detector job 정지freshness, row count, last successful run
Guardrail hit·approval queue 급증공격 증가 또는 app이 위험 action을 더 제안proposed tool span, prompt·policy version
비용과 지연이 함께 증가context 누적, retry, tool loop, cache misstoken/step, retry, cache, critical path
Trace에서 PII·secret 발견redaction이 늦거나 원문 수집 범위 과다ingestion 전 처리와 접근·보존 정책
Rollback 후 metric만 회복, eval은 실패성능 증상은 완화됐지만 품질 원인 미해결실패 slice replay와 gold dataset result

Silent failure의 공통점은 대시보드가 조용하다는 것이 아니라 관측 신호와 실제 사용자 결과 사이 연결이 끊긴 것이다. 각 파이프라인에는 데이터 freshness, join 성공률, grader 실행 성공률 같은 “관측·평가 시스템 자체의 건강 지표”도 필요하다.

10. 도구는 순위가 아니라 책임으로 고른다

섹션 제목: “10. 도구는 순위가 아니라 책임으로 고른다”

관측·평가 제품의 기능과 포맷은 계속 바뀐다. 따라서 특정 연도 라인업이나 하나의 순위보다 필요한 책임을 먼저 나눈다. 아래 도구명은 범주를 이해하기 위한 예시이며, 도입 시점의 공식 문서에서 지원 범위·export 형식·배포 모델을 다시 확인한다.

책임필요한 능력예시선택 질문
계측·전송OTel SDK, auto/manual instrumentation, OTLP exportOpenTelemetry, OpenLLMetry/Traceloop핵심 provider·framework를 계측하고 다른 backend로 내보낼 수 있는가
Trace 저장·탐색hierarchical trace, prompt·tool observation, samplingLangfuse, LangSmith, Phoenix, Weave, Datadog, Logfire필요한 span과 version을 검색하고 접근·보존 정책을 적용할 수 있는가
Dataset·evaldataset version, runner, grader, report, CI gatePromptfoo, Braintrust, LangSmith, Langfuse, Ragastrace와 eval run을 join하고 rubric·grader 버전을 남길 수 있는가
Gateway·비용provider routing, usage, cache, rate limitHelicone, Portkeygateway 밖의 retrieval·tool span도 연결할 수 있는가
Drift·분석slice, distribution comparison, data qualityPhoenix, Evidently, WhyLabs 계열version·seasonality·detector freshness를 구분할 수 있는가

도구 하나가 여러 책임을 제공할 수 있지만, UI에서 기능이 함께 보인다고 데이터 모델까지 하나로 합칠 필요는 없다. OTel 공통 필드, app version key, dataset/eval contract를 먼저 정하면 backend를 바꿀 때 학습 자산과 추적 키를 보존하기 쉽다.

가상의 사내 RAG 챗봇이 일 100,000 요청, CI 30분 이내, 400개 gold dataset, 민감 trace 자체 환경 보존이라는 조건을 가진다고 하자. 아래 수치와 가격은 계산법을 익히기 위한 가상 pilot 결과이며 실제 제품 성능·가격 주장이 아니다.

필수 조건은 OTLP export, retrieval·LLM·tool·policy span, dataset versioning, score-trace join, 수집 전 redaction, 30일 hot retention이다. 관리형·self-host·gateway 중 무엇이 유명한지가 아니라 이 조건을 어느 조합이 충족하는지 비교한다.

저장량을 먼저 손으로 계산한다

섹션 제목: “저장량을 먼저 손으로 계산한다”

대표 무작위 trace 10%인 10,000건/일과, 중복 제거한 tail diagnostic trace 2,000건/일을 보존한다고 하자. 한 trace는 평균 7 span, content를 제외한 압축 span 하나는 평균 2.5KB로 pilot에서 관찰했다.

일 ingest = 12,000 trace/day x 7 span/trace x 2.5KB/span
= 210,000KB/day ≈ 210MB/day
30일 raw ingest = 210MB/day x 30 = 6.3GB/month
index + replica 계수 2.5 적용 = 6.3GB x 2.5 = 15.75GB provisioned

100%를 같은 조건으로 저장하면 100,000 x 7 x 2.5KB x 30 = 52.5GB/month이고 index·replica 적용 시 약 131.25GB다. 10% 대표 표본과 tail 보존 조합은 저장량을 약 88% 줄이지만, audit route까지 버려도 된다는 뜻은 아니다. 또한 이 계산은 prompt·output 원문을 제외한다. Sampled trace마다 content가 평균 12KB 더 붙으면 12,000 x 12KB x 30 = 4.32GB/month가 raw ingest에 추가되고 PII 위험도 함께 커진다.

Baseline 1개와 candidate 2개를 400 item에 실행하면 app 실행은 400 x 3 = 1,200회다. Pilot에서 concurrency 40, p95 app latency 4초였지만 rate limit·retry를 포함한 실제 app 단계는 11분이 걸렸다. Baseline과 두 candidate의 400 pair를 swap test까지 평가하면 judge 호출은 400 x 2 candidate x 2 order = 1,600회이고, concurrency 20에서 9분이 걸렸다. Fixture 준비·report 생성 2분을 더하면 총 22분이므로 30분 gate에 8분 여유가 남는다. 평균 latency만으로 이론값을 계산했다면 rate limit과 retry를 놓쳤을 것이다.

가상 후보Pilot 결과월 비용·운영 가정판정
A. 관리형 trace+eval 통합CI 18분, 핵심 span 99%, score-trace join 충족$420, raw data 외부 반출 필요기능은 통과, 데이터 경계 위반으로 제외
B. Self-host OTel+eval storeCI 22분, 핵심 span 98%, 15.75GB hot storage$180 + 운영 6시간모든 필수 조건 충족
C. Gateway+별도 eval runnerCI 27분, retrieval·tool 포함 핵심 span coverage 82%$140 + 별도 join 파이프라인원인 span 누락이 필수 조건에 미달

이 가상 팀은 B를 선택한다. A보다 CI가 4분 느리고 운영 시간이 들지만 30분 budget 안이며 데이터 경계를 지킨다. C는 가장 싸지만 18%의 핵심 span 누락 때문에 환불 agent의 검색 실패와 tool 실패를 분리할 수 없다. Traffic이 작고 데이터 반출이 허용되면 A가 더 나을 수 있고, retrieval·tool 원인 분석이 필요 없는 단순 proxy라면 C가 합리적일 수 있다. 판단을 닫는 기준은 가격 순위가 아니라 필수 계약, 저장량, CI 시간, 사람이 감당할 운영 부담이다.

개념 A개념 B구분
ObservabilityMonitoring내부 상태를 추론하는 능력 vs 미리 고른 신호를 계속 확인하는 활동
TelemetryObservability수집 데이터 vs 그 데이터로 질문에 답할 수 있는 시스템 성질
TraceSpan요청·작업 전체 경로 vs 그 안의 개별 작업 구간
TraceMetric한 실행의 맥락 vs 여러 실행의 집계·분포
MetricEvalruntime measurement의 집계 stream vs 기대 조건과 비교해 의미를 만드는 판정
TraceEval run실제 또는 평가 실행의 경로 vs dataset 후보 전체의 비교 단위
Offline evalOnline eval고정 dataset 재실행 vs production trace·output 채점
Online evalA/B·canaryproduction 결과를 평가하는 활동 vs version 노출을 정하는 실험·배포 방식
Online evalShadow eval노출된 결과 채점 vs 미노출 candidate를 격리 실행해 비교
gold datasetProduction sample사람이 검수한 기준 평가 데이터셋 vs 아직 정답이 확정되지 않은 운영 표본
DriftQuality degradation분포 변화 vs 중요한 결과가 나빠진 상태
FaithfulnessCorrectness주어진 근거에 충실함 vs 실제·정책상 정답임
ScoreRubric판정 결과 값 vs 그 값을 만드는 기준과 예시
Guardrail hitSecurity incident통제가 위험을 막은 사건 vs 실제 경계가 뚫리거나 피해가 발생한 사건
RedactionReplayability민감 정보 제거 vs 승인 원본·version·fixture로 같은 조건을 재구성하는 능력

작은 LLM 기능이라면 다음 순서면 충분하다.

  1. 업무 성공을 정의한다: schema, task success, 금지 action, 비용·지연 budget을 나눈다.
  2. 핵심 trace를 만든다: request, retrieval, LLM, tool, policy span과 version key를 연결한다.
  3. route별 metric을 만든다: 지연·비용·품질·안전 축을 같은 시간창에서 본다.
  4. 작은 gold dataset을 만든다: 정상, 경계, 실패, 고위험 slice를 포함하고 version을 붙인다.
  5. grader를 층으로 쌓는다: 결정적 검사부터 시작하고 judge·사람을 필요한 곳에 추가한다.
  6. offline에서 변경을 비교한다: 평균뿐 아니라 새 실패와 slice를 본다.
  7. shadow 또는 canary로 production 조건을 확인한다: shadow tool side effect를 격리하고, canary는 최대 허용 피해와 rollback 단위를 정한다.
  8. 실패 trace를 다음 dataset version에 편입한다: feedback loop를 실제 gate로 닫는다.

처음부터 모든 도구와 통계 검정을 도입할 필요는 없다. 반대로 trace dashboard만 만들고 품질 기대 조건을 정의하지 않으면 이 문서의 절반만 구현한 것이다.

LLM 관측성과 평가 학습 확인

  • 관측 가능성, telemetry, trace/span, metric, eval, gold dataset, drift를 역할과 함께 설명할 수 있다
  • metric이 영향 범위를 찾고 trace가 원인을 좁히며 eval이 변경 결정을 검증하는 흐름을 설명할 수 있다
  • trace_id와 eval_run_id가 왜 다른지, 어떤 version key로 연결하는지 말할 수 있다
  • online eval이 production trace 채점이고 A/B·canary는 노출 방식임을 구분하며, shadow side effect를 격리할 수 있다
  • LLM-as-judge의 position·verbosity·rubric·judge drift 한계와 calibration 방법을 설명할 수 있다
  • drift가 있어도 품질은 유지될 수 있고, drift 없이도 품질 회귀가 생길 수 있음을 반례로 설명할 수 있다
  • Agent·Cost·Security 실패를 trace, metric, eval asset으로 바꾸는 방법을 설계할 수 있다
  • 고정 임계값 대신 route 기준선, 위험도, 표본 수, rollback 속도로 gate를 보정할 수 있다
  • hash만으로 replay할 수 없음을 설명하고 immutable artifact·HMAC·보존 기간·replay 성공 기준을 설계할 수 있다
  • 도구를 연도별 순위가 아니라 계측·저장·eval·gateway·drift 책임으로 선택할 수 있다

본문 결론은 아래 부록 없이 이해할 수 있다. 직접 손으로 연결 구조를 확인하거나 incident drill을 설계할 때만 펼친다.

선택 실습: 한 요청을 trace -> metric -> eval로 연결하기

가상의 환불 agent 요청 20건을 대상으로, 실행 경로·집계·품질 판정이 서로 다른 데이터이면서 같은 version key로 연결된다는 점을 확인한다.

trace:
trace_id, route, app_version, prompt_version,
retrieval_ms, llm_ms, tool_ms,
input_tokens, output_tokens, tool_name, policy_decision,
sampling_policy_version, sampling_reason, inclusion_probability
eval:
eval_run_id, trace_id, dataset_item_id,
immutable_input_ref, input_hmac,
task_success, policy_compliance,
grader_name, grader_version, rubric_version,
judge_model, judge_prompt_version_or_hash,
temperature, top_p, max_output_tokens, seed
  1. 20건 중 5건은 긴 입력, 3건은 오래된 정책 문서, 2건은 권한 없는 write tool 제안을 포함하도록 fixture를 만든다.
  2. 요청마다 retrieval, LLM, tool, policy span을 기록한다.
  3. 대표 무작위 표본에서 route, app_version별 p95 latency, 평균 token, task success, policy hit를 집계한다.
  4. 실패 trace를 열어 어떤 span과 version이 공통인지 찾는다.
  5. 오래된 문서 3건과 권한 tool 2건을 gold dataset candidate로 만들고 기대 판정을 검수한다.
  6. prompt 또는 retriever 후보를 바꿔 같은 item을 재실행한다.
  • 낮은 task_success metric에서 실제 실패 trace로 이동할 수 있다.
  • trace_id만으로 실행 경로를, eval_run_id로 baseline/candidate 비교를 찾을 수 있다.
  • 권한 없는 tool이 제안됐지만 policy가 차단한 경우를 task 실패와 security incident로 무조건 동일 취급하지 않는다.
  • HMAC만으로 원문을 복구하려 하지 않고, 접근 통제된 immutable fixture reference와 version으로 입력을 재구성한다.
  • Replay 결과가 byte-for-byte 같지 않아도 schema·정책·권한·환불액 invariant로 성공 여부를 판정한다.

Metric만 만들었다면 어느 문서와 prompt가 원인인지 모른다. Trace만 만들었다면 20건 중 영향 비율과 후보 개선 여부를 모른다. Eval만 만들었다면 실제 production 경로와 비용을 놓친다. 세 데이터가 join될 때 운영 결정이 된다.

선택 시나리오: 장문 대화 비용·지연 incident와 rollback drill

가상의 사내 챗봇에서 p95 TTFT와 provider 시간당 비용이 함께 약 두 배로 늘었다. HTTP 오류율은 변하지 않았고, 탐지부터 제한된 rollback까지 목표는 30분이다.

Metric:
p95 TTFT 0.9s -> 4.8s
provider cost/hour $25 -> $50
input token/request 800 -> 5,000
cache hit ratio 70% -> 5%
negative feedback 4% -> 9%
Trace diff:
prompt_version chat-v20 -> chat-v21
history_window 12 turns -> unlimited
cache_key_version stable-v3 -> dynamic-v4

Provider 장애라면 여러 app version과 route에서 queue·TTFT가 함께 움직일 가능성이 높다. 이 사례는 새 prompt version에서 history가 무한 누적되고 동적 값이 prefix에 들어가 cache가 깨진 가설이 더 강하다. Drift metric이 input 길이 변화를 보여도 원인은 사용자 행동이 아니라 app config 변경일 수 있다.

Provider 비용만 보면 초과 burn은 $50 - $25 = $25/hour다. 30분 안에 rollback하면 직접 초과 비용은 약 $12.50로 제한되지만 6시간 뒤 발견하면 $150가 된다. 실제 incident 비용에는 느린 응답으로 인한 상담 전환, 이탈, on-call 시간이 더해지므로 이 계산은 하한이다. 30분 목표는 모든 원인을 확정하는 시간이 아니라 영향 version을 격리하고 비용·지연 증가를 멈추는 시간이다.

  1. 30분 mitigation 목표 안에서 chat-v21의 신규 traffic만 중지하고 chat-v20으로 route를 되돌린다.
  2. metric 정상화와 함께 long-conversation gold dataset을 재실행한다.
  3. history_window, cache key, prompt를 한 축씩 바꿔 원인을 확인한다.
  4. 실패 trace 20건을 중복·PII 처리 후 long-context slice candidate로 등록한다.
  5. 다음 배포 gate에 token growth, p95 TTFT, task success를 함께 넣는다.

지연과 비용 metric이 기준선으로 돌아오는 것만으로는 충분하지 않다. Long-context slice의 task success와 정책 준수 eval이 통과하고, replay manifest와 접근 통제된 immutable fixture로 입력 조건을 재구성할 수 있어야 한다. 수치는 이 가상 workload의 관찰값일 뿐 다른 route의 보편 임계가 아니다.

선택 심화: LLM-as-judge calibration 점검

Judge 결과를 hard gate로 쓰기 전에 순서 편향과 rubric 변경 영향을 확인한다.

  1. 사람이 A/B 승패와 tie를 판정한 calibration pair를 준비한다.
  2. [A, B][B, A] 순서로 같은 rubric을 실행한다.
  3. 순서 변경 flip rate, 사람 agreement, slice별 오류를 기록한다.
  4. Rubric 문구를 바꾸면 version을 올리고 기존 calibration을 다시 실행한다.
  5. 코드·수학·정책 항목은 실행 verifier 또는 전문가 판정과 따로 비교한다.

Judge model과 rubric이 바뀌어도 score의 의미를 추적할 수 있어야 한다. Flip·agreement가 후보 간 기대 차이보다 크다면 judge score를 hard block에 쓰지 않고 사람 검수나 결정적 verifier를 우선한다. 특정 숫자를 보편 통과선으로 외우는 것이 목적이 아니다.

  1. Observability는 telemetry를 연결해 예상하지 못한 내부 상태를 추론하는 능력이며, trace·span·metric은 서로 다른 질문에 답한다.
  2. Metric은 영향 범위를 찾고, trace는 실패 위치와 version 차이를 좁히며, eval은 결과 품질과 배포 후보를 판정한다.
  3. gold dataset은 사람이 검수한 버전 있는 기준 평가 데이터셋이며, online eval은 production trace 채점이고 shadow·canary는 실행·노출 방식이다.
  4. LLM-as-judge와 drift detector는 유용하지만 bias·version·seasonality·표본 의존성이 있어 고정 임계와 단일 점수로 운영 결정을 자동화하면 안 된다.
  5. 이 문서는 Agent·Cost·Security에서 배운 실패를 통합 trace와 production eval의 증거·회귀 자산으로 바꾸는 책임을 맡는다.

최종 수정: 2026-07-10