AI Engineering Academy · 강의

지연 시간, 비용, 품질 저하 알림

p99 지연 시간, 요청당 비용, 자동화된 품질 점수에 대한 알림 임계값을 정의하고, LLM 파이프라인의 품질이 저하되면 Slack 또는 PagerDuty로 알림을 전달합니다.

레슨 4/413개 단계

지연 시간, 비용, 품질 저하 알림은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

LLM 시스템에서 알림이 중요한 이유

LLM 애플리케이션은 미묘하고 점진적인 방식으로 장애가 발생합니다. 프롬프트 변경으로 평균 지연 시간이 30% 증가할 수 있고, 검색 기능 개선으로 답변 품질이 약간 낮아질 수 있으며, 사용량 급증으로 일일 비용이 예산의 5배까지 늘어날 수 있습니다. 사전 예방적 알림이 없으면 사용자가 불만을 제기하거나 월별 청구서가 도착한 뒤에야 이러한 문제를 발견하게 됩니다. 알림은 사후 대응 중심의 장애 처리를 사전 예방적 운영으로 전환합니다.

세 가지 알림 범주

LLM 애플리케이션 알림은 세 가지 범주로 나뉩니다. 지연 시간 알림은 응답 시간이 사용자 경험 기준을 초과할 때 발생합니다(예: p99 > 10초). 비용 알림은 요청당 비용 또는 일일 지출액이 예산 기준을 초과할 때 발생하여 예상치 못한 청구 금액을 방지합니다. 품질 알림은 자동 품질 지표(LLM-as-Judge 점수, 사용자 만족도, 충실성 점수)가 허용 가능한 하한선 아래로 떨어질 때 발생합니다. 각 범주에는 서로 다른 계측 방식과 알림 채널이 필요합니다.

from dataclasses import dataclass

@dataclass
class AlertThresholds:
    # Latency (milliseconds)
    p50_latency_ms: int = 2000    # median should be under 2s
    p99_latency_ms: int = 10000   # 99th percentile under 10s
    # Cost (USD)
    max_cost_per_request: float = 0.05  # alert if one request costs > 5 cents
    max_daily_spend: float = 50.00      # alert if daily spend exceeds $50
    # Quality (0.0 to 1.0 scale)
    min_quality_score: float = 0.75     # alert if rolling avg drops below 75%
    min_faithfulness: float = 0.80      # alert if RAGAS faithfulness drops below 80%

DEFAULT_THRESHOLDS = AlertThresholds()

지연 시간 지표 수집

지연 시간에 대한 알림을 설정하려면 먼저 지연 시간을 일관되게 수집해야 합니다. 세 가지 지연 시간 구성 요소를 পৃথ따로 측정하세요. 첫 토큰까지의 시간(TTFT, 스트리밍 사용자 경험에 중요), 전체 응답 시간, 검색 및 생성의 단계별 지연 시간입니다. 이를 시계열 데이터(요청당 데이터 포인트 하나)로 저장하면 지난 N분 또는 N시간 동안의 백분위수와 이동 평균을 계산할 수 있습니다.

import time
from collections import deque
from statistics import quantiles

class LatencyTracker:
    def __init__(self, window_size=100):
        self.window = deque(maxlen=window_size)  # rolling window of latencies

    def record(self, latency_ms: float):
        self.window.append(latency_ms)

    def p50(self) -> float:
        if not self.window:
            return 0
        return quantiles(self.window, n=100)[49]

    def p99(self) -> float:
        if not self.window:
            return 0
        return quantiles(self.window, n=100)[98]

    def check_alert(self, thresholds: AlertThresholds) -> list[str]:
        alerts = []
        if self.p99() > thresholds.p99_latency_ms:
            alerts.append(f'p99 latency {self.p99():.0f}ms exceeds {thresholds.p99_latency_ms}ms')
        if self.p50() > thresholds.p50_latency_ms:
            alerts.append(f'p50 latency {self.p50():.0f}ms exceeds {thresholds.p50_latency_ms}ms')
        return alerts

latency_tracker = LatencyTracker()

비용 추적 및 비용 알림

