Kostenoptimierung für LLM-Systeme: Wo das Geld tatsächlich fließt
Verwende Tokens dort, wo es wirklich zählt.
Die Kosten für LLMs steigen linear mit der Nutzung. Ein System, das täglich 10.000 Anfragen mit $0,01 pro Anfrage verarbeitet, kostet täglich $100 — also $365 pro Jahr. Im Unternehmensmaßstab belaufen sich die Kosten auf über $10.000.
Kostenoptimierung bedeutet nicht, an Ecken und Kanten zu sparen. Es geht darum, 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 unterschiedliche Kontextmengen:
task_budgets:
classify:
max_tokens: 100
model: qwen3-1.7b
summarize:
max_tokens: 500
model: qwen3-8b
code_review:
max_tokens: 2000
model: qwen2.5-coder-7b
reason:
max_tokens: 4000
model: qwen3-32b
Strategie 3: Adaptive Budgets
Adaptive Budgets passen sich an das tatsächliche Geschehen an. Wenn Klassifizierungsaufgaben konsistent 80 Tokens verbrauchen, hören Sie auf, 100 zuzuweisen:
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 aktuelle Nutzung stärker gewichtet wird als die Historie. 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-Raten 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 |
| qwen3-32b | $0,30 / $1,20 | ~$0,20 | ~2 Stunden/Tag |
| qwen3-8b | $0,10 / $0,40 | ~$0,05 | ~1 Stunde/Tag |
Die Hardware-Rechnung:
| Hardware | Vorabkosten | Monatlicher Stromverbrauch | 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 die lokale Inferenz. Bei hoher Nutzung sind die Einsparungen erheblich. Der Haken ist das Vorabkapital. 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, greifen Sie auf etwas Günstigeres zurück. Der Schlüssel liegt darin zu wissen, wann die Qualität „gut genug“ ist.
Strategie 1: Qualität basierter Fallback
Qualitätsbasierter Fallback versucht Modelle, bis die Ausgabe eine Schwelle 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": "qwen3-32b", "cost": 0.001},
{"model": "qwen3-8b", "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 Evaluation selbst. Wie messen Sie die Qualität, ohne ein weiteres Modell aufzurufen? Einige Systeme nutzen einen kleinen Klassifizierer. Andere verwenden heuristische Prüfungen — Länge, Struktur, Vorhandensein von Schlüsselwörtern. Keines davon ist perfekt.
Strategie 2: Latenz basierter Fallback
Latenzbasierter Fallback ist einfacher. Leiten Sie zum schnellsten Modell weiter, das Ihrem Zeitbudget entspricht:
class LatencyFallback:
def __init__(self, max_latency: float = 5.0):
self.max_latency = max_latency
self.models = [
{"model": "qwen3-1.7b", "latency": 0.5},
{"model": "qwen3-8b", "latency": 2.0},
{"model": "qwen3-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 Kostenoptimierung. Identische Prompts treten häufiger auf, als man denkt — Klassifizierungsanfragen, FAQ-artige 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 erkennt Prompts, 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
Die Schwelle ist wichtig. 0,95 ist aggressiv — nur sehr ähnliche Prompts werden übereinstimmend erkannt. 0,85 ist nachsichtiger, birgt aber das Risiko, falsche Antworten zurückzugeben. Messen Sie Ihre Fehlerrate und passen Sie an.
Auch das Caching von Antworten bei häufigen Abfragen lohnt sich. Wenn Nutzer immer wieder 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 nicht ausgefeilt, aber es funktioniert. Häufige Abfragen sind aus einem Grund häufig.
Wann Optimierung hilft
Optimierung ist relevant, wenn Sie große Volumina verarbeiten, gemischte Arbeitslasten ausführen oder API-Kosten zahlen, die sich summieren.
Sie ist irrelevant, wenn Sie prototypen, ein einzelnes Modell verwenden oder niedrige Volumina verarbeiten. Die Komplexität von Budgetierung, Fallback und Caching lohnt sich nicht für ein System, das täglich 100 Anfragen stellt.
Lassen Sie zuerst den grundlegenden Workflow funktionieren. Fügen Sie Optimierung hinzu, wenn die Rechnung kommt.
Zielkonflikte
| Strategie | Kosten | Qualität | Komplexität |
|---|---|---|---|
| Keine Optimierung | Höchste | Konsistent | Niedrigste |
| Token-Budgetierung | Moderat | Variabel | Mittel |
| Fallback-Modelle | Niedrig-Mittel | Variabel | Mittel |
| Caching | Niedrigste | Hoch (bei Cache-Treffern) | Mittel |
| Hybrid | Optimiert | Optimiert | Höchste |
Produktsysteme laufen normalerweise hybrid. Budgetieren Sie pro Sitzung, nutzen Sie Fallbacks basierend auf Qualität oder Latenz und cachen Sie, was Sie können. Die Komplexität ist real, aber auch die Einsparungen.
Verwandte Themen
- Modell-Routing-Strategien — routing basierend auf Fähigkeiten, Kosten und Latenz
- LLM-Wachstumsbegrenzungen in der Praxis — Eingabevalidierung, Ausgabe-Filterung, Sicherheit
- Design von Multi-Modell-Systemen — Architektur für mehrere Modelle
- LLM-Architektur — Säule des Systemdesigns: Routing, Kosten, Wachstumsbegrenzungen und Orchestrierung