에이전트 도구 호출을 재현 가능한 트레이스로 만드는 법
어제 오후 한 사용자가 에이전트가 “이상한 짓”을 했다고 보고했습니다. 로그를 확인하니 다음과 같았습니다.
INFO agent run started
INFO calling tool: updateOrder
INFO tool returned 200
INFO agent run completed
에이전트가 updateOrder를 호출했다는 사실만 알 수 있습니다. 어떤 인수를 사용했는지, 어떤 주문에 대해 호출했는지, 왜 해당 도구를 선택했는지, 어떤 결과를 받았는지는 알 수 없습니다. 모든 측정치는 실행이 성공했다고 표시하지만, 에이전트가 내린 결정은 하나도 재구성할 수 없습니다.
에이전트 시스템은 시간이 지난 뒤에야 원인을 이해할 수 있는 방식으로 실패합니다. 따라서 로그는 단순한 운영 기록이 아니라 사후 분석의 핵심 결과물이어야 합니다.
이 글에서는 다음을 다룹니다.
- 모든 도구 호출에 기록해야 할 정보
- 모델의 결정을 생성된 HTTP 요청과 연결하는 방법
- 저장 전에 삭제하거나 해시해야 할 정보
- 트레이스를 회귀 테스트로 전환하는 방법
API 가시성에 대한 글이 서비스 계층을 다룬다면, 이 글은 그 위에 있는 에이전트 계층을 다룹니다.
Apidog은 트레이스가 있을 때 특히 유용합니다. 잘못된 호출을 이해하는 가장 빠른 방법은 동일한 엔드포인트를 다시 실행하고 실제 동작을 확인하는 것이기 때문입니다.
세 가지 계층, 하나의 트레이스
에이전트는 세 가지 수준에서 이벤트를 생성하지만, 많은 팀이 중간 계층만 기록합니다.
추론 계층(reasoning layer)은 모델이 결정을 내리는 곳입니다.
- 어떤 컨텍스트를 보았는가
- 어떤 도구가 제공되었는가
- 어떤 도구를 선택했는가
- 어떤 인수를 생성했는가
도구 계층(tool layer)은 실행자입니다.
- 인수 검증
- 정책 적용
- 도구 호출을 HTTP 요청으로 매핑
- 결과 처리
HTTP 계층(HTTP layer)은 네트워크 전송을 기록합니다.
- 메서드
- URL
- 헤더
- 본문
- 상태 코드
- 지연 시간
디버깅은 대부분 이 세 계층을 오갑니다. 예를 들어 “에이전트가 잘못된 고객 ID를 보냈다”는 HTTP 계층에서 발견되지만 원인은 추론 계층에 있을 수 있습니다. 반대로 “API가 빈 본문과 함께 200을 반환했다”는 HTTP 문제이며, 몇 단계 뒤 잘못된 추론으로 나타날 수 있습니다.
세 계층이 공유 식별자로 연결되어 있지 않으면 타임스탬프로 상관관계를 찾아야 합니다. 실행이 겹치는 순간 이 방식은 작동하지 않습니다.
따라서 첫 번째 규칙은 간단합니다.
- 에이전트 실행마다 하나의 트레이스 ID
- 도구 호출마다 하나의 스팬 ID
- 모든 계층의 모든 로그에 두 ID 기록
OpenTelemetry 트레이스는 이 구조를 이미 모델링합니다. 데이터 이식성을 위해 속성 이름을 표준화하는 GenAI 시맨틱 규칙도 확장되고 있습니다.
모든 도구 호출에서 기록할 내용
실제 질문에 답할 수 있는 로그는 대략 다음과 같은 형태입니다.
{
"trace_id": "run_01J8ZK3M2Q",
"span_id": "call_004",
"parent_span_id": "call_003",
"timestamp": "2026-08-26T14:03:11.482Z",
"agent": "billing",
"step": 4,
"tool_name": "refundOrder",
"tool_args": { "orderId": "ord_92", "amount": 1200, "reason": "duplicate" },
"tools_available": ["getOrder", "listOrders", "refundOrder", "voidInvoice"],
"http": {
"method": "POST",
"url": "/v1/orders/ord_92/refund",
"request_body_hash": "sha256:1f4c...",
"status": 200,
"duration_ms": 412,
"retry_count": 1,
"idempotency_key": "9f2b7c14-6d3a-4b18"
},
"outcome": "success",
"tokens": { "prompt": 8420, "completion": 96 },
"policy": { "approval_required": true, "approved_by": "user_31", "dry_run": false }
}
특히 다음 다섯 가지 필드가 중요합니다.
tool_args
가장 자주 누락되지만 반드시 필요한 필드입니다. 실행자가 인수를 정규화하기 전에 모델이 생성한 원본 인수를 기록하십시오. 에이전트가 잘못된 ID를 보냈다면 이 필드에서 원인을 찾을 수 있습니다.
tools_available
모델의 선택을 설명하는 필드입니다. 모델이 이상한 도구를 선택했다면 먼저 어떤 다른 선택지가 있었는지 확인해야 합니다. 몇 바이트만 추가해도 이 질문에 즉시 답할 수 있습니다.
retry_count
“API가 느렸다”와 “API가 두 번 실패한 뒤 성공했다”를 구분합니다. 재시도 횟수가 없으면 세 번의 시도가 하나의 호출처럼 보입니다.
outcome
상태 코드에서 추론하지 말고 명시적인 열거형으로 기록하십시오.
success
failed
timed_out
blocked_by_policy
rejected_by_human
blocked_by_policy와 rejected_by_human은 특히 중요합니다. 차단된 호출은 오류가 아니라 정상적으로 작동한 안전장치일 수 있습니다. 이를 실패와 섞으면 실패율이 왜곡됩니다.
policy
정책 감사 추적입니다. 파괴적인 작업이 승인되었는지 확인해야 할 때 사용합니다. AI 에이전트 가드레일에서 설명하는 강제 정책과 함께 사용하십시오.
행동뿐 아니라 결정도 기록하기
가장 어려운 에이전트 버그는 실행 자체가 아니라 선택에서 발생합니다. 따라서 결정을 재구성할 수 있을 만큼의 정보를 저장해야 합니다.
도구 정의 또는 해시
실행에 사용된 도구 정의 전체 또는 그 해시를 보관하십시오. 선택 정확도가 변했다면 도구 설명이 수정되었는지 먼저 확인해야 합니다. 해시는 정상 실행과 비정상 실행 사이에서 도구 세트가 변경되었는지 빠르게 알려줍니다.
도구 스키마 설계에 대한 글에서 설명하듯, 도구 설명의 작은 변화도 모델의 행동에 큰 영향을 줄 수 있습니다.
모델과 설정
다음 정보를 실행 기록에 포함하십시오.
- 모델 ID
- 온도
- 프롬프트 버전
- 도구 세트 해시
모델 버전이 바뀌면 동작도 달라질 수 있습니다. 이 정보가 없으면 원인을 찾기 위해 자체 코드만 하루 종일 조사하게 됩니다.
모델이 본 입력의 크기
전체 프롬프트를 저장하면 비용이 커지고 민감한 정보가 포함될 수 있습니다. 대신 다음을 기록하는 방법이 실용적입니다.
- 프롬프트 토큰 수
- 프롬프트 해시
- 컨텍스트 크기
프롬프트 크기가 평소의 두 배인 실행은 의도하지 않은 데이터가 추가되었을 가능성이 높습니다.
잘라내기 전의 도구 결과
실행자가 모델에 전달하기 전에 도구 응답을 축소한다면, 컨텍스트 창에서 도구 응답을 제외하는 방식과 원본 결과를 비교할 수 있어야 합니다.
원본 페이로드를 트레이스에 보관하지 않으면 데이터가 처음부터 누락된 것인지, 실행자가 삭제한 것인지 알 수 없습니다.
저장하기 전에 삭제하기
에이전트 트레이스에는 요청과 추론에 사용된 컨텍스트가 함께 들어갑니다. 프롬프트에는 개인정보가 포함되기 쉬우므로 트레이스는 매우 민감한 데이터입니다.
다음 네 가지 규칙으로 관리하십시오.
자격 증명은 절대 저장하지 않기
다음 값을 제거하십시오.
-
Authorization헤더 - API 키
- 쿠키
- 서명된 URL
대신 키 ID처럼 자격 증명을 식별할 수 있는 값만 기록하십시오. 에이전트를 위한 최소 권한 API 키에 대한 글에서 설명하듯, 어떤 에이전트가 작업했는지 식별하는 정보는 여전히 필요합니다.
조회 시점이 아니라 경계에서 삭제하기
읽을 때 필터링하면 비밀이 이미 디스크에 기록되고 복제되며 백업되었을 수 있습니다. 기록이 애플리케이션을 떠나기 전에 로깅 미들웨어에서 삭제하십시오.
저장할 수 없는 본문은 해시하기
요청 본문 해시는 페이로드를 보관하지 않고도 두 요청이 동일했는지 증명할 수 있게 해줍니다. 중복 호출을 조사하는 데 필요한 정보 대부분을 유지할 수 있습니다.
민감도에 따라 보존 기간 설정하기
예를 들어 다음과 같이 계층화할 수 있습니다.
- 1주일: 전체 트레이스
- 1년: 민감한 본문을 삭제한 요약 트레이스
대부분의 디버깅은 며칠 안에 이루어지고, 대부분의 감사 질문은 몇 달 안에 발생합니다.
트레이스를 테스트로 전환하기
좋은 트레이싱의 이점은 디버깅 속도 향상에 그치지 않습니다. 현실적인 테스트 케이스의 원천이 되기도 합니다.
모든 실패한 실행을 하나의 시나리오로 취급하십시오. 실패한 트레이스에서 도구 호출을 가져와 API에 다시 실행하면 재현 가능한 테스트가 됩니다. 수정한 뒤에는 해당 재생 케이스를 회귀 테스트로 유지하십시오.
Apidog에서는 실패한 요청을 저장된 케이스로 재구성하고, 수정된 동작을 어설션으로 검증하며, CI에서 실행할 수 있습니다. 일회성 장애를 영구적인 테스트 커버리지로 바꾸는 방식입니다.
트레이스는 무엇을 모의해야 하는지도 알려줍니다. 에이전트가 가장 자주 호출하는 엔드포인트와 실제로 발생한 오류 상태를 추측하지 않고 확인할 수 있습니다. 생산 환경 대신 모의 환경에서 에이전트 실행에 대한 글을 참고해 해당 엔드포인트를 중심으로 모의를 구성하십시오.
또한 트레이스는 느린 변화를 드러냅니다. 매주 다음 지표를 확인하십시오.
- 도구 선택 분포
- 엔드포인트별 재시도율
- 완료된 작업당 호출 수
- 정책에 의해 차단된 실행 비율
이 중 하나라도 변하면 장애가 되기 전에 조사할 신호가 됩니다. API 계약 테스트와 같은 계약 수준 검사는 원인이 된 상류 변경을 포착하는 데 도움이 됩니다.
트레이스가 답해야 하는 세 가지 조사
“에이전트가 잘못된 고객에게 청구했습니다”
다음 정보를 확인할 수 있어야 합니다.
- 모델이 생성한 인수
- 최종적으로 해석된 URL
- 이전 단계의 도구 결과
- 해당 결과의 모호성
- 모델이 선택한 항목
10번 중 9번은 여러 항목을 반환한 이전 도구 결과에서 모델이 첫 번째 항목을 선택한 경우입니다. 트레이스는 이전 결과, 모호성, 최종 선택을 보여줘야 합니다.
tool_args가 없으면 200 상태 코드와 불만족한 고객만 남습니다.
“화요일부터 작동하지 않습니다”
정상 실행과 비정상 실행을 필드별로 비교하십시오.
- 모델 ID
- 도구 세트 해시
- 프롬프트 버전
- 평균 응답 크기
무언가 변경되었다면 대개 이 네 가지 중 하나가 원인을 가리킵니다. 실행 기록에 이벤트뿐 아니라 구성 정보도 함께 기록해야 하는 이유입니다.
“누군가 이것을 승인했습니까?”
정책 블록은 결정 시점에 작성되어야 합니다. 나중에 기억을 복원하려고 하지 마십시오.
approval_requiredapproved_by- 승인 타임스탬프
이 정보가 있으면 긴 대화가 검색 가능한 감사 기록으로 바뀝니다.
세 조사 모두 “도구가 200을 반환했다”는 로그만으로는 답할 수 없습니다. 하지만 기록 비용이 거의 없는 필드만 추가하면 사후에 복구할 수 없는 질문에도 답할 수 있습니다.
샘플링할 것과 샘플링하지 말아야 할 것
모든 실행을 고정밀로 추적하면 트래픽이 늘어날수록 비용도 증가합니다. 따라서 샘플링이 필요하지만, 에이전트 트래픽은 균일하지 않으므로 신호 중심으로 설계해야 합니다.
다음 실행은 항상 보관하십시오.
- 실패한 모든 실행
- 정책 차단에 도달한 모든 실행
- 쓰기 작업을 포함한 모든 실행
성공한 읽기 전용 실행은 대부분의 트래픽을 차지하고 개별적으로는 덜 흥미롭습니다. 다만 기준선을 계산하려면 충분한 양이 필요하므로 일부를 샘플링해 보관하십시오.
구글의 SRE 책 모니터링 챕터는 볼륨이 아니라 신호를 기준으로 샘플링해야 하는 이유를 명확하게 설명합니다.
페이로드를 삭제하더라도 실행 기록 자체는 보관하십시오. 도구 이름, 결과, 지속 시간만 포함한 스켈레톤 트레이스도 다음 핵심 지표를 지원합니다. 비용이 큰 본문과 프롬프트부터 삭제하는 것이 좋습니다.
테일 샘플링을 사용할 때는 실행이 끝난 뒤 최종 결과를 확인하고 보존 여부를 결정해야 합니다. 3단계까지 정상으로 보였지만 9단계에서 실패한 실행은 전체를 보관해야 합니다. 따라서 실행 중에 버리지 말고 버퍼링하십시오.
트레이스를 어디에 보관할 것인가
앞의 원칙은 저장소를 직접 소유한다는 가정을 전제로 합니다. 에이전트가 자체 API를 호출하는 서비스라면 적절한 방식입니다.
하지만 에이전트가 개발자 머신의 코딩 런타임에서 실행된다면 상황이 다릅니다. 트레이스는 해당 실행을 담당한 터미널에 존재하기 때문입니다.
Sharkly는 다른 접근 방식을 취합니다. 실행 트레이스를 에이전트에게 할당된 작업에 첨부합니다. 실행 기록, 로그, 결과가 목표, 상태, 사람의 검토 댓글과 같은 위치에 모입니다.
실질적인 차이는 검색 방식입니다. “에이전트가 왜 그렇게 했지?”라는 질문에 답하기 위해 머신, 세션, 스크롤백을 찾는 대신 작업을 열어 확인할 수 있습니다.
이 방식이 앞서 설명한 트레이싱이나 런타임을 대체하는 것은 아닙니다. Claude Code와 Codex는 여전히 작업을 수행합니다. 달라지는 점은 에이전트가 배포한 서비스가 아닐 때 실행 기록을 어디에 저장하느냐입니다.
주시해야 할 네 가지 숫자
트레이스는 누군가 확인할 때 가장 유용합니다. 다음 지표를 대시보드에 추가하십시오.
완료된 작업당 호출 수
가장 명확한 효율성 지표입니다. 증가한다면 에이전트가 더 많이 탐색하고 있다는 뜻이며, 보통 도구 설명이 나빠졌거나 엔드포인트가 실패하기 시작한 경우입니다.엔드포인트별 재시도율
신뢰성이 낮은 종속성을 순위별로 보여줍니다. 에이전트 오류 복구에 대한 글에서 상위 항목에 대응하는 방법을 확인할 수 있습니다.정책 차단 비율
낮고 안정적이어야 합니다. 급증하면 에이전트가 해서는 안 되는 작업을 시도하거나, 정책이 지나치게 엄격해 병목이 발생하고 있다는 의미입니다.첫 도구 호출까지의 시간
시작이 느리다면 비대한 프롬프트가 원인일 가능성이 높습니다. 프롬프트 크기는 아무도 의도하지 않은 채 증가하기 쉽습니다.
체크리스트
- 실행마다 하나의 트레이스 ID, 도구 호출마다 하나의 스팬 ID를 발급하고 세 계층 모두에 기록합니다.
- 인수를 정규화하기 전에 모델이 생성한 원본 인수를 기록합니다.
- 모든 호출에서 사용 가능한 도구 목록을 기록합니다.
- 정책 차단을 포함해 명시적인 열거형으로 결과를 기록합니다.
- 호출 횟수와 재시도 횟수를 별도로 기록합니다.
- 모델, 온도, 프롬프트 버전, 도구 세트 해시를 실행 기록에 포함합니다.
- 모델에 전달하기 전 잘라낸 결과가 아니라 원본 도구 결과를 저장합니다.
- 미들웨어에서 자격 증명을 제거하고, 저장할 수 없는 본문은 해시합니다.
- 민감도에 따른 계층형 보존 정책을 적용합니다.
- 실패한 트레이스를 재생 가능한 테스트 케이스로 전환합니다.
목표는 간단합니다. 누군가 에이전트가 왜 그렇게 행동했는지 물었을 때, 추측이 아니라 기록으로 답할 수 있어야 합니다.
Apidog을 다운로드해 트레이스의 호출을 재생하고, 재현된 동작을 테스트 케이스로 보관하십시오.
자주 묻는 질문
OpenTelemetry를 사용해야 하나요, 아니면 에이전트 전용 관찰성 도구를 사용해야 하나요?
상관관계를 이미 처리하고 인프라가 지원할 가능성이 높으므로 전송 계층과 트레이스 모델에는 OpenTelemetry를 사용하십시오. 에이전트 전용 도구는 그 위에 유용한 뷰를 추가하면 됩니다. 기본 데이터는 계속 이식 가능해야 합니다.
전체 트레이싱을 저장하면 비용이 얼마나 드나요?
계층화하면 예상보다 적게 듭니다. 며칠 동안은 전체 페이로드를 보관하고, 이후에는 본문을 제거한 구조화된 기록만 유지하십시오. 프롬프트 덤프는 비용이 가장 크므로 기본적으로 저장하기보다 해시와 크기만 기록하는 편이 좋습니다.
모델의 추론 텍스트를 기록해야 하나요?
대부분의 경우 필요하지 않습니다. 선택한 도구, 생성한 인수, 사용 가능한 옵션만으로도 대부분의 결정을 설명할 수 있습니다. 제공자가 추론 콘텐츠를 노출한다면 실패한 실행에 한해 저장하고 민감한 정보로 취급하십시오.
여러 에이전트의 실행은 어떻게 트레이싱하나요?
전체 작업에 하나의 트레이스 ID를 유지하고, 각 에이전트에 고유한 스팬을 할당하십시오. 에이전트 간 핸드오프는 이벤트로 기록합니다. 다중 에이전트 핸드오프에 대한 글에서 핸드오프 기록에 포함해야 할 정보를 확인할 수 있습니다.
에이전트가 고객의 컴퓨터에서 실행된다면 어떻게 해야 하나요?
로컬에 기록하고 적극적으로 삭제하십시오. 사용자가 동의하지 않는 한 집계된 메트릭만 전송해야 합니다. 도구 이름, 결과, 지속 시간만으로도 페이로드가 장치를 벗어나지 않는 플릿 수준 모니터링이 가능합니다.
요청 본문 해시가 실제로 유용한가요?
유용합니다. 두 호출이 동일했음을 증명하므로 페이로드를 보관하지 않고도 대부분의 중복 쓰기 조사를 수행할 수 있습니다. 중복을 방지해야 하는 멱등성 키와 함께 사용하십시오.

Top comments (0)