+200 XP

Tool use und structured outputs

# Tool use und structured outputs

Ein Modell, das nur Text ausgeben kann, ist eine Sackgasse, sobald es eine Live-Datenbank abfragen, eine API ansprechen oder saubere JSON an den nächsten Service in Ihrer pipeline übergeben soll. Tool use und structured outputs schließen diese Lücke bei Claude: Sie beschreiben Fähigkeiten, Claude entscheidet, wann es sie aufruft, und Sie bekommen Daten zurück, die Ihr Code tatsächlich verarbeiten kann.

Diese Lektion behandelt beide Mechaniken auf der Anthropic Messages API, die Request- und Response-Strukturen, die Sie wirklich senden und parsen, und wie die beiden Techniken zusammenhängen.

Was "tool use" tatsächlich bedeutet

Tool use (manchmal function calling genannt) ist ein Protokoll, keine Magie. Claude führt Ihren Code niemals aus. Sie übergeben eine Liste von Tool-Definitionen, jede mit einem Namen, einer Beschreibung und einem JSON Schema für die Inputs. Wenn Claude entscheidet, dass ein Tool hilfreich wäre, hört es auf, Prosa zu generieren, und gibt stattdessen einen strukturierten Request aus: "rufe get_weather mit {"location": "Paris"} auf." Ihre Anwendung führt diese Funktion aus, sendet das Ergebnis zurück, und Claude macht weiter.

Dieses Hin und Her ist der tool-use loop. Ihn zu verstehen, ist das ganze Spiel.

Die vier Schritte des Loops

1. Sie senden eine User-Nachricht plus Ihr tools-Array.

2. Claude antwortet mit stop_reason: "tool_use" und einem tool_use-Content-Block.

3. Sie führen das Tool aus und senden die Antwort als tool_result zurück.

4. Claude liest das Ergebnis und erzeugt seine endgültige Textantwort.

Ein konkreter get_weather-Loop

Hier ist der komplette Loop in Python mit dem offiziellen SDK. Lesen Sie ihn einmal durch, dann zerlegen wir jede Struktur.

python
import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "get_weather",
    "description": "Get current temperature for a given city.",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {"type": "string", "description": "City name, e.g. 'Paris'"}
        },
        "required": ["location"],
    },
}]

def get_weather(location):
    # Im echten Leben hier eine Wetter-API aufrufen.
    return {"location": location, "temp_c": 14, "conditions": "cloudy"}

messages = [{"role": "user", "content": "What's the weather in Paris right now?"}]

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

# Schritt 2: Claude hat ein Tool angefragt.
if response.stop_reason == "tool_use":
    tool_call = next(b for b in response.content if b.type == "tool_use")
    result = get_weather(**tool_call.input)

    # Schritt 3: Claudes Request UND Ihr Ergebnis anhängen.
    messages.append({"role": "assistant", "content": response.content})
    messages.append({
        "role": "user",
        "content": [{
            "type": "tool_result",
            "tool_use_id": tool_call.id,
            "content": str(result),
        }],
    })

    # Schritt 4: Claude liest das Ergebnis und schreibt die Antwort.
    final = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )
    print(final.content[0].text)

Die Response-Struktur lesen

Wenn Claude ein Tool will, ist response.content eine Liste von Blöcken. Der relevante sieht so aus:

json
{
  "type": "tool_use",
  "id": "toolu_01A09q90qw90lq917835lq9",
  "name": "get_weather",
  "input": {"location": "Paris"}
}

Drei Felder zählen. name sagt Ihnen, welche Funktion Sie ausführen müssen. input ist bereits ein geparstes Objekt, das Ihrem Schema entspricht, also kein String-Parsing. id ist der Handle, den Sie zurückspiegeln müssen.

Das Ergebnis zurücksenden

Das entscheidende Detail, das oft übersehen wird: Sie müssen zwei Nachrichten anhängen, in dieser Reihenfolge. Zuerst Claudes eigenen Assistant-Turn (den vollständigen response.content, inklusive des tool_use-Blocks). Dann einen User-Turn mit dem tool_result. Die tool_use_id in Ihrem Ergebnis muss exakt der id entsprechen, die Claude gesendet hat, sonst bricht der Loop.

Wenn Ihr Tool fehlschlägt, setzen Sie "is_error": true im Result-Block. Claude sieht den Fehler und kann sich entschuldigen, mit anderen Inputs erneut versuchen oder ein anderes Tool wählen.

Mehrere Tools und parallele Calls

