0Pricing
AI Engineering Academy · 강의

일괄 처리, 모델 라우팅, 비용 대시보드

간단한 요청은 GPT-4o-mini 같은 저렴한 모델로, 복잡한 요청은 GPT-4o로 라우팅하고, 긴급하지 않은 요청은 일괄 처리하며, 기능별 지출을 추적하는 비용 대시보드를 구축합니다.

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

비용 최적화를 위한 세 가지 추가 수단

캐싱 외에도 세 가지 전략이 LLM 운영 비용을 크게 줄입니다. 일괄 처리(긴급하지 않은 요청을 미루고 더 낮은 API 요금으로 한꺼번에 제출), 모델 라우팅(간단한 쿼리는 저렴한 모델로, 복잡한 쿼리는 강력한 모델로 전달), 비용 대시보드(기능별 지출을 추적하여 최적화 효과가 가장 큰 영역을 파악)입니다. 이들을 함께 사용하면 캐싱만 적용했을 때보다 비용을 40~60퍼센트 더 줄일 수 있습니다.

OpenAI Batch API: 비동기 작업 50% 할인

OpenAI의 Batch API는 최대 50,000개의 요청이 포함된 JSONL 파일을 받아 24시간 이내에 비동기적으로 처리하며, 요금은 표준 가격의 50퍼센트입니다. 대규모 문서 모음 임베딩, 제품 설명 생성, 야간 평가 실행, 학습 데이터 전처리와 같은 비대화형 작업에 적합합니다. 단점은 지연 시간입니다. 결과를 즉시 받을 수 없고 몇 시간 후에 확인할 수 있습니다.

import json
from openai import OpenAI

client = OpenAI()

# Prepare batch file
requests = [
    {
        'custom_id': f'req_{i}',
        'method': 'POST',
        'url': '/v1/chat/completions',
        'body': {
            'model': 'gpt-4o-mini',
            'messages': [
                {'role': 'user', 'content': f'Summarize: {document}'}
            ],
            'max_tokens': 150,
        }
    }
    for i, document in enumerate(documents_to_process)
]

# Write JSONL batch file
with open('/tmp/batch_requests.jsonl', 'w') as f:
    for req in requests:
        f.write(json.dumps(req) + '\n')

# Upload and submit batch
with open('/tmp/batch_requests.jsonl', 'rb') as f:
    batch_file = client.files.create(file=f, purpose='batch')

batch = client.batches.create(
    input_file_id=batch_file.id,
    endpoint='/v1/chat/completions',
    completion_window='24h',
)
print(f'Batch {batch.id} submitted, status: {batch.status}')

일괄 처리 결과 폴링

일괄 처리를 제출한 후 완료될 때까지 상태를 폴링하십시오(상태가 in_progress에서 completed로 변경됩니다). 완료되면 모든 요청의 결과가 포함된 출력 파일을 다운로드합니다. 각 출력 줄은 요청의 custom_id와 response 또는 error 필드 중 하나를 포함하는 JSON 객체입니다. 일괄 처리 내 개별 요청은 서로 독립적으로 실패할 수 있으므로 항상 두 경우를 모두 처리하십시오.

import time

def wait_for_batch(batch_id: str, poll_interval: int = 60) -> str:
    while True:
        batch = client.batches.retrieve(batch_id)
        print(f'Status: {batch.status}, completed: {batch.request_counts.completed}')

        if batch.status == 'completed':
            return batch.output_file_id
        elif batch.status == 'failed':
            raise RuntimeError(f'Batch failed: {batch.errors}')

        time.sleep(poll_interval)

def download_batch_results(output_file_id: str) -> list[dict]:
    content = client.files.content(output_file_id)
    results = []
    for line in content.text.strip().split('\n'):
        results.append(json.loads(line))
    return results

output_file_id = wait_for_batch(batch.id)
results = download_batch_results(output_file_id)
for result in results[:3]:
    print(result['custom_id'], result.get('response', {}).get('body', {}).get('choices', [{}])[0])

모델 라우팅: 복잡도에 맞는 모델 크기 선택

모델 라우팅은 각 요청을 제대로 처리할 수 있는 모델 중 가장 저렴한 모델에 요청을 할당합니다. GPT-4o-mini는 GPT-4o보다 비용이 약 30배 저렴하지만, 간단한 분류, 추출, 짧은 질의응답 작업은 똑같이 잘 처리합니다. 간단하고 구조화된 작업은 작고 저렴한 모델로 라우팅하고, 복잡한 추론, 긴 문맥 종합, 미묘한 차이를 반영해야 하는 생성 작업은 크고 강력한 모델로 라우팅하십시오. 트래픽의 60%만 저렴한 모델로 라우팅해도 비용을 크게 절감할 수 있습니다.

