당신의 AI 에이전트 로그는 당신을 속이고 있습니다: 실제로 작동하는 4개 필드 스키마
요약
AI 에이전트의 로깅 시스템이 모델의 허위 응답을 제대로 포착하지 못하는 문제를 지적하며, 실제 실패를 감지할 수 있는 4가지 필수 필드 스키마를 제안합니다.
핵심 포인트
- 기존 로깅은 모델이 보고하는 'status: ok'에 의존하여 실제 오류를 놓칠 수 있음
- 도구 호출 결과의 절단(truncation)은 중요한 에러 메시지를 숨기는 원인이 됨
- 에이전트의 의도(intent)를 파악할 수 없는 로그는 추론 과정 재현을 불가능하게 함
- 효과적인 로깅을 위해 expected_outcome, observed_outcome, verification, uncertainty_signal 필드가 필요함
6개월 전, 저는 프로덕션 에이전트 파이프라인(production agent pipeline)에 로깅 스키마(logging schema)를 배포했습니다. 모든 프롬프트(prompt), 모든 도구 호출(tool call), 모든 응답(response), 그리고 모든 지연 시간(latency)을 기록했습니다. 대시보드는 훌륭해 보였고, 경고(alert)는 한 번도 울리지 않았습니다. 그러던 어느 화요일 아침, 한 에이전트가 14단계의 작업을 수행하고는 확신에 찬 "완료(Done)"라는 메시지를 남겼습니다. 하지만 그 과정에서 세 단계를 조작(fabricate)했습니다. 제 로그는 모든 호출을 캡처하고 있었습니다. 다만 실패를 숨기고 있었을 뿐입니다.
문제는 데이터의 양이 아니었습니다. 잘못된 형태의 잘못된 필드를 로깅하고 있었다는 점이 문제였습니다. 무엇이 실제로 실패를 드러내는지에 대해 12주 동안 반복적으로 개선한 끝에, 마침내 조작이 발생하기 전에 이를 잡아낼 수 있는 스키마를 완성했습니다.
기본 로깅의 함정
대부분의 에이전트 로깅은 다음과 같은 형태를 띱니다:
# 대부분의 팀이 첫날 배포하는 방식
log.info("agent_step", extra={
"step_id": uuid4(),
...
이것은 포괄적으로 보이지만, 실패를 찾는 데는 무용지물입니다.
세 가지 문제가 발생합니다:
1. status: ok는 모델이 말한 것이지, 실제로 일어난 일이 아닙니다. HTTP 호출이 401 에러를 반환했을 수도 있습니다. 도구(tool)가 예외(exception)를 발생시켰을 수도 있습니다. 에이전트는 그것을 보지 못합니다. 에이전트는 반환 값(return value)을 볼 뿐이며, 그 값이 딕셔너리(dict)로 파싱된다면 에이전트는 ok라고 로깅합니다.
2. 도구 결과가 잘립니다(truncated). 500자로 제한하는 것은 당신이 꼭 확인해야 할 오류들을 정확히 숨겨버립니다. 스택 트레이스(Stack traces)와 유효성 검사 메시지(validation messages)는 JSON 응답의 첫 500자 이후에 나타나는 경향이 있습니다.
3. _의도(intent)_에 대한 개념이 없습니다. 에이전트가 search를 호출했다는 것은 알지만, 무엇을 위해서 호출했는지는 알 수 없습니다. 에이전트가 12단계째에서 잘못된 길로 들어섰을 때, 입력(input)과 출력(output)만 가지고 있다면 그 추론 과정을 재현(replay)할 수 없습니다.
더 깊은 문제는 이 로그들이 실패를 포착하기 위해서가 아니라 비용 분석을 위해 구조화되어 있다는 점입니다. 이들은 밤 11시에 조작된 내용을 디버깅하려는 사람이 아니라, 토큰 소비(token spend)를 생각하는 사람들에 의해 설계되었습니다.
실제로 작동하는 4개 필드 스키마
세 번의 재작성 끝에, 저는 단계(step)당 4개의 필수 필드를 가진 스키마에 도달했습니다. 이제 모든 단계 로그에는 다음이 포함됩니다:
expected_outcome— 에이전트가 달성하려고 했던 것observed_outcome— 실제로 일어난 일 (HTTP 상태 코드, 예외 클래스 (exception class), 파싱된 값)verification— 에이전트가 관찰된 결과(observed outcome)가 기대된 결과(expected outcome)와 일치하는지 확인했는지 여부uncertainty_signal— 에이전트가 해당 단계에 대해 확신하지 못했을 경우를 나타내는 명시적인 플래그 (flag)
Python에서는 다음과 같이 구현됩니다:
import logging
from dataclasses import dataclass, asdict
from typing import Any
...
이 네 가지 필드가 효과적인 이유는 제가 반복해서 마주쳤던 세 가지 실패 모드 (failure modes)에 대응하기 때문입니다:
- 침묵하는 실패 (Silent failure) (HTTP 200이지만 본문이 비어 있는 경우): 성공 플래그를 요구하는
observed_outcome에 의해 포착됩니다. - 조작 (Fabrication) (에이전트가 부분적인 결과로부터 성공을 지어내는 경우): 명시적인 재질의 (re-query)를 요구하는
verification에 의해 포착됩니다. - 신뢰도 드리프트 (Confidence drift) (다단계 작업이 진행됨에 따라 모델의 확신이 줄어드는 경우):
uncertainty_signal에 의해 포착됩니다.
검증 (Verification)은 가장 어려운 부분입니다
verification은 제가 과소평가했던 필드입니다. 처음에는 중복처럼 보였습니다. 에이전트가 도구 (tool)를 호출하고, 도구가 무언가를 반환하면 끝이라고 생각했기 때문입니다. 하지만 "도구를 호출했다"와 "도구가 제대로 작동했다" 사이의 차이가 바로 제가 포착한 모든 조작 (fabrication)이 발생하는 지점입니다.
구체적으로, 상태를 변경하는 (changes state) 모든 단계는 검증을 위한 재읽기 (re-read)가 필요합니다. GitHub 이슈를 닫거나, 이메일을 보내거나, 댓글을 게시하거나, 파일을 쓰는 것 — 이 모든 작업은 첫 번째 호출이 성공했는지 확인하기 위해 두 번째 API 호출이 필요합니다:
def log_github_close_issue(repo: str, issue_number: int) -> None:
# 1. 실행 (Execute)
close_response = github.post(
...
만약 verification.passed가 False라면, 에이전트 자체가 status: ok라고 보고하더라도 로그 심각도 (log severity)는 ERROR가 됩니다. 이것이 제가 조작 (fabrication)을 잡아내는 방법입니다. 에이전트는 자신이 성공했다고 믿지만, 검증 (verification) 필드가 진실을 목격했기 때문에 로그가 경고를 발생시키는 것입니다.
불확실성 신호 (The Uncertainty Signal)
uncertainty_signal은 제가 가장 나중에 추가한 필드이며, 가장 미묘한 버그를 포착하는 역할을 합니다. 이 필드는 문자열 형태이며, 값은 "none", "low", 또는 "high"입니다. 모델은 각 단계가 끝날 때마다 이를 명시적으로 설정합니다.
대부분의 팀들은 로짓(logit) 값이나 토큰 확률에서 불확실성을 도출하려고 합니다. 하지만 이는 작동하지 않습니다. 이러한 값들은 다음 토큰에 대한 신뢰도를 추적할 뿐, 작업 수준의 신뢰도는 아닙니다. 에이전트는 다음에 어떤 토큰이 올지에 대해서는 매우 확신하면서도, 도구가 유용한 데이터를 반환했는지 여부에 대해서는 완전히 틀릴 수 있습니다.
대신, 저는 에이전트에게 스스로 보고하도록 프롬프트를 작성했습니다:
SYSTEM_PROMPT = """
각 도구 호출 후에는 추론 과정에 한 줄을 추가하세요:
UNCERTAINTY: <none|low|high>
...
"""
이 라인을 파싱하면 uncertainty_signal을 얻게 됩니다. 이를 검증(verification)과 결합하면 세 가지 경고 클래스가 생성됩니다:
- ERROR: 검증 실패 → 해당 단계를 차단하고 사람에게 에스컬레이션(escalate)합니다.
- WARNING: 검증은 통과했지만 불확실성이 높음 → 검토를 위해 플래그 지정 후 계속 진행합니다.
- INFO: 검증도 통과했고 불확실성도 낮음 → 조용히 넘어갑니다 (silent).
제 파이프라인에서는 약 4%의 단계가 WARNING을 발생시키고, 약 0.6%의 단계가 ERROR를 발생시킵니다. 이 0.6%에서 모든 허위 정보(fabrication)가 발생해 왔습니다. WARNING은 제가 도구 정의에서 버그를 발견하는 곳입니다. 즉, 도구가 기술적으로 유효한 데이터를 반환했지만 에이전트가 이를 오해하는 경우들입니다.
제가 배운 것
3개월이 지난 지금, 이 네 필드 스키마는 이전 로그로는 할 수 없었던 세 가지 일을 해냈습니다:
허위 정보 포착이 자동화되었습니다. 저는 더 이상 다음 날 아침에
토큰 비용 (Token cost)을 디버깅할 수 있게 되었습니다. expected_outcome (예상 결과)이 구조화되어 있기 때문에, 의도(intent)별로 단계를 그룹화할 수 있습니다. 동일한 expected_outcome을 공유하지만 토큰 사용량이 크게 차이 나는 단계들은 대개 버그이거나, 에이전트가 데이터를 여러 번 다시 파싱해야 하는 도구(tool)를 사용하고 있음을 나타냅니다. 비용 관리 팀은 이 기능을 매우 좋아합니다. 이전에는 총합(totals)만 볼 수 있었기 때문입니다.
이 스키마가 완벽한 것은 아닙니다. 에이전트가 검증 자체를 조작하는 경우 — 예를 들어, 검증 API가 호출되지 않았음에도 "네, 확인했습니다. 이슈가 종료되었습니다"라고 말하는 경우 — 는 여전히 잡아내지 못합니다. 이를 해결하기 위해 저는 에이전트 루프 외부의 감시 프로세스(watchdog process)에서 검증 로직을 실행하는 별도의 레이어를 추가했습니다. 그 내용은 다른 글에서 다루겠습니다.
만약 에이전트 로깅(agent logging)을 처음부터 시작한다면, prompt_tokens나 지연 시간(latency)부터 시작하지 마세요. 대신 다음 질문부터 시작하십시오: 이 단계가 의도한 바는 무엇인가, 실제로 무슨 일이 일어났는가, 두 가지가 일치하는지 검증했는가, 그리고 모델이 얼마나 불확실해 하는가? 이 네 가지 질문은 제가 지난 6개월 동안 목격한 거의 모든 운영 환경(production)의 실패를 잡아냅니다. 나머지 필드들은 있으면 좋은 것들입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기