Sie shippen selten ein einzelnes Tool. Übergeben Sie mehrere im tools-Array, und Claude routet anhand der Beschreibungen zum richtigen. Schreiben Sie diese Beschreibungen wie Dokumentation, denn sie sind das Einzige, worauf Claude seine Entscheidung stützt. "Get current temperature for a given city" schlägt "weather tool" jedes Mal.

Claude kann auch mehrere Tools in einem Turn anfragen (parallel tool use). Dann enthält response.content mehrere tool_use-Blöcke. Führen Sie alle aus und geben Sie dann alle tool_result-Blöcke gemeinsam in einer User-Nachricht zurück, bevor Sie die API erneut aufrufen.

Um zu loopen, bis Claude fertig ist (es kann mehrere Tools verketten), packen Sie den Call in einen while response.stop_reason == "tool_use"-Loop statt in ein einzelnes if.

Structured outputs: sauberes JSON zurückbekommen

Tool use löst "tu etwas". Structured outputs lösen "gib mir Daten in einer exakten Form". Nehmen wir an, Sie extrahieren Felder aus einer Support-E-Mail und brauchen jedes Mal {"category": ..., "urgency": ..., "summary": ...}, ohne Prosa, ohne Markdown-Fences.

Der zuverlässigste Trick ist, den Tool-Mechanismus selbst zu nutzen. Definieren Sie ein Tool, das Ihr gewünschtes Output-Schema repräsentiert, und erzwingen Sie dann, dass Claude es verwendet.

python
import anthropic

client = anthropic.Anthropic()

extract_tool = {
    "name": "record_ticket",
    "description": "Record the structured fields of a support ticket.",
    "input_schema": {
        "type": "object",
        "properties": {
            "category": {"type": "string", "enum": ["billing", "bug", "feature", "other"]},
            "urgency": {"type": "string", "enum": ["low", "medium", "high"]},
            "summary": {"type": "string", "description": "One sentence."},
        },
        "required": ["category", "urgency", "summary"],
    },
}

email = "I was charged twice this month and need this fixed before Friday!"

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=[extract_tool],
    tool_choice={"type": "tool", "name": "record_ticket"},
    messages=[{"role": "user", "content": email}],
)

ticket = next(b.input for b in response.content if b.type == "tool_use")
print(ticket)
# {'category': 'billing', 'urgency': 'high', 'summary': 'Customer charged twice and wants a fix by Friday.'}

Warum das funktioniert

Der Parameter tool_choice ist der Hebel. Setzen Sie ihn auf {"type": "tool", "name": "record_ticket"}, und Sie erzwingen, dass Claude genau dieses Tool aufruft, was bedeutet, dass es ein input erzeugen muss, das Ihrem Schema entspricht. Sie führen hier nie eine Funktion aus. Sie lesen einfach b.input, was bereits ein validiertes Objekt ist. Keine Code-Fences zum Entfernen, kein halbfertiges JSON.

Die enum-Constraints leisten ebenfalls echte Arbeit. Sie verhindern, dass Claude eine vierte Urgency-Stufe erfindet. Je enger Ihr Schema, desto enger Ihre Outputs.

Weitere nützliche tool_choice-Werte:

  • {"type": "auto"} (Standard): Claude entscheidet, ob es ein Tool nutzt.
  • {"type": "any"}: Claude muss *irgendein* Tool nutzen, wählt aber selbst welches.
  • {"type": "tool", "name": "..."}: erzwingt ein bestimmtes Tool, wie oben.

Die Anthropic-Docs behandeln Schema-Grenzfälle und Streaming im Detail. Der tool use guide ist die maßgebliche Referenz.

Claude Tool Use Explained

Watch on YouTube

Wissenscheck

1. Was passiert im beschriebenen tool-use-Protokoll tatsächlich, wenn Claude ein Tool "nutzt"?

2. Warum sind tool use und structured outputs wertvoll, wenn Sie Claude in eine Software-pipeline integrieren?

3. Nachdem Sie ein tool_result an Claude zurückgesendet haben, was passiert im letzten Schritt des tool-use loops?

MEHRFACHAUSWAHL

4. Wählen Sie ALLE Elemente aus, die eine einzelne Tool-Definition enthalten muss, wenn Sie Ihr tools-Array an Claude senden.

Wählen Sie alle richtigen Antworten aus.

MEHRFACHAUSWAHL

5. Wählen Sie ALLE Aussagen aus, die das Verhalten des tool-use loops korrekt beschreiben.

Wählen Sie alle richtigen Antworten aus.

Wie das mit MCP und dem Agent SDK zusammenhängt

