使用 Google Chat API 串流訊息

本指南說明如何使用 Chat API 中的 StreamMessage 資源,在 Google Chat 中逐步串流訊息。串流功能可讓 Google Chat 應用程式在完成訊息前,顯示即時進度指標、中間推論或工具執行步驟,以及遞增文字回覆。

串流功能適用於採用生成式 AI 的 Chat 擴充應用程式,或長時間執行的後端工作流程,這類工作流程需要幾秒或幾分鐘才能生成完整回覆。

訊息串流的運作方式

串流訊息會先經過有效串流狀態,然後才會成為聊天室中的標準 Message 資源:

  1. 發起串流 (spaces.streamMessages.create):Chat 應用程式會在討論串聊天室中建立 StreamMessage,並參照要回覆的使用者互動訊息。API 會在執行緒中顯示即時進度指標,並傳回 StreamMessage 資源名稱和初始 nextChunkToken。
  2. 更新串流 (appendStreamMessageContent 或 patch):Chat 應用程式生成輸出內容時,會使用最新的 nextChunkToken 傳送依序更新:
    • 呼叫 spaces.streamMessages.appendStreamMessageContent 即可串連新的文字區塊、附加中間步驟、新增內嵌引用資料,或更新 progressText。
    • 呼叫 spaces.streamMessages.patch,取代串流內容區塊或就地更新 progressText。
  3. 完成或捨棄串流 (commit 或 delete):
    • 呼叫 spaces.streamMessages.commit 即可完成串流,並將其轉換為永久 Message 資源。您也可以在提交時附加僅限提交的元素,例如資訊卡、配件小工具和訊息底部來源。
    • 呼叫 spaces.streamMessages.delete 即可捨棄進行中的串流,而不將訊息儲存至聊天室。

串流進行時,只有所參照訊息 (referencedMessage) 的傳送者可以點選「Stop」(停止) 取消生成。Chat 會向 Chat 應用程式傳送取消系統指令事件,以便您停止後端工作,並提交部分輸出內容或刪除串流。

必要條件

如要使用 Chat API 串流訊息,請先完成下列前置作業:

  • Business 或 Enterprise Google Workspace 帳戶,且可存取 Google Chat。
  • 設定環境:
  • 將 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-auth
    from 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 接受:

Chunk 欄位 支援的方法 最終 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 會傳送系統指令互動事件,其中包含 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 事件時:

  1. 向背景 worker 發出信號,停止為 streamMessage 生成更多權杖或執行下游工具。
  2. 請使用下列任一方法解決進行中的串流問題:
    • 保留部分輸出內容:使用最新的 chunkToken 呼叫 spaces.streamMessages.commit (可視需要附加附註,例如 "\n\n_Generation stopped._")。
    • 捨棄回覆:呼叫 spaces.streamMessages.delete,這樣就不會將任何內容儲存至討論串。
  3. 從互動事件處理常式傳回空白的 HTTP 200 OK JSON 回應 ({})。

限制

訊息串流有下列限制:

  • 適用平台:訊息串流功能僅適用於網頁版 Chat。我們很快就會支援 Android 和 iOS。
  • 串流逾時:StreamMessage 建立後 10 分鐘會逾時。Chat 擴充應用程式必須在串流開始後 10 分鐘內呼叫 spaces.streamMessages.commit 或 spaces.streamMessages.delete。