Das Dilemma: Performance kostet Geld. Wir wollen beides: schnell UND billig.

Problem-Szenario

Millionen API Calls/Monat, Claude API:
- Input: 1M × 500 Tokens @ $3/1M = $1.500
- Output: 1M × 200 Tokens @ $15/1M = $3.000
Total: $4.500/Monat = $54.000/Jahr

Das ist TEUER!

Aber: Du kannst 70% sparen.

Teil 1: Prompt Caching

Wie es funktioniert:

Wenn du denselben Kontext für viele Abfragen nutzt:

  • First Call: Normaler Preis
  • Nachfolgende Calls: Cache Hit = 90% günstiger!
# prompt_caching.py
from anthropic import Anthropic

client = Anthropic()

# 1000 Seiten eines PDF — laden einmal
LARGE_CONTEXT = """
[Full PDF Content hier — 1M Tokens]
...
"""

def query_with_cache(question: str) -> str:
    """Nutze Prompt Caching"""

    response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": "Du bist ein Dokumenten-Expert."
            },
            {
                "type": "text",
                "text": LARGE_CONTEXT,
                "cache_control": {"type": "ephemeral"}  # Cache diese Tokens!
            }
        ],
        messages=[
            {"role": "user", "content": question}
        ]
    )

    # Check Cache-Performance
    usage = response.usage
    print(f"Input Tokens: {usage.input_tokens}")
    print(f"Cache Read Tokens: {getattr(usage, 'cache_read_input_tokens', 0)}")
    print(f"Cache Creation Tokens: {getattr(usage, 'cache_creation_input_tokens', 0)}")

    return response.content[0].text

# First Call: ~1M Tokens
# Cost: 1M × $3/1M = $3

print("Call 1:")
answer1 = query_with_cache("Was ist auf Seite 42?")

# Second Call: Nur neue Tokens
# Cost: 500 × $3/1M + 1M × $0.30/1M = $0.33 ← 90% günstiger!

print("\nCall 2:")
answer2 = query_with_cache("Erkläre den Abschnitt über ...")

Kostenrechnung:

  • Ohne Caching: 1M × $3 × 100 Calls = $300
  • Mit Caching: $3 + (100 × $0.30) = $33
  • Einsparung: 89%!

Wann Caching?

  • ✓ Großer Kontext + viele ähnliche Abfragen
  • ✓ Dokumenten-Analysen
  • ✓ Batch-Verarbeitung
  • ✗ Einmalige Calls

Teil 2: Model Routing

Nicht alle Aufgaben brauchen das teuerste Modell!

# model_routing.py
from anthropic import Anthropic

client = Anthropic()

def classify_task(question: str) -> str:
    """Bestimme Task-Komplexität"""

    # Haiku ist billiger und schnell
    response = client.messages.create(
        model="claude-3-5-haiku-20241022",  # $0.80/$2.40 pro 1M Tokens (vs Sonnet $3/$15)
        max_tokens=100,
        messages=[
            {
                "role": "user",
                "content": f"""Klassifiziere diese Aufgabe als EINFACH oder KOMPLEX:
"{question}"

Antworte nur mit EINFACH oder KOMPLEX."""
            }
        ]
    )

    return response.content[0].text.strip()

def route_query(question: str) -> dict:
    """Nutze billiges Modell für einfache Tasks"""

    complexity = classify_task(question)

    if complexity == "EINFACH":
        # Haiku: $0.80 pro 1M Input Tokens
        model = "claude-3-5-haiku-20241022"
        max_tokens = 256
    else:
        # Sonnet: $3 pro 1M Input Tokens
        model = "claude-3-5-sonnet-20241022"
        max_tokens = 1024

    response = client.messages.create(
        model=model,
        max_tokens=max_tokens,
        messages=[
            {"role": "user", "content": question}
        ]
    )

    # Cost berechnen
    input_cost = response.usage.input_tokens * (0.80 if "haiku" in model else 3.0) / 1_000_000
    output_cost = response.usage.output_tokens * (2.40 if "haiku" in model else 15.0) / 1_000_000

    return {
        "answer": response.content[0].text,
        "model": model,
        "cost": input_cost + output_cost
    }