비용 모니터링에는 여러 세부 수준에서 지출을 추적하는 작업이 필요합니다. 요청당 비용(비용이 비정상적으로 높은 쿼리 포착), 시간별 및 일일 총액(사용량 급증 포착), 기능별 비용(애플리케이션에서 어느 부분이 가장 비용이 많이 드는지 식별)을 추적해야 합니다. 요청당 비용의 이상 징후에는 즉시 알림을 보내고, 일일 예산 한도는 예약된 확인 작업을 사용하세요.

from datetime import datetime, date
import threading

class CostTracker:
    def __init__(self):
        self._lock = threading.Lock()
        self._daily_spend = {}  # date -> total USD
        self._per_request = []  # list of (timestamp, cost)

    def record(self, cost_usd: float) -> list[str]:
        today = date.today().isoformat()
        alerts = []
        
        with self._lock:
            # Track daily spend
            self._daily_spend[today] = self._daily_spend.get(today, 0) + cost_usd
            self._per_request.append((datetime.now(), cost_usd))
        
        # Alert on expensive single requests
        if cost_usd > DEFAULT_THRESHOLDS.max_cost_per_request:
            alerts.append(f'Expensive request: ${cost_usd:.4f} (threshold: ${DEFAULT_THRESHOLDS.max_cost_per_request})')
        
        # Alert on daily budget exceeded
        daily_total = self._daily_spend[today]
        if daily_total > DEFAULT_THRESHOLDS.max_daily_spend:
            alerts.append(f'Daily budget exceeded: ${daily_total:.2f} > ${DEFAULT_THRESHOLDS.max_daily_spend}')
        
        return alerts

cost_tracker = CostTracker()

품질 점수 알림

품질 알림은 인프라 지표만으로 품질을 측정할 수 없고 의미론적 평가가 필요하기 때문에 가장 복잡한 범주입니다. 가장 확장성 있는 접근 방식은 샘플링된 자동 평가입니다. 프로덕션 요청의 무작위 표본(5~10%)에 대해 LLM-as-Judge 평가기를 비동기적으로 실행하고, 점수를 저장한 다음 이동 평균을 계산하세요. 이동 평균이 품질 하한선 아래로 떨어지면 알림을 보내세요.

import random
from openai import OpenAI

client = OpenAI()

def evaluate_response_quality(question: str, answer: str) -> float:
    judge_prompt = f'''Rate the quality of this AI assistant response on a scale from 0 to 1.

Question: {question}
Answer: {answer}

Return only a JSON object: {{"score": 0.85, "reason": "brief reason"}}

Scoring guide:
1.0 = Perfect, accurate, helpful
0.75 = Good, minor issues
0.5 = Acceptable but incomplete
0.25 = Poor, major gaps
0.0 = Completely wrong or harmful'''
    
    response = client.chat.completions.create(
        model='gpt-4o-mini',  # cheaper model for evaluation
        messages=[{'role': 'user', 'content': judge_prompt}],
        response_format={'type': 'json_object'}
    )
    import json
    result = json.loads(response.choices[0].message.content)
    return float(result['score'])

def maybe_evaluate(question: str, answer: str, sample_rate=0.1):
    if random.random() < sample_rate:
        score = evaluate_response_quality(question, answer)
        quality_tracker.record(score)
        return score
    return None

추세 감지를 위한 이동 평균

응답 하나가 잘못되었다고 해서 시스템 전체의 문제를 의미하지는 않습니다. 시간 구간(예: 최근 1시간 또는 최근 100개 요청)에 대한 이동 평균을 사용하여 추세를 감지하세요. 이동 평균이 기준을 넘은 상태로 10분 이상 유지되면 실제 문제일 가능성이 높지만, 짧은 급등은 잡음일 수 있습니다. 지수 가중 이동 평균(EWMA)은 단순 이동 평균보다 최근 변경 사항에 더 빠르게 반응합니다.

class RollingQualityMonitor:
    def __init__(self, window=50, alert_threshold=0.75, ewma_alpha=0.1):
        self.scores = []
        self.window = window
        self.alert_threshold = alert_threshold
        self.alpha = ewma_alpha  # EWMA decay factor
        self.ewma_score = None

    def record(self, score: float):
        self.scores.append(score)
        if len(self.scores) > self.window:
            self.scores.pop(0)
        
        # Update exponentially weighted moving average
        if self.ewma_score is None:
            self.ewma_score = score
        else:
            self.ewma_score = self.alpha * score + (1 - self.alpha) * self.ewma_score

    def should_alert(self) -> bool:
        if len(self.scores) < 10:  # not enough data yet
            return False
        return self.ewma_score < self.alert_threshold

    def summary(self) -> dict:
        if not self.scores:
            return {}
        return {
            'rolling_avg': sum(self.scores) / len(self.scores),
            'ewma': self.ewma_score,
            'sample_count': len(self.scores),
            'alert': self.should_alert()
        }

