Kostenoptimierung für LLM-Systeme: Wohin das Geld wirklich fließt
Verwenden Sie Token dort, wo sie wirklich wichtig sind.
Die Kosten für LLMs steigen linear mit der Nutzung an. Ein System, das täglich 10.000 Anfragen mit jeweils $0,01 pro Anfrage verarbeitet, kostet täglich $100 — das sind $365 im Jahr. Im Unternehmensmaßstab belaufen sich die Kosten auf über $10.000.
Kostenoptimierung bedeutet nicht, an falschen Stellen zu sparen. Sie bedeutet, Tokens dort einzusetzen, wo sie wirklich zählen.
Jeder Token, den Sie verschwenden, ist ein Token, den Sie für eine bessere Antwort hätten ausgeben können.

Token-Budgetierung
Der einfachste Weg, Kosten zu kontrollieren, ist das Setzen von Limits. Pro Sitzung, pro Aufgabe oder pro Tag.
Strategie 1: Budgets pro Sitzung
Budgets pro Sitzung sind unkompliziert:
class SessionBudget:
def __init__(self, budget_tokens: int = 10000):
self.budget = budget_tokens
self.used = 0
def allocate(self, tokens: int) -> bool:
if self.used + tokens <= self.budget:
self.used += tokens
return True
return False
def remaining(self) -> int:
return self.budget - self.used
Strategie 2: Budgets pro Aufgabe
Budgets pro Aufgabe sind praktischer. Verschiedene Aufgaben benötigen unterschiedlich viel Kontext:
task_budgets:
classify:
max_tokens: 100
model: qwen2.5-1.5b
summarize:
max_tokens: 500
model: qwen2.5-7b
code_review:
max_tokens: 2000
model: qwen2.5-coder-7b
reason:
max_tokens: 4000
model: qwen2.5-32b
Strategie 3: Adaptive Budgets
Adaptive Budgets passen sich an die tatsächliche Nutzung an. Wenn Klassifizierungsaufgaben konsistent 80 Tokens verbrauchen, sollten Sie nicht länger 100 Tokens zuweisen:
class AdaptiveBudget:
def __init__(self):
self.task_history = {}
def allocate(self, task_type: str) -> int:
if task_type in self.task_history:
return int(self.task_history[task_type] * 1.5)
return 1000
def record(self, task_type: str, tokens_used: int):
if task_type not in self.task_history:
self.task_history[task_type] = tokens_used
else:
self.task_history[task_type] = (
0.9 * self.task_history[task_type] + 0.1 * tokens_used
)
Der exponentielle gleitende Durchschnitt (mit einem Gewicht von 0,9) bedeutet, dass die jüngste Nutzung stärker gewichtet wird als die Vergangenheit. Passen Sie das Gewicht an die Volatilität Ihrer Arbeitslasten an.
API vs. lokale Inferenz
Lokale Inferenz ist im großen Maßstab günstiger. Der Break-even-Punkt hängt von Ihrer Hardware und den API-Tarifen ab.
| Modell | API ($/M Tokens) | Lokale Kosten/Stunde | Break-even |
|---|---|---|---|
| GPT-4o | $2,50 / $10,00 | — | N/A |
| Claude Sonnet 4 | $3,00 / $15,00 | — | N/A |
| Qwen2.5-72B | $0,50 / $2,00 | ~$0,50 | ~4 Stunden/Tag |
| Qwen2.5-32B | $0,30 / $1,20 | ~$0,20 | ~2 Stunden/Tag |
| Qwen2.5-7B | $0,10 / $0,40 | ~$0,05 | ~1 Stunde/Tag |
Die Hardware-Rechnung:
| Hardware | Vorabkosten | Monatlicher Strom | Break-even vs. API |
|---|---|---|---|
| RTX 3090 (gebraucht) | $600 | $15 | ~4 Monate |
| RTX 4090 | $1.500 | $20 | ~6 Monate |
| RTX 5080 | $1.000 | $18 | ~5 Monate |
| DGX Spark | $2.000 | $30 | ~8 Monate |
Bei moderater Nutzung — eine Stunde oder mehr pro Tag — amortisiert sich lokale Inferenz. Bei hoher Nutzung sind die Einsparungen dramatisch. Der Haken sind die initialen Kapitalkosten. Eine RTX 5080 kostet $1.000. Eine API-Rechnung können Sie pausieren. Hardware nicht.
Fallback-Strategien
Wenn Ihr bevorzugtes Modell zu teuer oder zu langsam ist, wechseln Sie auf eine günstigere Alternative. Der Schlüssel liegt darin zu wissen, wann die Qualität „gut genug“ ist.
Strategie 1: Qualitätsbasierter Fallback
Qualitätsbasierter Fallback versucht verschiedene Modelle, bis die Ausgabe einen bestimmten Schwellenwert erreicht:
class QualityFallback:
def __init__(self, quality_threshold: float = 0.8):
self.threshold = quality_threshold
self.models = [
{"model": "claude-sonnet-4", "cost": 0.015},
{"model": "qwen2.5-72b", "cost": 0.002},
{"model": "qwen2.5-32b", "cost": 0.001},
{"model": "qwen2.5-7b", "cost": 0.0004},
]
def route(self, prompt: str) -> str:
for model_config in self.models:
result = self.call_model(model_config["model"], prompt)
if self.evaluate_quality(result) >= self.threshold:
return result
return self.call_model(self.models[0]["model"], prompt)
Das Problem ist die Bewertung selbst. Wie messen Sie die Qualität, ohne ein weiteres Modell aufzurufen? Einige Systeme verwenden einen kleinen Klassifizierer. Andere nutzen heuristische Prüfungen — Länge, Struktur, Vorhandensein von Schlüsselwörtern. Keine dieser Methoden ist perfekt.
Strategie 2: Latenzbasierter Fallback
Latenzbasierter Fallback ist einfacher. Leiten Sie Anfragen an das schnellste Modell weiter, das Ihr Zeitbudget erfüllt:
class LatencyFallback:
def __init__(self, max_latency: float = 5.0):
self.max_latency = max_latency
self.models = [
{"model": "qwen2.5-1.5b", "latency": 0.5},
{"model": "qwen2.5-7b", "latency": 2.0},
{"model": "qwen2.5-32b", "latency": 10.0},
{"model": "claude-sonnet-4", "latency": 5.0},
]
def route(self, prompt: str) -> str:
for model_config in sorted(self.models, key=lambda x: x["latency"]):
if model_config["latency"] <= self.max_latency:
return self.call_model(model_config["model"], prompt)
return self.call_model(self.models[0]["model"], prompt)
Caching
Caching ist die am wenigsten geschätzte Form der Kostenoptimierung. Identische Prompts treten häufiger auf, als man denkt — Klassifizierungsanfragen, FAQ-ähnliche Abfragen, wiederholte Tool-Aufrufe.
Strategie 1: Prompt-Caching
Exaktes Prompt-Caching ist einfach:
import hashlib
class PromptCache:
def __init__(self, max_size: int = 1000):
self.cache = {}
self.max_size = max_size
def get(self, prompt: str) -> str | None:
key = hashlib.sha256(prompt.encode()).hexdigest()
return self.cache.get(key)
def set(self, prompt: str, response: str):
key = hashlib.sha256(prompt.encode()).hexdigest()
if len(self.cache) >= self.max_size:
self.cache.pop(next(iter(self.cache)))
self.cache[key] = response
Strategie 2: Semantisches Caching
Semantisches Caching ist nützlicher. Es fängt Prompts ein, die unterschiedlich sind, aber dasselbe bedeuten:
from sentence_transformers import SentenceTransformer
class SemanticCache:
def __init__(self, similarity_threshold: float = 0.95):
self.model = SentenceTransformer('all-MiniLM-L6-v2')
self.cache = {}
self.threshold = similarity_threshold
def get(self, prompt: str) -> str | None:
prompt_embedding = self.model.encode([prompt])[0]
for cached_prompt, cached_response in self.cache.items():
cached_embedding = self.model.encode([cached_prompt])[0]
similarity = self.cosine_similarity(
prompt_embedding, cached_embedding
)
if similarity >= self.threshold:
return cached_response
return None
def set(self, prompt: str, response: str):
self.cache[prompt] = response
Der Schwellenwert ist entscheidend. 0,95 ist aggressiv — nur sehr ähnliche Prompts werden gematcht. 0,85 ist nachsichtiger, birgt aber das Risiko, falsche Antworten zurückzugeben. Messen Sie Ihre Verfehlungsrate (Miss Rate) und passen Sie den Wert an.
Auch das Caching von Antworten bei häufigen Abfragen lohnt sich. Wenn Nutzer wiederholt fragen „Wie ist das Wetter?“ oder „Wie spät ist es?“, cachen Sie das Muster, nicht nur den exakten Prompt:
class ResponseCache:
def __init__(self):
self.common_queries = {
"what is the weather": "Check weather API",
"what is the time": "Check system time",
"who is the president": "Check current president",
}
def get(self, query: str) -> str | None:
query_lower = query.lower()
for common_query, response in self.common_queries.items():
if common_query in query_lower:
return response
return None
Das ist zwar nicht hochkomplex, aber es funktioniert. Häufige Abfragen sind aus einem bestimmten Grund häufig.
Wann Optimierung hilft
Optimierung ist relevant, wenn Sie große Volumina verarbeiten, gemischte Arbeitslasten ausführen oder API-Kosten haben, die sich summieren.
Sie ist irrelevant, wenn Sie prototypieren, ein einzelnes Modell verwenden oder kleine Volumina verarbeiten. Die Komplexität von Budgetierung, Fallback und Caching lohnt sich für ein System, das nur 100 Anfragen pro Tag stellt, nicht.
Stellen Sie zunächst sicher, dass der grundlegende Ablauf funktioniert. Fügen Sie Optimierung hinzu, wenn die Rechnung kommt.
Trade-offs
| Strategie | Kosten | Qualität | Komplexität |
|---|---|---|---|
| Keine Optimierung | Höchst | Konsistent | Niedrigst |
| Token-Budgetierung | Moderat | Variabel | Mittel |
| Fallback-Modelle | Niedrig-Mittel | Variabel | Mittel |
| Caching | Niedrigst | Hoch (bei Cache-Treffer) | Mittel |
| Hybrid | Optimal | Optimal | Höchst |
Produktionssysteme laufen meist hybrid. Budgetieren Sie pro Sitzung, nutzen Sie Fallback basierend auf Qualität oder Latenz und cachen Sie, was möglich ist. Die Komplexität ist real, aber auch die Einsparungen.
Verwandte Themen
- Model Routing Strategies — routing nach Fähigkeiten, Kosten und Latenz
- LLM Guardrails in Practice — Eingabevalidierung, Ausgabe-Filterung, Sicherheit
- Multi-Model System Design — Architektur für mehrere Modelle
- LLM Architecture — Systemdesign-Säule: Routing, Kosten, Guardrails und Orchestrierung