CHEAP_MODEL = 'gpt-4o-mini'
POWERFUL_MODEL = 'gpt-4o'

def classify_query_complexity(query: str) -> str:
    # Heuristic-based routing (replace with ML classifier for production)
    words = query.split()
    has_code = any(c in query for c in ['```', 'def ', 'class ', 'SELECT ', 'function '])
    is_multi_step = any(w in query.lower() for w in ['compare', 'analyze', 'explain why', 'evaluate'])
    is_long = len(words) > 50

    if has_code or is_multi_step or is_long:
        return POWERFUL_MODEL
    return CHEAP_MODEL

def routed_completion(messages: list[dict]) -> str:
    user_query = messages[-1].get('content', '')
    model = classify_query_complexity(user_query)
    print(f'Routing to: {model}')
    response = client.chat.completions.create(model=model, messages=messages)
    return response.choices[0].message.content

더 높은 정확도를 위한 LLM 기반 라우팅

휴리스틱 라우팅은 빠르지만 견고하지 않습니다. 더 정확한 방법은 작고 저렴한 분류 모델을 사용해 어떤 모델로 라우팅할지 결정하는 것입니다. 사용자의 분야에서 간단한 질의와 복잡한 질의의 예시를 사용해 소형 모델을 미세 조정하거나, GPT-4o-mini 자체를 사용해 소수 예시 프롬프트를 적용할 수 있습니다. 분류기 호출에는 입력 토큰이 수백 개 정도만 사용되므로, 복잡한 질의를 저렴한 모델로 잘못 라우팅해 오답을 생성하는 것보다 비용이 훨씬 적게 듭니다.

CLASSIFIER_SYSTEM = '''You are a query complexity classifier.
Classify the user query as SIMPLE or COMPLEX.
SIMPLE: factual lookup, extraction, classification with clear answer.
COMPLEX: multi-step reasoning, synthesis, comparison, code generation, long-form writing.
Reply with just SIMPLE or COMPLEX.'''

def llm_classify_complexity(query: str) -> str:
    response = client.chat.completions.create(
        model='gpt-4o-mini',  # use cheap model for routing
        messages=[
            {'role': 'system', 'content': CLASSIFIER_SYSTEM},
            {'role': 'user', 'content': query},
        ],
        max_tokens=10,
        temperature=0,
    )
    label = response.choices[0].message.content.strip()
    return POWERFUL_MODEL if label == 'COMPLEX' else CHEAP_MODEL

기능별 비용 추적

최적화 작업을 어디에 집중할지 파악하려면 총 지출뿐 아니라 애플리케이션 기능별 비용을 추적해야 합니다. 모든 LLM 호출에 기능 태그를 붙이고 태그별 토큰 비용을 누적하십시오. '검색_요약' 기능은 트래픽의 5%만 처리하면서 예산의 40%를 사용할 수 있으므로 우선순위가 높은 최적화 대상이 됩니다. '사용자_온보딩' 기능은 비용이 많이 들더라도 가치가 높은 흐름을 제공하므로 품질을 낮추고 싶지 않을 수 있습니다.

from collections import defaultdict

cost_tracker = defaultdict(lambda: {'prompt_tokens': 0, 'completion_tokens': 0, 'cost_usd': 0.0})

MODEL_PRICING = {
    'gpt-4o-mini': {'input': 0.15 / 1e6, 'output': 0.60 / 1e6},
    'gpt-4o':      {'input': 2.50 / 1e6, 'output': 10.00 / 1e6},
}

def tracked_completion(feature: str, messages: list[dict], model: str = 'gpt-4o-mini') -> str:
    response = client.chat.completions.create(model=model, messages=messages)
    usage = response.usage
    pricing = MODEL_PRICING.get(model, {'input': 0, 'output': 0})
    cost = usage.prompt_tokens * pricing['input'] + usage.completion_tokens * pricing['output']

    cost_tracker[feature]['prompt_tokens'] += usage.prompt_tokens
    cost_tracker[feature]['completion_tokens'] += usage.completion_tokens
    cost_tracker[feature]['cost_usd'] += cost

    return response.choices[0].message.content

