Neste guia, explicamos como transmitir mensagens de forma incremental no Google Chat usando
o recurso StreamMessage na API Chat. Com o streaming, seu
app do Google Chat pode mostrar indicadores de progresso em tempo real, etapas intermediárias
de raciocínio ou execução de ferramentas e respostas de texto incrementais antes
de finalizar uma mensagem.
O streaming foi projetado para apps do Chat com tecnologia de IA generativa ou fluxos de trabalho de back-end de longa duração, em que a geração de uma resposta completa leva vários segundos ou minutos.
Como funciona o streaming de mensagens
Uma mensagem de streaming passa por um estado de streaming ativo antes de se tornar um recurso padrão Message em um espaço:
- Iniciar uma conversa (
spaces.streamMessages.create): o app do Chat cria umStreamMessageem um espaço com conversas agrupadas, referenciando a mensagem de interação do usuário que está sendo respondida. A API mostra um indicador de progresso em tempo real na conversa e retorna um nome de recursoStreamMessagee umnextChunkTokeninicial. - Atualizar o stream (
appendStreamMessageContentoupatch): à medida que o app do Chat gera uma resposta, ele envia atualizações sequenciais usando onextChunkTokenmais recente:- Chame
spaces.streamMessages.appendStreamMessageContentpara concatenar novos blocos de texto, adicionar ao final etapas intermediárias, adicionar citações inline ou atualizarprogressText. - Chame
spaces.streamMessages.patchpara substituir os blocos de conteúdo transmitidos ou atualizarprogressTextno local.
- Chame
- Finalize ou descarte o stream (
commitoudelete):- Chame
spaces.streamMessages.commitpara finalizar o stream e convertê-lo em um recursoMessagepermanente. Também é possível anexar elementos somente de commit, como fichas de informações, widgets de acessórios e fontes do rodapé, ao fazer o commit. - Chame
spaces.streamMessages.deletepara descartar um stream ativo sem salvar uma mensagem no espaço.
- Chame
Enquanto uma transmissão está ativa, apenas o remetente da mensagem referenciada
(referencedMessage) que está sendo transmitida pode clicar em Parar para cancelar
a geração. O Chat envia um evento de comando do sistema de cancelamento para
seu app do Chat para que você possa interromper o trabalho de back-end e
confirmar a saída parcial ou excluir o stream.
Pré-requisitos
Para transmitir mensagens com a API Chat, conclua os seguintes pré-requisitos:
- Uma conta do Google Workspace Business ou Enterprise com acesso ao Google Chat.
- Configure o ambiente:
- Crie um projeto do Google Cloud.
- Ative e configure a API Google Chat com um nome, ícone e descrição para o app do Chat.
- Crie as credenciais da conta de serviço e salve-as como
credentials.json. O streaming de mensagens requer autenticação do app com o escopo de autorizaçãohttps://www.googleapis.com/auth/chat.bot. A autenticação do usuário não é aceita.
- Adicione o app do Chat a um espaço do Chat com conversas agrupadas (ou uma mensagem direta individual que já contenha pelo menos uma mensagem) e receba o nome do recurso de uma mensagem acessível para responder (
spaces/SPACE_ID/messages/MESSAGE_ID). Para amostras em Python, instale a biblioteca de cliente da API do Google para Python e
google-authe inicialize o cliente de serviço da 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)
Iniciar um stream
Para iniciar o streaming de uma resposta, chame spaces.streamMessages.create e transmita um
StreamMessage no corpo da solicitação:
referencedMessage(obrigatório): nome do recurso da mensagem que acionou a resposta, formatado comospaces/{space}/messages/{message}. A mensagem referenciada precisa pertencer ao mesmo espaço e ser acessível ao seu app do Chat. O Chat posta a resposta transmitida na mesma conversa quereferencedMessage.progressText(obrigatório): rótulo de status de texto simples não vazio (até 250 caracteres) mostrado ao lado do indicador de progresso, como"Thinking...".markupSyntax(opcional): sintaxe de formatação de texto para a mensagem, comoMARKUP_SYNTAX_MARKDOWNouMARKUP_SYNTAX_CHAT(o padrão é a sintaxe configurada do app do Chat seMARKUP_SYNTAX_UNSPECIFIED). DefinaMARKUP_SYNTAX_MARKDOWNse você planeja incluir citações inline.streamContentChunks(opcional): blocos iniciais detext,stepoucitationpara exibição imediata.
Salve name (spaces/{space}/streamMessages/{stream_message}) e
nextChunkToken da resposta para chamadas subsequentes.
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"]
O método retorna um recurso StreamMessage:
{
"name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
"nextChunkToken": "INITIAL_CHUNK_TOKEN"
}
Anexar blocos de conteúdo
À medida que o app do Chat produz etapas intermediárias ou tokens de texto, chame spaces.streamMessages.appendStreamMessageContent:
- Transmita o
nextChunkTokenmais recente emchunkToken. Cada chamada bem-sucedida retorna um novonextChunkTokenque você precisa transmitir na próxima solicitação de atualização ou commit. - Os blocos
textsão concatenados diretamente no texto transmitido anteriormente. Inclua espaços ou novas linhas onde for necessário entre os blocos. - Os blocos
stepsão anexados à lista de etapas intermediárias mostradas na mensagem. progressTexté opcional. Se você omitirprogressTextou definir como"", o Chat vai continuar mostrando oprogressTextdefinido anteriormente.
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"]
Substituir o conteúdo transmitido ou o texto de progresso
Se você precisar reescrever conteúdo transmitido anteriormente, por exemplo, depois de executar um
filtro de pós-processamento ou de segurança, ou atualizar apenas progressText sem
anexar blocos, chame spaces.streamMessages.patch:
- Defina
updateMaskcomo uma lista separada por vírgulas de campos a serem atualizados:progressText,streamContentChunksouprogressText,streamContentChunks. - Inclua o
nextChunkTokenmais recente no corpo da solicitaçãoStreamMessagee registre o novonextChunkTokenretornado na resposta. - Quando
streamContentChunksestá emupdateMask, os blocos fornecidos substituem o conteúdo transmitido anteriormente em vez de serem anexados a ele. Quando
progressTextestá emupdateMask,progressTextprecisa ser um texto simples não vazio de até 250 caracteres.
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"]
Finalizar um stream
Quando a geração terminar, chame spaces.streamMessages.commit com o chunkToken mais recente para finalizar o stream em um recurso permanente Message:
- Se quiser, inclua o
streamContentChunksfinal na solicitação de commit, incluindo tipos de blocos somente de commit (card,accessoryWidgetecitedSource). Se você já tiver transmitido conteúdo não vazio em chamadas anteriores, poderá omitir
streamContentChunksao confirmar. A mensagem final confirmada não pode estar vazia.
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']}")
Descartar um stream ativo
Se ocorrer um erro irrecuperável ou se um usuário cancelar a geração e você não
quiser deixar uma resposta parcial na conversa, chame
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()
Depois que um stream for confirmado em um Message, chame
spaces.messages.delete se precisar
excluir a mensagem finalizada.
Tipos de blocos de conteúdo compatíveis
Cada entrada em streamContentChunks especifica um campo na união StreamContentChunk. Alguns tipos de bloco podem ser enviados durante todo o stream,
enquanto outros são aceitos apenas em spaces.streamMessages.commit:
| Campo de fragmento | Métodos aceitos | Campo Message final |
Observações |
|---|---|---|---|
text |
create, appendStreamMessageContent,
patch, commit |
text |
Texto incremental. Concatenado em
appendStreamMessageContent; substituído em patch.
O texto final confirmado não pode exceder 32.000 bytes. |
step |
create, appendStreamMessageContent,
patch, commit |
elements.steps.steps[] |
Etapa de processamento intermediária com title de texto simples
(obrigatório) e description opcional. |
citation |
create, appendStreamMessageContent,
patch, commit |
elements.citations[] |
Citation inline com um id alfanumérico (menos de 63
caracteres, sublinhados permitidos) e citedSources. Requer
tags markupSyntax: "MARKUP_SYNTAX_MARKDOWN" e <chat-citation data-id="ID">...</chat-citation>
correspondentes
em text. O envio de um citation com um
id atual substitui a citação anterior. |
card |
Somente commit |
cardsV2[] |
Card interativo ou informativo
CardWithId. |
accessoryWidget |
Somente commit |
accessoryWidgets[] |
AccessoryWidget botões exibidos abaixo da mensagem,
como botões de feedback. |
citedSource |
Somente commit |
elements.citedSources[] |
CitedSource link de rodapé exibido abaixo da mensagem
sem tags de citação inline. |
Processar o cancelamento de stream iniciado pelo usuário
Enquanto uma transmissão está ativa, apenas o remetente da mensagem referenciada
(referencedMessage) que está sendo transmitida pode clicar em Parar na mensagem
de transmissão. Quando o usuário interrompe um stream, o Chat envia um evento de interação de comando do sistema contendo cancelStream.streamMessage (o nome do recurso spaces/{space}/streamMessages/{stream_message} do stream interrompido) para o endpoint do app Chat.
A estrutura do payload JSON recebido depende de como você cria o app do Chat: como um complemento do Google Workspace ou usando eventos de interação da API Chat:
Complementos do 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 o app do Chat recebe um evento cancelStream:
- Sinalize para o worker em segundo plano parar de gerar mais tokens ou executar
ferramentas downstream para
streamMessage. - Resolva o stream ativo usando uma das seguintes abordagens:
- Manter saída parcial: chame
spaces.streamMessages.commitcom ochunkTokenmais recente (opcionalmente, adicionando uma nota como"\n\n_Generation stopped._"). - Descartar a resposta: chame
spaces.streamMessages.deletepara que nada seja salvo na conversa.
- Manter saída parcial: chame
- Retorne uma resposta JSON HTTP
200 OKvazia ({}) do manipulador de eventos de interação.
Limitações
O streaming de mensagens tem as seguintes limitações:
- Disponibilidade da plataforma: o streaming de mensagens só aparece no Chat na Web. O suporte para Android e iOS será lançado em breve.
- Tempo limite do stream: um
StreamMessageativo expira 10 minutos após a criação. O app do Chat precisa chamarspaces.streamMessages.commitouspaces.streamMessages.deleteem até 10 minutos após o início do stream.