
AI 에이전트 관측 가능성(Observability)은 대화 ID(Conversation IDs)를 기반으로 작동합니다
요약
AI 에이전트의 관측 가능성은 단순한 LLM 호출 로깅을 넘어, 에이전트가 유발하는 도구 호출, API, 데이터베이스 쿼리 등 전체 실행 경로를 추적해야 합니다. 대화 ID(Conversation ID)를 기반으로 에이전트의 부작용과 런타임 작업을 통합적으로 모니터링하는 것이 중요합니다.
핵심 포인트
- LLM 호출 중심의 로깅은 에이전트의 실제 소프트웨어 동작을 추적하지 못함
- 에이전트의 관측 단위는 모델 호출뿐만 아니라 도구, API, DB 쿼리를 포함해야 함
- 대화 ID를 활용해 에이전트가 유발한 전체 트레이스를 연결하는 것이 핵심
- 실행 경로가 런타임에 결정되는 에이전트 특성상 통합적인 트레이싱이 필수적임
에이전트 관측 가능성(Agent observability)은 정작 참견을 해야 할 바로 그 순간에 이상할 정도로 예의 바르게 행동합니다. 모델 호출을 기록하고, 프롬프트(Prompt)를 저장하고, 토큰(Token)을 계산하지만, 에이전트가 소프트웨어를 건드리기 시작하는 바로 그 시점에 관심을 잃어버립니다.
이로 인해 트레이스(Trace)는 깨끗해 보이지만, 장애(Incident)는 해결 불가능한 것처럼 느껴지게 됩니다.
실패는 LLM 스팬(LLM span) 내부에서 발생하는 경우가 드뭅니다. Honeycomb의 에이전트 타임라인 계측 가이드에 따르면, GenAI 스팬은 에이전트가 유발한 모든 작업, 즉 모델 호출(Model calls), 도구 호출(Tool calls), 핸드오프(Handoffs), 다운스트림 서비스(Downstream services), 데이터베이스 쿼리(Database queries), 그리고 백그라운드 작업(Background jobs)이 될 수 있습니다. 이것이 올바른 관측 단위입니다. 에이전트는 선택을 내리고 부작용(Side effects)을 일으키는 런타임 소프트웨어(Runtime software)입니다.
대화 ID(Conversation ID)는 바로 그 현실이 드러나기 시작하는 지점입니다.
작업이 시작되는 곳에서 트레이스가 끊긴다
LLM 호출 로깅(LLM-call logging)은 팀에게 결과물을 제공하기 때문에 유용하게 느껴집니다. 프롬프트는 이것이었고, 응답은 저것이었으며, 모델이 도구 호출을 반환했다는 식입니다. 좋습니다.
하지만 그 후 도구가 서비스를 호출합니다. 서비스가 행(Row)을 기록합니다. 큐 워커(Queue worker)가 작업을 가져갑니다. 다운스트림 API(Downstream API)가 재시도합니다. 에이전트가 이미 사용자에게 답변을 보낸 후 데이터베이스 쿼리(Database query)가 타임아웃됩니다. 모델 트레이스(Model trace)는 작업을 놓쳤기 때문에 계속 초록색(정상) 상태로 남아 있습니다.
기존의 관측 가능성 사고방식은 에이전트와 함께할 때 훨씬 더 중요해집니다. 로그(Logs)는 개발자가 미리 추측한 질문에 답을 줍니다. 트레이스(Traces)는 이상한 일이 이미 발생한 후에 팀이 시스템의 형태를 추적할 수 있게 해줍니다. 에이전트 시스템은 실행 경로가 부분적으로 런타임에 선택되기 때문에, 동일한 문제의 더 심각한 버전을 추가합니다.
토큰 수(Token count)는 환불 도구가 왜 잘못된 계좌에 기록했는지 설명해주지 못합니다. 프롬프트 기록(Prompt transcript)은 서브 에이전트(Sub-agent)가 왜 두 가지 경로를 통해 동일한 API 호출을 재시도했는지 설명해주지 못합니다. 모델 지연 시간(Model latency) 차트는 왜 백그라운드 조정 작업(Background reconciliation job)이 사용자에게 보이는 실패를 일으켰는지 설명해주지 못합니다.
에이전트가 작업을 유발했습니다. 트레이스는 그 작업을 따라가야 합니다.
유용한 트레이스(trace)는 모델이 수행한 호출이 아니라, 에이전트가 유발한 작업입니다.
대화 ID(Conversation ID)는 살아있는 단위입니다
Honeycomb의 문서는 최소 기능 제품(minimum viable product)의 형태를 매우 명확하게 제시합니다. 에이전트 스팬(span)에는 gen_ai.conversation.id, gen_ai.agent.name, 그리고 gen_ai.operation.name이 필요합니다. 그래야 타임라인이 스팬을 하나의 세션으로 그룹화하고, 작업을 특정 에이전트에 귀속시키며, 에이전트 타임라인(Agent Timeline) 내에서 chat, execute_tool, invoke_agent, invoke_workflow와 같은 작업(operation)을 구분할 수 있기 때문입니다.
이 문구는 마치 메타데이터처럼 들리지만, 팀이 트레이스(trace)를 갖게 될지 아니면 영수증(receipt)을 갖게 될지를 결정합니다.
트레이스 ID(trace ID)는 하나의 분산 실행을 설명합니다. 대화 ID(conversation ID)는 트레이스, 서비스, 그리고 턴(turn)을 가로질러 사용자에게 보이는 작업 단위를 설명합니다. 지원 에이전트가 요청을 분류하고, 계정 상태를 가져오고, 결제 에이전트에게 넘기고, 최종 응답을 보내기 전에 큐 워커(queue worker)를 기다리는 과정을 가정해 봅시다. 이러한 단계들은 서로 다른 트레이스에 기록될 수 있습니다. 고객은 하나의 대화를 경험했지만, 시스템은 스팬(span)의 더미를 생성한 것입니다.
gen_ai.conversation.id가 없다면, 그 더미는 그저 더미로 남을 뿐입니다.
여기서 한 가지 하기 쉬운 실수가 있습니다: 말단(leaf)에서 식별자를 가짜로 만들지 마십시오. OpenTelemetry GenAI 에이전트 스팬 컨벤션(conventions)에 따르면, gen_ai.conversation.id는 실제 식별자를 즉시 사용할 수 있을 때만 채워져야 하며, 대화 식별자가 존재하지 않을 때 새로운 UUID, 트레이스 ID, 또는 요청 콘텐츠의 해시(hash)로 대체해서는 안 됩니다. 이 지침은 매우 중요합니다. 만약 모든 서비스가 각자 고유한 대화 ID를 만들어낸다면, 해당 속성은 이름만 더 나은 '컨페티(confetti, 무의미한 데이터의 파편)'가 되어버릴 것입니다.
제품이 상호작용을 이해하는 경계 지점에서 대화(conversation) 또는 세션(session) ID를 발행(mint)하세요. 이를 내부로 전달하십시오. 에이전트 스팬(agent spans), 도구 스팬(tool spans), HTTP 클라이언트 스팬(HTTP client spans), 큐 작업(queue jobs), DB 스팬(DB spans), 그리고 평가 이벤트(eval events)에 이를 부착하세요. 만약 하위 서비스(downstream service)가 이를 전달할 수 없다면, 해당 서비스는 트레이스(trace)가 끊기는 지점이 됩니다.
우리는 이전에 도구 경계를 넘나드는 트레이스 컨텍스트(trace context crossing the tool boundary)에 대해 작성한 바 있습니다. 대화 ID(Conversation IDs)는 이러한 기술적 경계 전반에 걸쳐 사용자 중심의 스레드(thread)를 추가합니다. 트레이스 컨텍스트(Trace context)는 부모-자식 관계를 온전하게 유지하며, 대화 컨텍스트(Conversation context)는 팀이 "이 에이전트 대화가 유발한 모든 것을 보여달라"고 요청할 수 있게 해줍니다. 이 두 가지 모두 도구 호출(tool call) 과정에서 살아남아야 합니다.
스팬 계보(Span ancestry)는 단순한 서류 작업이 아닙니다
LangSmith의 OpenTelemetry 문서에는 까다로운 실패 모드(failure mode)가 포함되어 있습니다. 부모 스팬이 LangSmith에 도달하지 못할 경우, 자식 스팬이 200 상태 코드로 수락되어 버퍼링된 후, OTEL 엔드포인트가 스팬을 비동기적으로 처리하기 때문에 부모가 도착하지 않으면 나중에 삭제될 수 있습니다. 이는 CI(지속적 통합) 환경에서는 멀쩡해 보이지만, 실제 장애 검토(incident review) 시에는 상황을 망쳐버리는 전형적인 관측 가능성(observability) 버그입니다.
시스템은 승인했습니다. 하지만 증거는 사라졌습니다.
이것이 부분적인 에이전트 트레이싱(agent tracing)이 위험한 이유입니다. 한 팀은 오케스트레이터(orchestrator)에 대한 아름다운 트레이스를 가질 수 있지만, 도구 실행(tool execution) 데이터는 놓칠 수 있습니다. 다른 팀은 도구 서비스를 내보내면서(export) 부모 에이전트 스팬을 잃어버릴 수도 있습니다. 샘플링(Sampling)은 비용이 적게 드는 부분은 유지하면서 인과 관계의 뿌리(causal root)를 버릴 수도 있습니다. 벤더 콘솔(vendor console)은 데이터 수집이 정상(green)이라고 표시하지만, 정작 유용한 실행 기록은 나타나지 않을 수도 있습니다.
에이전트 관측 가능성(Agent observability)은 반드시 내보내기 계약(export contract)으로 취급되어야 합니다.
에이전트 경로(agent path)에 참여하는 모든 서비스는 계보(ancestry), 대화 ID(conversation ID), 샘플링 동작(sampling behavior), 비식별화 규칙(redaction rules) 및 소유권(ownership)에 대해 합의해야 합니다. 여기에는 지루한 서비스들도 포함됩니다. 특히 아주 지루한 서비스들 말입니다. 결제 API(Billing APIs), CRM 업데이트, 검색 인덱스(search indexes), 권한 확인(authorization checks), 큐 워커(queue workers) 및 데이터베이스 쓰기(database writes)는 에이전트가 실제 소프트웨어가 되는 지점입니다. 모델 스팬(model span)은 단지 기록(transcript)을 가진 플래너(planner)일 뿐입니다.
이것이 바로 인프라로서의 에이전트 모니터링(agent monitoring as infrastructure)이 올바른 운영 모델인 이유입니다. 앱 팀이 에이전트 래퍼(agent wrapper)에 트레이싱(tracing)을 살짝 뿌려놓고 끝냈다고 말할 수는 없습니다. 플랫폼은 전파(propagation)를 쉽게 만들고, 수집기(collectors)를 안전하게 하며, 샘플링을 읽기 쉽게 만들고, 누락된 스팬(missing-span)으로 인한 실패를 가시화해야 합니다.
에이전트 이름이 책임 소재를 결정합니다
멀티 에이전트 시스템(Multi-agent systems)은 명명(naming) 문제를 더욱 까다롭게 만듭니다.
Honeycombs의 문서에서는 각 에이전트가 고유한 gen_ai.agent.name을 가져야 하며, 서브 에이전트(sub-agents)가 부모 에이전트의 이름을 상속받아서는 안 된다고 경고합니다. 중복되거나 누락된 이름은 그룹화된 에이전트 타임라인 내부에서 조사를 불가능하게 만들기 때문입니다. 이는 첫 번째 라이브 핸드오프(handoff)가 실패하기 전까지는 까다로운 요구처럼 들릴 수 있습니다.
결제 에이전트가 정책 에이전트를 호출했습니다. 정책 에이전트는 CRM 도구를 호출했습니다. CRM 도구는 다음 사용자 메시지를 변경하는 필드를 작성했습니다. 만약 모든 스팬이 support_agent라고 되어 있다면, 팀은 기록(transcript)은 가지고 있지만 소유권 기록은 갖지 못하게 됩니다. 만약 모든 서브 에이전트가 안정적인 이름을 갖는다면, invoke_agent는 증거가 첨부된 런타임 전송(runtime transfer)이 됩니다.
이는 런타임 상태로서의 에이전트 핸드오프(agent handoffs as runtime state)에서 마주쳤던 것과 동일한 경계입니다. 핸드오프는 단순한 느낌(vibes)이 아닙니다. 그것은 소유권의 이전입니다. 수신 에이전트는 상태(state)가 필요합니다. 송신 에이전트는 영수증(receipt)이 필요합니다. 트레이스(trace)는 누가 행동했는지, 누가 위임했는지, 누가 결과를 관찰했는지, 그리고 어떤 이름이 실패에 대한 책임을 지는지 보여주어야 합니다.
에이전트 이름(Agent names) 또한 릴리스 아티팩트(release artifacts)입니다. 이름이 변경된 에이전트는 대시보드를 망가뜨릴 수 있습니다. 재사용된 이름은 오래된 레이블 뒤에 새로운 구현을 숨길 수 있습니다. 임시 실험용 이름은 라이브 트래픽으로 유출되어 일주일 치의 트레이스(traces)를 고고학 유물로 만들어 버릴 수 있습니다. 에이전트의 이름은 서비스(service)처럼 명명하십시오. 동작(behavior)의 버전 관리는 다른 곳에서 수행하십시오.
프롬프트(Prompts)는 거버넌스 경계(governance boundary)를 가진 데이터입니다
유용한 에이전트 트레이스(agent trace)는 보안 팀이 저장하기 싫어하는 것들, 즉 프롬프트(prompts), 응답(responses), 도구 인자(tool arguments), 도구 출력(tool outputs), 검색된 컨텍스트(retrieved context), 그리고 평가 노트(evaluation notes)를 포함하고자 합니다.
이러한 긴장 관계는 사라지지 않을 것입니다. 이는 설계되어야만 합니다.
Honeycomb는 전체 프롬프트, 채팅 기록(chat history), 그리고 완성문(completions)이 크기가 크거나 민감한 데이터를 포함할 수 있기 때문에 스팬 이벤트(span events)로 저장할 것을 권장하는 반면, 문서에서는 OpenTelemetry Collector가 이벤트에 프롬프트와 완성문 콘텐츠가 포함된 경우 수집(ingestion) 전에 이를 필터링하거나 비식별화(redact)할 수 있다고 명시합니다. LangSmith 또한 동일한 아키텍처적 움직임을 문서화하고 있습니다: 애플리케이션 트레이스(application traces)를 OpenTelemetry collector를 통해 라우팅하고, 민감한 스팬 속성(span attributes)을 비식별화하는 변환 규칙(transform rules)을 적용한 뒤, 게이트웨이 방식의 비식별화 경로(gateway-style redaction path)를 통해 정화된(sanitized) 트레이스를 LangSmith로 전달하는 것입니다.
해당 collector는 운영 증거(operational evidence)와 데이터 확산(data sprawl) 사이의 경계선을 긋습니다.
에이전트 관측 가능성(Agent observability)에는 여전히 데이터 거버넌스 경계가 존재합니다.
안전한 패턴은 지루할 정도로 단순합니다: 스팬(spans)에는 카디널리티가 낮은(low-cardinality) 라우팅 속성을 유지하고, 민감한 프롬프트 및 완성문 페이로드(payloads)는 이벤트(events)나 제어된 속성(controlled attributes)에 넣으며, collector에서 비식별화(redact)를 수행하고, 비식별화 정책을 테스트 가능하게 만드는 것입니다. 팀은 데이터가 벤더(vendor)나 공유 백엔드(shared backend)에 도달하기 전에 어떤 필드가 유지되고, 어떤 필드가 삭제되며, 어떤 필드가 변환되는지 알고 있어야 합니다.
여기에 숨겨진 또 다른 경계가 있습니다. LangSmith의 분산 트레이싱(distributed tracing) 가이드에서는 langsmith-trace 및 baggage 헤더를 공개 호출자(public callers)가 아닌 신뢰할 수 있는 내부 서비스로부터만 수락해야 한다고 경고합니다. 왜냐하면 이 헤더들은 신뢰할 수 있는 트레이싱 컨텍스트(tracing context)로 소비되며, 게이트웨이가 이를 전달할 경우 실행(runs)이 기록되는 방식에 영향을 미칠 수 있기 때문입니다. 좋습니다. 트레이스 컨텍스트는 단순히 메타데이터라고 해서 무해한 것이 아닙니다.
공개 요청(public request)은 거짓을 담을 수 있습니다. 내부 전파(internal propagation)는 증거를 담을 수 있습니다. 게이트웨이는 이 차이를 알아야 합니다.
트레이스는 제어(control)의 토대가 되어야 합니다
에이전트 관측 가능성(agent observability)의 목적은 단순히 더 예쁜 사고 스크린샷을 얻는 것이 아닙니다.
Candidly의 LangSmith 관련 글이 유용한 이유는 트레이스가 제어 표면(control surface)으로 변모하는 과정을 보여주기 때문입니다. 그들의 Cait 금융 계획 에이전트는 사후 대화 평가(post-hoc conversation evaluation) 방식에서 턴 단위 상태 추론(turn-level state inference) 방식으로 전환되었습니다. 레이블링 파이프라인은 사람이 레이블을 붙인 LangSmith 데이터셋과 92.3%의 일치율을 달성했으며, 트레이스 유도 기능(trace-derived features)은 Candidly 사례 연구에서 해결된 대화와 중단된 대화를 0.90 AUC로 예측했습니다.
이것이 바로 복제할 가치가 있는 루프(loop)입니다.
실시간 트레이스는 회귀 테스트 케이스(regression cases), 평가 데이터셋(eval datasets), 정책 변경(policy changes), 라우팅 수정(routing fixes), 그리고 릴리스 게이트(release gates)가 되어야 합니다. 대화 ID(Conversation ID)는 팀이 최종 답변뿐만 아니라 전체 인과 경로(causal path)를 수집할 수 있게 해주기 때문에 이를 가능하게 합니다. 트레이스는 어떤 도구(tool)가 실행되었는지, 어떤 상태(state)가 변경되었는지, 어떤 하위 에이전트(sub-agent)가 핸드오프(handoff)를 담당했는지, 어떤 프롬프트 버전이 실행되었는지, 그리고 어떤 다운스트림 스팬(downstream span)이 실패했는지를 보여줍니다. 평가는 실제 증거를 바탕으로 동작을 점수화할 수 있습니다.
이것이 바로 AI 에이전트 평가가 하네스(harness)를 조종해야 하는 이유이기도 합니다. 트레이스는 텔레메트리(telemetry) 수집 단계에서 멈춰서는 안 됩니다. 실시간 대화에서 잘못된 핸드오프가 노출된다면, 그 해결책은 Slack에 스크린샷을 올리는 것이어서는 안 됩니다. 그것은 테스트 케이스이자 제어 수단이 되어야 합니다.
최고의 트레이스(Traces)는 소프트웨어가 됩니다.
지루한 경로부터 계측(Instrument)하세요
첫 번째 단계에서 거창한 관측 가능성(Observability) 플랫폼이 필요하지는 않습니다. 필요한 것은 깔끔한 전파 계약(Propagation contract)입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기