텔레메트리와 트레이싱
텔레메트리와 트레이싱
AI SDK가 내부에서 어떤 모델을 얼마나 호출했고, 도구가 얼마나 걸렸는지 궁금할 때가 있어요. AI SDK는 OpenTelemetry를 사용해 이런 텔레메트리 데이터를 수집하고, 스팬(span)으로 트레이싱을 제공해요. 기본적으로는 꺼져 있지 않고 통합을 등록하면 켜진다는 점, 그리고 입력·출력을 어떻게 다룰지를 여기서 정리해 볼게요.
출처: 공식문서
본문
텔레메트리 켜기
OpenTelemetry 스팬 수집은 @ai-sdk/otel 패키지가 필요해요. 설치하고 애플리케이션 시작 시 한 번 등록하면 돼요.
pnpm install @ai-sdk/otel
import { registerTelemetry } from 'ai';
import { OpenTelemetry } from '@ai-sdk/otel';
registerTelemetry(new OpenTelemetry());
Next.js라면 프로젝트 루트의 instrumentation.ts에 OpenTelemetry 프로바이더 설정과 함께 등록해요. 등록이 끝나면 모든 AI SDK 호출이 기본적으로 텔레메트리 이벤트를 내보내요. telemetry 옵션으로 functionId 같은 메타데이터를 붙이거나 특정 호출을 끌 수도 있어요.
const result = await generateText({
model: "xai/grok-4.6",
prompt: 'Write a short story about a cat.',
telemetry: {
functionId: `story-agent`,
},
});
기본적으로 입력과 출력이 모두 기록돼요. 민감 정보가 입력에 들어간다면 recordInputs와 recordOutputs를 false로 꺼서 프라이버시·전송량·성능을 고려할 수 있어요. 텔레메트리는 opt-out 방식이라, 호출 하나만 끄려면 telemetry: { isEnabled: false }, 전체를 끄려면 registerTelemetry()를 아예 호출하지 않으면 돼요.
텔레메트리 메타데이터와 컨텍스트 필터링
functionId로 이 데이터가 어느 함수 것인지 식별하고, runtimeContext로 사용자 ID나 요청 ID 같은 추가 정보를 넣을 수 있어요. 다만 runtimeContext에는 프로바이더에 보내면 안 되는 값(자격 증명 등)이 섞일 수 있어요. telemetry.includeRuntimeContext로 텔레메트리에 포함할 최상위 프로퍼티만 골라낼 수 있어요.
const result = await generateText({
model: "xai/grok-4.6",
prompt: 'Write a short story about a cat.',
runtimeContext: {
userId: 'user_123',
requestId: 'req_abc',
},
telemetry: {
includeRuntimeContext: {
requestId: true,
},
},
});
이 예시에서 통합은 runtimeContext를 { requestId: 'req_abc' }로만 받아요. false나 생략된 프로퍼티는 제외돼요. includeRuntimeContext를 생략하면 런타임 컨텍스트 프로퍼티가 아예 포함되지 않아요. 이것은 generateText, streamText, ToolLoopAgent, embed, embedMany, rerank에서 지원돼요. 참고로 이 필터는 텔레메트리 통합(OpenTelemetry 포함)에만 적용되고, 라이프사이클 콜백이나 반환 결과는 전체 runtimeContext를 그대로 받아요.
도구 컨텍스트에도 API 키 같은 비밀이 있을 수 있어요. telemetry.includeToolsContext로 도구 컨텍스트의 선택된 최상위 프로퍼티만 텔레메트리에 포함할 수 있고, 이 역시 generateText, streamText, ToolLoopAgent에서 지원돼요.
커스텀 트레이서와 텔레메트리 통합
@opentelemetry/api 싱글톤이 제공하는 것과 다른 TracerProvider를 쓰고 싶다면 커스텀 Tracer를 OpenTelemetry 생성자에 넘기면 돼요.
텔레메트리 통합은 생성 라이프사이클에 끼어들어 커스텀 관찰성(로깅, 분석, DevTools 등)을 만드는 방법이에요. 매 호출마다 콜백을 붙이는 대신 Telemetry를 한 번 구현해 registerTelemetry로 전역 등록하거나, telemetry.integrations로 호출 단위로 넘길 수 있어요. registerTelemetryIntegration에 여러 통합을 넘겨 모두 같은 라이프사이클 이벤트를 받게 할 수도 있고, 호출 단위 통합을 넘기면 그 호출에서는 전역 등록 대신 해당 통합을 사용해요. 통합 내부에서 발생한 에러는 잡혀서 생성 흐름을 깨지 않아요.
Node.js에서는 ai:telemetry 트레이싱 채널(node:diagnostics_channel)로도 텔레메트리 라이프사이클·실행 이벤트가 전달돼요. 별도 통합 등록 없이 구독할 수 있고, 프로바이더 호출과 도구 실행에 걸쳐 비동기 컨텍스트를 묶을 수 있어요.
수집되는 데이터 (OpenTelemetry 통합)
@ai-sdk/otel은 두 가지 스팬 형식을 내보내는 통합을 제공해요. OpenTelemetry는 GenAI Semantic Conventions를 따르는 권장 통합이고, LegacyOpenTelemetry는 이전 AI SDK 전용 스팬을 내보내요.
generateText/streamText는 세 종류의 스팬을 기록해요.
invoke_agent {modelId}(루트 스팬) — 모든 스텝과 도구 호출을 포함한 전체 작업.gen_ai.operation.name은"invoke_agent",gen_ai.provider.name은 프로바이더,gen_ai.request.model은 요청한 모델 ID,gen_ai.agent.name은functionId를 담아요.recordInputs가 켜져 있으면gen_ai.input.messages와gen_ai.system_instructions가,recordOutputs가 켜져 있으면gen_ai.output.messages가 들어가요. 완료 시gen_ai.response.finish_reasons,gen_ai.usage.input_tokens,gen_ai.usage.output_tokens등이 설정돼요.chat {modelId}(스텝 스팬, CLIENT) — 루트 스팬 아래에 프로바이더 호출당 하나씩.gen_ai.tool.definitions, 응답 ID·모델,gen_ai.client.operation.duration, 스트리밍 시time_to_first_chunk·time_per_output_chunk같은 지표가 포함돼요.execute_tool {toolName}(도구 스팬, INTERNAL) — 스텝 스팬 아래에 도구 실행당 하나씩.gen_ai.tool.name,gen_ai.tool.call.arguments,gen_ai.execute_tool.duration,gen_ai.tool.call.result를 담아요.
embed/embedMany는 CLIENT 종류의 embeddings {modelId} 루트 스팬과 프로바이더 요청당 내부 스팬을 기록하고, rerank도 비슷한 구조예요. 임베딩 사용량은 요청당 내부 스팬에만 기록되므로, embedMany의 총 사용량은 내부 스팬을 더해서 구해야 해요.
추가로 gen_ai.* 의미 체계에 포함되지 않는 AI SDK 전용 속성을 원하면 OpenTelemetry 생성자에 usage, providerMetadata, runtimeContext, headers, toolChoice, schema 같은 옵션을 켤 수 있어요. 기본값은 모두 꺼져 있어서 원하는 것만 켜면 돼요. 전용 스팬(ai.generateText 등)을 새로 만들지 않고, 기존 스팬에 선택한 속성만 더하는 방식이에요.
더 알아보기
- 라이프사이클 콜백 — 코드로 직접 관찰 지점을 잡는 방법
- 런타임과 도구 컨텍스트 —
runtimeContext/toolsContext의 전체 범위 - DevTools — 개발 중 스팬 검사