quality_tracker = RollingQualityMonitor()

알림 라우팅: Slack 및 PagerDuty

알림은 적절한 채널을 통해 적절한 사람에게 전달될 때만 유용합니다. 심각도에 따라 알림을 라우팅하세요. 품질 저하 및 비용 예산 알림은 업무 시간에 팀이 모니터링하는 Slack 채널로 보냅니다. 심각한 기준을 초과한 지연 시간 알림(예: p99 > 30초)과 갑작스러운 비용 급증(예: 5분 동안 정상 수준의 10배 초과)은 즉시 대기 근무자에게 에스컬레이션하도록 PagerDuty로 보냅니다.

import requests

def send_slack_alert(message: str, severity: str, webhook_url: str):
    color = {'critical': '#ff0000', 'warning': '#ff9900', 'info': '#36a64f'}[severity]
    payload = {
        'attachments': [{
            'color': color,
            'title': f'LLM Alert [{severity.upper()}]',
            'text': message,
            'footer': 'AI Pipeline Monitor'
        }]
    }
    requests.post(webhook_url, json=payload)

def send_pagerduty_alert(title: str, details: str, routing_key: str):
    payload = {
        'routing_key': routing_key,
        'event_action': 'trigger',
        'payload': {
            'summary': title,
            'severity': 'critical',
            'source': 'ai-pipeline-monitor',
            'custom_details': {'details': details}
        }
    }
    requests.post('https://events.pagerduty.com/v2/enqueue', json=payload)

def route_alert(alert_text: str, severity: str):
    send_slack_alert(alert_text, severity, SLACK_WEBHOOK_URL)
    if severity == 'critical':
        send_pagerduty_alert(alert_text, alert_text, PAGERDUTY_ROUTING_KEY)

알림 평가 루프

알림 로직은 요청 처리 과정에 직접 포함하지 말고 예약된 루프에서 실행해야 합니다. 알림 확인을 비동기적으로 실행하면 프로덕션 요청에 지연 시간이 추가되는 것을 방지할 수 있습니다. 60초마다 실행되는 백그라운드 스레드 또는 예약 작업이면 대부분의 알림 요구 사항에 충분합니다. 요청 경로에서 지표를 수집하고, 백그라운드 루프에서 기준을 평가하세요.

import threading
import time

def alert_evaluation_loop(interval_seconds=60):
    while True:
        try:
            # Check latency
            latency_alerts = latency_tracker.check_alert(DEFAULT_THRESHOLDS)
            for alert in latency_alerts:
                route_alert(f'LATENCY: {alert}', 'warning')
            
            # Check quality
            quality_summary = quality_tracker.summary()
            if quality_summary.get('alert'):
                msg = f'QUALITY DEGRADATION: Rolling avg {quality_summary["ewma"]:.2f} below threshold {DEFAULT_THRESHOLDS.min_quality_score}'
                route_alert(msg, 'warning')
            
            print(f'Alert check complete. Quality: {quality_summary.get("ewma", "N/A")}')
        except Exception as e:
            print(f'Alert evaluation error: {e}')
        
        time.sleep(interval_seconds)

# Start in background thread
alert_thread = threading.Thread(target=alert_evaluation_loop, daemon=True)
alert_thread.start()

알림 피로와 기준 조정

알림 피로는 알림이 너무 자주 발생하여 팀이 알림을 무시하기 시작하는 현상입니다. 다음 방법으로 이를 방지하세요. 보수적인(높은) 기준에서 시작한 뒤 기준선을 파악하면서 점차 엄격하게 조정하고, 알림이 발생하기 전에 여러 평가 주기 동안 지속되도록 요구하며, 관련 알림을 하나의 통지로 묶고, 의미 있는 조치로 이어지지 않는 알림을 정기적으로 검토하여 폐기하세요.

