13 min Lesezeit

AI Agents Architektur mit Claude Code: Patterns & Tools 2026

ReAct, Tool Use, Memory-Systeme, LangGraph, Multi-Agent-Orchestrierung — die wichtigsten Architektur-Muster für autonome KI-Agenten, erklärt am Beispiel von Claude Code.

Wer heute produktive AI-Agenten bauen will, kommt an einer soliden Architektur nicht vorbei. Die Zeit der einfachen Chatbots ist vorbei — moderne Agenten verwalten eigenen Zustand, nutzen Dutzende von Tools, arbeiten im Team und laufen rund um die Uhr in der Produktion. Claude Code ist selbst ein solcher Agent: Er plant, schreibt Code, führt ihn aus, korrigiert Fehler und delegiert Subtasks an spezialisierte Sub-Agenten.

In diesem Artikel zeigen wir die sechs wichtigsten Architektur-Patterns, die 2026 den Unterschied machen — mit konkreten TypeScript- und Python-Beispielen, die direkt auf dem Anthropic Claude SDK aufbauen.


1. ReAct Pattern — Reason & Act Loop

Das ReAct-Pattern (Reason + Act) ist das Fundament fast aller modernen AI-Agenten. Die Idee: Der Agent wechselt in einem kontinuierlichen Loop zwischen Denken (Thought), Handeln (Action) und Beobachten (Observation), bis das Ziel erreicht ist.

ReAct

Der ReAct-Loop im Überblick

Jeder Zyklus besteht aus drei Phasen:

Der Reason+Act Loop visualisiert

StartUser Task
Phase 1Thought
Phase 2Action
Phase 3Observation
EntscheidFertig?

Der Loop läuft so lange, bis der Agent entscheidet, dass er eine finale Antwort liefern kann — oder ein Abbruchkriterium greift (Max-Steps, Timeout, Human-in-the-Loop).

Anthropic tool_use API — wie ReAct technisch funktioniert

Claude implementiert ReAct nativ über den tool_use-Mechanismus: Der Agent gibt einen tool_use-Block zurück, das System führt das Tool aus, schickt das Ergebnis als tool_result zurück — und der Loop beginnt von vorne.

TypeScript
// ReAct-Loop mit @anthropic-ai/sdk — TypeScript import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic(); async function reactLoop(userTask: string, tools: Anthropic.Tool[]): Promise<string> { const messages: Anthropic.MessageParam[] = [ { role: 'user', content: userTask } ]; let maxSteps = 20; while (maxSteps-- > 0) { const response = await client.messages.create({ model: 'claude-opus-4-5', max_tokens: 4096, tools, messages }); // Agent hat finale Antwort — Loop beenden if (response.stop_reason === 'end_turn') { const text = response.content.find(b => b.type === 'text'); return text?.text ?? ''; } // Tool-Use-Blocks extrahieren (Action-Phase) const toolUseBlocks = response.content.filter(b => b.type === 'tool_use'); if (toolUseBlocks.length === 0) break; // Agenten-Antwort zur History hinzufügen messages.push({ role: 'assistant', content: response.content }); // Tools parallel ausführen (Observation-Phase) const toolResults = await Promise.all( toolUseBlocks.map(async (block) => { const result = await executeTool(block.name, block.input); return { type: 'tool_result' as const, tool_use_id: block.id, content: JSON.stringify(result) }; }) ); messages.push({ role: 'user', content: toolResults }); } throw new Error('Max steps reached without final answer'); }
Tipp: Parallel Tool Calls

Claude kann mehrere Tools gleichzeitig aufrufen. Nutze Promise.all() um alle Tool-Calls eines Turns parallel auszuführen — das reduziert die Latenz im Loop erheblich.

Wann bricht der ReAct-Loop ab?


2. Tool Use mit dem Claude SDK

Tools sind das Herzstück jedes autonomen Agenten. Ohne Tools kann Claude nur Text produzieren — mit Tools kann er Code ausführen, APIs ansprechen, Datenbanken abfragen, Dateien lesen und schreiben. Das @anthropic-ai/sdk macht Tool-Integration geradlinig.

