Token-Streaming verstehen
Verstehen Sie, wie die Streaming-API während der Generierung bereits fertiggestellte Teile einer Antwort sendet, wie der OpenAI-Parameter stream=True funktioniert und wann Streaming die Nutzererfahrung verbessert.
Token-Streaming verstehen ist eine kostenlose AI Engineering Academy-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Engineering Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.
Warum Streaming für die Benutzererfahrung wichtig ist
Ohne Streaming muss Ihre Anwendung warten, bis das LLM die vollständige Antwort generiert hat, bevor sie etwas anzeigen kann – bei langen Antworten oft 5 bis 30 Sekunden. Mit Streaming erscheint das erste Token innerhalb von 200 bis 500 ms nach dem Senden der Anfrage, und nachfolgende Tokens werden übertragen, sobald sie generiert wurden. Dadurch verändert sich die wahrgenommene Benutzererfahrung: Aus Warten wird ein ansprechender Live-Generierungseffekt. Die wahrgenommene Reaktionsfähigkeit verbessert sich deutlich, obwohl die gesamte Generierungszeit identisch bleibt.
Wie LLMs Tokens generieren
LLMs sind autoregressiv: Sie generieren Text Token für Token, wobei jedes neue Token von allen vorherigen Tokens abhängt. Wenn die API eine Anfrage erhält, beginnt die GPU unmittelbar nach der Verarbeitung des Prompts mit dem Sampling des ersten Tokens. Jedes weitere Token benötigt ungefähr gleich viel Zeit. Streaming sendet jedes Token unmittelbar nach dem Sampling an den Client, anstatt alle Tokens zwischenzuspeichern und erst am Ende die vollständige Zeichenkette zu senden.
# Conceptual model of autoregressive generation
prompt = 'The capital of France is'
# Step 1: process full prompt, predict next token
# token_1 = sample(logits) → ' Paris'
# Step 2: append token_1 to context, predict next
# token_2 = sample(logits) → '.'
# Step 3: append token_2 to context, predict next
# token_3 = sample(logits) → '<|end|>'
# Total time: time_to_process_prompt + n_tokens * time_per_token
# With streaming: first token arrives after time_to_process_prompt (TTFT)
# Without streaming: everything arrives after TTFT + n_tokens * time_per_tokenTTFT und TPOT: Zwei Latenzmetriken
Streaming führt zwei unterschiedliche Latenzkonzepte ein. TTFT (Time to First Token) ist die Verzögerung vom Senden der Anfrage bis zum Empfang des ersten Tokens und wird hauptsächlich durch die Prompt-Verarbeitungszeit bestimmt. TPOT (Time Per Output Token) ist die Zeit zwischen aufeinanderfolgenden Tokens und wird durch die Modellgröße und die Hardware bestimmt. TTFT beeinflusst, wie schnell die Benutzeroberfläche reagiert; TPOT beeinflusst, wie gleichmäßig der Text übertragen wird. Beide Werte sollten in Ihrem Observability-Stack getrennt erfasst werden.
import time
from openai import OpenAI
client = OpenAI()
def measure_streaming_latency(prompt: str):
t_start = time.perf_counter()
t_first_token = None
token_times = []
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
t_now = time.perf_counter()
if t_first_token is None:
t_first_token = t_now
print(f'TTFT: {(t_first_token - t_start) * 1000:.0f}ms')
else:
token_times.append(t_now - token_times[-1] if token_times else t_now - t_first_token)
token_times.append(t_now)
print(f'TPOT avg: {1000 * (token_times[-1] - t_first_token) / max(len(token_times)-1, 1):.1f}ms')Der Parameter stream=True
Um Streaming im OpenAI SDK zu aktivieren, setzen Sie stream=True im Aufruf chat.completions.create. Der Antworttyp ändert sich von einem Objekt vom Typ ChatCompletion zu einem Iterator vom Typ Stream[ChatCompletionChunk]. Jeder Chunk enthält ein delta mit entweder einem Fragment der Zeichenkette content oder None, wenn das Token ein Tool-Aufruf ist oder der Stream endet.
from openai import OpenAI
client = OpenAI()
# Non-streaming: wait for complete response
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
)
full_text = response.choices[0].message.content
# Streaming: receive tokens incrementally
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta: # delta can be None for non-content chunks
print(delta, end='', flush=True)
print() # newline at endDie vollständige Antwort sammeln
In vielen Anwendungsabläufen benötigen Sie beides: Sie müssen Tokens für eine reaktionsfähige Benutzeroberfläche streamen und den vollständigen Antworttext für nachgelagerte Verarbeitung wie Protokollierung, Caching oder weitere Pipelineschritte sammeln. Das Muster ist einfach: Iterieren Sie über den Stream, geben Sie jeden Chunk an den Client aus oder liefern Sie ihn an ihn weiter und fügen Sie den Inhalt gleichzeitig zu einer vollständigen Zeichenkette zusammen.
def stream_and_accumulate(prompt: str) -> str:
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
stream=True,
)
full_text = ''
finish_reason = None
for chunk in stream:
choice = chunk.choices[0]
delta = choice.delta.content
if delta:
print(delta, end='', flush=True) # real-time display
full_text += delta # accumulate
if choice.finish_reason:
finish_reason = choice.finish_reason
print() # newline
print(f'Finished: {finish_reason}, total chars: {len(full_text)}')
return full_textStreaming mit Nutzungsstatistiken
Standardmäßig enthält die Streaming-Antwort keine Statistiken zur Token-Nutzung (Prompt-Tokens, Completion-Tokens). Um sie einzubeziehen, übergeben Sie stream_options={'include_usage': True}. Die Nutzungsdaten treffen in einem abschließenden Chunk ein, nachdem der Inhaltsstream beendet wurde. Das ist wichtig für die Kostenverfolgung und die Überwachung von Rate-Limits in Produktionsanwendungen.
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': 'What is a vector database?'}],
stream=True,
stream_options={'include_usage': True}, # include token counts
)
full_text = ''
usage = None
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
full_text += chunk.choices[0].delta.content
if chunk.usage: # arrives in the final chunk
usage = chunk.usage
if usage:
print(f'Prompt tokens: {usage.prompt_tokens}')
print(f'Completion tokens: {usage.completion_tokens}')
print(f'Total tokens: {usage.total_tokens}')Wann Sie kein Streaming verwenden sollten
Streaming ist nicht immer die richtige Wahl. Vermeiden Sie Streaming, wenn: (1) Sie die vollständige Antwort benötigen, bevor Sie etwas damit tun, etwa für das Parsen von JSON oder die Erkennung von Tool-Aufrufen; (2) die Antwort sehr kurz ist (unter 30 Tokens), sodass der Streaming-Overhead mehr Verzögerung verursacht, als er einspart; oder (3) Sie viele Anfragen im Batch-Modus verarbeiten, wobei der Durchsatz wichtiger ist als die Latenz einzelner Antworten. In diesen Fällen sind standardmäßige Aufrufe ohne Streaming einfacher und genauso schnell.
Streaming mit den Anthropic- und Gemini-APIs
Streaming ist über die APIs aller großen LLM-Anbieter verfügbar, nicht nur über OpenAI. Das Muster ist ähnlich, aber die SDK-Schnittstellen unterscheiden sich geringfügig. Das Python-SDK von Anthropic verwendet client.messages.stream() als Kontextmanager, während Gemini generate_content(stream=True) nutzt. Wenn Sie anbieterunabhängige Anwendungen entwickeln, kapseln Sie die Streaming-Schnittstelle hinter einer gemeinsamen Generatorfunktion.
import anthropic
ant_client = anthropic.Anthropic(api_key='YOUR_KEY')
# Anthropic streaming
with ant_client.messages.stream(
model='claude-sonnet-4-5',
max_tokens=1024,
messages=[{'role': 'user', 'content': 'Explain hybrid search briefly.'}],
) as stream:
for text in stream.text_stream:
print(text, end='', flush=True)
# Final message with usage stats
final_msg = stream.get_final_message()
print(f'\nInput tokens: {final_msg.usage.input_tokens}')
print(f'Output tokens: {final_msg.usage.output_tokens}')Streaming-Schnittstelle auf Generatorbasis
Ein sauberes Architektur-Muster kapselt Streaming in einer Python-Generatorfunktion, die Token-Zeichenketten liefert. Dadurch wird die Streaming-Logik von der Verarbeitungslogik entkoppelt: Aufrufer können den Generator durchlaufen, in eine Datei schreiben, an einen WebSocket weiterleiten oder zu einer Zeichenkette zusammenfügen, ohne dass der Streaming-Code wissen muss, wie seine Ausgabe verwendet wird. Dies bildet die Grundlage der meisten produktiven Streaming-APIs.
from typing import Generator
def stream_completion(
messages: list[dict],
model: str = 'gpt-4o-mini',
**kwargs,
) -> Generator[str, None, None]:
stream = client.chat.completions.create(
model=model,
messages=messages,
stream=True,
**kwargs,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
yield delta
# Usage: pipe to stdout
for token in stream_completion([{'role': 'user', 'content': 'Hello!'}]):
print(token, end='', flush=True)
# Usage: accumulate
full = ''.join(stream_completion([{'role': 'user', 'content': 'Hello!'}]))Streaming in Terminal- und CLI-Anwendungen
In Terminalanwendungen sieht gestreamte Ausgabe genauso aus wie beim Tippen: Jedes Zeichen erscheint sofort, sobald es generiert wurde. Entscheidend ist, in jedem print-Aufruf flush=True zu verwenden. Ohne Flush puffert Python die Ausgabe bis zu einem Zeilenumbruch, wodurch der Zweck von Streaming verfehlt wird. Für eine bessere Kontrolle über die Ausgabeformatierung können Sie auch sys.stdout.write(token) gefolgt von sys.stdout.flush() verwenden.
import sys
def stream_to_terminal(messages: list[dict]):
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True,
)
token_count = 0
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
sys.stdout.write(delta) # no newline added
sys.stdout.flush() # MUST flush or output buffers
token_count += 1
print() # final newline
print(f'({token_count} tokens generated)')Streaming und Fehlerbehandlung
Streaming erschwert die Fehlerbehandlung, da ein Fehler mitten im Stream auftreten kann, nachdem Sie bereits einige Token an den Client gesendet haben. Das empfohlene Muster besteht darin, die Iteration über den Stream in einen try/except-Block einzuschließen und im Fehlerfall entweder ein Fehlersentinel an den Client zu senden oder den Stream ordnungsgemäß zu schließen. Implementieren Sie immer ein Timeout für den gesamten Stream, um Fälle zu behandeln, in denen der Server mit dem Streaming beginnt, dann aber die Generierung abbricht.
import signal
def stream_with_timeout(messages, timeout_seconds=30):
def timeout_handler(signum, frame):
raise TimeoutError('LLM stream timed out')
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds)
try:
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
yield delta
except TimeoutError:
yield '\n[Response timed out]'
except Exception as e:
yield f'\n[Error: {str(e)}]'
finally:
signal.alarm(0) # cancel timeoutKurzer Wissenstest
Testen Sie Ihr Verständnis des LLM-Token-Streamings aus dieser Lektion.
Zusammenfassung der Lektion
In dieser Lektion haben Sie gelernt: Streaming sendet jedes generierte Token unmittelbar nach dem Sampling an den Client und verbessert dadurch die wahrgenommene Reaktionsfähigkeit erheblich. TTFT und TPOT sind die beiden wichtigsten Latenzmetriken und sollten getrennt erfasst werden. stream=True ändert die Antwort des OpenAI-SDKs in einen Chunk-Iterator, den Sie mit einer for-Schleife verarbeiten. Kapseln Sie Streams in Generatorfunktionen, um eine saubere, wiederverwendbare Schnittstelle zu erhalten. Als Nächstes implementieren Sie asynchrones Streaming mit dem Python-SDK.
Häufig gestellte Fragen
Ist die Lektion „Token-Streaming verstehen“ kostenlos?
Ja — der vollständige Text von „Token-Streaming verstehen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Engineering Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Engineering Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Token-Streaming verstehen“?
Verstehen Sie, wie die Streaming-API während der Generierung bereits fertiggestellte Teile einer Antwort sendet, wie der OpenAI-Parameter stream=True funktioniert und wann Streaming die Nutzererfahru… Du übst AI Engineering Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um AI Engineering Academy zu starten?
Keine Vorkenntnisse erforderlich. AI Engineering Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.
Wie lange dauert die Lektion „Token-Streaming verstehen“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser AI Engineering Academy-Lektion Code schreiben und ausführen?
Ja. Jede AI Engineering Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Token-Streaming verstehen
- Streams mit dem Python-SDK verarbeiten
- Streaming in FastAPI mit Server-Sent Events
- Tool-Aufrufe in gestreamten Antworten verarbeiten