class AlertDebouncer:
    def __init__(self, required_consecutive_fires=3):
        self.required = required_consecutive_fires
        self.fire_counts = {}  # alert_name -> consecutive fires
        self.already_firing = set()  # alert_names currently active

    def should_fire(self, alert_name: str, condition: bool) -> bool:
        if condition:
            self.fire_counts[alert_name] = self.fire_counts.get(alert_name, 0) + 1
            if self.fire_counts[alert_name] >= self.required and alert_name not in self.already_firing:
                self.already_firing.add(alert_name)
                return True  # fire the alert (first time condition sustained)
        else:
            if self.fire_counts.get(alert_name, 0) > 0:
                self.fire_counts[alert_name] = 0
                self.already_firing.discard(alert_name)  # condition cleared
        return False  # either not sustained or already firing (no duplicate alert)

debouncer = AlertDebouncer(required_consecutive_fires=3)

통계적 방법으로 비용 이상 징후 모니터링

단순한 기준 알림은 이틀에 걸쳐 비용이 점진적으로 50% 증가하는 것과 같은 미묘한 비용 이상 징후를 놓칩니다. 통계적 이상 징후 감지를 사용하세요. 시간별 비용의 이동 평균과 표준 편차를 계산하고, 현재 시간의 비용이 평균보다 N 표준 편차 이상 높을 때 알림을 보냅니다(Z 점수 알림). 이 방식은 평일과 주말 트래픽처럼 자연스러운 사용 패턴에 자동으로 적응합니다.

import statistics

def compute_z_score(recent_value: float, historical_values: list[float]) -> float:
    if len(historical_values) < 5:
        return 0  # not enough data
    mean = statistics.mean(historical_values)
    stdev = statistics.stdev(historical_values)
    if stdev == 0:
        return 0
    return (recent_value - mean) / stdev

def check_cost_anomaly(current_hour_cost: float, historical_hourly_costs: list[float]) -> str | None:
    z = compute_z_score(current_hour_cost, historical_hourly_costs)
    if z > 3.0:  # more than 3 standard deviations above mean
        mean = statistics.mean(historical_hourly_costs)
        return f'Cost anomaly: ${current_hour_cost:.2f} this hour (normal: ${mean:.2f}, z={z:.1f})'
    return None

운영 대시보드 구축

알림이 사후 대응 계층이라면 실시간 운영 대시보드는 사전 예방 계층입니다. 현재 p50/p99 지연 시간, 분당 요청 수, 현재 품질 점수(이동 EWMA), 현재까지의 일일 비용과 예산의 비교, 지난 1시간 동안 비용이 가장 많이 발생한 요청 5개 등 실시간 지표를 보여 주는 간단한 대시보드를 구축하세요. 이를 통해 알림이 발생하기를 기다리지 않고도 팀이 시스템 상태를 한눈에 파악할 수 있습니다.

빠른 확인

이 강의에서 배운 LLM 애플리케이션 지표 알림에 대한 이해도를 확인해 보세요.

강의 요약

이 강의에서는 다음을 배웠습니다. 세 가지 경고 범주가 LLM 시스템을 포괄하며, 지연 시간·비용·품질 각각에 서로 다른 계측이 필요합니다. 또한 개별 요청의 임계값 확인보다 이동 평균과 EWMA가 점진적인 품질 저하를 더 잘 감지하며, 경고 디바운싱은 경고를 발생시키기 전에 여러 평가 주기 동안 조건이 지속되도록 요구하여 경고 피로를 방지합니다. 다음으로 프롬프트 인젝션 공격과 AI 보안을 살펴보겠습니다.

무료로 시작

AI 튜터와 함께 Python을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
30
레슨
120

자주 묻는 질문

“지연 시간, 비용, 품질 저하 알림” 강의는 무료인가요?

네 — “지연 시간, 비용, 품질 저하 알림” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“지연 시간, 비용, 품질 저하 알림”에서 뭘 배우나요?

p99 지연 시간, 요청당 비용, 자동화된 품질 점수에 대한 알림 임계값을 정의하고, LLM 파이프라인의 품질이 저하되면 Slack 또는 PagerDuty로 알림을 전달합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“지연 시간, 비용, 품질 저하 알림” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. LLM 애플리케이션을 디버깅하기 어려운 이유
  2. LangSmith를 활용한 추적
  3. 모델에 구애받지 않는 관측성을 위한 Langfuse
  4. 지연 시간, 비용, 품질 저하 알림
← AI Engineering Academy(으)로 돌아가기