Tool Use

Anatomie eines Tool-Definitions

Jedes Tool besteht aus drei Teilen, die Claude helfen, das richtige Tool zur richtigen Zeit zu wählen:

Tools-Array definieren

TypeScript
const tools: Anthropic.Tool[] = [ { name: 'web_search', description: 'Führt eine Websuche durch und gibt aktuelle Ergebnisse zurück. ' + 'Nutze dieses Tool wenn du aktuelle Informationen brauchst.', input_schema: { type: 'object', properties: { query: { type: 'string', description: 'Der Suchbegriff auf Deutsch oder Englisch' }, max_results: { type: 'number', description: 'Anzahl der Ergebnisse (1-10, default: 5)', default: 5 } }, required: ['query'] } }, { name: 'execute_python', description: 'Führt Python-Code in einer isolierten Sandbox aus. ' + 'Ideal für Berechnungen, Datenanalyse und Dateitransformationen.', input_schema: { type: 'object', properties: { code: { type: 'string', description: 'Vollständiger Python-Code' }, timeout_seconds: { type: 'number', default: 30 } }, required: ['code'] } }, { name: 'read_file', description: 'Liest eine Datei aus dem Arbeitsverzeichnis.', input_schema: { type: 'object', properties: { path: { type: 'string', description: 'Relativer Dateipfad' } }, required: ['path'] } } ];

tool_use und tool_result Messages

Der Nachrichtenfluss zwischen Claude und Tools folgt einem festen Schema: Claude gibt einen tool_use-Block zurück, das System führt das Tool aus und schickt das Ergebnis als tool_result zurück. Wichtig: Beide müssen korrekt in die Message-History eingefügt werden.

TypeScript
// Korrekte Message-Struktur für Tool-Interaktionen // 1. Benutzer-Anfrage const messages: Anthropic.MessageParam[] = [ { role: 'user', content: 'Suche nach den neuesten LangGraph Releases und erstelle eine Zusammenfassung.' } ]; // 2. Antwort von Claude — enthält tool_use Block // response.content = [ // { type: 'text', text: 'Ich suche jetzt...' }, // { type: 'tool_use', id: 'toolu_01...', name: 'web_search', input: { query: 'LangGraph releases 2026' } } // ] // 3. Zur History hinzufügen (PFLICHT — sonst API-Error) messages.push({ role: 'assistant', content: response.content // gesamter Content-Block }); // 4. Tool ausführen + Ergebnis als tool_result zurückgeben const searchResult = await webSearch({ query: 'LangGraph releases 2026' }); messages.push({ role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_01...', // muss mit tool_use.id übereinstimmen content: JSON.stringify(searchResult), is_error: false // bei Fehler: true + Fehlermeldung als content } ] }); // 5. Nächster Claude-Call im Loop const finalResponse = await client.messages.create({ model: 'claude-opus-4-5', max_tokens: 4096, tools, messages });

Error Handling bei Tool-Calls

Wichtig: Fehler niemals verschweigen

Wenn ein Tool fehlschlägt, immer is_error: true setzen und die Fehlermeldung als content übergeben. Claude kann dann entscheiden ob er es nochmal versucht, ein alternatives Tool nutzt oder den Nutzer informiert.

