Streaming von Nachrichten
Streame Antworten der Messages API inkrementell mit Server-Sent Events, einschließlich Text-, Tool-Nutzungs- und erweiterten Denk-Deltas.
Beim Erstellen einer Message kannst du "stream": true setzen, um die Antwort inkrementell mithilfe von Server-Sent Events (SSE) zu streamen.
Streaming mit SDKs
Das Python SDK und das TypeScript SDK bieten mehrere Möglichkeiten für „streaming“ (Streaming). Das PHP SDK stellt Streaming über createStream() bereit. Das Python SDK erlaubt sowohl synchrone als auch asynchrone Streams. Details findest du in der Dokumentation des jeweiligen SDK.
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
model="claude-opus-5-5",
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)Die finale Nachricht ohne Event-Verarbeitung erhalten
Wenn du Text nicht verarbeiten musst, während er eintrifft, bieten die SDKs eine Möglichkeit, Streaming intern zu verwenden und dabei das vollständige Message-Objekt zurückzugeben, identisch mit dem, was .create() zurückgibt. Dies ist besonders nützlich für Anfragen mit großen max_tokens-Werten, bei denen die SDKs Streaming erfordern, um HTTP-Timeouts zu vermeiden.
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=128000,
messages=[{"role": "user", "content": "Write a detailed analysis..."}],
model="claude-opus-5-5",
) as stream:
message = stream.get_final_message()
for block in message.content:
if block.type == "text":
print(block.text)Der .stream()-Aufruf hält die HTTP-Verbindung mit Server-Sent Events offen, anschließend sammelt .get_final_message() (Python) bzw. .finalMessage() (TypeScript) alle Events und gibt das vollständige Message-Objekt zurück. In Go rufst du message.Accumulate(event) innerhalb der Stream-Schleife auf, um dieselbe vollständige Message aufzubauen. In Java verwendest du MessageAccumulator.create() und rufst accumulator.accumulate(event) für jedes Event auf. In C# wartest du mit await auf die .Aggregate()-Erweiterungsmethode des Streams, um die vollständige Message zu erhalten, oder übergibst einen MessageContentAggregator an .CollectAsync(), um während der Event-Verarbeitung zu aggregieren. In Ruby rufst du .accumulated_message auf dem Stream auf. Im PHP SDK iterierst du manuell über die Stream-Events, um die Antwort zu akkumulieren.
Event-Typen
Jedes Server-Sent Event enthält einen benannten Event-Typ und zugehörige JSON-Daten. Jedes Event verwendet einen SSE-Event-Namen (zum Beispiel event: message_stop) und enthält den passenden Event-type in seinen Daten.
Jeder Stream verwendet den folgenden Event-Ablauf:
message_start: enthält einMessage-Objekt mit leeremcontent. Unter dem Beta-Headerthinking-binding-controls-2026-08-01trägt diesesMessage-Objekt zusätzlich das Arrayinput_transformations. Nach einem serverseitigen Fallback mitten im Stream trägt das finalemessage_delta-Event das Array erneut mit den Einträgen des ausliefernden Modells.- Eine Reihe von Content-Blöcken, von denen jeder ein
content_block_start-Event, ein oder mehrerecontent_block_delta-Events und eincontent_block_stop-Event hat. Jeder Content-Block hat einenindex, der seinem Index imcontent-Array der finalen Message entspricht. Eine Ausnahme: Bei Antworten mit serverseitigem Fallback trifft an jeder Modellgrenze einfallback-Content-Block als Paar auscontent_block_startundcontent_block_stopohne Deltas dazwischen ein. - Ein oder mehrere
message_delta-Events, die Änderungen auf oberster Ebene am finalenMessage-Objekt anzeigen. - Ein abschließendes
message_stop-Event.
Ping-Events
Event-Streams können außerdem eine beliebige Anzahl von ping-Events enthalten.
Error-Events
Die API kann gelegentlich Fehler im Event-Stream senden. Beispielsweise kannst du in Zeiten hoher Auslastung einen overloaded_error erhalten, der in einem Nicht-Streaming-Kontext normalerweise einem HTTP 529 entsprechen würde:
event: error
data: {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}}Andere Events
Gemäß der Versionierungsrichtlinie können neue Event-Typen hinzugefügt werden, und dein Code sollte unbekannte Event-Typen robust behandeln.
Content-Block-Delta-Typen
Jedes content_block_delta-Event enthält ein delta eines Typs, der den content-Block an einem bestimmten index aktualisiert.
Text-Delta
Ein text-Content-Block-Delta sieht so aus:
event: content_block_delta
data: {"type": "content_block_delta","index": 0,"delta": {"type": "text_delta", "text": "ello frien"}}Input-JSON-Delta
Die Deltas für tool_use-Content-Blöcke entsprechen Aktualisierungen des input-Felds des Blocks. Um maximale Granularität zu unterstützen, sind die Deltas partielle JSON-Strings, während das finale tool_use.input immer ein Objekt ist.
Du kannst die String-Deltas akkumulieren und das JSON parsen, sobald du ein content_block_stop-Event erhältst, indem du eine Bibliothek wie Pydantic für partielles JSON-Parsing verwendest oder die SDKs nutzt, die Hilfsfunktionen für den Zugriff auf geparste inkrementelle Werte bereitstellen.
Ein tool_use-Content-Block-Delta sieht so aus:
event: content_block_delta
data: {"type": "content_block_delta","index": 1,"delta": {"type": "input_json_delta","partial_json": "{\"location\": \"San Fra"}}}Hinweis: Aktuelle Modelle unterstützen nur die Ausgabe jeweils einer vollständigen Schlüssel-Wert-Eigenschaft aus input auf einmal. Daher kann es bei der Verwendung von Tools zu Verzögerungen zwischen Streaming-Events kommen, während das Modell arbeitet. Sobald ein input-Schlüssel und -Wert akkumuliert sind, werden sie als mehrere content_block_delta-Events mit gestückeltem partiellem JSON ausgegeben, sodass das Format in zukünftigen Modellen automatisch feinere Granularität unterstützen kann.
Thinking-Delta
Wenn du Thinking mit aktiviertem Streaming verwendest, erhältst du Thinking-Inhalte über thinking_delta-Events. Diese Deltas entsprechen dem thinking-Feld der thinking-Content-Blöcke.
Für Thinking-Inhalte wird unmittelbar vor dem content_block_stop-Event ein spezielles signature_delta-Event gesendet. Diese Signatur wird verwendet, um die Integrität des Thinking-Blocks zu verifizieren.
Wenn in der Thinking-Konfiguration display: "omitted" gesetzt ist, wird kein Thinking-Text gestreamt. Der Thinking-Block wird geöffnet, erhält ein thinking_delta mit einem leeren thinking-String und anschließend ein einzelnes signature_delta und wird dann geschlossen. Mit display: "updates" (Beta) werden Reasoning-Blöcke auf dieselbe Weise gestreamt, und nur die Fortschrittsaktualisierungen, die manche Modelle zwischen Tool-Aufrufen schreiben, streamen thinking_delta-Events, die Text enthalten. Siehe Anzeige des Nachdenkens steuern.
Ein typisches Thinking-Delta sieht so aus:
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "I need to find the GCD of 1071 and 462 using the Euclidean algorithm.\n\n1071 = 2 × 462 + 147"}}Das Signatur-Delta sieht so aus:
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "EqQBCgIYAhIM1gbcDa9GJwZA2b3hGgxBdjrkzLoky3dl1pkiMOYds..."}}Vollständige HTTP-Stream-Antwort
Verwende die Client-SDKs, wenn du den Streaming-Modus nutzt. Wenn du jedoch eine direkte API-Integration baust, musst du diese Events selbst verarbeiten.
Eine Stream-Antwort besteht aus:
- Einem
message_start-Event - Möglicherweise mehreren Content-Blöcken, von denen jeder Folgendes enthält:
- Ein
content_block_start-Event - Möglicherweise mehrere
content_block_delta-Events - Ein
content_block_stop-Event
- Ein
- Einem oder mehreren
message_delta-Events - Einem
message_stop-Event
Es können außerdem ping-Events über die gesamte Antwort verteilt sein. Weitere Details zum Format findest du unter Event-Typen.
Einfache Streaming-Anfrage
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-5-5",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=256,
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)event: message_start
data: {"type": "message_start", "message": {"id": "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY", "type": "message", "role": "assistant", "content": [], "model": "claude-opus-5-5", "stop_reason": null, "stop_sequence": null, "usage": {"input_tokens": 25, "output_tokens": 1}}}
event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}
event: ping
data: {"type": "ping"}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Hello"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "!"}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 0}
event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence":null}, "usage": {"output_tokens": 15}}
event: message_stop
data: {"type": "message_stop"}
Streaming-Anfrage mit Tool-Nutzung
Diese Anfrage bittet Claude, ein Tool zu verwenden, um das Wetter zu melden.
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
}
]
with client.messages.stream(
model="claude-opus-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "any"},
messages=[
{"role": "user", "content": "What is the weather like in San Francisco?"}
],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)event: message_start
data: {"type":"message_start","message":{"id":"msg_014p7gG3wDgGV9EUtLvnow3U","type":"message","role":"assistant","model":"claude-opus-5","stop_sequence":null,"usage":{"input_tokens":472,"output_tokens":2},"content":[],"stop_reason":null}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: ping
data: {"type": "ping"}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Okay"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" let"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"'s"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" check"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" the"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" weather"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" for"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" San"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" Francisco"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" CA"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":":"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01T1x1fJ34qAmk2tNTrN7Up6","name":"get_weather","input":{}}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"location\":"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":" \"San"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":" Francisc"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"o,"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":" CA\"}"}}
event: content_block_stop
data: {"type":"content_block_stop","index":1}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":89}}
event: message_stop
data: {"type":"message_stop"}Streaming-Anfrage mit Thinking
Diese Anfrage aktiviert Thinking mit Streaming. Die Einstellung display: "summarized" streamt eine verdichtete Zusammenfassung von Claudes Überlegungen anstelle der vollständigen Gedankenkette.
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-5-5",
max_tokens=20000,
thinking={"type": "adaptive", "display": "summarized"},
messages=[
{
"role": "user",
"content": "What is the greatest common divisor of 1071 and 462?",
}
],
) as stream:
for event in stream:
if event.type == "content_block_delta":
delta = event.delta
match delta.type:
case "thinking_delta":
print(delta.thinking, end="", flush=True)
case "text_delta":
print(delta.text, end="", flush=True)event: message_start
data: {"type": "message_start", "message": {"id": "msg_01...", "type": "message", "role": "assistant", "content": [], "model": "claude-opus-5-5", "stop_reason": null, "stop_sequence": null}}
event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "", "signature": ""}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "I need to find the GCD of 1071 and 462 using the Euclidean algorithm.\n\n1071 = 2 × 462 + 147"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\n462 = 3 × 147 + 21"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\n147 = 7 × 21 + 0"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\nThe remainder is 0, so GCD(1071, 462) = 21."}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "EqQBCgIYAhIM1gbcDa9GJwZA2b3hGgxBdjrkzLoky3dl1pkiMOYds..."}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 0}
event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "text", "text": ""}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "text_delta", "text": "The greatest common divisor of 1071 and 462 is **21**."}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 1}
event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": null}}
event: message_stop
data: {"type": "message_stop"}Streaming-Anfrage mit Web-Search-Tool-Nutzung
Diese Anfrage bittet Claude, im Web nach aktuellen Wetterinformationen zu suchen.
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-5-5",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 5}],
messages=[
{"role": "user", "content": "What is the weather like in New York City today?"}
],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)event: message_start
data: {"type":"message_start","message":{"id":"msg_01G...","type":"message","role":"assistant","model":"claude-opus-5-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":2679,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":3}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"I'll check"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" the current weather in New York City for you"}}
event: ping
data: {"type": "ping"}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"."}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"server_tool_use","id":"srvtoolu_014hJH82Qum7Td6UV8gDXThB","name":"web_search","input":{}}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"query"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\":"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":" \"weather"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":" NY"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"C to"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"day\"}"}}
event: content_block_stop
data: {"type":"content_block_stop","index":1 }
event: content_block_start
data: {"type":"content_block_start","index":2,"content_block":{"type":"web_search_tool_result","tool_use_id":"srvtoolu_014hJH82Qum7Td6UV8gDXThB","content":[{"type":"web_search_result","title":"Weather in New York City in May 2025 (New York) - detailed Weather Forecast for a month","url":"https://world-weather.info/forecast/usa/new_york/may-2025/","encrypted_content":"Ev0DCioIAxgCIiQ3NmU4ZmI4OC1k...","page_age":null},...]}}
event: content_block_stop
data: {"type":"content_block_stop","index":2}
event: content_block_start
data: {"type":"content_block_start","index":3,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":3,"delta":{"type":"text_delta","text":"Here's the current weather information for New York"}}
event: content_block_delta
data: {"type":"content_block_delta","index":3,"delta":{"type":"text_delta","text":" City:\n\n# Weather"}}
event: content_block_delta
data: {"type":"content_block_delta","index":3,"delta":{"type":"text_delta","text":" in New York City"}}
event: content_block_delta
data: {"type":"content_block_delta","index":3,"delta":{"type":"text_delta","text":"\n\n"}}
...
event: content_block_stop
data: {"type":"content_block_stop","index":17}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":10682,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":510,"server_tool_use":{"web_search_requests":1}}}
event: message_stop
data: {"type":"message_stop"}Fehlerbehebung
Claude 4.5 und früher
Bei Claude-4.5-Modellen und früheren kannst du eine Streaming-Anfrage, die aufgrund von Netzwerkproblemen, Timeouts oder anderen Fehlern unterbrochen wurde, wiederherstellen, indem du an der Stelle fortsetzt, an der der Stream unterbrochen wurde. Dieser Ansatz erspart dir die erneute Verarbeitung der gesamten Antwort.
Die grundlegende Wiederherstellungsstrategie umfasst:
- Die partielle Antwort erfassen: Speichere alle Inhalte, die vor dem Auftreten des Fehlers erfolgreich empfangen wurden.
- Eine Fortsetzungsanfrage konstruieren: Erstelle eine neue API-Anfrage, die die partielle Assistant-Antwort als Beginn einer neuen Assistant-Nachricht enthält.
- Streaming fortsetzen: Empfange den Rest der Antwort ab der Stelle, an der sie unterbrochen wurde.
Claude 4.6 und später
Für Claude 4.6 und spätere Modelle gilt dieselbe Erfassen-und-Fortsetzen-Strategie, aber Schritt 2 ändert sich: Anstatt die partielle Antwort in eine Assistant-Nachricht zu legen, füge eine User-Nachricht hinzu, die das Modell anweist, dort fortzufahren, wo es aufgehört hat.
- Die partielle Antwort erfassen: Speichere alle Inhalte, die vor dem Auftreten des Fehlers erfolgreich empfangen wurden.
- Eine Fortsetzungsanfrage konstruieren: Erstelle eine neue API-Anfrage mit einer User-Nachricht, die die partielle Antwort und eine Anweisung zum Fortfahren enthält, zum Beispiel:
Sample prompt
Your previous response was interrupted and ended with [previous_response]. Continue from where you left off. - Streaming fortsetzen: Empfange den Rest der Antwort ab der Stelle, an der sie unterbrochen wurde.
Best Practices zur Fehlerbehebung
- SDK-Funktionen nutzen: Nutze die integrierten Funktionen des SDK zur Nachrichtenakkumulation und Fehlerbehandlung.
- Content-Typen berücksichtigen: Beachte, dass Nachrichten mehrere Content-Blöcke enthalten können (
text,tool_use,thinking). Blöcke für Tool-Nutzung und erweitertes Nachdenken können nicht partiell wiederhergestellt werden. Du kannst das Streaming ab dem letzten Text-Block fortsetzen.
Nächste Schritte
Behandle jeden stop_reason-Wert, sobald ein Stream abgeschlossen ist.
Streame Tool-Input-JSON ohne serverseitiges Puffern für geringere Latenz.
Streame Thinking-Ausgaben mit thinking_delta- und signature_delta-Events.
Verwende die offiziellen SDKs, die Streaming, Akkumulation und Wiederverbindung für dich übernehmen.
Verarbeite große Mengen von Anfragen asynchron, wenn du keine Echtzeit-Antworten benötigst.
Was this page helpful?