본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 8장: AI 시스템의 관측 가능성
2026년 3월 9일·아키텍처·

8장: AI 시스템의 관측 가능성

LLM 기반 시스템의 관측 가능성 설계 — 트레이싱, 메트릭, 로깅, 프롬프트 버전 관리, 품질 모니터링, 그리고 AI 특화 대시보드 구축을 다룹니다.

16분539자5개 섹션
architecturellminfrastructure
공유
ai-architecture8 / 10
12345678910
이전7장: 장애 대응과 회복 탄력성다음9장: 확장성과 멀티테넌시 설계

전통적 APM으로는 부족합니다

웹 서비스의 관측 가능성은 성숙한 분야입니다. Datadog, New Relic, Grafana 같은 도구로 HTTP 응답 시간, 에러율, CPU 사용률을 모니터링하는 것은 이미 표준 관행입니다. 그러나 AI 시스템에서는 이러한 전통적 메트릭만으로는 시스템의 실제 상태를 파악할 수 없습니다.

API가 200 OK를 반환하고, 응답 시간이 정상 범위 내이며, 서버 리소스에 여유가 있어도, 모델이 생성하는 응답의 품질이 하락하고 있을 수 있습니다. 이를 감지하려면 AI 시스템에 특화된 관측 가능성 체계가 필요합니다.

AI 관측 가능성의 고유한 과제

비결정적 출력은 동일한 입력에 대해 매번 다른 출력이 생성된다는 점입니다. 전통적인 "기대값과 실제값 비교" 방식의 모니터링이 어렵습니다.

품질의 주관성은 응답이 "좋은지"를 판단하는 기준이 작업마다 다르다는 것입니다. 정형화된 메트릭으로 포착하기 어려운 뉘앙스가 존재합니다.

긴 피드백 루프는 응답의 품질 문제가 즉시 드러나지 않을 수 있다는 점입니다. 환각이 포함된 응답을 사용자가 나중에야 발견하는 경우가 흔합니다.

블랙박스 모델은 외부 API를 사용하는 경우 모델 내부를 관찰할 수 없다는 제약입니다. 입출력만으로 상태를 추론해야 합니다.

세 기둥: 트레이스, 메트릭, 로그

관측 가능성의 세 기둥은 AI 시스템에서도 유효하지만, 각 기둥의 내용을 AI에 맞게 확장해야 합니다.

트레이싱 -- LLM 호출 체인 추적

AI 시스템의 단일 요청은 여러 LLM 호출과 도구 실행을 거칩니다. RAG 파이프라인의 경우 검색, 리랭킹, 프롬프트 구성, LLM 추론, 후처리가 순차적으로 발생합니다. 각 단계의 입출력, 소요 시간, 토큰 수를 추적하는 분산 트레이싱이 핵심입니다.

src/observability/tracer.ts
typescript
interface LLMSpan {
  traceId: string;
  spanId: string;
  parentSpanId?: string;
  operation: string;
  startTime: number;
  endTime?: number;
  attributes: {
    model?: string;
    inputTokens?: number;
    outputTokens?: number;
    promptVersion?: string;
    temperature?: number;
    toolCalls?: string[];
    cacheHit?: boolean;
    costUsd?: number;
  };
  status: "ok" | "error";
  errorMessage?: string;
}
 
class LLMTracer {
  private spans: Map<string, LLMSpan> = new Map();
 
  startSpan(
    traceId: string,
    operation: string,
    parentSpanId?: string
  ): LLMSpan {
    const span: LLMSpan = {
      traceId,
      spanId: crypto.randomUUID(),
      parentSpanId,
      operation,
      startTime: Date.now(),
      attributes: {},
      status: "ok",
    };
    this.spans.set(span.spanId, span);
    return span;
  }
 
  endSpan(spanId: string, attributes: Partial<LLMSpan["attributes"]>): void {
    const span = this.spans.get(spanId);
    if (!span) return;
 
    span.endTime = Date.now();
    span.attributes = { ...span.attributes, ...attributes };
 
    this.export(span);
  }
 