TypeScript
async function executeToolSafe( name: string, input: Record<string, unknown>, toolUseId: string ): Promise<Anthropic.ToolResultBlockParam> { try { const result = await dispatchTool(name, input); return { type: 'tool_result', tool_use_id: toolUseId, content: JSON.stringify(result), is_error: false }; } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Unknown error'; console.error(`Tool ${name} failed:`, message); return { type: 'tool_result', tool_use_id: toolUseId, content: `Error: ${message}`, is_error: true // Claude informieren → kann reagieren }; } }

3. Memory-Systeme: Short-Term, Long-Term, Episodic

Ein Agent ohne Memory ist wie ein Mitarbeiter mit Amnesie — er beginnt jeden Tag von vorne. Gute Agenten-Architekturen unterscheiden drei Memory-Schichten, die zusammen ein vollständiges Gedächtnis ergeben.

Short-Term

Short-Term Memory — Conversation History

Der einfachste Memory-Typ: die Liste der bisherigen Nachrichten im aktuellen Kontext-Fenster. Claude hat ein Kontext-Fenster von 200k Tokens — das entspricht etwa 150.000 Wörtern oder mehreren Stunden Conversation-History.

Limitation: Geht verloren wenn die Session endet. Für persistente Agenten nicht ausreichend.

Long-Term

Long-Term Memory — Vectorstore + RAG

Wichtige Informationen werden als Embeddings in einem Vectorstore gespeichert (Qdrant, Chroma, Pinecone) und bei Bedarf per semantischer Suche abgerufen. Der Agent "erinnert" sich an vergangene Sessions, Nutzerpräferenzen und domänenspezifisches Wissen.

Episodic

Episodic Memory — Checkpoints & Task-State

Der Agent speichert seinen Fortschritt in definierten Checkpoints (JSON-Dateien, Datenbanken). Bei einem Absturz kann er genau dort weitermachen. Claude Code nutzt dieses Muster intensiv.

Long-Term Memory mit Qdrant und Python

Python
from qdrant_client import QdrantClient, models from anthropic import Anthropic import uuid client = Anthropic() qdrant = QdrantClient(url="http://localhost:6333") COLLECTION = "agent_memory" def save_to_memory(content: str, metadata: dict) -> str: """Speichert einen Memory-Eintrag als Embedding in Qdrant.""" # Embedding via Anthropic (oder OpenAI ada-002 als Alternative) embedding_response = client.embeddings.create( model="voyage-3", input=[content] ) vector = embedding_response.embeddings[0] point_id = str(uuid.uuid4()) qdrant.upsert( collection_name=COLLECTION, points=[models.PointStruct( id=point_id, vector=vector, payload={"content": content, **metadata} )] ) return point_id def recall_from_memory(query: str, top_k: int = 5) -> list[dict]: """Sucht relevante Erinnerungen per semantischer Ähnlichkeit.""" embedding_response = client.embeddings.create( model="voyage-3", input=[query] ) query_vector = embedding_response.embeddings[0] results = qdrant.search( collection_name=COLLECTION, query_vector=query_vector, limit=top_k, with_payload=True ) return [ {"content": hit.payload["content"], "score": hit.score} for hit in results ] def build_system_prompt_with_memory(user_query: str) -> str: """Reichert den System-Prompt mit relevanten Memories an.""" memories = recall_from_memory(user_query) memory_text = "\n".join( [f"- {m['content']}" for m in memories if m['score'] > 0.75] ) return f"""Du bist ein autonomer AI-Agent. Relevante Erinnerungen: {memory_text} Nutze diese Erinnerungen um konsistente, kontextsensitive Antworten zu geben."""

Episodic Memory — Checkpoint-Muster

Python
import json from pathlib import Path from datetime import datetime class AgentCheckpointer: """Speichert und lädt Agent-State für crash-sichere Ausführung.""" def __init__(self, checkpoint_dir: str = ".checkpoints"): self.dir = Path(checkpoint_dir) self.dir.mkdir(exist_ok=True) def save(self, task_id: str, state: dict) -> None: checkpoint = { "task_id": task_id, "timestamp": datetime.utcnow().isoformat(), "state": state } path = self.dir / f"{task_id}.json" path.write_text(json.dumps(checkpoint, indent=2)) def load(self, task_id: str) -> dict | None: path = self.dir / f"{task_id}.json" if not path.exists(): return None data = json.loads(path.read_text()) return data["state"] # Verwendung im Agenten: checkpointer = AgentCheckpointer() # Vor jedem kritischen Schritt speichern: checkpointer.save("research-task-001", { "step": 3, "completed_tools": ["web_search", "read_file"], "messages": messages, "results_so_far": results }) # Bei Neustart wiederherstellen: state = checkpointer.load("research-task-001") if state: messages = state["messages"] print(f"Resuming from step {state['step']}")

4. LangGraph Stateful Agents

LangGraph ist das Framework der Wahl wenn ein Agent komplexen, verzweigten Workflow-Logik folgen soll. Statt einem linearen Loop arbeitet LangGraph mit einem StateGraph: Nodes verarbeiten Zustand, Edges definieren Übergänge, und der Checkpointer sorgt für Persistenz.

LangGraph

StateGraph — das Herzstück von LangGraph

Ein StateGraph ist ein gerichteter Graph, in dem jeder Node den Agenten-Zustand transformiert. Edges können bedingt sein (Conditional Routing) — der Agent entscheidet selbst welchen Pfad er nimmt.

LangGraph mit Claude — vollständiges Beispiel

Python
from langgraph.graph import StateGraph, END from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.prebuilt import ToolNode from typing_extensions import TypedDict, Annotated from langchain_core.messages import BaseMessage, AnyMessage from langchain_anthropic import ChatAnthropic import operator # 1. State definieren class AgentState(TypedDict): messages: Annotated[list[AnyMessage], operator.add] research_complete: bool draft_written: bool final_output: str # 2. Modell + Tools model = ChatAnthropic(model="claude-opus-4-5") tools = [web_search_tool, write_file_tool, read_file_tool] model_with_tools = model.bind_tools(tools) # 3. Nodes definieren def research_node(state: AgentState) -> AgentState: """Recherchiert das Thema via Tools.""" response = model_with_tools.invoke(state["messages"]) return {"messages": [response], "research_complete": True} def write_node(state: AgentState) -> AgentState: """Schreibt Draft basierend auf Recherche-Ergebnissen.""" response = model.invoke(state["messages"] + [ {"role": "user", "content": "Schreibe jetzt einen ausführlichen Draft."} ]) return {"messages": [response], "draft_written": True} def review_node(state: AgentState) -> AgentState: """Reviewt und finalisiert den Draft.""" response = model.invoke(state["messages"] + [ {"role": "user", "content": "Review und korrigiere den Draft."} ]) return {"messages": [response], "final_output": response.content} # 4. Conditional Routing def should_continue_research(state: AgentState) -> str: last_msg = state["messages"][-1] if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: return "tools" # → Tool-Node return "write" # → Write-Node # 5. Graph aufbauen graph = StateGraph(AgentState) graph.add_node("research", research_node) graph.add_node("tools", ToolNode(tools)) graph.add_node("write", write_node) graph.add_node("review", review_node) graph.set_entry_point("research") graph.add_conditional_edges("research", should_continue_research) graph.add_edge("tools", "research") # Tool-Ergebnis → zurück zu research graph.add_edge("write", "review") graph.add_edge("review", END) # 6. Checkpointer für Persistenz with SqliteSaver.from_conn_string(":memory:") as checkpointer: app = graph.compile(checkpointer=checkpointer) config = {"configurable": {"thread_id": "session-001"}} result = app.invoke( {"messages": [{"role": "user", "content": "Recherchiere LangGraph 0.3 Neuerungen"}]}, config=config )

LangChain vs. LangGraph — wann was?

Kriterium LangChain Chains LangGraph
Workflow-Typ Linear, einfach Verzweigt, zyklisch
State-Management Keins (zustandslos) Expliziter TypedDict State
Checkpointing Manuell Built-in (Sqlite, Postgres)
Human-in-the-Loop Schwierig Native via interrupt_before
Multi-Agent Möglich, komplex First-class (Supervisor Pattern)
Lernkurve Gering Mittel
Empfehlung

Starte mit einfachen ReAct-Loops direkt auf dem Anthropic SDK. Wechsle zu LangGraph wenn du komplexes Conditional Routing, persistente State-Verwaltung über Sessions hinweg oder echte Human-in-the-Loop-Workflows brauchst.


5. Multi-Agent Orchestrierung

Ein einzelner Agent hat Grenzen — Kontext-Fenster, Spezialwissen, Parallelisierung. Multi-Agent-Architekturen lösen diese Grenzen indem spezialisierte Agenten zusammenarbeiten: ein Orchestrator delegiert Subtasks an Worker-Agenten, koordiniert Ergebnisse und trifft übergeordnete Entscheidungen.

Multi-Agent

Manager / Worker Pattern

Der Manager-Agent empfängt den Haupt-Task, zerlegt ihn in Subtasks und delegiert diese an spezialisierte Worker. Worker-Agenten haben begrenzte Verantwortlichkeiten aber tiefes Domänen-Wissen. Der Manager aggregiert die Ergebnisse.

Claude Code Team Feature — Multi-Agent in der Praxis

Claude Code implementiert Multi-Agent nativ über das Team Feature. Der CEO-Agent spawnt Department-Heads als eigenständige Sessions, die wiederum Worker-Subagents über das Agent()-Konstrukt starten. Jeder Agent hat eigenes Kontext-Fenster, eigene Tools und kommuniziert über strukturierte Messages.

Python
from anthropic import Anthropic client = Anthropic() async def orchestrate_research_task(main_task: str) -> dict: """Manager-Agent delegiert Subtasks parallel an Worker.""" # 1. Manager erstellt Plan plan_response = client.messages.create( model="claude-opus-4-5", # Manager = bestes Modell max_tokens=2048, system="Du bist ein Projekt-Manager. Zerlege den Task in 3-5 Subtasks.", messages=[{"role": "user", "content": main_task}] ) subtasks = parse_subtasks(plan_response.content[0].text) # 2. Worker-Agenten parallel starten import asyncio async def run_worker(subtask: str, worker_type: str) -> str: system_prompts = { "researcher": "Du bist ein Researcher. Sammle relevante Informationen.", "coder": "Du bist ein Senior Developer. Schreibe sauberen, getesteten Code.", "reviewer": "Du bist ein Code Reviewer. Finde Bugs und Sicherheitsprobleme." } response = client.messages.create( model="claude-haiku-4-5", # Worker = günstigeres Modell max_tokens=4096, system=system_prompts.get(worker_type, ""), messages=[{"role": "user", "content": subtask}] ) return response.content[0].text # Alle Worker gleichzeitig (parallel) worker_futures = [ run_worker(subtask, worker_type) for subtask, worker_type in subtasks ] worker_results = await asyncio.gather(*worker_futures) # 3. Manager aggregiert Ergebnisse aggregation_input = "\n\n".join([ f"## Worker {i+1} Ergebnis:\n{result}" for i, result in enumerate(worker_results) ]) final_response = client.messages.create( model="claude-opus-4-5", max_tokens=4096, system="Du bist ein Projekt-Manager. Aggregiere die Worker-Ergebnisse zu einem kohärenten Output.", messages=[ {"role": "user", "content": f"Ursprünglicher Task: {main_task}\n\n{aggregation_input}"} ] ) return { "plan": subtasks, "worker_outputs": worker_results, "final_output": final_response.content[0].text }

Swarm-Pattern: Dynamische Agent-Teams

Beim Swarm-Pattern gibt es keine feste Hierarchie — Agenten übergeben Tasks dynamisch aneinander je nach Spezialisierung. Der aktive Agent entscheidet wer als nächstes übernimmt. Dieses Muster eignet sich für Customer-Support-Systeme oder komplexe Triage-Workflows.

Python
# Swarm mit Handoff-Tool — Agent kann anderen Agent aktivieren handoff_tools = [ { "name": "transfer_to_billing", "description": "Übergib an Billing-Agent wenn Zahlungsfragen auftreten.", "input_schema": { "type": "object", "properties": { "reason": {"type": "string"}, "context": {"type": "string"} } } }, { "name": "transfer_to_technical", "description": "Übergib an Technical-Support wenn technische Probleme auftreten.", "input_schema": { "type": "object", "properties": { "reason": {"type": "string"}, "error_details": {"type": "string"} } } } ] AGENTS = { "triage": "Du bist ein Triage-Agent. Klassifiziere Anfragen und leite weiter.", "billing": "Du bist ein Billing-Spezialist. Bearbeite Zahlungsfragen.", "technical": "Du bist ein Tech-Support Experte. Löse technische Probleme." } def swarm_loop(user_message: str, start_agent: str = "triage") -> str: active_agent = start_agent messages = [{"role": "user", "content": user_message}] while True: response = client.messages.create( model="claude-haiku-4-5", max_tokens=1024, system=AGENTS[active_agent], tools=handoff_tools, messages=messages ) if response.stop_reason == "end_turn": return response.content[0].text # Handoff-Tool wurde aufgerufen → Agent wechseln tool_use = next((b for b in response.content if b.type == "tool_use"), None) if tool_use and tool_use.name.startswith("transfer_to_"): new_agent = tool_use.name.replace("transfer_to_", "") active_agent = new_agent # Kontext für neuen Agent aufbauen messages.append({"role": "user", "content": tool_use.input.get("context", "")})
Kostenstrategie: Modell-Mix

Manager-Agenten brauchen starke Reasoning-Fähigkeiten — hier lohnt sich Claude Opus. Worker-Agenten führen meist einfachere, wiederholbare Tasks aus — Claude Haiku ist 30x günstiger und für viele Worker-Tasks ausreichend.


6. Production Agents: Observability & Reliability

Ein Agent der lokal funktioniert ist eine Demo. Ein Agent der 24/7 in der Produktion läuft ist ein Produkt. Der Weg dazwischen führt über Observability, Retry-Logik, Human-in-the-Loop und Cost Tracking.

Production

Observability — LangSmith & Langfuse

Ohne Tracing ist Debugging ein Ratespiel. LangSmith (von LangChain) und Langfuse (Open-Source) tracken jeden LLM-Call, Tool-Aufruf und Token-Verbrauch. Beide unterstützen Claude nativ.

Langfuse Integration — vollständiges Tracing

Python
from langfuse import Langfuse from langfuse.decorators import observe, langfuse_context from anthropic import Anthropic langfuse = Langfuse( public_key="pk-lf-...", secret_key="sk-lf-...", host="https://cloud.langfuse.com" ) client = Anthropic() @observe() # Decorator für automatisches Tracing def research_with_tracing(query: str) -> str: # Metadaten zum aktuellen Trace hinzufügen langfuse_context.update_current_trace( name="research-agent", user_id="user-123", tags=["production", "v2.1"], metadata={"query": query} ) response = client.messages.create( model="claude-haiku-4-5", max_tokens=2048, messages=[{"role": "user", "content": query}] ) # Score für RLHF / Qualitäts-Monitoring langfuse_context.score_current_trace( name="response-quality", value=0.9, comment="Auto-evaluation: coherent response" ) return response.content[0].text

Retry-Logik mit exponentialem Backoff

Python
import time import random from anthropic import Anthropic, RateLimitError, APIStatusError client = Anthropic() def call_with_retry( messages: list, max_retries: int = 5, base_delay: float = 1.0 ) -> str: """API-Call mit exponentiellem Backoff + Jitter.""" for attempt in range(max_retries): try: response = client.messages.create( model="claude-haiku-4-5", max_tokens=2048, messages=messages ) return response.content[0].text except RateLimitError: if attempt == max_retries - 1: raise # Exponentieller Backoff mit Jitter delay = base_delay * (2 ** attempt) + random.uniform(0, 1) print(f"Rate limited. Retry {attempt+1}/{max_retries} in {delay:.1f}s") time.sleep(delay) except APIStatusError as e: if e.status_code >= 500: # Server-Fehler → retry if attempt == max_retries - 1: raise delay = base_delay * (2 ** attempt) time.sleep(delay) else: raise # Client-Fehler (400er) → nicht retry

Human-in-the-Loop — der Safety-Schalter

Nicht jede Agent-Entscheidung sollte vollautomatisch durchgehen. Human-in-the-Loop (HITL) pausiert den Agenten an kritischen Punkten und wartet auf menschliche Freigabe — ideal für irreversible Aktionen wie E-Mail-Versand, Deployments oder Datenbankänderungen.

TypeScript
// Human-in-the-Loop mit LangGraph interrupt_before import { interrupt } from '@langchain/langgraph'; async function deploymentNode(state: AgentState) { // Vor kritischer Aktion: Mensch fragen const approval = interrupt({ message: `Soll ich deployen? Änderungen:\n${state.diff}`, options: ['deploy', 'abort', 'review_first'] }); if (approval === 'abort') { return { ...state, status: 'aborted_by_human' }; } if (approval === 'review_first') { return { ...state, status: 'needs_review' }; } // Freigabe erteilt — weiter deployen await deployToProduction(state.artifacts); return { ...state, status: 'deployed' }; }

Cost Tracking — Token-Verbrauch im Griff behalten

Python
from dataclasses import dataclass, field from datetime import datetime # Claude Preise (Mai 2026, in USD per 1M Tokens) PRICING = { "claude-opus-4-5": {"input": 15.0, "output": 75.0}, "claude-sonnet-4-5": {"input": 3.0, "output": 15.0}, "claude-haiku-4-5": {"input": 0.8, "output": 4.0}, } @dataclass class CostTracker: session_id: str total_input_tokens: int = 0 total_output_tokens: int = 0 total_cost_usd: float = 0.0 calls: list = field(default_factory=list) def track(self, response, model: str): if model not in PRICING: return inp = response.usage.input_tokens out = response.usage.output_tokens price = PRICING[model] cost = (inp * price["input"] + out * price["output"]) / 1_000_000 self.total_input_tokens += inp self.total_output_tokens += out self.total_cost_usd += cost self.calls.append({ "ts": datetime.utcnow().isoformat(), "model": model, "tokens": inp + out, "cost_usd": cost }) def summary(self) -> str: return ( f"Session {self.session_id}: " f"{self.total_input_tokens + self.total_output_tokens:,} Tokens | " f"${self.total_cost_usd:.4f} USD | " f"{len(self.calls)} API-Calls" )
Achtung: Prompt Caching nutzen!

Das Anthropic SDK unterstützt Prompt Caching für System-Prompts die sich wiederholen. Mit cache_control: {"type": "ephemeral"} können System-Prompt-Tokens gecacht werden — das spart bis zu 90% der Input-Token-Kosten bei langen Loops.

Production-Checkliste für AI-Agenten

Checklist

Vor dem Go-Live prüfen


Fazit: Die richtige Architektur für deinen Agent

AI-Agenten-Architekturen sind 2026 kein Hexenwerk mehr — die Bausteine sind klar. ReAct für den Grundloop, Tool Use für Aktionsfähigkeit, Memory für Persistenz, LangGraph für komplexe Workflows, Multi-Agent für Skalierung und Observability für die Produktion.

Der Schlüssel liegt nicht in der ausgeklügeltsten Architektur, sondern in der richtigen Architektur für den jeweiligen Use Case. Ein einfacher ReAct-Loop mit drei Tools schlägt einen überarchitektierten LangGraph-Workflow wenn das Problem einfach ist.

Claude Code selbst ist das beste Beispiel: Er kombiniert alle sechs Patterns — ReAct-Loop, Tool Use (Bash, Edit, Read, Grep), Memory (MEMORY.md + Qdrant), Checkpoint-basiertes State-Management, Multi-Agent-Delegation und Produktions-Observability — zu einem autonomen System, das rund um die Uhr arbeitet.

Baue deinen ersten Production Agent

Starte mit SpockyMagicAI und baue autonome AI-Agenten die echte Ergebnisse liefern — von ReAct bis Multi-Agent-Orchestrierung.

Kostenlos starten — 14 Tage Trial