
에이전트 의사결정 품질의 침묵하는 드리프트(Silent Drift): 사용자가 발견하기 전에 포착하는 방법
요약
에이전트가 에러 없이도 잘못된 결정을 내리는 '침묵하는 드리프트(Silent Drift)' 현상을 설명합니다. 이를 포착하기 위해 단일 트레이스 분석을 넘어 의사결정 분포를 계측하고 모니터링하는 방법론을 제시합니다.
핵심 포인트
- 침묵하는 드리프트는 에러 없이 의사결정 품질만 저하되는 현상임
- 단일 트레이스보다는 수천 번의 실행에 걸친 의사결정 분포를 분석해야 함
- 의사결정 단계와 도구 호출 정보를 속성(attribute)으로 계측하여 모니터링할 것
- 도서: Observability for LLM Applications — Tracing, Evals, and Shipping AI You Can Trust
- 저자의 다른 저서: Agents in Production — The AI Engineer's Library (2권 시리즈)의 동반 도서
- 내 프로젝트: Hermes IDE | GitHub — Claude Code 및 기타 AI 코딩 도구를 사용하여 배포하는 개발자를 위한 IDE
- 나에 대하여: xgabriel.com | GitHub
당신의 트리아지 에이전트(triage agent)가 운영 환경(production)에 배포된 지 3개월이 되었습니다. 트레이스(traces)는 깨끗해 보입니다. 모든 스팬(span)은 녹색이고, 모든 실행은 종료되며, p95 지연 시간(latency)은 일정하고, 토큰 비용은 평이합니다. 그러던 중 고객 지원팀에서 스크린샷 하나를 전달해 옵니다. 에이전트가 결제 환불 요청을 보안 큐(security queue)로 라우팅했습니다. 당신은 궤적(trajectory)을 추출해 봅니다. 아무것도 고장 나지 않았습니다. 에이전트는 합리적인 인자(arguments)와 함께 합리적인 도구(tool)를 호출했고, 합리적인 결과를 얻었으며, 매우 확신에 찬 상태로 잘못된 큐를 선택했습니다.
이것이 바로 침묵하는 드리프트(silent drift)입니다. 트레이스는 에이전트가 무엇을 했는지는 보여줍니다. 하지만 에이전트가 한 행동이 정말 좋았는지는 알려주지 않습니다. 모델 제공업체의 마이너 버전 업데이트, 누군가 화요일에 배포한 프롬프트(prompt) 수정, 그리고 유입되는 트래픽의 느린 변화 사이에서, 에이전트의 의사결정 품질은 변합니다. 이는 에러와 함께 자신을 드러내는 경우가 드뭅니다. 대신 고객 지원 티켓으로 나타나고, 그다음 두 번째 티켓으로, 그리고 결국 계정 해지(churn)로 이어집니다.
당신은 메모리 누수(memory leak)를 잡는 것과 같은 방식으로 이를 포착해야 합니다. 대시보드를 뚫어지게 쳐다보는 것이 아니라, 베이스라인(baseline)과 알람(alarm)을 통해 잡아야 합니다.
의사결정 품질은 숫자가 아니라 분포입니다
_Agents in Production_의 12장에서 다루는 실패한 트레이스들은 쉬운 사례입니다. 동일한 빈 검색을 20번 재시도하는 것은 눈에 띄게 잘못된 것입니다. invoke_agent 부모 프로세스에서 루프 횟수를 보고 바로 알 수 있습니다. 대부분의 품질 저하(regression)는 단일 트레이스에서는 보이지 않습니다. 수천 번의 실행에 걸친 의사결정의 분포(distribution)를 살펴볼 때에만 나타납니다.
따라서 가장 먼저 계측(instrument)해야 할 것은 의사결정(decision) 그 자체입니다. 트레이싱(tracing) 장을 따라오셨다면, 이미 채팅 스팬(chat span)당 작은 고정 어휘를 방출하고 있을 것입니다:
span.set_attribute("gen_ai.agent.step", 3)
span.set_attribute("gen_ai.agent.decision", "call_tool")
span.set_attribute(
...
이 네 가지 속성(attribute)은 의사결정 품질을 측정 가능한 대상으로 바꿔줍니다. decision과 decision.tool은 궤적(trajectory)의 형태를 보여줍니다. version은 어떤 에이전트 수정 버전(revision)이 이를 생성했는지 알려줍니다. 이 값들을 기준으로 그룹화(group)하면 분포(distribution)를 얻을 수 있습니다. 즉, 각 의사결정 유형이 얼마나 자주 발생하는지, 모델이 어떤 도구(tool)를 사용하는지, 한 번의 실행에 몇 단계(step)가 소요되는지 등을 알 수 있습니다. 이 분포가 움직인다면, 품질도 함께 움직이고 있는 것입니다.
정답을 알고 있을 때 베이스라인(baseline)을 설정하세요
베이스라인은 에이전트가 건강할 때 한 번 점수를 매겨둔, 좋은 궤적이 어떤 모습인지 이미 알고 있는 태스크들의 골든 세트(golden set)입니다. 수천 개까지는 필요하지 않습니다. 실제 트래픽 혼합(traffic mix)을 반영하여 수작업으로 확인한 100개의 케이스가 1만 개의 합성(synthetic) 케이스보다 훨씬 낫습니다.
각 케이스를 두 가지 축으로 평가하세요. 결과(Outcome): 실행이 올바른 최종 답변에 도달했는가. 궤적(Trajectory): 그곳에 도달하기 위해 합리적인 경로를 거쳤는가. 실행이 잘못된 경로(세 번의 중복된 도구 호출, 운 좋게 맞춘 추측 등)를 통해 올바른 답변에 도달할 수도 있지만, 그 경로는 다음 모델 업데이트 시 살아남지 못할 것입니다. 목적지뿐만 아니라 경로를 평가하세요.
from dataclasses import dataclass
@dataclass
...
전체 골든 세트를 에이전트에 실행하고, 집계된 수치(aggregate)를 기록하세요: 결과 정확도(outcome accuracy), 정확한 궤적 비율(exact-trajectory rate), 중앙값 단계(median steps). 이 수치들을 생성한 에이전트 버전과 함께 고정(freeze)해 두십시오. 이 고정된 기록이 향후 모든 실행이 비교 대상이 될 기준이 됩니다. 이것이 없다면 "에이전트가 예전보다 못한 것 같다"는 말은 측정이 아니라 주장에 불과하게 됩니다.
프롬프트가 변경될 때마다, 모델 버전(model pin)이 올라갈 때마다, 도구가 추가될 때마다 CI(지속적 통합)에서 골든 세트를 다시 실행하세요. 이것이 비용이 적게 드는 게이트(gate) 역할을 합니다. 사용자가 발견하기 전에 여러분 스스로 도입한 회귀(regression)를 잡아낼 수 있습니다.
온라인 샘플링(Online sampling): CI뿐만 아니라 운영 환경(production)도 평가하세요
CI는 여러분이 수행한 변경 사항을 다룹니다. 하지만 여러분에게 가해진 변경 사항은 다루지 못합니다. 예를 들어, 제공업체가 동일한 모델 별칭(model alias) 뒤에 새로운 스냅샷(snapshot)을 배포하거나, 트래픽이 더 어려운 세그먼트로 이동하거나, 상위 도구(upstream tool)가 미묘하게 다른 데이터를 반환하기 시작하는 경우입니다. 이러한 상황에 대비하기 위해서는 운영 중인 프로덕션(production) 트래픽을 실시간으로 평가해야 합니다.
모든 실행(run)을 평가할 수는 없습니다. 평가에는 모델 호출 비용이나 인간의 시간이 소요되며, 여러분은 시간당 수천 번의 실행을 수행하기 때문입니다. 따라서 샘플링(sampling)을 해야 합니다. 두 가지 종류의 샘플링이 있으며, 두 가지 모두를 활용해야 합니다.
무작위 샘플링(Random sampling)은 전체 모집단에 대해 편향되지 않은 읽기 값을 제공합니다. 완료된 궤적(trajectories) 중 작은 고정 비율을 균등하게 추출하십시오. 타겟 샘플링(Targeted sampling)은 문제가 발생할 가능성이 가장 높은 실행을 포착합니다. 예를 들어 높은 스텝 수(step counts), 베이스라인(baseline)이 드물다고 판단한 결정, 또는 에이전트가 짧고 신뢰도가 낮은 채팅 구간(chat span)과 함께 생성한 최종 답변 등이 해당됩니다.
import random
def should_grade(run, base_rate=0.02):
...
무작위 슬라이스(random slice)는 품질 추정치의 정직함을 유지해 줍니다. 타겟 슬라이스(targeted slice)는 문제를 빠르게 찾아냅니다. 저장 시 이들을 분리하여 타겟 샘플이 모집단 추정치를 오염시키지 않도록 하십시오. 의심스러운 실행에 대해서만 계산된 지표는 완벽하게 건강한 에이전트에 대해서도 매우 나쁘게 보일 수 있습니다.
샘플링된 실행을 채점하는 판사(Judge)
샘플링된 실행에 대해서는 점수가 필요합니다. 인간의 검토(Human review)는 골드 스탠다드(gold standard)이지만, 유일한 도구로 쓰기에는 너무 느립니다. 강력한 모델과 엄격한 루브릭(rubric)에 고정된 LLM 판사(LLM judge)는 대량의 데이터를 처리하며, 인간은 판사가 수행한 호출 중 일부를 감사(auditing)합니다.
from anthropic import Anthropic
client = Anthropic()
...
판사 모델을 명시적으로 고정하십시오. 여러분도 모르게 업그레이드되는 판사는 여러분이 관찰하려는 에이전트와 똑같이 드리프트(drift)하며, 그렇게 되면 두 개의 움직이는 베이스라인을 갖게 되어 기준점(ground)이 사라지게 됩니다. 루브릭은 짧게 유지하고 출력은 구조화하십시오. 정기적인 슬라이스에 대해 인간의 라벨(human labels)과 비교하여 판사를 감사하십시오. 만약 판사와 인간의 일치도가 임계값(threshold) 아래로 떨어지면, 판사 자체가 퇴보한 것이므로 다른 점수를 신뢰하기 전에 이를 먼저 수정해야 합니다.
노이즈가 아닌 드리프트에 반응하는 알람
이제 시간의 흐름에 따라 두 가지 신호를 갖게 됩니다: CI(지속적 통합)의 골든 세트(golden-set) 점수와 프로덕션(production)의 등급 지정 샘플(graded-sample) 점수입니다. 드리프트 탐지(Drift detection)는 이 점수들이 기준선(baseline)에 대해 어떻게 움직이는지 관찰하며, 그 움직임이 무작위적인 것이 아니라 실제적인 변화일 때 사람에게 알림을 보냅니다.
함정은 단 한 번의 잘못된 실행에 대해 알람을 울리는 것입니다. 확률적 시스템(stochastic system)에서 한 번의 잘못된 궤적(trajectory)은 예상 가능한 일입니다. 여러분이 원하는 것은 지속적인 변화입니다. 최소 샘플 크기를 가진 이동 창(rolling window)을 사용하면 노이즈 때문에 알람이 울리는 상황을 방지할 수 있습니다.
def drift_alarm(window, baseline, min_n=200, drop=0.05):
if len(window) < min_n:
return None
...
비교하기 전에 gen_ai.agent.version을 기준으로 창(window)을 세분화하십시오. 롤아웃(rollout) 중에 두 버전이 섞인 비율을 사용하면 실제 성능 저하(regression)가 보이지 않게 뭉개지거나, 단순히 혼합 비율의 변화(mix shift)일 뿐인 가짜 저하를 만들어낼 수 있습니다. 디버깅 챕터에서도 압박감이 심한 상황에서 트레이스(trace)를 읽을 때 동일한 내용을 언급합니다: 먼저 버전별로 필터링하십시오. 드리프트 탐지는 이러한 직관을 상시 업무로 전환한 것입니다.
결과 정확도(outcome accuracy)뿐만 아니라 궤적 분포(trajectory distribution)도 관찰하십시오. 만약 classify_ticket이 이전에는 실행의 92%에서 발생했는데 현재 74%에서 발생한다면, 에이전트가 예전에 사용하던 것과는 다른 도구를 찾고 있다는 뜻이며, 이는 결과 점수가 알람을 울릴 만큼 변하기 전에 얻을 수 있는 선행 지표(leading indicator)입니다. 의사결정 혼합 드리프트(Decision-mix drift)는 연기 감지기이고, 결과 드리프트(Outcome drift)는 실제 화재입니다.
이미 보유한 루프에 연결하기
이 중 어느 것도 별도의 두 번째 시스템이 아닙니다. 기준선(baseline)은 테이블입니다. 온라인 샘플러(online sampler)는 실행 끝에 수행되는 동전 던지기입니다. 판사(judge)는 샘플링된 슬라이스에 대해 고정된 모델 호출(pinned model call)입니다. 알람은 정해진 일정에 따라 실행되는 창 기반 비교(windowed comparison)입니다. 이 모든 것은 여러분의 트레이스가 이미 보유하고 있는 gen_ai.agent.* 속성들을 읽어 들입니다.
월요일에 가장 저렴한 버전으로 시작하세요. 현재 에이전트 버전(agent version)에 대해 100개의 케이스로 구성된 골든 세트(golden set)를 고정하세요. 모든 변경 사항에 대해 CI(지속적 통합)에서 이를 실행하세요. 프로덕션 트래젝토리(production trajectories)의 2%를 무작위 샘플링하여 LLM 심판(LLM judge)에 전달하고, 그 점수를 버전 태그(version tag)와 함께 저장하세요. 200회 실행 윈도우(run window)와 5포인트 하락 임계값(threshold)을 설정하여 결과율(outcome rate)에 알람을 하나 설정하세요. 이는 단 하루의 작업이며, 이를 통해 "사용자가 에이전트 성능이 저하된 것을 발견했다"라는 상황을 "새벽 2시에 알람이 울렸고, 페이로드(payload)에는 문제를 일으킨 버전이 이미 명시되어 있다"라는 상황으로 전환해 줍니다.
프로덕션 환경에서 계속해서 좋은 상태를 유지하는 에이전트는 드리프트(drift)가 전혀 발생하지 않는 에이전트가 아닙니다. 고객이 발견하기 전에 소유자가 의도적으로 드리프트를 측정한 에이전트들입니다.
이 내용의 전체 버전을 원하신다면, _The AI Engineer's Library_에 수록된 두 권의 책이 작업을 나누어 다루고 있습니다. _Agents in Production_은 에이전트 루프(agent loop)를 구축하고 실행하는 방법—트래젝토리 속성(trajectory attributes), 의사결정 어휘(decision vocabulary), 그리고 이러한 알람의 기준이 되는 가드레일(guardrails)—을 다룹니다. _Observability for LLM Applications_는 트레이싱(tracing), 평가 하네스(eval harness), 그리고 트레이스 트리(trace tree)를 신뢰할 수 있는 품질 측정값으로 바꿔주는 비용 신호(cost signals)를 심도 있게 다룹니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기