  markError(spanId: string, error: string): void {
    const span = this.spans.get(spanId);
    if (!span) return;
 
    span.status = "error";
    span.errorMessage = error;
  }
 
  private export(span: LLMSpan): void {
    // OpenTelemetry Collector 또는 Langfuse로 전송
    const duration = span.endTime
      ? span.endTime - span.startTime
      : 0;
 
    console.info(JSON.stringify({
      trace_id: span.traceId,
      span_id: span.spanId,
      parent_span_id: span.parentSpanId,
      operation: span.operation,
      duration_ms: duration,
      ...span.attributes,
      status: span.status,
    }));
  }
}
Info

트레이싱의 핵심은 하나의 traceId로 요청의 전체 여정을 연결하는 것입니다. API Gateway에서 생성한 traceId가 AI Gateway, 모델 라우터, 캐시 조회, LLM 호출, 후처리까지 전파되어야 합니다. OpenTelemetry 표준을 따르면 기존 APM 도구와의 통합이 용이합니다.

메트릭 -- AI 특화 지표

전통적 메트릭(응답 시간, 에러율)에 더해, AI 시스템은 다음의 특화 메트릭을 추적해야 합니다.

성능 메트릭:

  • TTFB(Time to First Byte)는 스트리밍 응답에서 첫 번째 토큰이 도착하기까지의 시간입니다. 사용자 체감 속도에 직접적으로 영향합니다.
  • 토큰 처리 속도(Tokens per Second)는 출력 토큰 생성 속도입니다. 모델 성능과 제공자 인프라 상태를 반영합니다.
  • 종단 간 지연 시간(End-to-End Latency)은 프롬프트 구성부터 최종 응답 전달까지의 전체 시간입니다.

품질 메트릭:

  • 가드레일 트리거율(Guardrail Trigger Rate)은 안전 필터가 응답을 수정하거나 차단한 비율입니다. 급격한 변화는 모델 동작의 변화를 의미합니다.
  • 환각 감지율(Hallucination Detection Rate)은 자동 검증 시스템이 탐지한 사실과 다른 응답의 비율입니다.
  • 사용자 피드백 점수: 좋아요/싫어요, 별점 등 사용자가 직접 평가한 응답 품질입니다.

비용 메트릭:

  • 요청당 비용(Cost per Request): 6장에서 설계한 비용 추적 체계의 집계입니다.
  • 모델별 호출 분포: 모델 라우터가 각 모델 등급으로 라우팅한 비율입니다.
  • 캐시 절감액: 캐시 적중으로 절약된 추정 비용입니다.
src/observability/metrics.ts
typescript
interface AIMetrics {
  // 성능
  ttfbMs: number;
  tokensPerSecond: number;
  endToEndLatencyMs: number;
 
  // 토큰
  inputTokens: number;
  outputTokens: number;
  totalTokens: number;
 
  // 품질
  guardrailTriggered: boolean;
  hallucinationDetected: boolean;
  userFeedbackScore?: number;
 
  // 비용
  estimatedCostUsd: number;
  modelUsed: string;
  cacheHit: boolean;
}
 
class AIMetricsCollector {
  private metrics: AIMetrics[] = [];
 
  record(metric: AIMetrics): void {
    this.metrics.push(metric);
    this.emitToPrometheus(metric);
  }
 
  getAggregated(windowMinutes: number): {
    avgLatency: number;
    p95Latency: number;
    guardrailRate: number;
    hallucinationRate: number;
    avgCost: number;
    cacheHitRate: number;
  } {
    const recent = this.metrics.filter(
      (_, i) => i > this.metrics.length - 1000
    );
 
    const latencies = recent.map((m) => m.endToEndLatencyMs).sort((a, b) => a - b);
    const guardrailCount = recent.filter((m) => m.guardrailTriggered).length;
    const hallucinationCount = recent.filter((m) => m.hallucinationDetected).length;
    const cacheHitCount = recent.filter((m) => m.cacheHit).length;
 
    return {
      avgLatency: latencies.reduce((a, b) => a + b, 0) / latencies.length,
      p95Latency: latencies[Math.floor(latencies.length * 0.95)] ?? 0,
      guardrailRate: guardrailCount / recent.length,
      hallucinationRate: hallucinationCount / recent.length,
      avgCost:
        recent.reduce((sum, m) => sum + m.estimatedCostUsd, 0) / recent.length,
      cacheHitRate: cacheHitCount / recent.length,
    };
  }
 
