このガイドでは、Chat API の StreamMessage リソースを使用して、Google Chat でメッセージを段階的にストリーミングする方法について説明します。ストリーミングを使用すると、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リソースに変換します。commit 時に、カード、アクセサリー ウィジェット、フッターソースなどの commit 専用要素を添付することもできます。spaces.streamMessages.deleteを呼び出して、スペースにメッセージを保存せずにアクティブなストリームを破棄します。
ストリームがアクティブな間は、ストリーミングされている参照メッセージ(referencedMessage)の送信者のみが [停止] をクリックして生成をキャンセルできます。Chat は、キャンセル システム コマンド イベントを Chat アプリに送信します。これにより、バックエンドの作業を停止し、部分的な出力を commit するか、ストリームを削除できます。
前提条件
Chat API を使用してメッセージをストリーミングするには、次の前提条件を満たしている必要があります。
- Google Chat へのアクセス権を持つ Business または Enterprise の Google Workspace アカウント。
- 環境を設定します。
- Google Cloud プロジェクトを作成します。
- Chat 用アプリの名前、アイコン、説明を指定して、Google Chat API を有効にして構成します。
- サービス アカウントの認証情報を作成し、
credentials.jsonとして保存します。メッセージ ストリーミングには、https://www.googleapis.com/auth/chat.bot認可スコープを使用したアプリ認証が必要です。ユーザー認証はサポートされていません。
- Chat 用アプリをスレッド形式の Chat スペース(または、すでに 1 つ以上のメッセージが含まれている 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 を呼び出します。
- 最新の
nextChunkTokenをchunkTokenに渡します。呼び出しが成功するたびに、次の更新リクエストまたは commit リクエストで渡す必要がある新しい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 リソースに確定します。
- 必要に応じて、commit-only チャンクタイプ(
card、accessoryWidget、citedSource)を含む最終的なstreamContentChunksを commit リクエストに含めることができます。 以前の呼び出しで空でないコンテンツをすでにストリーミングしている場合は、commit 時に
streamContentChunksを省略できます。最終的に commit されたメッセージを空にすることはできません。
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 に commit された後、確定されたメッセージを削除する必要がある場合は、代わりに spaces.messages.delete を呼び出します。
サポートされているコンテンツ チャンクタイプ
streamContentChunks の各エントリは、StreamContentChunk 共用体の 1 つのフィールドを指定します。一部のチャンクタイプはストリーム全体で送信できますが、他のチャンクタイプは spaces.streamMessages.commit でのみ受け入れられます。
| チャンク フィールド | サポートされるメソッド | 最終的な Message フィールド |
メモ |
|---|---|---|---|
text |
create、appendStreamMessageContent、patch、commit |
text |
増分テキスト。appendStreamMessageContent で連結。patch で置き換え。最終的に commit されるテキストは 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 は cancelStream.streamMessage(停止したストリームの spaces/{space}/streamMessages/{stream_message} リソース名)を含むシステム コマンド インタラクション イベントを Chat アプリのエンドポイントに送信します。
受信 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を呼び出す必要があります。