한 요청의 비용이 대체로 고정된다
LLM 요청은 input/output token, reasoning, tool 횟수, retry, model route에 따라 비용이 달라진다.
token usage와 model·tool span을 같은 trace에 연결한다.분류: Layer 12 - AI 시스템 & LLM 애플리케이션 | 선수지식: L6-60 (Logs/Metrics/Traces), L11-80 (모델 평가), L12-60 (Agent), L12-70 (비용/지연), L12-80 (보안)
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 token과 first chunk를 섞지 않는다.
따라서 대시보드가 많다고 관측 가능성이 높은 것은 아니다. 모든 차트가 route=chat 전체 평균만 보여주고 trace에는 prompt·model·retriever 버전이 없다면, 이상은 감지해도 원인을 국소화하지 못한다. 반대로 trace 한 건이 상세하더라도 전체 사용자 중 몇 퍼센트가 영향을 받았는지는 metric이 필요하다.
Eval 결과도 집계하면 metric stream의 measurement가 될 수 있지만, 둘의 출발점은 다르다.
faithfulness, policy_compliance, task_success는 기대 조건과 비교하는 grader가 판정하고, 그 결과를 시간대·route별 metric stream으로 집계할 수 있다.즉 metric은 숫자의 저장 형태가 아니라 실행 중 생긴 측정값을 동일한 이름·단위·dimension 집합으로 묶어 시간에 따라 집계한 신호이고, eval은 출력에 품질 의미를 부여하는 판정 과정이다. helpfulness=0.82라는 숫자만 저장하고 rubric, grader, dataset 버전을 남기지 않으면 같은 0.82가 다음 달에도 같은 뜻인지 알 수 없다.
APM(Application Performance Monitoring, 애플리케이션 성능 모니터링)은 요청 수, 오류율, 지연, DB·외부 API 구간을 보는 데 강하다. LLM 애플리케이션도 이 기반이 필요하지만, HTTP 200 OK가 성공을 뜻하지 않는 경우가 많다.
LLM 요청은 input/output token, reasoning, tool 횟수, retry, model route에 따라 비용이 달라진다.
token usage와 model·tool span을 같은 trace에 연결한다.sampling과 provider 변경 때문에 같은 질문도 정답, 거절, 환각으로 갈릴 수 있다.
한 건의 trace와 반복 eval, 표본 집계를 함께 본다.문법상 정상인 답이 근거를 꾸미거나 잘못된 tool argument를 제안할 수 있다.
schema·정책·품질 grader와 사용자 피드백을 붙인다.RAG, reranker, LLM, tool, approval, critic loop가 한 작업 안에서 중첩된다.
agent trajectory를 parent-child span으로 표현한다.가상의 환불 agent가 200 OK를 반환했다고 하자. 실제로는 검색이 오래된 정책을 골랐고, 모델이 잘못된 환불액을 만들었으며, 승인 tool이 권한 밖 요청을 거절했을 수 있다. APM은 지연과 tool의 403을 보여줄 수 있지만, 검색 실패인지 모델 판단 실패인지, 보안 통제가 피해를 막은 정상 거절인지까지는 eval과 업무 맥락이 필요하다.
L11의 모델 평가는 고정된 데이터로 모델·prompt 후보를 비교하는 기반을 제공했다. 하지만 배포 뒤에는 다음 조건이 바뀐다.
그래서 production eval은 오프라인 시험을 버리는 것이 아니라, 실패한 production trace를 다시 gold dataset으로 가져와 오프라인 회귀 검증을 갱신하는 닫힌 루프를 만든다.
앞선 L12 토픽은 각각 실패를 예방하는 메커니즘을 다뤘다.
이 문서의 책임은 그 설명을 반복하는 것이 아니다. 그 실패가 실제 production에서 일어났는지 통합 trace로 증거를 남기고, 영향 범위를 metric으로 재며, 재발 여부를 production eval과 gold dataset으로 검증하는 계약을 소유한다.
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 call | model, 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 비용과 탐색 시간이 늘어난다.
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는 개별 원인을 보존하므로, 둘의 데이터 모델을 억지로 합치지 않고도 이동할 수 있다.
Eval은 하나 이상의 grader를 사용해 결과를 판정한다.
한 grader가 모든 품질을 대표하지 않는다. JSON schema를 통과한 답이 사실과 다를 수 있고, 사용자 만족도가 높은 답이 보안 정책을 어길 수 있다. 그래서 품질 축과 안전 축을 하나의 평균 점수로 너무 빨리 합치지 않는다.
Trace, metric, eval은 다음 키로 연결한다.
| 키 | 역할 | 누락되면 생기는 문제 |
|---|---|---|
trace_id, span_id | 한 실행과 세부 단계를 찾는다 | 점수가 낮아도 어느 단계가 원인인지 모름 |
eval_run_id, dataset_version | 어떤 평가 실행과 시험지를 썼는지 고정한다 | 같은 이름의 평가를 재현할 수 없음 |
app_version, git_sha | 배포된 코드와 설정을 찾는다 | 회귀 시작 시점을 배포와 연결하지 못함 |
prompt_version, model_route | prompt와 model 변경을 분리한다 | 모델 전체를 과잉 rollback하게 됨 |
retriever_version, document_id | 검색 경로와 근거를 재현한다 | 생성 실패와 검색 실패를 구분하지 못함 |
tool_schema_version, guardrail_policy_version | 실행 계약과 보안 정책을 찾는다 | 정상 차단과 계약 회귀를 혼동함 |
grader_name, grader_version, rubric_version | grader 구현과 점수의 의미를 보존한다 | grader 변경을 앱 품질 변화로 오해함 |
judge_model, judge_prompt_version 또는 hash | judge 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은 유지한다.
가상의 환불 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 -> 20prompt_version: refund-v12 -> refund-v13tool retry_count p95: 0 -> 3approval_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이 변경 결정을 검증하는 순서를 보여준다.
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 span과 GenAI 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.2gen_ai.usage.input_tokens: 1850gen_ai.usage.output_tokens: 286gen_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.model은 balanced 같은 애플리케이션 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(); } }, );}핵심은 세 가지다.
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의 중심은 대표성과 진단이며, 감사 보존은 다시 별도 목적이다.
| 목적 | 선택 방식 | 답할 수 있는 질문 |
|---|---|---|
| 대표 무작위 sampling | route·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 정책과 연결해야 한다.
일 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 수가 많아도 원인을 설명하지 못한다.
gold dataset은 단순한 질문 목록이 아니다. 최소한 다음을 포함한다.
| 필드 | 이유 |
|---|---|
dataset_item_id, dataset_version | 같은 사례와 평가셋 버전을 재현한다 |
| 입력 또는 안전한 입력 참조 | 모델·앱 후보를 다시 실행한다 |
| 기대 결과·허용 범위 | 정답 하나가 아닌 유효한 변형을 표현한다 |
slice_tags | 언어, route, 위험도, 길이, retrieval hit/miss를 나눈다 |
| provenance | production 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: 28A만 pass: 14B만 pass: 8핵심 정보는 A만 pass 14와 B만 pass 8이다. 표본이 작아 우연한 차이일 수 있고, B가 비용·지연을 크게 줄였을 수도 있다. 반대로 A만 통과한 14개가 권한·결제·법무 같은 high-risk slice라면 평균 차이가 작아도 배포를 막을 근거가 된다. 통계는 위험 우선순위를 대신하지 않는다.
신뢰구간은 “관측한 79%가 무한 반복에서도 정확히 79%인가”가 아니라, 제한된 표본 때문에 가능한 범위를 보여준다. Wilson interval은 pass/fail 비율의 불확실성을 다루는 한 방법이다. 공식을 외울 필요는 없지만, 표본이 작고 50% 근처일수록 구간이 넓어져 작은 차이를 구분하기 어렵다는 감각은 필요하다.
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 argument | schema validation, business rule | 사람 검수 | 문법 통과가 업무 정합성을 보장하지 않음 |
| 코드·수학 | 실행 테스트, exact match | LLM 설명 평가, 사람 | 표현 유사도보다 실행 결과가 직접적 |
| RAG 답변 | source 존재, citation 연결 | faithfulness judge, 사람 | 검색 문서 자체가 틀리면 충실한 오답 가능 |
| 자유 텍스트 도움됨 | rubric 기반 LLM-as-judge | 사용자·전문가 평가 | 길이·톤·judge 지식에 편향될 수 있음 |
| 보안 정책 | 금지 action·권한 invariant | red-team, 사람 | 평균 점수로 치명적 단일 위반을 숨기면 안 됨 |
| Agent task | 최종 task success | trajectory·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와 분리한다.
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 bias | A/B 순서를 바꾸자 승자가 바뀜 | 순서를 바꾼 swap test, tie 허용 |
| Verbosity bias | 핵심은 같은데 긴 답을 더 높게 평가 | 길이 제약, 핵심 기준을 rubric에 명시 |
| Self-enhancement | judge와 같은 model family 답을 선호 | cross-family·사람 calibration 비교 |
| Limited reasoning | 코드·수학·전문 정책의 오류를 놓침 | 실행 가능한 verifier와 전문가 검수 |
| Rubric drift | rubric 문구 변경 뒤 점수 분포가 이동 | 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가 모든 환각을 잡는다고 가정하지 않는다.
| 방법 | 잘 맞는 조건 | 반례·경계 |
|---|---|---|
| Faithfulness | RAG처럼 답변이 의존해야 할 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할 수 있을 정도면 된다.
| 레코드 | 핵심 필드 | 목적 |
|---|---|---|
datasets | name, version, owner, schema hash | 평가 기준을 고정한다 |
dataset_items | item ID, version, immutable input ref·HMAC, expected ref, slice | 입력과 기대 결과의 승인된 원본을 찾고 무결성을 확인한다 |
eval_runs | run ID, baseline run, app·prompt·model version, git SHA | 후보 실행 단위를 정의한다 |
eval_results | run ID, item ID, trace ID, output ref, latency, cost, status | 실행과 trace를 연결한다 |
scores | run 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을 피할 수 있다.
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 비율처럼 결합 분포를 보는 방법을 보강할 수 있지만, 첫 회독에서는 “한 숫자가 모든 분포 변화를 대표하지 않는다”는 경계만 잡아도 된다.
embedder_version을 분리하고 baseline을 다시 만든다.가상의 고객지원 챗봇에서 프로모션 주간에 평균 질문 길이가 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에서 실제로 재실행되어야 한다.
사용자 피드백에는 selection bias가 있다. 매우 만족하거나 불만인 사용자가 더 자주 평가하고, 재질문은 모델 실패가 아니라 요구사항 변화일 수 있다. 상담원 override도 모델 오답, 정책 변경, 상담원 개인 선호를 섞는다.
따라서 다음 단계를 둔다.
dataset_version을 만들고 baseline도 같은 버전으로 다시 평가한다.“부정 피드백만 계속 추가”하면 gold dataset이 실패 사례에 과도하게 치우쳐 production pass rate를 대표하지 못할 수 있다. 회귀 방지용 challenge slice와 대표성 측정용 traffic slice를 분리하면 목적이 선명해진다.
| 앞 토픽의 실패 | Trace에 남길 것 | Metric으로 볼 것 | Eval asset으로 남길 것 |
|---|---|---|---|
| Agent stuck loop | step, tool fingerprint, termination reason | step/request, repeated tool rate, recovery rate | 반복 tool trajectory와 종료 기대 조건 |
| Goal drift | original goal hash, plan·observation span | task success, human override | 목표를 유지해야 하는 multi-step 사례 |
| Prompt 누적·비용 폭주 | input token/step, retry, cache key version | cost/success, token growth, cache hit | long conversation과 retry budget 사례 |
| Tail latency | retrieval·LLM·tool·queue span | TTFT·total latency p95/p99 | latency budget을 포함한 route 사례 |
| Prompt injection | input/source ID, proposed tool, policy decision | attack pass, unauthorized attempt, guardrail hit | 정상·공격 pair와 허용/차단 기대 결과 |
| RAG permission leak | tenant/ACL decision, retrieved chunk ID | unauthorized source rate | 권한 밖 source가 0이어야 하는 fixture |
| PII trace 노출 | redaction policy version, detector result | PII finding rate, telemetry export failure | redaction 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 miss | token/step, retry, cache, critical path |
| Trace에서 PII·secret 발견 | redaction이 늦거나 원문 수집 범위 과다 | ingestion 전 처리와 접근·보존 정책 |
| Rollback 후 metric만 회복, eval은 실패 | 성능 증상은 완화됐지만 품질 원인 미해결 | 실패 slice replay와 gold dataset result |
Silent failure의 공통점은 대시보드가 조용하다는 것이 아니라 관측 신호와 실제 사용자 결과 사이 연결이 끊긴 것이다. 각 파이프라인에는 데이터 freshness, join 성공률, grader 실행 성공률 같은 “관측·평가 시스템 자체의 건강 지표”도 필요하다.
관측·평가 제품의 기능과 포맷은 계속 바뀐다. 따라서 특정 연도 라인업이나 하나의 순위보다 필요한 책임을 먼저 나눈다. 아래 도구명은 범주를 이해하기 위한 예시이며, 도입 시점의 공식 문서에서 지원 범위·export 형식·배포 모델을 다시 확인한다.
| 책임 | 필요한 능력 | 예시 | 선택 질문 |
|---|---|---|---|
| 계측·전송 | OTel SDK, auto/manual instrumentation, OTLP export | OpenTelemetry, OpenLLMetry/Traceloop | 핵심 provider·framework를 계측하고 다른 backend로 내보낼 수 있는가 |
| Trace 저장·탐색 | hierarchical trace, prompt·tool observation, sampling | Langfuse, LangSmith, Phoenix, Weave, Datadog, Logfire | 필요한 span과 version을 검색하고 접근·보존 정책을 적용할 수 있는가 |
| Dataset·eval | dataset version, runner, grader, report, CI gate | Promptfoo, Braintrust, LangSmith, Langfuse, Ragas | trace와 eval run을 join하고 rubric·grader 버전을 남길 수 있는가 |
| Gateway·비용 | provider routing, usage, cache, rate limit | Helicone, Portkey | gateway 밖의 retrieval·tool span도 연결할 수 있는가 |
| Drift·분석 | slice, distribution comparison, data quality | Phoenix, 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/monthindex + replica 계수 2.5 적용 = 6.3GB x 2.5 = 15.75GB provisioned100%를 같은 조건으로 저장하면 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 store | CI 22분, 핵심 span 98%, 15.75GB hot storage | $180 + 운영 6시간 | 모든 필수 조건 충족 |
| C. Gateway+별도 eval runner | CI 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 | 구분 |
|---|---|---|
| Observability | Monitoring | 내부 상태를 추론하는 능력 vs 미리 고른 신호를 계속 확인하는 활동 |
| Telemetry | Observability | 수집 데이터 vs 그 데이터로 질문에 답할 수 있는 시스템 성질 |
| Trace | Span | 요청·작업 전체 경로 vs 그 안의 개별 작업 구간 |
| Trace | Metric | 한 실행의 맥락 vs 여러 실행의 집계·분포 |
| Metric | Eval | runtime measurement의 집계 stream vs 기대 조건과 비교해 의미를 만드는 판정 |
| Trace | Eval run | 실제 또는 평가 실행의 경로 vs dataset 후보 전체의 비교 단위 |
| Offline eval | Online eval | 고정 dataset 재실행 vs production trace·output 채점 |
| Online eval | A/B·canary | production 결과를 평가하는 활동 vs version 노출을 정하는 실험·배포 방식 |
| Online eval | Shadow eval | 노출된 결과 채점 vs 미노출 candidate를 격리 실행해 비교 |
gold dataset | Production sample | 사람이 검수한 기준 평가 데이터셋 vs 아직 정답이 확정되지 않은 운영 표본 |
| Drift | Quality degradation | 분포 변화 vs 중요한 결과가 나빠진 상태 |
| Faithfulness | Correctness | 주어진 근거에 충실함 vs 실제·정책상 정답임 |
| Score | Rubric | 판정 결과 값 vs 그 값을 만드는 기준과 예시 |
| Guardrail hit | Security incident | 통제가 위험을 막은 사건 vs 실제 경계가 뚫리거나 피해가 발생한 사건 |
| Redaction | Replayability | 민감 정보 제거 vs 승인 원본·version·fixture로 같은 조건을 재구성하는 능력 |
작은 LLM 기능이라면 다음 순서면 충분하다.
처음부터 모든 도구와 통계 검정을 도입할 필요는 없다. 반대로 trace dashboard만 만들고 품질 기대 조건을 정의하지 않으면 이 문서의 절반만 구현한 것이다.
본문 결론은 아래 부록 없이 이해할 수 있다. 직접 손으로 연결 구조를 확인하거나 incident drill을 설계할 때만 펼친다.
가상의 환불 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, seedroute, app_version별 p95 latency, 평균 token, task success, policy hit를 집계한다.task_success metric에서 실제 실패 trace로 이동할 수 있다.trace_id만으로 실행 경로를, eval_run_id로 baseline/candidate 비교를 찾을 수 있다.Metric만 만들었다면 어느 문서와 prompt가 원인인지 모른다. Trace만 만들었다면 20건 중 영향 비율과 후보 개선 여부를 모른다. Eval만 만들었다면 실제 production 경로와 비용을 놓친다. 세 데이터가 join될 때 운영 결정이 된다.
가상의 사내 챗봇에서 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-v4Provider 장애라면 여러 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을 격리하고 비용·지연 증가를 멈추는 시간이다.
chat-v21의 신규 traffic만 중지하고 chat-v20으로 route를 되돌린다.history_window, cache key, prompt를 한 축씩 바꿔 원인을 확인한다.지연과 비용 metric이 기준선으로 돌아오는 것만으로는 충분하지 않다. Long-context slice의 task success와 정책 준수 eval이 통과하고, replay manifest와 접근 통제된 immutable fixture로 입력 조건을 재구성할 수 있어야 한다. 수치는 이 가상 workload의 관찰값일 뿐 다른 route의 보편 임계가 아니다.
Judge 결과를 hard gate로 쓰기 전에 순서 편향과 rubric 변경 영향을 확인한다.
[A, B]와 [B, A] 순서로 같은 rubric을 실행한다.Judge model과 rubric이 바뀌어도 score의 의미를 추적할 수 있어야 한다. Flip·agreement가 후보 간 기대 차이보다 크다면 judge score를 hard block에 쓰지 않고 사람 검수나 결정적 verifier를 우선한다. 특정 숫자를 보편 통과선으로 외우는 것이 목적이 아니다.
gold dataset은 사람이 검수한 버전 있는 기준 평가 데이터셋이며, online eval은 production trace 채점이고 shadow·canary는 실행·노출 방식이다.최종 수정: 2026-07-10