  private emitToPrometheus(metric: AIMetrics): void {
    // Prometheus 메트릭 내보내기
  }
}

로깅 -- 프롬프트와 응답 기록

AI 시스템의 로그는 일반적인 애플리케이션 로그에 더해 프롬프트-응답 쌍을 기록해야 합니다. 이 데이터는 디버깅, 품질 분석, 프롬프트 개선에 필수적입니다.

Warning

프롬프트와 응답 로그에는 사용자의 개인정보가 포함될 수 있습니다. 이름, 이메일, 주소 등의 PII(Personally Identifiable Information)는 로깅 전에 반드시 마스킹 처리해야 합니다. GDPR이나 개인정보보호법 위반은 기술적 실수로 면책되지 않습니다.

로그 레벨에 따라 기록하는 정보의 범위를 조절합니다. 프로덕션에서는 기본적으로 요약된 메타데이터만 기록하고, 디버깅이 필요한 경우 샘플링 비율을 높여 전체 프롬프트와 응답을 기록합니다.

프롬프트 버전 관리

프롬프트는 AI 시스템의 핵심 구성 요소이면서 가장 자주 변경되는 요소입니다. 코드에 대한 버전 관리(Git)와 동일한 수준의 관리가 프롬프트에도 필요합니다.

버전 관리의 핵심 요소

변경 추적: 언제, 누가, 왜 프롬프트를 변경했는지 기록합니다. 프롬프트 변경은 코드 변경과 동일한 수준의 리뷰를 거쳐야 합니다.

성능 연관 분석: 프롬프트 버전과 품질 메트릭을 연결하여, 특정 프롬프트 변경이 성능에 미친 영향을 추적합니다. 버전 A에서 B로 전환한 후 환각률이 증가했다면, 즉시 롤백할 수 있어야 합니다.

A/B 테스트: 새로운 프롬프트를 전체 트래픽에 적용하기 전에, 일부 트래픽에만 적용하여 성능을 비교합니다.

src/observability/prompt-registry.ts
typescript
interface PromptVersion {
  id: string;
  name: string;
  version: string;
  template: string;
  variables: string[];
  metadata: {
    author: string;
    createdAt: string;
    description: string;
    parentVersion?: string;
  };
  abTest?: {
    enabled: boolean;
    trafficPercent: number;
    controlVersion: string;
  };
}
 
class PromptRegistry {
  private versions: Map<string, PromptVersion[]> = new Map();
 
  register(prompt: PromptVersion): void {
    const existing = this.versions.get(prompt.name) ?? [];
    existing.push(prompt);
    this.versions.set(prompt.name, existing);
  }
 
  resolve(name: string, userId?: string): PromptVersion {
    const versions = this.versions.get(name);
    if (!versions || versions.length === 0) {
      throw new Error(`프롬프트를 찾을 수 없습니다: ${name}`);
    }
 
    // A/B 테스트가 활성화된 버전 확인
    const abVersion = versions.find((v) => v.abTest?.enabled);
    if (abVersion && userId) {
      const hash = this.hashUserId(userId);
      if (hash % 100 < (abVersion.abTest?.trafficPercent ?? 0)) {
        return abVersion;
      }
    }
 
    // 최신 안정 버전 반환
    return versions[versions.length - 1]!;
  }
 
  private hashUserId(userId: string): number {
    let hash = 0;
    for (let i = 0; i < userId.length; i++) {
      hash = (hash << 5) - hash + userId.charCodeAt(i);
      hash |= 0;
    }
    return Math.abs(hash);
  }
}

프로덕션 품질 모니터링

AI 시스템의 품질을 프로덕션 환경에서 자동으로 모니터링하는 것은 관측 가능성의 정점입니다. 오프라인 평가(벤치마크)만으로는 프로덕션의 다양한 입력 패턴을 포괄할 수 없습니다.

