Batching, Model Routing und Kosten-Dashboards
Leiten Sie einfache Anfragen an günstigere Modelle wie GPT-4o-mini und komplexe Anfragen an GPT-4o weiter, bündeln Sie nicht dringende Anfragen und erstellen Sie ein Kosten-Dashboard, das die Ausgaben nach Funktion erfasst.
Batching, Model Routing und Kosten-Dashboards ist eine kostenlose AI Engineering Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Engineering Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.
Drei weitere Stellschrauben zur Kostenoptimierung
Nach dem Caching können drei weitere Strategien die Betriebskosten von LLMs deutlich senken: Batch-Verarbeitung (nicht dringende Anfragen zurückstellen und gesammelt zu einem niedrigeren API-Tarif übermitteln), Model-Routing (einfache Anfragen an günstige Modelle und komplexe Anfragen an leistungsfähige Modelle weiterleiten) und Kosten-Dashboards (Ausgaben pro Funktion erfassen, um Bereiche mit dem höchsten ROI für Optimierungen zu identifizieren). Zusammen können diese Maßnahmen die Kosten zusätzlich zum Caching um weitere 40–60 Prozent senken.
OpenAI Batch API: 50 % Rabatt für asynchrone Workloads
Die Batch API von OpenAI akzeptiert eine JSONL-Datei mit bis zu 50.000 Anfragen und verarbeitet diese innerhalb von 24 Stunden asynchron zum halben Standardpreis. Sie eignet sich ideal für nicht interaktive Workloads: das Erstellen von Embeddings für große Dokumentkorpora, das Generieren von Produktbeschreibungen, das Ausführen nächtlicher Evaluierungen oder das Vorverarbeiten von Trainingsdaten. Der Nachteil ist die Latenz: Die Ergebnisse stehen erst Stunden später und nicht sofort zur Verfügung.
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}')Batch-Ergebnisse abfragen
Fragen Sie nach dem Absenden eines Batches dessen Status ab, bis die Verarbeitung abgeschlossen ist (der Status wechselt von in_progress zu completed). Laden Sie anschließend die Ausgabedatei mit den Ergebnissen aller Anfragen herunter. Jede Ausgabezeile ist ein JSON-Objekt mit der custom_id aus der Anfrage und entweder einem Feld response oder error – behandeln Sie immer beide Fälle, da einzelne Anfragen innerhalb eines Batches unabhängig voneinander fehlschlagen können.
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])Model-Routing: Komplexität und Modellgröße aufeinander abstimmen
Model-Routing weist jede Anfrage dem günstigsten Modell zu, das sie zuverlässig bearbeiten kann. GPT-4o-mini kostet etwa 30-mal weniger als GPT-4o, bewältigt einfache Klassifizierungs-, Extraktions- und kurze Frage-Antwort-Aufgaben jedoch ebenso gut. Leiten Sie einfache, strukturierte Aufgaben an kleine, günstige Modelle weiter und komplexes Schlussfolgern, Synthese langer Kontexte und anspruchsvolle Generierung an große, leistungsfähige Modelle. Wenn Sie 60 Prozent des Datenverkehrs an ein günstiges Modell weiterleiten, können Sie bereits erhebliche Kosten einsparen.
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.contentLLM-basiertes Routing für höhere Genauigkeit
Heuristisches Routing ist schnell, aber unzuverlässig. Ein genauerer Ansatz verwendet ein kleines, günstiges Klassifizierungsmodell, das entscheidet, an welches Modell eine Anfrage weitergeleitet wird. Stimmen Sie ein kleines Modell anhand von Beispielen für einfache und komplexe Anfragen aus Ihrer Domäne fein ab oder verwenden Sie Few-Shot-Prompting mit GPT-4o-mini selbst. Der Klassifizierungsaufruf kostet nur wenige hundert Eingabetokens – deutlich weniger, als wenn eine komplexe Anfrage fälschlicherweise an ein günstiges Modell weitergeleitet wird, das eine falsche Antwort erzeugt.
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_MODELKosten pro Funktion erfassen
Um zu wissen, worauf Sie Ihre Optimierungsarbeit konzentrieren sollten, müssen Sie die Kosten pro Anwendungsfunktion und nicht nur die Gesamtausgaben erfassen. Versehen Sie jeden LLM-Aufruf mit einem Funktions-Tag und summieren Sie die Tokenskosten pro Tag. „search_summarization“ könnte 40 Prozent Ihres Budgets verbrauchen, obwohl diese Funktion nur 5 Prozent des Datenverkehrs ausmacht, und wäre damit ein vorrangiges Optimierungsziel. „user_onboarding“ könnte zwar teuer sein, aber einen wichtigen Ablauf bedienen, dessen Qualität Sie nicht beeinträchtigen möchten.
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}')Ein einfaches Kosten-Dashboard erstellen
Ein praktisches Kosten-Dashboard fasst Ausgabendaten auf Funktionsebene zusammen und stellt sie über einen einfachen HTTP-Endpunkt bereit. Speichern Sie kumulierte Kosten in Redis mit täglichen Rollup-Schlüsseln, damit Sie die Ausgaben im Zeitverlauf verfolgen können. Integrieren Sie dieses Dashboard in Ihre internen Entwickler-Tools, damit das Team die Kostenauswirkungen von Funktions-Releases nahezu in Echtzeit sehen und ausufernde Ausgaben erkennen kann, bevor daraus eine hohe Rechnung entsteht.
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())}Monatliche Budgetwarnungen
Richten Sie monatliche Budgetwarnungen ein, um unerwartete Kostenspitzen zu erkennen, bevor daraus hohe Rechnungen entstehen. Berechnen Sie anhand Ihrer Kostenerfassung die rollierenden täglichen Ausgaben, prognostizieren Sie diese bis zum Monatsende und senden Sie eine Slack-Warnung, wenn die Prognose Ihren Budgetgrenzwert überschreitet. Eine einfache Prognose – daily_spend * days_remaining – erkennt ausufernde Anfragen frühzeitig, selbst wenn die tatsächlichen Muster nichtlinear sind.
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})Anfragewarteschlange zur Verwaltung von Ratenbegrenzungen
Bei Verkehrsspitzen stoßen Anfragen an die Ratenbegrenzungen von OpenAI und schlagen mit 429 Too Many Requests fehl. Eine Anfragewarteschlange puffert eingehende Anfragen und übermittelt sie mit kontrollierter Rate, wodurch Verkehrsspitzen geglättet werden. Verwenden Sie in der Produktion eine asynchrone Warteschlange auf Basis von Redis oder eines Message Brokers wie RabbitMQ und implementieren Sie für vorübergehende 429-Fehler eine Retry-Logik mit exponentiellem Backoff.
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 futureAlles zusammenführen: Stack zur Kostenoptimierung
Ein vollständiger Stack zur LLM-Kostenoptimierung arbeitet in mehreren Ebenen: Ein exakter Cache eliminiert Aufrufe für wiederholt identische Anfragen, ein semantischer Cache eliminiert Aufrufe für ähnliche Anfragen, Prefix-Caching senkt die Eingabekosten aller verbleibenden Aufrufe, Model-Routing verwendet günstige Modelle für einfache Anfragen, Batch-Verarbeitung verschiebt nicht dringende Aufgaben und bietet 50 Prozent Rabatt, und Dashboards und Warnungen machen Kosten sichtbar und halten sie unter Kontrolle. Implementieren Sie diese Maßnahmen schrittweise und entsprechend ihrer Wirkung für Ihre konkrete Anwendung.
# 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 visibleKaskadierendes Fallback bei Fehlern eines günstigen Modells
Wenn Sie Anfragen an ein günstiges Modell weiterleiten, müssen Sie Fälle behandeln, in denen es eine unzureichende Antwort erzeugt. Implementieren Sie eine Qualitätsprüfung für die Ausgabe des günstigen Modells – prüfen Sie beispielsweise die Antwortlänge und das Vorhandensein erforderlicher Felder oder lassen Sie eine schnelle LLM-as-judge-Bewertung durchführen – und wechseln Sie automatisch zum leistungsfähigen Modell, wenn die Qualität nicht ausreicht. Dieses Sicherheitsnetz ermöglicht ein konsequentes Routing zu günstigen Modellen, ohne eine schlechtere Benutzererfahrung zu riskieren.
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 answerKurztest
Testen Sie Ihr Verständnis von Batch-Verarbeitung, Model-Routing und Kosten-Dashboards aus dieser Lektion.
Zusammenfassung der Lektion
In dieser Lektion haben Sie gelernt: Die OpenAI Batch API bietet 50 Prozent Rabatt für asynchrone Workloads außerhalb der Echtzeit, Model-Routing verwendet günstige Modelle wie GPT-4o-mini für einfache Aufgaben und teure Modelle für komplexe Aufgaben, und die Kostenerfassung pro Funktion zeigt, welche Teile Ihrer Anwendung den größten Budgetanteil verbrauchen, sodass Sie Optimierungen wirksam priorisieren können. Zusammen mit den Caching-Strategien aus den vorherigen Lektionen können diese Techniken die Infrastrukturkosten für LLMs um 60–80 Prozent senken. Sie haben nun den Kurs zu LLM-Caching und Kostenoptimierung abgeschlossen.
Häufig gestellte Fragen
Ist die Lektion „Batching, Model Routing und Kosten-Dashboards“ kostenlos?
Ja — der vollständige Text von „Batching, Model Routing und Kosten-Dashboards“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Engineering Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Batching, Model Routing und Kosten-Dashboards“?
Leiten Sie einfache Anfragen an günstigere Modelle wie GPT-4o-mini und komplexe Anfragen an GPT-4o weiter, bündeln Sie nicht dringende Anfragen und erstellen Sie ein Kosten-Dashboard, das die Ausgabe… Du übst AI Engineering Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um AI Engineering Academy zu starten?
Keine Vorkenntnisse erforderlich. AI Engineering Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Batching, Model Routing und Kosten-Dashboards“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser AI Engineering Academy-Lektion Code schreiben und ausführen?
Ja. Jede AI Engineering Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Exaktes Caching mit Redis
- Semantisches Caching mit Embeddings
- Prompt-Prefix-Caching von OpenAI
- Batching, Model Routing und Kosten-Dashboards