# Test
tasks = [
    "Was ist 2+2?",  # EINFACH → Haiku
    "Erkläre Quantenmechanik im Detail",  # KOMPLEX → Sonnet
    "Wer war Napoleon?"  # EINFACH → Haiku
]

for task in tasks:
    result = route_query(task)
    print(f"Model: {result['model']}")
    print(f"Cost: ${result['cost']:.6f}")
    print(f"Answer: {result['answer'][:100]}...\n")

Cost Vergleich:

Haiku:  $0.80 input, $2.40 output
Sonnet: $3.00 input, $15.0 output

Einfache Query (500 input, 100 output):
- Haiku:  $0.0004 + $0.00024 = $0.00064
- Sonnet: $0.0015 + $0.00150 = $0.00300

Haiku ist 4.7x billiger!

Über 1M Calls:

Haiku Only:   $640
Sonnet Only:  $3.000
Hybrid (80% Haiku, 20% Sonnet): ~$1.200
Einsparung: 60%

Teil 3: Batch Processing

Batch-API hat 50% Discount!

# batch_processing.py
from anthropic import Anthropic
import json

client = Anthropic()

def create_batch_requests(prompts: list) -> list:
    """Erstelle Batch-Requests"""

    requests = []
    for i, prompt in enumerate(prompts):
        requests.append({
            "custom_id": f"request-{i}",
            "params": {
                "model": "claude-3-5-sonnet-20241022",
                "max_tokens": 512,
                "messages": [
                    {"role": "user", "content": prompt}
                ]
            }
        })

    return requests

def submit_batch(requests: list) -> str:
    """Reiche Batch ein"""

    # JSONL Format
    batch_input = "\n".join(json.dumps(r) for r in requests)

    # Batch API (nicht direkt — braucht Files API)
    # Das ist ein vereinfachtes Beispiel

    print(f"Batch submitted: {len(requests)} requests")
    print("Batch hat 50% Discount!")
    return "batch-id-123"

# Nutzung
prompts = [
    "Was ist AI?",
    "Erkläre Machine Learning",
    "Was ist ein Neural Network?"
]

requests = create_batch_requests(prompts)
batch_id = submit_batch(requests)

# Cost Vergleich:
print("\nCost für 1000 Queries:")
print("Regular API: $100")
print("Batch API: $50 (50% Rabatt)")

Wann Batch?

  • ✓ 100+ Anfragen zusammen
  • ✓ Nicht Zeit-kritisch (overnight Batch)
  • ✗ Real-Time Responses
  • ✗ < 100 Anfragen (setup overhead)

Teil 4: Context Window Optimization

Weniger Tokens = weniger Kosten.

# context_optimization.py
from anthropic import Anthropic

client = Anthropic()

# FALSCH: Gib gesamtes PDF (1M Tokens)
full_context = """[1.000.000 Tokens von großem PDF]"""

# RICHTIG: Nutze nur relevante Seiten
def extract_relevant_context(document: str, question: str, num_chunks: int = 3) -> str:
    """Extrahiere nur relevante Chunks"""

    # Split document in Chunks
    chunks = [document[i:i+50000] for i in range(0, len(document), 50000)]

    # Finde relevante Chunks (mit Haiku — billig!)
    relevant_chunks = []

    for chunk in chunks:
        response = client.messages.create(
            model="claude-3-5-haiku-20241022",
            max_tokens=50,
            messages=[
                {
                    "role": "user",
                    "content": f"Ist dieser Chunk relevant für '{question}'?\n\n{chunk[:1000]}"
                }
            ]
        )

        if "ja" in response.content[0].text.lower():
            relevant_chunks.append(chunk)

    # Gib nur relevante Chunks zurück
    return "\n---\n".join(relevant_chunks[:num_chunks])

