Streaming dell'output in LangChain
Implementerà lo streaming dei token attraverso chain LCEL, così che l'applicazione mostri ogni parola non appena arriva invece di attendere la risposta completa, migliorando la latenza percepita.
Streaming dell'output in LangChain è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.
Perché lo streaming è importante
Senza streaming, gli utenti fissano uno schermo vuoto mentre attendono che l'LLM termini la generazione, un'attesa che per le risposte lunghe può durare 5–30 secondi. Con lo streaming, i token vengono visualizzati man mano che sono generati, fornendo un feedback immediato. Questo migliora notevolmente la reattività percepita. L'LCEL di LangChain propaga automaticamente lo streaming attraverso l'intera catena quando chiama .stream().
Streaming di base con .stream()
Ogni catena LCEL espone un metodo .stream() che restituisce un iteratore di chunk. Per una catena che termina con StrOutputParser, ogni chunk è un frammento di stringa. È possibile iterare sui chunk e stamparli o restituirli man mano che arrivano. Lo streaming avviene a livello HTTP: ogni token proveniente dall'API OpenAI viene inoltrato attraverso il parser non appena arriva.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
chain = (
ChatPromptTemplate.from_template('Explain {topic} in detail.')
| ChatOpenAI(model='gpt-4o-mini')
| StrOutputParser()
)
# Stream tokens to stdout
for chunk in chain.stream({'topic': 'quantum entanglement'}):
print(chunk, end='', flush=True)
print() # final newlineStreaming asincrono con .astream()
.astream() è la versione asincrona di .stream(). Restituisce un iteratore asincrono che si consuma con async for. Questo è l'approccio corretto in FastAPI, Starlette e altri framework web asincroni, dove il gestore delle richieste è una coroutine. L'uso dello streaming sincrono in un gestore asincrono bloccherebbe l'event loop.
import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
chain = (
ChatPromptTemplate.from_template('Write a poem about {subject}')
| ChatOpenAI(model='gpt-4o-mini')
| StrOutputParser()
)
async def stream_response():
async for chunk in chain.astream({'subject': 'the ocean'}):
print(chunk, end='', flush=True)
asyncio.run(stream_response())Streaming in FastAPI con StreamingResponse
In FastAPI, racchiuda un generatore asincrono in StreamingResponse con media_type='text/plain' per inviare in streaming i token di testo al browser. Per gli eventi inviati dal server (SSE), utilizzi media_type='text/event-stream' e formatti ogni chunk come data: ...\n\n. Il browser riceverà così i token man mano che vengono generati, senza attendere la risposta completa.
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def generate_stream(topic: str):
async for chunk in chain.astream({'topic': topic}):
yield chunk
@app.get('/stream')
async def stream_endpoint(topic: str):
return StreamingResponse(
generate_stream(topic),
media_type='text/plain'
)
# SSE format for frontend EventSource
async def sse_stream(topic: str):
async for chunk in chain.astream({'topic': topic}):
yield f'data: {chunk}\n\n'Streaming attraverso i passaggi intermedi
Le catene LCEL propagano lo streaming attraverso ogni passaggio che lo supporta. StrOutputParser è compatibile con lo streaming e inoltra immediatamente i chunk. Tuttavia, alcuni parser, come JsonOutputParser, devono memorizzare l'output completo prima di analizzarlo, interrompendo lo streaming. LangChain rende questo comportamento esplicito: se un passaggio non è compatibile con lo streaming, accumula l'output prima di passarlo a valle.
from langchain_core.output_parsers import JsonOutputParser
# This chain does NOT stream token by token
# JsonOutputParser must buffer the full response before parsing JSON
json_chain = (
ChatPromptTemplate.from_template('Return JSON: {task}')
| ChatOpenAI(model='gpt-4o-mini')
| JsonOutputParser() # buffers until complete
)
# But partial JSON streaming IS possible with streaming_json_parser
for partial in json_chain.stream({'task': 'list 3 colors'}):
print(partial) # prints partial dict as it fills inastream_events per un controllo dettagliato
.astream_events() offre un'API di streaming più granulare che emette eventi per ogni passaggio della catena, non solo per l'output finale. Ogni evento contiene un campo kind (on_chain_start, on_llm_stream, on_chain_end) e un payload data. Questo consente di trasmettere separatamente i risultati delle chiamate agli strumenti, il ragionamento intermedio e l'output finale a diverse parti dell'interfaccia.
async def stream_with_events(question: str):
async for event in chain.astream_events(
{'question': question},
version='v2'
):
kind = event['event']
if kind == 'on_llm_stream':
chunk = event['data']['chunk'].content
print(chunk, end='', flush=True)
elif kind == 'on_chain_end':
print('\n[Done]')
elif kind == 'on_tool_start':
print(f'\n[Tool: {event["name"]}]')Bufferizzare l'output trasmesso in streaming
A volte è necessario sia trasmettere i token all'utente sia acquisire la risposta completa per la registrazione o un'ulteriore elaborazione. Utilizzi .astream() con un accumulatore basato su una lista. Unisca i chunk al termine del ciclo per ottenere il testo completo. Questo schema consente di mostrare l'output in streaming in tempo reale e di memorizzare contemporaneamente la risposta completa per analisi, caching o valutazione.
async def stream_and_capture(question: str) -> str:
full_response = []
async for chunk in chain.astream({'question': question}):
print(chunk, end='', flush=True) # stream to user
full_response.append(chunk) # also collect
print() # newline
complete = ''.join(full_response)
await log_response(question, complete) # log full text
return completeStreaming con chiamate agli strumenti
Quando un modello genera una tool call in una risposta trasmessa in streaming, gli argomenti della funzione arrivano come frammenti di token. Deve memorizzare nel buffer la stringa JSON degli argomenti finché la chiamata allo strumento non è completa, prima di eseguirla. LangChain gestisce automaticamente questo aspetto nei propri agent executor, ma se sta creando un ciclo di streaming personalizzato deve controllare finish_reason e accumulare i frammenti di tool_call.function.arguments.
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def stream_with_tools(prompt: str):
tool_call_buffer = {}
async with client.chat.completions.stream(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
tools=[weather_tool_schema]
) as stream:
async for chunk in stream:
delta = chunk.choices[0].delta
if delta.tool_calls:
for tc in delta.tool_calls:
idx = tc.index
if idx not in tool_call_buffer:
tool_call_buffer[idx] = ''
if tc.function.arguments:
tool_call_buffer[idx] += tc.function.argumentsAnnullamento e timeout durante lo streaming
Le risposte trasmesse a lungo in streaming richiedono il supporto all'annullamento. In Python asincrono, può annullare un asyncio.Task che avvolge lo stream. In FastAPI, il framework gestisce automaticamente l'annullamento dovuto alla disconnessione del client quando si utilizza StreamingResponse. Imposti un timeout tramite il parametro timeout del client OpenAI oppure racchiuda lo stream in asyncio.wait_for() per interromperlo dopo una durata massima.
import asyncio
async def stream_with_timeout(question: str, timeout: float = 30.0):
async def _stream():
async for chunk in chain.astream({'question': question}):
yield chunk
try:
async for chunk in asyncio.timeout(_stream(), timeout):
print(chunk, end='', flush=True)
except asyncio.TimeoutError:
print('\n[Stream timed out after 30 seconds]')
except asyncio.CancelledError:
print('\n[Stream cancelled by client disconnect]')SSE lato client con JavaScript
Nel frontend, l'API nativa EventSource API del browser consuma gli eventi inviati dal server. Quando l'endpoint FastAPI emette chunk data: token\n\n, EventSource genera un evento message per ciascuno di essi. Aggiunga ogni token al DOM non appena arriva per creare un effetto macchina da scrivere. Per un controllo maggiore, fetch() con response.body.getReader() offre accesso completo allo streaming.
// Frontend JavaScript (not Python)
const source = new EventSource('/stream?topic=quantum+computing');
const outputDiv = document.getElementById('output');
source.onmessage = (event) => {
outputDiv.textContent += event.data;
};
source.onerror = () => {
source.close();
outputDiv.textContent += ' [done]';
};
// Alternative: fetch with ReadableStream
const response = await fetch('/stream?topic=ai');
const reader = response.body.getReader();
while (true) {
const {done, value} = await reader.read();
if (done) break;
outputDiv.textContent += new TextDecoder().decode(value);
}Buone pratiche per lo streaming
Segua queste buone pratiche quando implementa lo streaming: utilizzi sempre flush=True durante la stampa su stdout per evitare il buffering. Imposti stream_usage=True se ha bisogno di conteggi accurati dei token durante lo streaming. Emetta un indicatore data: [DONE]\n\n al termine degli stream SSE, così il client saprà quando chiudere la connessione. Verifichi gli endpoint di streaming con curl --no-buffer per assicurarsi che i token arrivino progressivamente.
# Complete SSE endpoint with DONE sentinel
async def sse_generator(question: str):
try:
async for chunk in chain.astream({'question': question}):
# Escape any newlines in the chunk
safe_chunk = chunk.replace('\n', ' ')
yield f'data: {safe_chunk}\n\n'
finally:
yield 'data: [DONE]\n\n'
@app.get('/chat/stream')
async def chat_stream(question: str):
return StreamingResponse(
sse_generator(question),
media_type='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'}
)Verifica rapida
Verifichi la Sua comprensione dello streaming dell'output in LangChain.
Riepilogo della lezione
In questa lezione ha imparato che stream() e astream() consentono di iterare sui chunk di token man mano che vengono generati, eliminando la lunga attesa per la risposta completa; StreamingResponse in FastAPI, con il formato SSE, trasmette i token in tempo reale ai client browser; e astream_events() offre hook per eventi dettagliati relativi a ogni passaggio della catena, comprese le chiamate agli strumenti e gli output intermedi. Ora esamineremo la gestione della memoria per le conversazioni a più turni.
Domande Frequenti
La lezione «Streaming dell'output in LangChain» è gratuita?
Sì — il testo completo di «Streaming dell'output in LangChain» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.
Cosa imparerò in «Streaming dell'output in LangChain»?
Implementerà lo streaming dei token attraverso chain LCEL, così che l'applicazione mostri ogni parola non appena arriva invece di attendere la risposta completa, migliorando la latenza percepita. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Engineering Academy?
Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Streaming dell'output in LangChain»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?
Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Architettura di LangChain e astrazioni fondamentali
- Creare chain con LCEL
- Chain ramificate e parallele
- Streaming dell'output in LangChain