AI 에이전트의 응답이 20초 걸렸다고 해서 모델 API가 20초 동안 일한 것은 아니다. 모델 호출은 2초 만에 끝났지만 검색 결과를 기다리는 데 4초, 외부 도구의 타임아웃과 재시도에 10초, 사람의 승인을 기다리는 데 나머지 시간이 쓰였을 수 있다.
반대 상황도 있다. 에이전트가 2초 안에 응답했지만 오래된 문서를 검색했거나, 사용할 수 있는 도구 중 잘못된 도구를 선택했거나, 도구가 실패했는데도 성공한 것처럼 최종 답을 만들 수 있다. 이 경우 시스템 지연과 HTTP 상태는 정상으로 보인다.
따라서 AI 에이전트의 관측 단위는 개별 모델 호출이 아니라 사용자 요청이 들어온 시점부터 최종 결과가 전달되거나 작업이 중단될 때까지의 한 Run이어야 한다. 그 Run 안에서 Retrieval, Model Call, Tool Selection, Tool Execution, Retry, Guardrail, 외부 의존성, 비용과 사용자 결과를 연결해야 어디에서 문제가 시작됐는지 판단할 수 있다.
원문 Prompt와 Completion을 모두 저장하는 것이 출발점도 아니다. 먼저 구조와 결과를 기록하고 원문은 명확한 필요와 보호 정책이 있을 때만 제한적으로 수집하는 편이 안전하다.
현재 OpenTelemetry GenAI 규약은 Stable이 아니다
2026년 8월 27일 기준 OpenTelemetry의 코어 Semantic Conventions 문서는 1.44.0이다. 하지만 GenAI 관련 규약은 코어 문서에서 별도의 semantic-conventions-genai 저장소로 이동했다.
별도 저장소에서 GenAI 전체 규약, 모델·클라이언트 Span, 에이전트·프레임워크 Span, Metrics, Events는 모두 Development 상태다. Events는 언어별 구현이 아직 제공되지 않을 수도 있다.
OpenTelemetry를 사용하지 말아야 한다는 뜻은 아니다. 현재 규약은 invoke_agent, plan, retrieval, execute_tool처럼 서로 다른 런타임의 작업 이름을 맞추는 유용한 출발점이다. 다만 다음 두 가지가 필요하다.
첫째, Telemetry Schema Version과 Instrumentation Version을 함께 기록해야 한다. 둘째, 운영 대시보드와 데이터 분석을 실험적 Attribute 이름에 직접 결합하기보다 내부 Canonical Schema로 한 번 정규화해야 한다. 규약이 바뀌더라도 기존 Run과 새 Run을 비교할 수 있어야 하기 때문이다.
Session, Run, Span, Event를 구분한다
주요 에이전트 런타임은 서로 다른 이름을 사용하지만 구조는 비슷하다.
OpenAI Agents SDK는 같은 대화를 group_id로 묶고 한 작업을 Trace, 그 안의 생성·도구·Guardrail 작업을 Span으로 남긴다.
Google ADK는 Session 안에서 한 사용자 요청을 invocation_id로 묶고 모델 호출·도구 호출·상태 변경을 Event로 기록한다.
Amazon Bedrock AgentCore는 Session 아래에 요청·응답 단위의 Trace를 두고 내부 처리와 외부 호출을 Span으로 나눈다.
LangSmith는 개별 작업을 Run, Run의 트리를 Trace, 여러 Trace가 이어지는 대화를 Thread로 표현한다.
이를 런타임 중립적으로 정리하면 다음과 같다.
| 계층 | 의미 | 답해야 하는 질문 |
|---|---|---|
| Session | 여러 Turn이 이어지는 대화나 작업 맥락 | 같은 사용자가 여러 번 요청하는 동안 문제가 누적됐는가 |
| Run·Trace | 한 사용자 요청부터 최종 결과까지 | 이 요청은 전체적으로 성공했는가, 얼마나 걸렸는가 |
| Span | 시작과 끝이 있는 개별 작업 | Retrieval, 모델, Tool, 외부 API 중 어디에서 지연·실패했는가 |
| Event | 특정 시점의 결정이나 상태 변화 | Tool이 언제 선택됐는가, Retry가 왜 발생했는가, 승인이 언제 요청됐는가 |
session_id와 run_id를 같은 값으로 쓰면 다중 Turn 문제를 분석하기 어렵다. 반대로 모델 호출마다 새로운 Run을 만들면 하나의 사용자 요청이 여러 개의 독립 작업처럼 흩어진다.
권장 경계는 다음과 같다.
Session └─ Run / Trace: 한 사용자 요청 → 최종 사용자 결과 ├─ Retrieval ├─ Plan / Tool Selection ├─ Model Call ├─ Tool Execution │ └─ HTTP / DB / Queue / External API Span ├─ Retry / Loop Event ├─ Guardrail / Policy Event ├─ Human Intervention └─ User Outcome
비동기 작업, Queue Worker, 다른 Agent, 외부 Tool로 실행이 넘어가더라도 같은 Trace Context를 전달하거나 Span Link로 연결해야 한다. 그렇지 않으면 에이전트 Trace는 Tool을 호출했다는 사실까지만 보여주고 실제로 어느 외부 시스템에서 시간이 소모됐는지는 보여주지 못한다.
Agent Run Telemetry Model
아래 모델은 현재 OpenTelemetry GenAI 규약과 주요 런타임의 공통 구조를 정규화한 런타임 중립적 제안 모델이다. OpenTelemetry의 확정된 Stable Schema 자체로 오해해서는 안 된다.
YAML
agent_run:
identity:
trace_id: ""
run_id: ""
session_id: ""
parent_run_id: null
workflow_name: ""
workflow_version: ""
agent_name: ""
agent_version: ""
telemetry_schema_version: ""
instrumentation_name: ""
instrumentation_version: ""
environment: "production"
request:
started_at: ""
channel: ""
use_case: ""
turn_number: null
prompt_template_id: ""
prompt_template_version: ""
input_size_bytes: 0
input_language: ""
input_content_ref: null
execution:
spans:
- span_id: ""
parent_span_id: ""
stage: >
retrieval | plan | model_call | tool_selection | tool_execution | guardrail | human_wait | external_dependency sequence_no: 0 attempt_no: 1 loop_iteration: 0 started_at: “” ended_at: “” duration_ms: 0 wait_duration_ms: 0 status: “ok | error | cancelled | interrupted” error_type: null recovered: false
model:
provider: null
requested_model: null
response_model: null
time_to_first_chunk_ms: null
input_tokens: null
output_tokens: null
cache_read_tokens: null
finish_reason: null
retrieval:
data_source_id: null
data_source_version: null
query_fingerprint: null
top_k: null
result_count: null
document_refs: []
score_summary: null
tool:
available_toolset_version: null
selection_source: null
selected_tool: null
tool_name: null
tool_version: null
tool_call_id: null
argument_schema_valid: null
side_effect_class: null
result_status: null
argument_content_ref: null
result_content_ref: null
dependency:
service_name: null
operation: null
protocol_status: null
timeout: false
events:
- event_id: ""
span_id: ""
occurred_at: ""
event_type: >
tool_selected | retry_scheduled | loop_detected | guardrail_decision | policy_decision | human_approval_requested | human_approved | human_rejected | run_resumed | state_changed | output_delivered reason_code: “” policy_name: null policy_version: null decision: null
usage_and_cost:
model_call_count: 0
tool_call_count: 0
retry_count: 0
total_input_tokens: 0
total_output_tokens: 0
estimated_cost: null
actual_cost: null
currency: null
price_book_version: null
priced_at: null
cost_is_estimate: true
outcome:
terminal_state: >
completed | partial | failed | cancelled | abandoned | awaiting_human technical_success: false task_success: null final_output_present: false user_visible_latency_ms: 0 human_intervention_required: false human_wait_duration_ms: 0 user_feedback: null user_corrected_result: false user_undid_action: false business_outcome_ref: null
privacy:
capture_mode: >
metadata_only | redacted | external_reference | temporary_debug data_classes_detected:
- pii
- secret
- customer_data
- untrusted_instruction
redaction_profile_version: ""
content_storage: null
retention_class: ""
expires_at: null
모든 조직이 이 필드를 한 번에 구현할 필요는 없다. 다만 다음 정보는 가능한 한 첫 단계부터 남기는 편이 좋다.
session_id, run_id, trace_id
Agent·Workflow·Prompt·Tool·Data Source의 Version
단계별 시작·종료 시각과 상태
Model Call·Tool Call·Retry 횟수
최초 오류와 최종 오류의 구분
Token Usage와 비용 산정 기준
Human Intervention 여부와 대기 시간
기술적 성공과 사용자 작업 성공
Content Capture Mode
run_id나 session_id처럼 값의 종류가 계속 늘어나는 식별자는 개별 Trace와 Log에 두고 집계 Metric의 Label로 사용하지 않는 편이 낫다. 고유 ID를 Metric Attribute로 사용하면 Cardinality와 저장 비용이 빠르게 증가할 수 있다.
Retrieval은 검색 시간만 보면 부족하다
Retrieval Span에는 지연 시간 외에도 어떤 Data Source를 어떤 조건으로 조회했는지가 남아야 한다.
현재 OpenTelemetry GenAI 규약은 Retrieval에 data_source.id, top_k, 검색된 문서의 ID와 Score를 표현할 수 있도록 하고 있다. 다만 문서 목록과 Query Text는 Opt-In이며 Query Text에는 민감한 정보가 들어갈 수 있다고 경고한다.
운영 환경에서는 다음 정도를 기본값으로 삼을 수 있다.
Data Source ID와 Index·Snapshot Version
요청한 top_k
실제 반환 문서 수
문서의 비식별 Reference와 Score 요약
Filter 적용 여부와 결과 수
Cache Hit 여부
Retrieval Latency
Empty Result 여부
Query 길이·언어·분류와 같은 파생 정보
필요할 때만 원문 Query를 가리키는 별도 Content Reference
문서 전체를 Span에 복사하지 않아도 “새 배포 이후 특정 Data Source에서 결과가 비기 시작했는가”, “잘못된 Index Version이 선택됐는가”, “검색은 성공했지만 관련도 점수가 낮았는가”를 판단할 수 있다.
서로 다른 Retriever나 Embedding Model의 Score를 그대로 비교해서도 안 된다. Score의 의미는 검색기와 Index 구성에 따라 달라질 수 있으므로 Data Source Version, Retriever Version과 함께 해석해야 한다.
Tool Selection과 Tool Execution은 다른 단계다
에이전트가 잘못된 도구를 선택한 문제와 올바른 도구를 선택했지만 실행에 실패한 문제는 수정 위치가 다르다.
Tool Selection에서는 다음을 본다.
선택 가능한 Tool Set의 Version 또는 Hash
선택된 Tool
Tool을 선택한 주체: Model, Plan, Rule, Human
Tool을 선택하지 않은 경우의 상태
tool_call_id
Guardrail이나 Approval에 의해 선택이 거부·변경됐는지
Tool Execution에서는 다음을 본다.
Tool 이름과 Version
동일한 tool_call_id
Argument Schema Validation 결과
시작·종료 시각
실행 상태와 error_type
실제 호출한 외부 서비스와 Operation
결과가 없었는지, 일부만 처리됐는지
Side Effect 발생 여부
Retry 또는 중복 실행 여부
OpenTelemetry는 execute_tool Span에 Tool Name, Tool Type, Tool Call ID와 오류 유형을 기록하도록 정의한다. Tool Argument와 Result는 Opt-In이며 민감한 정보가 포함될 수 있다고 경고한다. 자동 계측에서 빠진 Tool은 애플리케이션이 직접 계측해야 할 수도 있다.
도구 선택을 설명하기 위해 모델의 내부 추론 원문을 저장할 필요는 없다. 어떤 Tool Set이 제공됐고 무엇이 선택됐으며 어떤 정책 결정과 실행 결과가 뒤따랐는지를 구조화하면 운영 진단에 필요한 대부분의 정보를 얻을 수 있다.
Retry와 Loop는 별도 Event가 필요하다
현재 OpenTelemetry의 모델 Span 규약은 일시적 문제로 요청이 자동 재시도된 경우, 여러 시도를 포함한 하나의 논리적 Operation Duration을 기록하도록 권고한다. 이 방식은 최종 지연을 계산하기에는 유용하지만 개별 시도의 실패 원인을 자동으로 보여준다는 뜻은 아니다.
따라서 Retry를 진단해야 한다면 다음 정보를 Event나 하위 Span으로 추가해야 한다.
attempt_no
Retry 이유
Backoff 시간
이전 오류 유형
같은 Provider·Model·Tool을 다시 호출했는지
이전 시도가 외부 시스템에 도달했을 가능성
Retry 후 복구됐는지
전체 Run을 종료시킨 마지막 실패인지
Loop는 단순 Retry와도 구분한다. Retry는 같은 작업의 일시적 실패를 다시 시도하는 것이고 Loop는 에이전트가 상태를 충분히 바꾸지 못한 채 Model Call과 Tool Call을 반복하는 현상이다.
Loop 탐지에는 다음 신호가 유용하다.
Run당 Model Call 수
Run당 Tool Call 수
같은 Tool과 유사한 Argument의 반복
동일한 Retrieval Query의 반복
반복되는 State Fingerprint
토큰과 비용은 늘지만 Outcome은 변하지 않는 상태
최대 Iteration 도달
Human Intervention이나 강제 중단 여부
OpenTelemetry의 현재 GenAI Metrics에도 Agent Invocation당 Inference Call 수와 Tool Call 수, Agent Duration, Tool Execution Duration이 제안돼 있다.
Guardrail과 Policy는 결과와 지연을 함께 남긴다
Guardrail은 실행됐다는 사실만으로 충분하지 않다. 어느 단계에서 어떤 결정을 내렸고 그 결정이 최종 결과에 어떤 영향을 줬는지가 필요하다.
최소한 다음을 기록한다.
검사 시점: Input, Retrieval, Model Output, Tool Selection, Tool Result
Policy·Rule 이름과 Version
결정: Allow, Block, Modify, Retry, Escalate
위반 Category
검사 Latency
Human Review로 전환됐는지
최종 Run이 완료됐는지
원문 대신 비식별 Content Reference가 있는지
Guardrail이 요청을 차단하면 보안 관점에서는 정상 작동일 수 있다. 그러나 사용자가 이유를 이해하지 못하고 작업을 완료하지 못했다면 사용자 결과는 실패 또는 부분 성공일 수 있다. 그래서 Policy Decision과 User Outcome을 분리해 기록해야 한다.
이 글의 범위는 Policy와 Authorization의 결과를 관측하는 것까지다. MCP Token Audience, Token Passthrough, Tool Allowlist, Secret Boundary, Tenant Isolation과 Revocation을 어떻게 설계할지는 별도의 Security Control 문제다.
Human Intervention은 지연이면서 정상 흐름일 수 있다
사람의 승인을 기다리는 시간은 Model Latency도 Tool Latency도 아니다. 이를 분리하지 않으면 사용자가 느끼는 지연 전체가 에이전트의 성능 문제로 집계된다.
Human Intervention에는 다음 상태가 필요하다.
승인 요청 시각
승인 대상 작업과 tool_call_id
승인, 거절, 수정, 만료
역할 기반 Reviewer 식별자
대기 시간
승인 이후 Run 재개 시각
재개 후 최종 결과
사람이 결과를 직접 수정했는지
OpenAI Agents SDK의 HITL 구조도 Tool Call 전에 Run을 중단하고 Pending Approval을 Interruption으로 노출한 뒤 직렬화된 Run State에서 승인·거절 후 원래 Run을 재개한다. 이와 같은 흐름에서는 Human Wait를 독립 단계로 기록해야 한다.
Human Intervention Rate가 높다고 항상 나쁜 것은 아니다. 위험한 Side Effect에 승인이 의도된 설계라면 정상이다. 대신 승인 대기 시간이 사용자 여정 목표를 초과하는지, 불필요한 승인 요청이 반복되는지, 사람이 자주 결과를 수정하는지를 함께 봐야 한다.
Stage → Signal → Failure Example → Diagnostic Question
| Stage | Signal | Failure Example | Diagnostic Question |
|---|---|---|---|
| Session·Run 시작 | session_id, run_id, Agent·Workflow Version | 한 사용자 요청이 여러 독립 Trace로 분리됨 | 같은 사용자 작업의 모든 단계가 하나의 Run으로 연결됐는가 |
| Retrieval | Data Source Version, top_k, Result Count, Score Summary, Latency | 오래된 Index에서 관련 없는 문서를 검색 | 어떤 Data Source와 Version이 사용됐고 실제 결과가 있었는가 |
| Plan | Plan Span, Agent Version, Child Model Call | 계획 단계가 반복되지만 실행으로 넘어가지 않음 | 계획이 몇 번 생성됐고 이전 계획과 무엇이 달라졌는가 |
| Model Call | Provider, Requested·Response Model, TTFC, Duration, Token, Finish Reason | 응답 시작이 늦거나 길이 제한으로 중단 | Provider 대기인가, 생성 지연인가, Output 제한인가 |
| Tool Selection | Tool Set Version, Selected Tool, tool_call_id, Selection Source | 검색 Tool 대신 데이터 변경 Tool을 선택 | 어떤 Tool이 제공됐고 어떤 Tool이 선택·거부됐는가 |
| Tool Execution | Tool Version, Schema Validation, Duration, Status, Dependency Span | 올바른 Tool이 외부 API Timeout으로 실패 | 선택은 맞았지만 실행이 실패한 것인가 |
| Retry | Attempt, Reason, Backoff, Recovered | Provider 오류가 세 번 재시도돼 지연 증가 | 몇 번째 시도에서 어떤 오류가 발생했고 복구됐는가 |
| Loop | Iteration, Repeated State, Model·Tool Call Count | 같은 검색과 Tool Call을 반복하며 비용 증가 | 상태가 변하지 않은 반복이 어디에서 시작됐는가 |
| Guardrail·Policy | Policy Version, Stage, Decision, Category, Latency | 정상 요청이 반복 차단되거나 검사 시간이 급증 | 어떤 정책이 어느 단계에서 결과를 변경했는가 |
| Human Intervention | Request·Decision Time, Wait Duration, Resume Result | Agent는 빨랐지만 승인 대기에 수십 분 소요 | 실제 실행 시간과 사람을 기다린 시간은 각각 얼마인가 |
| External Dependency | Service, Operation, Status, Timeout, Trace Context | Tool은 성공으로 보이지만 하위 DB가 지연 | Agent 바깥의 어느 의존성이 Critical Path에 있었는가 |
| Failure | First Error, Terminal Error, Recovered, User-visible Failure | 중간 오류는 복구됐지만 마지막 결과는 부분 완료 | 최초 실패와 최종 실패는 같은가 |
| User Outcome | Technical Success, Task Success, Feedback, Correction, Undo | HTTP 200과 답변은 있었지만 사용자가 작업을 다시 수행 | 사용자가 실제 목적을 달성했는가 |
| Cost | Usage, Estimated·Actual Cost, Price Book Version | 실패한 Run이 성공 Run보다 많은 Token과 Tool 비용 사용 | 비용이 어느 단계에서 늘었고 성공 결과로 이어졌는가 |
느린 Agent는 Critical Path부터 찾는다
한 Run 안에 여러 Tool이 병렬 실행될 수 있으므로 Child Span Duration을 모두 더한 값이 사용자 대기 시간과 같지는 않다. 진단할 때는 다음을 구분한다.
사용자 요청부터 첫 응답까지의 시간
첫 Chunk부터 최종 응답까지의 시간
실제 실행 시간
Queue·Backoff·Rate Limit 대기
Human Approval 대기
병렬 작업 중 가장 긴 Critical Path
결과 전달 이후 비동기 작업 시간
OpenTelemetry의 현재 GenAI Metrics는 전체 Model Operation Duration뿐 아니라 Streaming 응답의 Time to First Chunk와 이후 Chunk 간 시간을 구분한다. Agent·Workflow 단위의 End-to-End Duration도 개별 Model Call Duration과 별도로 제안돼 있다.
사용자가 “답이 늦게 나오기 시작한다”고 느끼는 문제와 “답은 바로 시작하지만 끝나지 않는다”는 문제는 같은 평균 Latency로 합치면 안 된다.
잘못 행동한 Agent는 Outcome에서 거꾸로 추적한다
에이전트가 최종 문자열을 만들었다고 해서 작업이 성공한 것은 아니다. 최소한 다음 두 상태를 분리해야 한다.
technical_success: Run이 오류 없이 끝나고 Final Output이 생성됨
task_success: 사용자가 원한 실제 작업이 완료됨
Task Success는 사용 사례마다 다르게 정의된다.
지원 Agent라면 문의가 해결됐는지
운영 Agent라면 변경이 실제 적용되고 검증됐는지
문서 Agent라면 필요한 파일이 생성·전달됐는지
구매 Agent라면 올바른 대상과 조건으로 주문됐는지
분석 Agent라면 사용자가 결과를 채택했는지
사용자 Feedback만으로 판단하기 어려우면 비즈니스 Event, 후속 수정, Undo, 재질문, Human Review, Evaluation Score를 같은 Run ID에 연결할 수 있다. LangSmith는 Feedback을 Run ID에 연결하고 Google ADK는 실제 Tool Trajectory와 Response를 평가할 수 있는 구조를 제공한다.
잘못된 결과를 진단할 때는 Outcome에서 시작해 다음 순서로 거슬러 올라가는 편이 효과적이다.
실제 Task Success가 실패했는가
Final Output이 근거와 일치했는가
Tool Execution 결과를 올바르게 반영했는가
올바른 Tool을 선택했는가
Retrieval 결과가 적절하고 최신이었는가
어떤 Model·Prompt·Agent·Tool Version이 사용됐는가
Token 수와 비용은 같은 뜻이 아니다
Token Usage는 비용 계산의 중요한 입력이지만 그 자체가 실제 비용은 아니다. Provider, Model, Input·Output 구분, Cache Read·Write, 사용 시점의 가격, 할인·계약 조건, Tool이나 Retrieval의 별도 비용에 따라 달라질 수 있다.
Run에는 다음을 구분해 남긴다.
Provider가 반환한 사용량
Input·Output·Cache 등 Token Breakdown
요청별 사용량
Tool·Retrieval의 별도 Usage
사용한 Price Book Version과 적용 시점
계산된 예상 비용
청구 데이터에서 확인한 실제 비용
비용이 Estimate인지 Actual인지
OpenTelemetry는 Token Usage Metric을 정의하면서 사용량과 Billable Token을 모두 제공하는 시스템에서는 Billable Token을 보고하도록 한다. OpenAI Agents SDK는 Run 전체와 개별 요청별 Token Usage를 별도로 보존할 수 있다.
운영 지표는 Run당 비용에서 멈추지 않는 편이 좋다. 성공한 작업당 비용, 사람의 수정이 필요 없었던 결과당 비용, 완료된 사용자 여정당 비용으로 연결해야 비용 증가가 실제 가치로 이어졌는지 판단할 수 있다.
Prompt·Completion·Tool Argument는 기본 수집 대상이 아니다
원문이 있으면 디버깅이 쉬워지는 경우가 있다. 그러나 다음 내용도 함께 Telemetry Backend로 들어갈 수 있다.
이름, 이메일, 전화번호 등 PII
인증정보, API Key, Session Token
고객 데이터와 영업기밀
내부 System Instruction
Tool Schema와 내부 API 구조
Tool Argument와 Tool Result
Retrieval Query와 원문 문서
외부 문서에 삽입된 Prompt Injection Payload
OpenTelemetry는 Model Instruction, User Message, Model Output을 기본적으로 기록하지 말고 Opt-In으로 제공할 것을 권고한다. 운영 환경에서 민감 데이터나 저장량이 문제가 된다면 Content를 별도 저장하고 Span에는 Reference만 두는 방식을 권장한다. Tool Argument와 Result, Retrieval Query 역시 민감 정보가 들어갈 수 있는 Opt-In 항목이다.
권장 수집 단계는 다음과 같다.
| 단계 | 기본 용도 | 수집 범위 |
|---|---|---|
| Metadata Only | 운영 기본값 | ID, Version, 크기, 지연, 상태, Token, Error, Outcome |
| Redacted·Derived | 제한적 원인 분석 | 언어, 길이, Schema 적합 여부, PII·Secret 탐지 Category, 비식별 Fingerprint |
| External Reference | 프로덕션 심층 분석 | 별도 보호 저장소의 Content Reference, 접근권한·보존기간 분리 |
| Temporary Debug | 한시적 재현 | 명시적 승인, 제한된 대상, 짧은 TTL, 접근 감사, 종료 후 삭제 |
프레임워크 기본값이 조직 정책과 일치한다고 가정해서도 안 된다.
OpenTelemetry는 원문 미수집을 기본 패턴으로 제시하지만 OpenAI Agents SDK의 trace_include_sensitive_data 기본값은 현재 true다. Microsoft는 개발·디버깅 때 Content Recording을 활성화하고 운영 환경에서는 끄도록 안내한다. Google ADK도 원문 Prompt, Tool Argument, Session State가 들어간 Debug Output을 민감 데이터로 취급하라고 경고하며 PII가 Span Attribute에 붙지 않도록 하는 설정을 제시한다.
결국 계측을 활성화하는 것과 원문 수집을 허용하는 것은 별도의 설정과 승인 절차여야 한다.
Telemetry에 들어온 Prompt Injection Payload도 신뢰하면 안 된다
외부 문서나 Tool Result에 포함됐던 악성 지시문은 Retrieval·Tool Result·Prompt·Completion과 함께 Telemetry로 복사될 수 있다. 이후 다른 AI Assistant가 장애 분석을 위해 이 Log를 읽는다면, Telemetry가 새로운 간접 Prompt Injection 입력이 될 수 있다.
OWASP는 AI가 소비하는 Log, Link, External Reference를 신뢰할 수 없는 입력으로 취급해야 한다고 지적한다. 일반 Logging Guidance도 외부 Trust Zone에서 들어온 Event Data를 검증하고 CR·LF와 구분자 등을 정리해 Log Injection을 막도록 권고한다. OWASP Cheat Sheet Series
원문 Telemetry를 다룰 때는 다음 원칙이 필요하다.
원문은 실행 가능한 Instruction이 아니라 비신뢰 데이터로 표시
Dashboard와 Viewer에서 적절히 Escape·Encode
외부 Link와 Active Content를 자동 실행하지 않음
다른 Agent에 전달할 때 원문과 운영 Instruction의 경계를 분리
Security Event 분석용 Payload는 별도 보존 정책 적용
Debug가 끝나면 Temporary Capture를 자동 종료
이는 MCP Authorization을 어떻게 구성할지에 대한 설계가 아니다. Telemetry 자체가 두 번째 데이터 저장소이자 입력 경로가 될 수 있다는 관측 데이터 관리 문제다.
대시보드는 Model이 아니라 사용자 결과를 중심으로 만든다
Agent Dashboard의 첫 화면을 평균 Model Latency와 총 Token 수만으로 구성하면 사용자 영향이 뒤로 밀린다.
먼저 봐야 할 Run-level 지표는 다음과 같다.
Task Success Rate
Partial·Failed·Abandoned Run Rate
사용자 체감 End-to-End Latency
Human Intervention Rate와 Human Wait
Retry·Loop Rate
Run당 Model Call·Tool Call 수
Guardrail Block·Modify·Escalate Rate
External Dependency Error Contribution
성공한 작업당 비용
사용자 수정·Undo·재실행 비율
그다음 Agent Version, Workflow Version, Prompt Version, Model, Tool Version, Data Source Version, 배포 환경별로 분해한다.
임계값은 모든 Agent에 공통으로 정하지 않는다. 사용자가 몇 초 안에 답을 받아야 하는 대화형 Agent와 몇 분 동안 백그라운드 분석을 수행하는 Agent는 같은 Latency 기준을 쓸 수 없다. 무엇을 SLI로 삼을지는 해당 Agent가 지원하는 Critical User Journey에서 출발해야 한다.
구현 순서
처음부터 모든 Prompt와 Tool Result를 수집하기보다 다음 순서가 안전하다.
Run의 시작과 끝을 정의한다
한 사용자 요청이 어디에서 시작되고 언제 완료·부분 완료·실패·중단되는지 정한다. 비동기 작업이라면 결과가 나중에 도착해도 원래 Run과 연결할 수 있어야 한다.
Session·Run·Trace Context를 전파한다
API, Queue, Worker, 다른 Agent, Retrieval System, Tool과 외부 서비스까지 Context를 전달한다. 이 단계가 없으면 이후의 세부 계측은 하나의 실행 경로로 조립되지 않는다.
원문 없이 Span Coverage를 만든다
Retrieval, Model Call, Tool Selection, Tool Execution, Guardrail, 외부 의존성을 먼저 Span으로 구분한다. Version, Duration, Status, Error Type과 Call ID를 남긴다.
Retry·Loop·Human Wait를 Event로 추가한다
자동 계측이 보여주지 못하는 Attempt, Backoff, 반복 상태와 승인 대기를 구조화한다.
User Outcome을 연결한다
Final Output 생성 여부와 실제 Task Success를 구분한다. Feedback, Business Event, Correction, Undo와 Human Review를 Run에 연결한다.
Usage와 비용 모델을 붙인다
Token Usage, Tool Usage, Price Book Version, Estimate·Actual 여부를 분리한다. 비용은 성공한 사용자 결과와 함께 본다.
Content Capture Policy를 별도로 승인한다
개발·운영 환경, 데이터 등급, Sampling 조건, Redaction, 접근권한, TTL과 삭제 절차를 정한다. 프레임워크 설정을 명시적으로 덮어쓴다.
Schema Adapter와 회귀 검증을 둔다
OpenTelemetry GenAI 규약과 Runtime Telemetry가 Development 상태라는 점을 고려해 내부 Canonical Schema와 Mapping Version을 둔다. SDK나 계측 라이브러리 업데이트 후 필드가 사라지거나 이름이 바뀌지 않았는지 테스트한다.
관측성의 목적은 모든 내용을 저장하는 것이 아니다
좋은 Agent Observability는 세 질문에 답할 수 있어야 한다.
이 Run에서 실제로 무슨 일이 일어났는가
어느 단계에서 처음 지연되거나 잘못된 경로로 들어갔는가
그 결과 사용자는 목적을 달성했고 얼마의 시간과 비용이 들었는가
이 질문에 답할 수 있다면 모든 Prompt, Completion, Tool Argument를 원문으로 보관하지 않아도 된다.
반대로 모델 호출 시간과 Token 수만 남기면 시스템이 느렸다는 사실은 알 수 있어도 Retrieval이 비었는지, 잘못된 Tool을 골랐는지, Retry Loop에 빠졌는지, Guardrail이 막았는지, 사람이 승인하지 않았는지, 사용자가 실제로 문제를 해결했는지는 알 수 없다.
AI 에이전트의 관측성은 모델을 더 자세히 보는 일이 아니다. 한 Run 안의 판단·행동·대기·비용과 사용자 결과를 하나의 실행 경로로 연결하는 일이다.