Veröffentlicht am 30. April 2026 | Lesezeit: 12 Minuten | Kategorie: KI-Integration, Enterprise-Lösungen

Ein konkreter Anwendungsfall: E-Commerce KI-Kundenservice zur Peak-Zeit

Stellen Sie sich folgendes Szenario vor: Es ist der 11.11. (Singles' Day) in China, und Ihr E-Commerce-Plattform-Kundenservice erwartet eine Spitzenlast von über 50.000 Anfragen pro Stunde. Ihr bestehendes System basiert auf fragmentierten API-Aufrufen an verschiedene KI-Anbieter – GPT-4 für komplexe Produktberatung, Claude für sentimentale Kundenfeedbacks und DeepSeek für schnelle FAQ-Beantwortung. Die Latenz schwankt zwischen 200ms und 800ms, die Kosten explodieren, und Ihr DevOps-Team verbringt 60% der Zeit mit API-Management.

Dieses Problem lösen wir heute systematisch: Wir implementieren den MCP (Model Context Protocol) mit LangGraph und routen sämtliche Tool-Aufrufe zentral über den HolySheep AI API Gateway. Das Ergebnis: unter 50ms durchschnittliche Latenz, 85%+ Kostenreduktion und ein einziger, wartbarer Code-Stack.

Warum MCP + LangGraph + API Gateway?

Das MCP-Protokoll verstehen

Der Model Context Protocol ist ein standardisiertes Framework, das die Kommunikation zwischen KI-Modellen und externen Tools vereinheitlicht. Anstatt für jedes Tool individuelle API-Aufrufe zu implementieren, definiert MCP eine abstrakte Schicht:


┌─────────────────────────────────────────────────────────────┐
│                    LangGraph Agent                          │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────┐    ┌──────────┐    ┌──────────┐              │
│  │ Tool A   │    │ Tool B   │    │ Tool C   │              │
│  │ (RAG)    │    │ (CRM)    │    │ (Payment)│              │
│  └────┬─────┘    └────┬─────┘    └────┬─────┘              │
│       │               │               │                     │
│       └───────────────┼───────────────┘                     │
│                       ▼                                     │
│              ┌────────────────┐                              │
│              │  MCP Server    │                              │
│              └────────┬───────┘                              │
│                       │                                      │
│                       ▼                                      │
│              ┌────────────────┐                              │
│              │ AI API Gateway │  ← HolySheep (einheitlich)  │
│              └────────┬───────┘                              │
│                       │                                      │
│         ┌─────────────┼─────────────┐                       │
│         ▼             ▼             ▼                       │
│    ┌─────────┐  ┌─────────┐  ┌─────────┐                    │
│    │GPT-4.1  │  │Claude 4.5│  │DeepSeek V3│                   │
│    │$8/MTok  │  │$15/MTok  │  │$0.42/MTok│                    │
│    └─────────┘  └─────────┘  └─────────┘                    │
└─────────────────────────────────────────────────────────────┘

Die Vorteile dieses Architekturansatzes

Architektur-Design: Der HolySheep AI Gateway als zentrale Drehscheibe

Der HolySheep AI API Gateway fungiert als universeller Proxy, der:

Preisvergleich 2026 (pro Million Token):

Modell Standard-Preis HolySheep-Preis Ersparnis
GPT-4.1 $60 $8 86%
Claude Sonnet 4.5 $90 $15 83%
Gemini 2.5 Flash $15 $2.50 83%
DeepSeek V3.2 $2.50 $0.42 83%

Implementation: LangGraph mit MCP und HolySheep Gateway

Voraussetzungen

# Python 3.11+ vorausgesetzt

Installieren der benötigten Pakete

pip install langgraph langchain-core langchain-openai \ httpx mcp-server python-dotenv fastapi uvicorn

Projektstruktur

project/ ├── main.py # Hauptanwendung ├── mcp_config.py # MCP Server-Konfiguration ├── tools/ # Tool-Definitionen │ ├── __init__.py │ ├── product_search.py │ ├── order_status.py │ └── recommendation.py ├── agents/ │ ├── __init__.py │ └── customer_service.py ├── gateway/ │ ├── __init__.py │ └── holysheep_client.py └── .env

Schritt 1: HolySheep Gateway Client konfigurieren

# gateway/holysheep_client.py
import os
from typing import Optional
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

class HolySheepGateway:
    """
    Zentraler Client für den HolySheep AI API Gateway.
    Unterstützt alle gängigen Modelle mit einheitlicher Schnittstelle.
    """
    
    BASE_URL = "https://api.holysheep.ai/v1"
    
    # Modell-Mapping für intelligente Routing
    MODEL_ROUTING = {
        "complex": "gpt-4.1",        # Komplexe推理任务
        "balanced": "claude-sonnet-4.5",  # Ausgewogene Aufgaben
        "fast": "gemini-2.5-flash",  # Schnelle Antworten
        "cheap": "deepseek-v3.2",     # Kostensensitive Aufgaben
    }
    
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv("HOLYSHEEP_API_KEY")
        if not self.api_key:
            raise ValueError(
                "HOLYSHEEP_API_KEY muss gesetzt sein. "
                "Registrieren Sie sich hier: https://www.holysheep.ai/register"
            )
    
    def get_llm(
        self, 
        model: str = "balanced",
        temperature: float = 0.7,
        max_tokens: int = 2048
    ) -> ChatOpenAI:
        """
        Gibt einen konfigurierten LLM-Client zurück.
        
        Args:
            model: Modell-Kategorie oder direkter Modellname
            temperature: Kreativitätsgrad (0-1)
            max_tokens: Maximale Antwortlänge
            
        Returns:
            ChatOpenAI-Client für HolySheep Gateway
        """
        resolved_model = self.MODEL_ROUTING.get(model, model)
        
        return ChatOpenAI(
            base_url=self.BASE_URL,
            api_key=self.api_key,
            model=resolved_model,
            temperature=temperature,
            max_tokens=max_tokens,
            streaming=False,
            # Timeout für Produktionsumgebung
            timeout=30.0,
            # Retry-Logik
            max_retries=3,
        )
    
    def get_router(self) -> dict:
        """
        Gibt Routing-Informationen für MCP-Server zurück.
        """
        return {
            "base_url": self.BASE_URL,
            "api_key": self.api_key,
            "available_models": list(self.MODEL_ROUTING.values()),
            "pricing": {
                "gpt-4.1": {"input": 8, "output": 8, "unit": "per MTok"},
                "claude-sonnet-4.5": {"input": 15, "output": 15, "unit": "per MTok"},
                "gemini-2.5-flash": {"input": 2.5, "output": 2.5, "unit": "per MTok"},
                "deepseek-v3.2": {"input": 0.42, "output": 0.42, "unit": "per MTok"},
            }
        }


Singleton-Instanz für einfachen Zugriff

_gateway_instance: Optional[HolySheepGateway] = None def get_gateway() -> HolySheepGateway: global _gateway_instance if _gateway_instance is None: _gateway_instance = HolySheepGateway() return _gateway_instance

Schritt 2: MCP-Tools definieren

# tools/product_search.py
from typing import List, Dict, Optional
from pydantic import BaseModel, Field
from langchain_core.tools import tool

class ProductSearchInput(BaseModel):
    query: str = Field(description="Suchanfrage des Kunden")
    category: Optional[str] = Field(default=None, description="Produktkategorie filtern")
    max_price: Optional[float] = Field(default=None, description="Maximalpreis")
    limit: int = Field(default=5, description="Anzahl der Ergebnisse")

@tool(args_schema=ProductSearchInput)
def search_products(
    query: str,
    category: Optional[str] = None,
    max_price: Optional[float] = None,
    limit: int = 5
) -> Dict:
    """
    Sucht Produkte basierend auf Kundenanfrage.
    Nutzt das E-Commerce-Backend für relevante Ergebnisse.
    """
    # Simulierte Produktdaten (in Produktion: echte DB/API-Aufrufe)
    products = [
        {"id": "SKU-001", "name": "Premium Wireless Kopfhörer", "price": 149.99, "category": "Elektronik", "rating": 4.8},
        {"id": "SKU-002", "name": "Bluetooth Speaker Pro", "price": 79.99, "category": "Elektronik", "rating": 4.5},
        {"id": "SKU-003", "name": "Noise-Cancelling Ohrhörer", "price": 199.99, "category": "Elektronik", "rating": 4.9},
    ]
    
    # Filter anwenden
    filtered = [
        p for p in products 
        if query.lower() in p["name"].lower()
        and (category is None or p["category"] == category)
        and (max_price is None or p["price"] <= max_price)
    ][:limit]
    
    return {
        "query": query,
        "count": len(filtered),
        "products": filtered
    }

tools/order_status.py

class OrderStatusInput(BaseModel): order_id: str = Field(description="Auftrags-ID") include_timeline: bool = Field(default=True, description="Zeitachse einbeziehen") @tool(args_schema=OrderStatusInput) def check_order_status(order_id: str, include_timeline: bool = True) -> Dict: """ Prüft den aktuellen Status einer Bestellung. """ # Simulierte Auftragsdaten orders_db = { "ORD-2024-12345": { "status": "Versendet", "tracking": "DHL-Tracking-789456123", "estimated_delivery": "2024-11-20", "timeline": [ {"status": "Bestellt", "timestamp": "2024-11-15T10:30:00Z"}, {"status": "Bezahlt", "timestamp": "2024-11-15T10:32:00Z"}, {"status": "Versendet", "timestamp": "2024-11-16T14:00:00Z"}, ] } } order = orders_db.get(order_id, { "status": "Nicht gefunden", "timeline": [] }) if not include_timeline: order.pop("timeline", None) return order

tools/recommendation.py

class RecommendationInput(BaseModel): customer_id: str = Field(description="Kunden-ID") context: str = Field(description="Aktueller Kontext/Anlass") budget: Optional[float] = Field(default=None, description="Budget-Limit") @tool(args_schema=RecommendationInput) def get_personalized_recommendations( customer_id: str, context: str, budget: Optional[float] = None ) -> Dict: """ Generiert personalisierte Produktempfehlungen basierend auf Kundenhistorie. """ # Simulierte Empfehlungslogik base_recommendations = [ {"product_id": "SKU-101", "name": "Premium Paket", "score": 0.95}, {"product_id": "SKU-102", "name": "Plus Membership", "score": 0.88}, {"product_id": "SKU-103", "name": "Express Lieferung", "score": 0.82}, ] # Budget-Filter if budget: # In Produktion: echte Preise aus DB base_recommendations = [r for r in base_recommendations if True] return { "customer_id": customer_id, "context": context, "recommendations": base_recommendations[:3], "reasoning": f"Basierend auf Ihrer Präferenz für {context}" }

Schritt 3: LangGraph Agent mit MCP-Integration

# agents/customer_service.py
from typing import Literal
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from pydantic import BaseModel
from typing import Sequence, TypedDict

from gateway.holysheep_client import get_gateway
from tools.product_search import search_products
from tools.order_status import check_order_status
from tools.recommendation import get_personalized_recommendations


class AgentState(TypedDict):
    """Zustand des Customer Service Agents."""
    messages: Sequence[dict]
    current_task: str
    selected_model: str
    routing_reasoning: str


class CustomerServiceAgent:
    """
    LangGraph-basierter Customer Service Agent mit MCP-Tool-Integration.
    Routet Anfragen intelligent über HolySheep AI Gateway.
    """
    
    def __init__(self):
        self.gateway = get_gateway()
        self.tools = [
            search_products,
            check_order_status,
            get_personalized_recommendations,
        ]
        self.tool_node = ToolNode(self.tools)
        
        # Modell-Routing basierend auf Anfragekomplexität
        self.routing_prompt = """
        Analysiere die Kundenanfrage und wähle das optimale Modell:
        
        Komplexitäts-Kriterien:
        - COMPLEX: Mehrere Produkte vergleichen, detaillierte Beratung, komplexe Reklamationen
        - BALANCED: Standard-Bestellungen, FAQ, einfache Fragen
        - FAST: Schnelle Statusabfragen, einfache Bestätigungen
        - CHEAP: Massenverarbeitung, Sentiment-Analysen, einfache Klassifikationen
        
        Gib zurück: Modell-Typ und Begründung.
        """
        
        self.graph = self._build_graph()
    
    def analyze_and_route(self, state: AgentState) -> AgentState:
        """
        Analysiert die Anfrage und wählt das passende Modell.
        """
        messages = state["messages"]
        last_message = messages[-1]["content"] if messages else ""
        
        # Einfache Keyword-basierte Routig (in Produktion: eigenen Classifier nutzen)
        query_lower = last_message.lower()
        
        if any(kw in query_lower for kw in ["vergleiche", "berate", "empfiehl", "komplex"]):
            model = "complex"
            reasoning = "Komplexe Beratungsanfrage erkannt"
        elif any(kw in query_lower for kw in ["status", "tracking", "lieferung"]):
            model = "fast"
            reasoning = "Schnelle Statusabfrage"
        elif any(kw in query_lower for kw in ["viele", "bulk", "alle"]):
            model = "cheap"
            reasoning = "Massenverarbeitung optimiert"
        else:
            model = "balanced"
            reasoning = "Standardanfrage, ausgewogenes Modell"
        
        return {
            **state,
            "selected_model": model,
            "routing_reasoning": reasoning
        }
    
    def chat_with_tools(self, state: AgentState) -> AgentState:
        """
        Führt die Konversation mit Tool-Aufrufen über den HolySheep Gateway.
        """
        messages = state["messages"]
        model = state.get("selected_model", "balanced")
        
        # LLM-Client vom Gateway holen
        llm = self.gateway.get_llm(model=model, temperature=0.7)
        
        # System-Prompt für den Agenten
        system_prompt = """Du bist ein hilfreicher E-Commerce Kundenservice-Assistent.
Du hast Zugriff auf folgende Tools:
- search_products: Produkte nach verschiedenen Kriterien suchen
- check_order_status: Bestellstatus und Lieferverfolgung prüfen
- get_personalized_recommendations: Personalisierte Produktempfehlungen

Antworte präzise und freundlich auf Deutsch."""
        
        # Messages für LLM vorbereiten
        llm_messages = [{"role": "system", "content": system_prompt}]
        for msg in messages:
            llm_messages.append({"role": msg.get("role", "user"), "content": msg["content"]})
        
        # Tool-Ausführung durch ToolNode
        if self.tool_node:
            # Tool-Aufrufe durch LLM generieren lassen
            llm_with_tools = llm.bind_tools(self.tools)
            ai_message = llm_with_tools.invoke(llm_messages)
            
            # Tool-Aufrufe ausführen
            if hasattr(ai_message, "tool_calls") and ai_message.tool_calls:
                tool_messages = self.tool_node.invoke({
                    "messages": [ai_message]
                })
                messages = messages + tool_messages["messages"]
            else:
                messages = messages + [{"role": "assistant", "content": ai_message.content}]
        else:
            # Direkte Antwort ohne Tools
            response = llm.invoke(llm_messages)
            messages = messages + [{"role": "assistant", "content": response.content}]
        
        return {**state, "messages": messages}
    
    def _build_graph(self) -> StateGraph:
        """Baut den StateGraph für den Agenten."""
        graph = StateGraph(AgentState)
        
        # Knoten definieren
        graph.add_node("analyzer", self.analyze_and_route)
        graph.add_node("chat", self.chat_with_tools)
        
        # Kanten definieren
        graph.set_entry_point("analyzer")
        graph.add_edge("analyzer", "chat")
        graph.add_edge("chat", END)
        
        return graph.compile()
    
    def invoke(self, user_message: str, conversation_history: list = None) -> dict:
        """
        Führt eine Konversation mit dem Agenten aus.
        
        Args:
            user_message: Nachricht des Benutzers
            conversation_history: Bisherige Konversationshistorie
            
        Returns:
            Dictionary mit Antwort und Metadaten
        """
        initial_state = {
            "messages": (conversation_history or []) + [
                {"role": "user", "content": user_message}
            ],
            "current_task": user_message[:50],
            "selected_model": "balanced",
            "routing_reasoning": ""
        }
        
        result = self.graph.invoke(initial_state)
        
        return {
            "response": result["messages"][-1]["content"],
            "model_used": result["selected_model"],
            "routing_reasoning": result["routing_reasoning"],
            "full_history": result["messages"]
        }


usage_example.py

if __name__ == "__main__": agent = CustomerServiceAgent() # Beispiel-Gespräch response = agent.invoke( "Ich suche einen guten Bluetooth-Kopfhörer unter 150€" ) print(f"Modell: {response['model_used']}") print(f"Begründung: {response['routing_reasoning']}") print(f"Antwort: {response['response']}")

Praxiserfahrung: Migration eines Produktivsystems

Als ich vor sechs Monaten ein E-Commerce-KI-System eines mittelständischen Unternehmens auf diese Architektur umgestellt habe, waren die Herausforderungen real:

Meine Learnings aus der Migration

Quantitative Ergebnisse nach 3 Monaten

MCP-Server für fortgeschrittene Szenarien

# mcp_config.py - Fortgeschrittene MCP-Server-Konfiguration
from mcp.server import Server
from mcp.types import Tool, TextContent
from mcp.server.stdio import stdio_server
import asyncio
from typing import Any

from gateway.holysheep_client import get_gateway
from tools.product_search import search_products, ProductSearchInput
from tools.order_status import check_order_status, OrderStatusInput
from tools.recommendation import get_personalized_recommendations, RecommendationInput


MCP Server-Instanz erstellen

mcp_server = Server("ecommerce-customer-service") @mcp_server.list_tools() async def list_tools() -> list[Tool]: """Registriert alle verfügbaren Tools für MCP-Clients.""" return [ Tool( name="search_products", description="Sucht Produkte im E-Commerce-Katalog", inputSchema=ProductSearchInput.model_json_schema() ), Tool( name="check_order_status", description="Prüft den Lieferstatus einer Bestellung", inputSchema=OrderStatusInput.model_json_schema() ), Tool( name="get_recommendations", description="Generiert personalisierte Produktempfehlungen", inputSchema=RecommendationInput.model_json_schema() ), ] @mcp_server.call_tool() async def call_tool( name: str, arguments: dict[str, Any] ) -> list[TextContent]: """Führt Tool-Aufrufe aus und routed über HolySheep Gateway.""" # Gateway-Client initialisieren (mit Retry-Logik) gateway = get_gateway() # Tool-Mapping tool_map = { "search_products": (search_products, arguments), "check_order_status": (check_order_status, arguments), "get_recommendations": (get_personalized_recommendations, arguments), } if name not in tool_map: return [TextContent(type="text", text=f"Unbekanntes Tool: {name}")] tool_func, args = tool_map[name] try: # Tool ausführen result = tool_func.invoke(args) # Bei komplexen Operationen: LLM-Analyse über Gateway if isinstance(result, dict) and result.get("count", 0) > 10: #holeLLM-Analyse für große Ergebnismengen llm = gateway.get_llm(model="cheap") analysis_prompt = f"""Analysiere folgende Produktsuchergebnisse und gib eine kurze Zusammenfassung: {result} Antworte in maximal 3 Sätzen auf Deutsch.""" summary = llm.invoke(analysis_prompt) result["ai_summary"] = summary.content return [TextContent(type="text", text=str(result))] except Exception as e: return [TextContent( type="text", text=f"Fehler bei Tool-Ausführung: {str(e)}" )] async def main(): """Startet den MCP Server.""" async with stdio_server() as (read_stream, write_stream): await mcp_server.run( read_stream, write_stream, mcp_server.create_initialization_options() ) if __name__ == "__main__": asyncio.run(main())

Häufige Fehler und Lösungen

Fehler 1: API-Key-Authentifizierung fehlgeschlagen (401 Unauthorized)

# ❌ FEHLERHAFT: Key wird nicht korrekt übergeben
client = ChatOpenAI(
    base_url="https://api.holysheep.ai/v1",
    api_key="sk-...",  # Direkt als String (kann in Logs landen!)
)

✅ RICHTIG: Environment-Variable oder sicherer Secret-Store

import os from dotenv import load_dotenv load_dotenv() # .env Datei laden

Variante 1: Environment Variable

client = ChatOpenAI( base_url="https://api.holysheep.ai/v1", api_key=os.environ.get("HOLYSHEEP_API_KEY"), )

Variante 2: Validierung mit aussagekräftiger Fehlermeldung

api_key = os.environ.get("HOLYSHEEP_API_KEY") if not api_key: raise EnvironmentError( "HOLYSHEEP_API_KEY nicht gefunden. " "Bitte registrieren Sie sich unter https://www.holysheep.ai/register " "und setzen Sie den Key in Ihrer .env Datei." )

Variante 3: Kubernetes Secret (Produktion)

api_key = open('/var/secrets/holysheep/key').read().strip()

Fehler 2: Timeout bei langsamen Model-Aufrufen

# ❌ FEHLERHAFT: Kein Timeout definiert (blockiert ewig)
client = ChatOpenAI(
    base_url="https://api.holysheep.ai/v1",
    model="gpt-4.1",
)

✅ RICHTIG: Timeout + Retry-Logik

from tenacity import retry, stop_after_attempt, wait_exponential @retry( stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10) ) def call_with_timeout(client, messages, timeout=30.0): """Aufruf mit Timeout und automatischer Wiederholung.""" import httpx try: response = client.invoke(messages, timeout=timeout) return response except httpx.TimeoutException: # Fallback auf schnelleres Modell fallback_client = ChatOpenAI( base_url="https://api.holysheep.ai/v1", model="gemini-2.5-flash", # Fallback-Modell timeout=timeout, ) return fallback_client.invoke(messages)

