本指南介绍了如何在 Google Chat 中使用 Chat API 中的 StreamMessage 资源以增量方式流式传输消息。借助流式传输,Google Chat 应用可以在最终确定消息之前显示实时进度指示器、中间推理或工具执行步骤,以及增量文本回答。
流式传输专为由生成式 AI 或长时间运行的后端工作流提供支持的 Chat 应用而设计,在这些应用中,生成完整回答需要几秒钟或几分钟的时间。
消息流式传输的运作方式
流式消息在成为聊天室中的标准 Message 资源之前,会经历活跃流式状态:
- 启动串流 (
spaces.streamMessages.create):Chat 应用在话题式聊天室中创建StreamMessage,并引用其回复的用户互动消息。该 API 会在会话中显示实时进度指示器,并返回StreamMessage资源名称和初始nextChunkToken。 - 更新串流(
appendStreamMessageContent或patch):当 Chat 应用生成输出时,它会使用最新的nextChunkToken发送依序更新:- 调用
spaces.streamMessages.appendStreamMessageContent以串联新的文本块、附加中间步骤、添加内嵌引用或更新progressText。 - 调用
spaces.streamMessages.patch以替换流式传输的内容块或就地更新progressText。
- 调用
- 完成或舍弃数据流(
commit或delete):- 调用
spaces.streamMessages.commit以最终确定数据流并将其转换为永久Message资源。您还可以在提交时附加仅限提交的元素,例如卡片、辅助微件和消息底部参考来源。 - 调用
spaces.streamMessages.delete可舍弃活跃流,而无需在聊天室中保存消息。
- 调用
在流式传输处于活跃状态时,只有正在流式传输的引用消息 (referencedMessage) 的发送者可以点击停止来取消生成。Chat 会向您的 Chat 应用发送取消系统命令事件,以便您停止后端工作,并提交部分输出或删除数据流。
前提条件
如需使用 Chat API 流式传输消息,请完成以下前提条件:
- 可访问 Google Chat 的 Google Workspace 商务版或企业版账号。
- 设置环境:
- 创建 Google Cloud 项目。
- 启用并配置 Google Chat API,为您的 Chat 应用设置名称、图标和说明。
- 创建服务账号凭证并将其保存为
credentials.json。消息流式传输需要使用https://www.googleapis.com/auth/chat.bot授权范围进行应用身份验证;不支持用户身份验证。
- 将您的 Chat 应用添加到串联式 Chat 聊天室(或已包含至少一条消息的 1 对 1 私信),并获取要回复的可访问消息的资源名称 (
spaces/SPACE_ID/messages/MESSAGE_ID)。 对于 Python 示例,请安装 Python 版 Google API 客户端库和
google-auth,并初始化 Chat API 服务客户端: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)
启动数据流
如需开始流式传输回答,请调用 spaces.streamMessages.create 并在请求正文中传递 StreamMessage:
referencedMessage(必需):触发响应的消息的资源名称,格式为spaces/{space}/messages/{message}。引用的消息必须属于同一聊天室,并且可供您的 Chat 应用访问。Chat 会在与referencedMessage相同的会话中发布流式回复。progressText(必需):显示在进度指示器旁边的非空纯文本状态标签(最多 250 个字符),例如"Thinking..."。markupSyntax(可选):消息的文本格式语法,例如MARKUP_SYNTAX_MARKDOWN或MARKUP_SYNTAX_CHAT(如果为MARKUP_SYNTAX_UNSPECIFIED,则默认为 Chat 应用配置的语法)。如果您打算添加内嵌引用,请设置MARKUP_SYNTAX_MARKDOWN。streamContentChunks(可选):要立即显示的初始text、step或citation块。
保存响应中的 name (spaces/{space}/streamMessages/{stream_message}) 和 nextChunkToken,以便在后续调用中使用。
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"]
该方法会返回一个 StreamMessage 资源:
{
"name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
"nextChunkToken": "INITIAL_CHUNK_TOKEN"
}
附加内容块
当 Chat 应用生成中间步骤或文本词元时,调用 spaces.streamMessages.appendStreamMessageContent:
- 在
chunkToken中传递最新的nextChunkToken。每次成功调用都会返回一个新的nextChunkToken,您必须在下一次更新或提交请求中传递该值。 text块会直接串接到之前流式传输的文本。根据需要在分块之间添加空格或换行符。step个分块会附加到消息中显示的中间步骤列表中。progressText为可选项。如果您省略progressText或将其设置为"",Chat 会继续显示之前设置的progressText。
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"]
替换流式传输的内容或进度文本
如果您需要重写之前流式传输的内容(例如,在运行后处理或安全过滤器之后),或者仅更新 progressText 而不附加分块,请调用 spaces.streamMessages.patch:
- 将
updateMask设置为要更新的字段的英文逗号分隔列表:progressText、streamContentChunks或progressText,streamContentChunks。 - 在
StreamMessage请求正文中添加最新的nextChunkToken,并记录响应中返回的新nextChunkToken。 - 当
streamContentChunks处于updateMask状态时,提供的分块会替换之前流式传输的内容,而不是附加到其后。 当
progressText位于updateMask中时,progressText必须是不超过 250 个字符的非空纯文本。
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"]
完成数据流
生成完成后,使用最新的 chunkToken 调用 spaces.streamMessages.commit,以将数据流最终确定为永久 Message 资源:
- 您可以选择在提交请求中包含最终
streamContentChunks,包括仅提交分块类型(card、accessoryWidget和citedSource)。 如果您已在之前的调用中流式传输了非空内容,则可以在提交时省略
streamContentChunks。最终提交的消息不能为空。
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']}")
舍弃活跃直播
如果发生不可恢复的错误,或者用户取消生成,并且您不想在会话串中留下部分回复,请调用 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()
将数据流提交到 Message 后,如果您需要删除最终确定的消息,请改为调用 spaces.messages.delete。
支持的内容分块类型
streamContentChunks 中的每个条目都指定了 StreamContentChunk 联合中的一个字段。某些分块类型可以在整个数据流中发送,而其他分块类型仅在 spaces.streamMessages.commit 中接受:
| 块字段 | 支持的方法 | 最终 Message 字段 |
备注 |
|---|---|---|---|
text |
create、appendStreamMessageContent、patch、commit |
text |
增量文本。已于 appendStreamMessageContent串联;已于 patch替换。
最终提交的文本不能超过 32,000 字节。 |
step |
create、appendStreamMessageContent、patch、commit |
elements.steps.steps[] |
包含纯文本 title(必需)和可选 description 的中间处理步骤。 |
citation |
create、appendStreamMessageContent、patch、commit |
elements.citations[] |
内嵌 Citation,其中包含字母数字 id(少于 63 个字符,允许使用下划线)和 citedSources。需要 text 中有 markupSyntax: "MARKUP_SYNTAX_MARKDOWN" 和匹配的 <chat-citation data-id="ID">...</chat-citation> 标记。发送带有现有 id 的 citation 会覆盖之前的引用。 |
card |
仅 commit |
cardsV2[] |
互动式或信息性 CardWithId 卡片。 |
accessoryWidget |
仅 commit |
accessoryWidgets[] |
消息下方显示的 AccessoryWidget 按钮,例如反馈按钮。 |
citedSource |
仅 commit |
elements.citedSources[] |
CitedSource 页脚链接显示在消息下方,不带内嵌引用标签。 |
处理用户发起的流取消
在流式传输处于活跃状态时,只有正在流式传输的引用消息 (referencedMessage) 的发送者才能点击流式传输消息上的停止。当用户停止直播时,Chat 会向您的 Chat 应用端点发送一个系统命令互动事件,其中包含 cancelStream.streamMessage(已停止直播的 spaces/{space}/streamMessages/{stream_message} 资源名称)。
传入 JSON 载荷的结构取决于您是将 Chat 应用构建为 Google Workspace 插件,还是使用 Chat API 互动事件:
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"
}
}
}
}
}
Google Chat API
{
"type": "SYSTEM_COMMAND",
"space": {
"name": "spaces/SPACE_ID"
},
"user": {
"name": "users/USER_ID"
},
"systemCommandMetadata": {
"cancelStream": {
"streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
}
}
}
当您的 Chat 应用收到 cancelStream 事件时:
- 向后台工作器发出信号,让其停止为
streamMessage生成更多词元或运行下游工具。 - 使用以下方法之一解决活跃数据流问题:
- 保留部分输出:使用最新的
chunkToken调用spaces.streamMessages.commit(可选择附加备注,例如"\n\n_Generation stopped._")。 - 舍弃回复:调用
spaces.streamMessages.delete,这样就不会向会话串保存任何内容。
- 保留部分输出:使用最新的
- 从互动事件处理脚本返回空的 HTTP
200 OKJSON 响应 ({})。
限制
消息流式传输存在以下限制:
- 平台可用性:消息流式传输仅在网页版 Chat 中可见。即将支持 Android 和 iOS。
- 流超时:活跃的
StreamMessage会在创建 10 分钟后超时。您的 Chat 应用必须在开始直播后的 10 分钟内调用spaces.streamMessages.commit或spaces.streamMessages.delete。