자동 평가 파이프라인

프로덕션 트래픽의 일정 비율(통상 1-5%)을 샘플링하여 자동 품질 평가를 수행합니다. 평가 항목은 작업 유형에 따라 달라지지만, 공통적으로 다음을 포함합니다.

  • 관련성(Relevance)은 응답이 질문에 적절한가를 평가합니다
  • 사실 정확성(Factuality)은 응답에 검증 가능한 사실 오류가 없는가를 확인합니다
  • 완전성(Completeness)은 질문의 모든 측면에 답변했는가를 검증합니다
  • 안전성(Safety)은 유해하거나 부적절한 내용이 포함되지 않았는가를 점검합니다
Tip

"LLM-as-Judge" 패턴을 활용하면 사람의 개입 없이 대규모로 품질을 평가할 수 있습니다. 단, 평가 모델은 응답 생성 모델과 다른 모델을 사용하여 편향을 줄이는 것이 좋습니다. 예를 들어, Sonnet이 생성한 응답을 Opus가 평가하는 구성입니다.

관측 가능성 도구 생태계

AI 관측 가능성 전용 도구들이 빠르게 성장하고 있습니다.

도구주요 기능특징
Langfuse트레이싱, 프롬프트 관리, 평가오픈소스, 셀프호스팅 가능
LangSmith트레이싱, 데이터셋, 평가LangChain 생태계 통합
Arize Phoenix트레이싱, 평가, 임베딩 분석오픈소스, 벡터 시각화
Helicone로깅, 비용 추적, 캐싱프록시 기반, 설정 간편

이들 도구는 기존 APM 도구를 대체하는 것이 아니라 보완합니다. 인프라 수준의 모니터링은 Datadog이나 Grafana로, AI 특화 메트릭은 Langfuse나 Arize로 추적하는 이중 구조가 현실적입니다.


이번 장에서는 AI 시스템에 특화된 관측 가능성 체계를 설계했습니다. 트레이싱으로 요청의 전체 여정을 추적하고, AI 특화 메트릭으로 품질과 비용을 측정하며, 프롬프트 버전 관리와 자동 평가로 지속적인 품질 개선을 가능하게 합니다. 다음 장에서는 이러한 관측 데이터를 기반으로 시스템을 확장하고 멀티테넌시를 지원하는 아키텍처를 다루겠습니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#architecture#llm#infrastructure

관련 글

아키텍처

9장: 확장성과 멀티테넌시 설계

AI 시스템의 수평 확장, 멀티테넌시 아키텍처, 속도 제한, 공정 스케줄링, 그리고 대규모 AI 서비스 운영을 위한 인프라 설계를 다룹니다.

2026년 3월 11일·17분
아키텍처

7장: 장애 대응과 회복 탄력성

AI 시스템의 장애 시나리오와 회복 탄력성 패턴 — 서킷 브레이커, 폴백, 재시도, 타임아웃, 모델 장애 조치, 그리고 그레이스풀 디그레이데이션을 다룹니다.

2026년 3월 7일·16분
아키텍처

10장: 실전 프로젝트 — AI-Native 시스템 아키텍처 설계

시리즈 전체의 아키텍처 패턴을 종합하여 프로덕션 AI-Native 시스템을 설계합니다. 전체 아키텍처 다이어그램, 기술 선택, 배포 전략을 다룹니다.

2026년 3월 13일·17분
이전 글7장: 장애 대응과 회복 탄력성
다음 글9장: 확장성과 멀티테넌시 설계

댓글

목차

약 16분 남음
  • 전통적 APM으로는 부족합니다
    • AI 관측 가능성의 고유한 과제
  • 세 기둥: 트레이스, 메트릭, 로그
    • 트레이싱 -- LLM 호출 체인 추적
    • 메트릭 -- AI 특화 지표
    • 로깅 -- 프롬프트와 응답 기록
  • 프롬프트 버전 관리
    • 버전 관리의 핵심 요소
  • 프로덕션 품질 모니터링
    • 자동 평가 파이프라인
  • 관측 가능성 도구 생태계