# Nutze optimierte Kontext
def query_with_optimization(document: str, question: str) -> tuple:
    """Query mit Context-Optimierung"""

    # Extracting: Billiger (Haiku)
    context = extract_relevant_context(document, question)

    # Answering: Sonnet für Quality
    response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=512,
        system=f"Context:\n{context}",
        messages=[
            {"role": "user", "content": question}
        ]
    )

    return response.content[0].text, len(context)

# Cost Vergleich:
print("1M Token Document:")
print("- Full Context: 1M × $0.003 = $3")
print("- Optimized: 50k × $0.003 = $0.15 (95% cheaper!)")

Teil 5: Embedding Reuse

Generiere Embeddings einmal, nutze sie wieder.

# embedding_reuse.py
from anthropic import Anthropic
import json
import hashlib

client = Anthropic()

class EmbeddingCache:
    def __init__(self, cache_file: str = "embeddings.json"):
        self.cache_file = cache_file
        self.cache = self._load()

    def _load(self):
        try:
            with open(self.cache_file) as f:
                return json.load(f)
        except FileNotFoundError:
            return {}

    def _save(self):
        with open(self.cache_file, "w") as f:
            json.dump(self.cache, f)

    def get_or_create_embedding(self, text: str) -> list:
        """Rufe oder erstelle Embedding"""

        text_hash = hashlib.md5(text.encode()).hexdigest()

        if text_hash in self.cache:
            print(f"✓ Embedding Cache Hit")
            return self.cache[text_hash]

        # Erstelle Embedding (nur einmal!)
        print(f"✗ Embedding Miss, creating...")
        from anthropic import Anthropic as AnthropicClient
        # Note: Anthropic hat keine native Embedding API, aber z.B. OpenAI:
        # embedding = openai_client.embeddings.create(input=text)["data"][0]["embedding"]

        # For demo:
        embedding = [0.1, 0.2, 0.3]  # Placeholder

        self.cache[text_hash] = embedding
        self._save()

        return embedding

# Nutzung
cache = EmbeddingCache()

# First time: Kostet API Call
emb1 = cache.get_or_create_embedding("Künstliche Intelligenz")

# Second time: Cache Hit, kostenlos!
emb2 = cache.get_or_create_embedding("Künstliche Intelligenz")

# Different text: Neuer Call
emb3 = cache.get_or_create_embedding("Machine Learning")

Zusammenfassung: Kostenoptimierungs-Strategie

Methode Einsparung Komplexität Best für
Prompt Caching 90% Mittel Große Kontexte
Model Routing 60% Niedrig Mixed Workloads
Batch API 50% Hoch Bulk Processing
Context Opt. 95% Hoch Large Documents
Embedding Cache 100% Niedrig Repeated Queries

Praktische Kombinat...ion (Real World):

# ultimate_optimization.py
def process_query(document: str, question: str) -> str:
    # 1. Prüfe Embedding Cache
    embedding = cache.get_or_create_embedding(question)

    # 2. Optimiere Kontext
    context = extract_relevant_context(document, question)

    # 3. Klassifiziere Komplexität
    complexity = classify_task(question)

    # 4. Route zu billiges Modell wenn möglich
    model = "haiku" if complexity == "EINFACH" else "sonnet"

    # 5. Nutze Cache für System Prompt
    response = client.messages.create(
        model=model,
        system=[
            {"type": "text", "text": SYSTEM_PROMPT},
            {"type": "text", "text": context, "cache_control": {"type": "ephemeral"}}
        ],
        messages=[...]
    )

    return response.content[0].text

Gesamt-Einsparung: 70-85% je nach Workload!

Top Tips

  • ✓ Cache System Prompts & Templates
  • ✓ Haiku für Klassifikation & Routen
  • ✓ Batch API für Overnight Runs
  • ✓ Speichere Embeddings lokal
  • ✓ Track die tatsächlichen Kosten (setup monitoring!)