Alles oben ist die reine API. Sie definieren Tools inline und führen sie selbst aus. Das ist perfekt für eine Handvoll maßgeschneiderter Tools innerhalb eines Services.

Aber Sie werden schnell wiederverwendbare Tools wollen, die jede Claude-Oberfläche aufrufen kann: die Desktop-App, Claude Code, Ihr eigener Agent. Genau das standardisiert MCP (Model Context Protocol). Ein MCP-Server stellt Tools (sowie Resources und Prompts) über ein definiertes Protokoll bereit, sodass Sie ein Tool einmal schreiben und in viele Clients einstecken. Die Claude-Apps zeigen diese als Connectors an, und es gibt einen Connector-Marketplace für gängige wie Google Drive oder GitHub.

Die Beziehung ist klar: MCP-Tools und API-Tools sind *dieselbe Idee* in unterschiedlichem Umfang. Die Tool-Liste eines MCP-Servers wird letztlich zu Einträgen in einem tools-Array, und der Call/Result-Handshake spiegelt den Loop, den Sie gerade gelernt haben. Lernen Sie das Protokoll auf modelcontextprotocol.io.

Das Claude Agent SDK sitzt noch eine Ebene höher. Es führt den tool-use loop für Sie aus, verwaltet den Kontext und übernimmt mehrstufiges Agent-Verhalten, sodass Sie Tools und Ziele beschreiben statt die while stop_reason == "tool_use"-Mechanik selbst zu coden. Wenn Sie einen echten Agent bauen, greifen Sie zum SDK. Wenn Sie chirurgische Kontrolle über einen einzelnen Austausch brauchen, gehen Sie auf die Messages API hinunter, wie oben gezeigt. Beide sprechen dieselbe tool-use-Sprache, und genau deshalb zahlt sich das Lernen des rohen Loops aus.

Praktische Stolperfallen

Ein paar Dinge, die Sie in der Produktion einholen werden:

  • Token-Budget. Tool-Definitionen liegen im Context Window. Zwanzig weitschweifige Tools kosten bei jedem Call echte Tokens. Halten Sie Schemas schlank und Beschreibungen präzise.
  • Validieren Sie den Input trotzdem. Claude respektiert Ihr Schema meistens, aber Sie führen mit diesen Werten echten Code aus. Behandeln Sie input wie jeden nicht vertrauenswürdigen User-Input: Typen prüfen, Wertebereiche begrenzen, bereinigen, bevor Sie auf eine Datenbank zugreifen.
  • Erzwungene Tools können nicht gleichzeitig "laut denken". Wenn Sie tool_choice auf ein bestimmtes Tool zwingen, springt Claude direkt zum Call. Wenn Sie vorher Reasoning brauchen, lassen Sie es auf auto oder teilen Sie in zwei Calls.
  • Fehler sind Signal, nicht Scheitern. is_error: true mit einer nützlichen Nachricht zurückzugeben, lässt Claude im nächsten Turn oft selbst korrigieren, was robuster ist, als Ihren Loop abstürzen zu lassen.

Zentrale Erkenntnisse

  • Tool use ist ein vierstufiger Loop: Tools senden, einen tool_use-Block empfangen, die Funktion ausführen, ein tool_result mit der passenden tool_use_id zurückgeben. Spiegeln Sie Claudes Assistant-Turn vor dem Ergebnis zurück, sonst bricht der Loop.
  • Für garantiertes JSON erzwingen Sie ein Tool. Definieren Sie Ihren Output als Tool-Schema, setzen Sie tool_choice auf dieses Tool und lesen Sie das bereits geparste input. Nutzen Sie enum und required, um die Form festzunageln.
  • Schreiben Sie Tool-Beschreibungen wie Docs. Sie sind das Einzige, worauf Claude beim Routing zurückgreift, also seien Sie spezifisch und konkret.
  • Validieren Sie Tool-Inputs immer in Ihrem Code. Claude schlägt vor; Ihre Anwendung führt aus. Behandeln Sie die Inputs als nicht vertrauenswürdig.
  • Skalieren Sie bewusst: die reine Messages API für einen maßgeschneiderten Austausch, MCP für wiederverwendbare App-übergreifende Tools, das Agent SDK, wenn der Loop für Sie verwaltet werden soll. Alle sprechen dasselbe Protokoll.

Was Sie aus dieser Lektion umsetzen

Diese Maßnahmen sind im Playbook der Rolle zusammengefasst.

  • Erzwingen Sie ein Tool-Schema, wenn Sie garantiert geparstes JSON brauchen
Vollständiges Action Playbook ansehen