def print_cost_report():
    print(f'{"Feature":<30} {"Prompt":<10} {"Completion":<12} {"Cost USD":<12}')
    for feature, stats in sorted(cost_tracker.items(), key=lambda x: -x[1]['cost_usd']):
        print(f'{feature:<30} {stats["prompt_tokens"]:<10} {stats["completion_tokens"]:<12} ${stats["cost_usd"]:.4f}')

간단한 비용 대시보드 구축

실용적인 비용 대시보드는 기능 수준의 지출 데이터를 집계하고 간단한 HTTP 엔드포인트를 통해 제공합니다. Redis에 일별 집계 키를 사용해 누적 비용을 저장하면 시간에 따른 지출 추이를 확인할 수 있습니다. 이 대시보드를 내부 개발자 도구에 추가하면 팀에서 기능 출시가 비용에 미치는 영향을 거의 실시간으로 확인하고, 지출이 급증해 큰 청구서로 이어지기 전에 파악할 수 있습니다.

from fastapi import FastAPI
import datetime

app = FastAPI()

async def record_cost(feature: str, model: str, prompt_tokens: int, completion_tokens: int):
    pricing = MODEL_PRICING.get(model, {'input': 0, 'output': 0})
    cost = prompt_tokens * pricing['input'] + completion_tokens * pricing['output']
    today = datetime.date.today().isoformat()
    key = f'cost:{today}:{feature}:{model}'
    await async_r.incrbyfloat(key, cost)
    await async_r.expire(key, 86400 * 30)  # keep 30 days

@app.get('/dashboard/costs')
async def cost_dashboard():
    today = datetime.date.today().isoformat()
    pattern = f'cost:{today}:*'
    costs = {}
    async for key in async_r.scan_iter(match=pattern):
        value = await async_r.get(key)
        parts = key.split(':')
        feature_model = ':'.join(parts[2:])
        costs[feature_model] = float(value or 0)
    return {'date': today, 'costs': costs, 'total': sum(costs.values())}

월간 예산 알림

월간 예산 알림을 설정하면 예상치 못한 비용 급증이 큰 청구서로 이어지기 전에 파악할 수 있습니다. 비용 추적기에서 최근 일일 지출을 계산하고 월말까지의 지출을 예측한 다음, 예측값이 예산 임계값을 초과하면 Slack 알림을 보내십시오. 간단한 예측식인 daily_spend * days_remaining은 실제 패턴이 비선형적이더라도 지출 폭증을 조기에 포착합니다.

import datetime
import httpx

SLACK_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK'
MONTHLY_BUDGET_USD = 500.0

async def check_budget_alert():
    today = datetime.date.today()
    days_in_month = 30
    day_of_month = today.day
    days_remaining = days_in_month - day_of_month

    # Sum today's costs
    today_total = sum(cost_tracker[f]['cost_usd'] for f in cost_tracker)
    avg_daily = today_total  # simplified: just today's spend
    projected_month = avg_daily * days_in_month

    if projected_month > MONTHLY_BUDGET_USD:
        message = (
            f'LLM Budget Alert: Projected monthly spend ${projected_month:.2f} '
            f'exceeds budget ${MONTHLY_BUDGET_USD:.2f}. '
            f'Today spend: ${today_total:.2f}'
        )
        async with httpx.AsyncClient() as client:
            await client.post(SLACK_WEBHOOK, json={'text': message})

속도 제한 관리를 위한 요청 대기열

트래픽이 급증하면 요청이 OpenAI의 속도 제한에 도달하고 429 Too Many Requests 오류와 함께 실패합니다. 요청 대기열은 들어오는 요청을 버퍼링한 후 제어된 속도로 제출하여 트래픽 피크를 완화합니다. 프로덕션 환경에서는 Redis나 RabbitMQ 같은 메시지 브로커를 기반으로 하는 비동기 대기열을 사용하고, 일시적인 429 오류에 대해 지수 백오프 재시도 로직을 구현하십시오.

import asyncio
from asyncio import Queue

class RateLimitedLLMClient:
    def __init__(self, requests_per_minute: int = 500):
        self.rpm = requests_per_minute
        self.queue: Queue = Queue(maxsize=1000)
        self.interval = 60.0 / requests_per_minute

    async def start(self):
        asyncio.create_task(self._worker())

    async def _worker(self):
        while True:
            request_fn, future = await self.queue.get()
            try:
                result = await request_fn()
                future.set_result(result)
            except Exception as e:
                future.set_exception(e)
            await asyncio.sleep(self.interval)

    async def submit(self, request_fn) -> str:
        loop = asyncio.get_event_loop()
        future = loop.create_future()
        await self.queue.put((request_fn, future))
        return await future

종합하기: 비용 최적화 스택

