Questa guida spiega come trasmettere i messaggi in modo incrementale in Google Chat utilizzando la risorsa StreamMessage nell'API Chat. Lo streaming consente alla tua app Google Chat di visualizzare indicatori di avanzamento in tempo reale, passaggi di ragionamento intermedio o di esecuzione degli strumenti e risposte di testo incrementali prima di finalizzare un messaggio.
Lo streaming è progettato per le app di Chat basate sull'AI generativa o per i workflow di backend a lunga esecuzione in cui la generazione di una risposta completa richiede diversi secondi o minuti.
Come funziona lo streaming dei messaggi
Un messaggio in streaming passa attraverso uno stato di streaming attivo prima di diventare una risorsa Message standard in uno spazio:
- Avvia un flusso (
spaces.streamMessages.create): la tua app di Chat crea unStreamMessagein uno spazio con thread, facendo riferimento al messaggio di interazione dell'utente a cui sta rispondendo. L'API visualizza un indicatore di avanzamento in tempo reale nel thread e restituisce un nome della risorsaStreamMessagee unnextChunkTokeniniziale. - Aggiorna lo stream (
appendStreamMessageContentopatch): man mano che l'app Chat genera l'output, invia aggiornamenti sequenziali utilizzando l'ultimonextChunkToken:- Chiama
spaces.streamMessages.appendStreamMessageContentper concatenare nuovi blocchi di testo, aggiungere passaggi intermedi, aggiungere citazioni in linea o aggiornareprogressText. - Chiama
spaces.streamMessages.patchper sostituire i blocchi di contenuti in streaming o aggiornaprogressTextin loco.
- Chiama
- Finalizza o ignora lo stream (
commitodelete):- Chiama
spaces.streamMessages.commitper finalizzare lo stream e convertirlo in una risorsaMessagepermanente. Puoi anche allegare elementi di solo commit, come schede, widget accessori e fonti a piè di pagina, durante il commit. - Chiama
spaces.streamMessages.deleteper eliminare uno stream attivo senza salvare un messaggio nello spazio.
- Chiama
Mentre uno stream è attivo, solo il mittente del messaggio di riferimento (referencedMessage) su cui viene eseguito lo streaming può fare clic su Interrompi per annullare la generazione. Chat invia un evento di comando di sistema di annullamento alla tua app di Chat, in modo che tu possa interrompere il lavoro di backend ed eseguire il commit dell'output parziale o eliminare lo stream.
Prerequisiti
Per trasmettere messaggi in streaming con l'API Chat, completa i seguenti prerequisiti:
- Un account Google Workspace Business o Enterprise con accesso a Google Chat.
- Configura l'ambiente:
- Crea un progetto Google Cloud.
- Attiva e configura l'API Google Chat con un nome, un'icona e una descrizione per la tua app di Chat.
- Crea le credenziali del service account e salvale come
credentials.json. Lo streaming dei messaggi richiede l'autenticazione dell'app con l'ambito di autorizzazionehttps://www.googleapis.com/auth/chat.bot; l'autenticazione dell'utente non è supportata.
- Aggiungi l'app di Chat a uno spazio di Chat con thread (o a un messaggio diretto 1:1 che contiene già almeno un messaggio) e ottieni il nome della risorsa di un messaggio accessibile a cui rispondere (
spaces/SPACE_ID/messages/MESSAGE_ID). Per gli esempi di Python, installa la libreria client delle API di Google per Python e
google-auth, quindi inizializza il client del servizio API Chat:pip install google-api-python-client google-authfrom google.oauth2 import service_account from googleapiclient.discovery import build SCOPES = ["https://www.googleapis.com/auth/chat.bot"] creds = service_account.Credentials.from_service_account_file( "credentials.json", scopes=SCOPES ) chat = build("chat", "v1", credentials=creds)
Avvio di un flusso
Per avviare lo streaming di una risposta, chiama spaces.streamMessages.create e passa un StreamMessage nel corpo della richiesta:
referencedMessage(obbligatorio): nome della risorsa del messaggio che ha attivato la risposta, formattato comespaces/{space}/messages/{message}. Il messaggio a cui viene fatto riferimento deve appartenere allo stesso spazio ed essere accessibile alla tua app di Chat. Chat pubblica la risposta in streaming nello stesso thread direferencedMessage.progressText(obbligatorio): etichetta di stato in testo normale non vuota (fino a 250 caratteri) mostrata accanto all'indicatore di avanzamento, ad esempio"Thinking...".markupSyntax(Facoltativo): sintassi di formattazione del testo per il messaggio, ad esempioMARKUP_SYNTAX_MARKDOWNoMARKUP_SYNTAX_CHAT(per impostazione predefinita, la sintassi configurata dell'app di Chat seMARKUP_SYNTAX_UNSPECIFIED). ImpostaMARKUP_SYNTAX_MARKDOWNse prevedi di includere citazioni in linea.streamContentChunks(facoltativo): blocchi inizialitext,stepocitationda visualizzare immediatamente.
Salva sia name (spaces/{space}/streamMessages/{stream_message}) sia
nextChunkToken dalla risposta per le chiamate successive.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN"
}
Python
stream = (
chat.spaces()
.streamMessages()
.create(
parent="spaces/SPACE_ID",
body={
"referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
},
)
.execute()
)
stream_name = stream["name"]
chunk_token = stream["nextChunkToken"]
Il metodo restituisce una risorsa StreamMessage:
{
"name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
"nextChunkToken": "INITIAL_CHUNK_TOKEN"
}
Aggiungi blocchi di contenuti
Man mano che l'app di Chat produce passaggi intermedi o token di testo, chiama spaces.streamMessages.appendStreamMessageContent:
- Passa l'ultimo
nextChunkTokeninchunkToken. Ogni chiamata riuscita restituisce un nuovonextChunkTokenche devi passare nella richiesta di aggiornamento o commit successiva. - I chunk
textvengono concatenati direttamente al testo precedentemente trasmesso in streaming. Includi spazi o ritorni a capo dove necessario tra i chunk. - I chunk
stepvengono aggiunti all'elenco dei passaggi intermedi mostrati nel messaggio. progressTextè facoltativo. Se omettiprogressTexto lo imposti su"", Chat continua a visualizzare il valoreprogressTextimpostato in precedenza.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:appendStreamMessageContent HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"chunkToken": "INITIAL_CHUNK_TOKEN",
"progressText": "Searching documentation...",
"streamContentChunks": [
{
"step": {
"title": "Search knowledge base",
"description": "Found 3 matching articles."
}
},
{
"text": "See <chat-citation data-id=\"c1\">guide</chat-citation>:\n"
},
{
"citation": {
"id": "c1",
"citedSources": [
{
"title": "System Architecture Guide",
"uri": "https://example.com/docs/arch"
}
]
}
}
]
}
Python
stream = (
chat.spaces()
.streamMessages()
.appendStreamMessageContent(
name=stream_name,
body={
"chunkToken": chunk_token,
"progressText": "Searching documentation...",
"streamContentChunks": [
{
"step": {
"title": "Search knowledge base",
"description": "Found 3 matching articles.",
}
},
{
"text": (
'See <chat-citation data-id="c1">'
"guide</chat-citation>:\n"
)
},
{
"citation": {
"id": "c1",
"citedSources": [
{
"title": "System Architecture Guide",
"uri": "https://example.com/docs/arch",
}
],
}
},
],
},
)
.execute()
)
chunk_token = stream["nextChunkToken"]
Sostituisci i contenuti in streaming o il testo di avanzamento
Se devi riscrivere contenuti precedentemente trasmessi in streaming, ad esempio dopo aver eseguito un filtro di post-elaborazione o di sicurezza, o aggiornare solo progressText senza aggiungere chunk, chiama spaces.streamMessages.patch:
- Imposta
updateMasksu un elenco separato da virgole di campi da aggiornare:progressText,streamContentChunksoprogressText,streamContentChunks. - Includi l'ultimo
nextChunkTokennel corpo della richiestaStreamMessagee registra il nuovonextChunkTokenrestituito nella risposta. - Quando
streamContentChunksè inupdateMask, i chunk forniti sostituiscono i contenuti precedentemente trasmessi in streaming anziché aggiungersi. Quando
progressTextè inupdateMask,progressTextdeve essere un testo normale non vuoto di massimo 250 caratteri.
HTTP
PATCH https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID?updateMask=progressText,streamContentChunks HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"nextChunkToken": "LATEST_CHUNK_TOKEN",
"progressText": "Formatting response...",
"streamContentChunks": [
{
"text": "Updated response text."
}
]
}
Python
stream = (
chat.spaces()
.streamMessages()
.patch(
name=stream_name,
updateMask="progressText,streamContentChunks",
body={
"nextChunkToken": chunk_token,
"progressText": "Formatting response...",
"streamContentChunks": [
{"text": "Updated response text."}
],
},
)
.execute()
)
chunk_token = stream["nextChunkToken"]
Finalizza uno stream
Al termine della generazione, chiama spaces.streamMessages.commit con l'ultimo
chunkToken per finalizzare lo stream in una risorsa
Message
permanente:
- Se vuoi, puoi includere i
streamContentChunksfinali nella richiesta di commit, inclusi i tipi di chunk di solo commit (card,accessoryWidgetecitedSource). Se hai già eseguito lo streaming di contenuti non vuoti in chiamate precedenti, puoi omettere
streamContentChunksdurante il commit. Il messaggio finale di commit non può essere vuoto.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:commit HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"chunkToken": "LATEST_CHUNK_TOKEN",
"streamContentChunks": [
{
"text": "All checks passed."
},
{
"citedSource": {
"title": "Deployment Runbook",
"uri": "https://example.com/docs/runbook"
}
},
{
"card": {
"cardId": "details-card",
"card": {
"header": {
"title": "Build Status"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "All 42 checks succeeded."
}
}
]
}
]
}
}
}
]
}
Python
status_card = {
"cardId": "details-card",
"card": {
"header": {"title": "Build Status"},
"sections": [
{
"widgets": [
{"textParagraph": {"text": "All 42 checks succeeded."}}
]
}
],
},
}
commit_response = (
chat.spaces()
.streamMessages()
.commit(
name=stream_name,
body={
"chunkToken": chunk_token,
"streamContentChunks": [
{"text": "All checks passed."},
{
"citedSource": {
"title": "Deployment Runbook",
"uri": "https://example.com/docs/runbook",
}
},
{"card": status_card},
],
},
)
.execute()
)
final_message = commit_response["message"]
print(f"Committed message: {final_message['name']}")
Ignora uno stream attivo
Se si verifica un errore irreversibile o un utente annulla la generazione e non vuoi lasciare una risposta parziale nel thread, chiama spaces.streamMessages.delete:
HTTP
DELETE https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Python
chat.spaces().streamMessages().delete(name=stream_name).execute()
Una volta eseguito il commit di uno stream in un Message, chiama
spaces.messages.delete se devi
eliminare il messaggio finalizzato.
Tipi di segmenti di contenuti supportati
Ogni voce in streamContentChunks specifica un campo nell'unione StreamContentChunk. Alcuni tipi di chunk possono essere inviati durante lo stream,
mentre altri sono accettati solo in spaces.streamMessages.commit:
| Campo Chunk | Metodi supportati | Campo Message finale |
Note |
|---|---|---|---|
text |
create, appendStreamMessageContent,
patch, commit |
text |
Testo incrementale. Concatenato il giorno appendStreamMessageContent; sostituito il giorno patch.
Il testo finale confermato non può superare i 32.000 byte. |
step |
create, appendStreamMessageContent,
patch, commit |
elements.steps.steps[] |
Passaggio di elaborazione intermedio con testo normale title
(obbligatorio) e description facoltativo. |
citation |
create, appendStreamMessageContent,
patch, commit |
elements.citations[] |
Citation in linea con un id alfanumerico (meno di 63 caratteri, trattini bassi consentiti) e citedSources. Richiede i tag markupSyntax: "MARKUP_SYNTAX_MARKDOWN" e <chat-citation data-id="ID">...</chat-citation> corrispondenti in text. L'invio di un citation con un id esistente sovrascrive la citazione precedente. |
card |
Solo commit |
cardsV2[] |
Scheda interattiva o informativa CardWithId. |
accessoryWidget |
Solo commit |
accessoryWidgets[] |
Pulsanti AccessoryWidget visualizzati sotto il messaggio,
ad esempio i pulsanti di feedback. |
citedSource |
Solo commit |
elements.citedSources[] |
Link nel piè di pagina CitedSource visualizzato sotto il messaggio
senza tag di citazione in linea. |
Gestisci l'annullamento dello stream avviato dall'utente
Mentre uno stream è attivo, solo il mittente del messaggio di riferimento (referencedMessage) su cui viene eseguito lo streaming può fare clic su Interrompi nel messaggio di streaming. Quando l'utente interrompe uno stream, Chat invia un evento di interazione con un comando di sistema contenente cancelStream.streamMessage (il nome della risorsa spaces/{space}/streamMessages/{stream_message} dello stream interrotto) all'endpoint dell'app Chat.
La struttura del payload JSON in entrata dipende dal fatto che tu crei l'app di Chat come componente aggiuntivo di Google Workspace o utilizzando gli eventi di interazione dell'API Chat:
Componenti aggiuntivi di Google Workspace
{
"commonEventObject": {
"hostApp": "CHAT"
},
"chat": {
"user": {
"name": "users/USER_ID"
},
"space": {
"name": "spaces/SPACE_ID"
},
"systemCommandPayload": {
"systemCommandMetadata": {
"cancelStream": {
"streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
}
}
}
}
}
API Google Chat
{
"type": "SYSTEM_COMMAND",
"space": {
"name": "spaces/SPACE_ID"
},
"user": {
"name": "users/USER_ID"
},
"systemCommandMetadata": {
"cancelStream": {
"streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
}
}
}
Quando l'app di Chat riceve un evento cancelStream:
- Invia un segnale al worker in background per interrompere la generazione di altri token o l'esecuzione di strumenti downstream per
streamMessage. - Risolvi lo stream attivo utilizzando uno dei seguenti approcci:
- Mantieni l'output parziale: chiama
spaces.streamMessages.commitcon l'ultimochunkToken(facoltativamente aggiungendo una nota come"\n\n_Generation stopped._"). - Ignora la risposta: chiama
spaces.streamMessages.deletein modo che non venga salvato nulla nel thread.
- Mantieni l'output parziale: chiama
- Restituisci una risposta JSON HTTP
200 OKvuota ({}) dal gestore di eventi di interazione.
Limitazioni
Il flusso di messaggi presenta le seguenti limitazioni:
- Disponibilità della piattaforma: il flusso di messaggi è visibile solo in Chat sul web. Il supporto per Android e iOS sarà disponibile a breve.
- Timeout dello stream: un
StreamMessageattivo scade 10 minuti dopo la creazione. L'app di Chat deve chiamarespaces.streamMessages.commitospaces.streamMessages.deleteentro 10 minuti dall'avvio dello stream.