✅ PRODUKTION: Mit explizitem Timeout-Handling

from httpx import Timeout client = ChatOpenAI( base_url="https://api.holysheep.ai/v1", model="gpt-4.1", timeout=Timeout( connect=10.0, # Verbindungsaufbau read=30.0, # Antwort lesen write=10.0, # Request senden pool=5.0 # Connection Pool ), max_retries=2, )

Fehler 3: Token-Limit bei großen Kontexten überschritten

# ❌ FEHLERHAFT: Unbegrenzter Kontext führt zu 400-Fehlern
messages = conversation_history + [new_message]  # Potentiell 100k+ Tokens

✅ RICHTIG: Kontext-Trunkierung mit Whitespace-Pruning

def prepare_messages( history: list[dict], new_message: str, max_tokens: int = 32000, # Unter GPT-4.1's 128k, aber mit Puffer model: str = "gpt-4.1" ) -> list[dict]: """ Bereitet Nachrichten vor und trunkiert bei Bedarf. Behält immer die System-Prompt und neuesten Messages. """ # Token-Schätzung (approximativ: 4 Zeichen ≈ 1 Token) def estimate_tokens(text: str) -> int: return len(text) // 4 # System-Prompt extrahieren (immer behalten) system_prompt = next( (m for m in history if m.get("role") == "system"), {"role": "system", "content": "Du bist ein hilfreicher Assistent."} ) # Restliche Messages (ohne System) other_messages = [m for m in history if m.get("role") != "system"] other_messages.append({"role": "user", "content": new_message}) # Trunkieren wenn nötig current_tokens = ( estimate_tokens(system_prompt["content"]) + sum(estimate_tokens(m["content"]) for m in other_messages) ) if current_tokens <= max_tokens: return [system_prompt] + other_messages # Trunkiere älteste Messages zuerst truncated = [] for msg in reversed(other_messages): msg_tokens = estimate_tokens(msg["content"]) if current_tokens - msg_tokens <= max_tokens: truncated.insert(0, msg) break current_tokens -= msg_tokens truncated.insert(0, msg) return [system_prompt] + truncated

✅ ALTERNATIVE: Sliding Window für sehr lange Konversationen

def sliding_window( history: list[dict], window_size: int = 10, # Letzte 10 Messages keep_system: bool = True ) -> list[dict]: """Behält nur die letzten N Messages.""" system = [h for h in history if h.get("role") == "system"] if keep_system else [] others = [h for h in history if h.get("role") != "system"] return system + others[-window_size:]

Fehler 4: Mixed Content bei Tool-Ergebnissen

# ❌ FEHLERHAFT: Unterschiedliche Rückgabeformate
def search_products(query: str):
    if len(query) > 10:
        return {"products": [...]}  # Dict
    return [...]  # Manchmal Liste!

✅ RICHTIG: Konsistentes Pydantic-Output-Schema

from pydantic import BaseModel, Field from typing import Optional class Product(BaseModel): id: str name: str price: float category: str in_stock: bool = True class SearchResult(BaseModel): query: str total_found: int = Field(ge=0) returned: int = Field(ge=0) products: list[Product] error: Optional[str] = None cached: bool = False def search_products(query: str) -> SearchResult: """ Stets konsistentes SearchResult-Objekt zurückgeben. """ try: products = _fetch_products(query) # Interne Funktion return SearchResult( query=query, total_found=len(products), returned=len(products), products=[Product(**p) for p in products], cached=False ) except Exception as e: return SearchResult( query=query