완전한 LLM 비용 최적화 스택은 여러 계층으로 작동합니다. 정확한 캐시는 완전히 동일한 질의에 대한 호출을 제거하고, 의미 기반 캐시는 유사한 질의에 대한 호출을 제거하며, 접두사 캐싱은 나머지 모든 호출의 입력 비용을 줄입니다. 모델 라우팅은 간단한 질의에 저렴한 모델을 사용하고, 일괄 처리는 긴급하지 않은 작업을 지연하여 50% 할인된 비용으로 처리하며, 대시보드와 알림은 비용을 가시화하고 한정된 범위 안에서 관리합니다. 애플리케이션에 미치는 영향이 큰 순서대로 이러한 기능을 점진적으로 구현하십시오.

# Decision framework for cost optimization priority:
#
# 1. Enable prefix caching (free, zero effort, automatic)
# 2. Add exact caching (high hit rate for FAQ/support bots)
# 3. Add model routing (simple heuristics first, ML classifier later)
# 4. Add semantic caching (complex, high ROI for paraphrase-heavy use cases)
# 5. Enable batch API (only for non-real-time pipelines)
# 6. Build cost dashboard (essential for ongoing monitoring)
#
# Typical combined result in a customer support bot:
# Before: $1,000/month
# After step 1-2: $400/month (-60%)
# After step 3-4: $200/month (-50% of remaining)
# After step 5-6: $150/month and visible

저렴한 모델 실패 시 연쇄 대체

저렴한 모델로 라우팅할 때는 만족스럽지 않은 답변을 생성하는 경우를 처리해야 합니다. 저렴한 모델의 출력에 대해 품질 검사를 구현하십시오. 응답 길이와 필수 필드의 존재 여부를 확인하거나 빠른 LLM 심판 점수를 실행한 다음, 품질이 충분하지 않으면 강력한 모델로 자동 대체하십시오. 이 안전장치를 사용하면 사용자 경험 저하의 위험 없이 저렴한 모델로 적극적으로 라우팅할 수 있습니다.

async def routing_with_fallback(messages: list[dict], min_length: int = 50) -> str:
    # Try cheap model first
    cheap_response = await async_client.chat.completions.create(
        model=CHEAP_MODEL, messages=messages, temperature=0.0
    )
    answer = cheap_response.choices[0].message.content

    # Quality check: response too short indicates poor answer
    if len(answer.strip()) < min_length:
        print(f'Cheap model answer too short ({len(answer)} chars), escalating...')
        powerful_response = await async_client.chat.completions.create(
            model=POWERFUL_MODEL, messages=messages, temperature=0.0
        )
        return powerful_response.choices[0].message.content

    return answer

빠른 확인

이 강의에서 배운 일괄 처리, 모델 라우팅, 비용 대시보드에 대한 이해도를 확인해 보십시오.

강의 요약

이 강의에서는 다음을 배웠습니다. OpenAI Batch API는 비동기 비실시간 작업에 50% 할인을 제공합니다. 모델 라우팅은 간단한 작업에 GPT-4o-mini 같은 저렴한 모델을 사용하고 복잡한 작업에는 비싼 모델을 사용합니다. 또한 기능별 비용 추적은 애플리케이션의 어느 부분이 예산을 가장 많이 사용하는지 보여 주므로 최적화 우선순위를 효과적으로 정할 수 있습니다. 이전 강의에서 배운 캐싱 전략과 이러한 기법을 함께 사용하면 LLM 인프라 비용을 60~80% 줄일 수 있습니다. 이제 LLM 캐싱 및 비용 최적화 과정을 모두 마쳤습니다.

자주 묻는 질문

“일괄 처리, 모델 라우팅, 비용 대시보드” 강의는 무료인가요?

네 — “일괄 처리, 모델 라우팅, 비용 대시보드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“일괄 처리, 모델 라우팅, 비용 대시보드”에서 뭘 배우나요?

간단한 요청은 GPT-4o-mini 같은 저렴한 모델로, 복잡한 요청은 GPT-4o로 라우팅하고, 긴급하지 않은 요청은 일괄 처리하며, 기능별 지출을 추적하는 비용 대시보드를 구축합니다. 브라우저에서 직접 실행하는 실습 코드로 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. Redis를 활용한 정확한 캐싱
  2. 임베딩을 활용한 의미 기반 캐싱
  3. OpenAI 프롬프트 접두사 캐싱
  4. 일괄 처리, 모델 라우팅, 비용 대시보드
← AI Engineering Academy(으)로 돌아가기