Przesyłanie wiadomości strumieniowo za pomocą interfejsu Google Chat API

Z tego przewodnika dowiesz się, jak przyrostowo przesyłać wiadomości w Google Chat za pomocą zasobu StreamMessage w interfejsie Chat API. Przesyłanie strumieniowe umożliwia aplikacji Google Chat wyświetlanie wskaźników postępu na żywo, pośrednich wniosków lub kroków wykonywania narzędzia oraz przyrostowych odpowiedzi tekstowych przed sfinalizowaniem wiadomości.

Przesyłanie strumieniowe jest przeznaczone dla aplikacji do Google Chat opartych na generatywnej AI lub długotrwałych przepływach pracy backendu, w których wygenerowanie pełnej odpowiedzi zajmuje kilka sekund lub minut.

Jak działa strumieniowe przesyłanie wiadomości

Zanim wiadomość przesyłana strumieniowo stanie się standardowym zasobem Message w pokoju, przechodzi przez stan aktywnego przesyłania strumieniowego:

  1. Rozpoczęcie wątku (spaces.streamMessages.create): aplikacja do Google Chat tworzy obiekt StreamMessage w pokoju z wątkami, odwołując się do wiadomości o interakcji użytkownika, na którą odpowiada. Interfejs API wyświetla w wątku wskaźnik postępu na żywo i zwraca nazwę zasobu StreamMessage oraz początkowy nextChunkToken.
  2. Zaktualizuj strumień (appendStreamMessageContent lub patch): gdy aplikacja Google Chat generuje dane wyjściowe, wysyła kolejne aktualizacje przy użyciu najnowszej wersji nextChunkToken:
    • Wywołaj spaces.streamMessages.appendStreamMessageContent, aby połączyć nowe fragmenty tekstu, dołączyć kroki pośrednie, dodać cytaty w tekście lub zaktualizować progressText.
    • Wywołaj spaces.streamMessages.patch, aby zastąpić fragmenty treści przesyłanych strumieniowo lub zaktualizować progressText na miejscu.
  3. Sfinalizuj lub odrzuć strumień (commit lub delete):
    • Wywołaj spaces.streamMessages.commit, aby sfinalizować strumień i przekonwertować go na stały zasób Message. Podczas zatwierdzania możesz też dołączać elementy, które można tylko zatwierdzić, takie jak karty, widżety akcesoriów i źródła w stopce.
    • Wywołaj spaces.streamMessages.delete, aby odrzucić aktywny strumień bez zapisywania wiadomości w pokoju.

Gdy strumień jest aktywny, tylko nadawca wiadomości, do której się odnosi (referencedMessage), może kliknąć Zatrzymaj, aby anulować generowanie. Google Chat wysyła do aplikacji do Google Chat zdarzenie polecenia systemowego anulowania, dzięki czemu możesz zatrzymać pracę backendu i zatwierdzić częściowe dane wyjściowe lub usunąć strumień.

Wymagania wstępne

Aby przesyłać wiadomości strumieniowo za pomocą interfejsu Chat API, musisz spełnić te wymagania wstępne:

  • Konto Google Workspace w wersji Business lub Enterprise z dostępem do Google Chat.
  • Skonfiguruj środowisko:
  • Dodaj aplikację do Google Chat do pokoju w Google Chat z wątkami (lub do czatu indywidualnego, który zawiera już co najmniej 1 wiadomość) i uzyskaj nazwę zasobu dostępnej wiadomości, na którą chcesz odpowiedzieć (spaces/SPACE_ID/messages/MESSAGE_ID).
  • W przypadku przykładów w Pythonie zainstaluj bibliotekę klienta interfejsu API Google dla Pythona i google-auth, a następnie zainicjuj klienta usługi 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)
    

Rozpoczynanie transmisji

Aby rozpocząć strumieniowe przesyłanie odpowiedzi, wywołaj spaces.streamMessages.create i przekaż StreamMessage w treści żądania:

  • referencedMessage (wymagany): nazwa zasobu wiadomości, która wywołała odpowiedź, sformatowana jako spaces/{space}/messages/{message}. Wiadomość, do której się odwołujesz, musi należeć do tego samego pokoju i być dostępna dla aplikacji Google Chat. Google Chat opublikuje odpowiedź w tym samym wątku co referencedMessage.
  • progressText (wymagany): niepusta etykieta stanu w formie zwykłego tekstu (do 250 znaków) wyświetlana obok wskaźnika postępu, np. "Thinking...".
  • markupSyntax (opcjonalnie): składnia formatowania tekstu wiadomości, np. MARKUP_SYNTAX_MARKDOWN lub MARKUP_SYNTAX_CHAT (domyślnie jest to składnia skonfigurowana w aplikacji Google Chat, jeśli MARKUP_SYNTAX_UNSPECIFIED). Ustaw MARKUP_SYNTAX_MARKDOWN, jeśli planujesz dołączyć cytaty w tekście.
  • streamContentChunks (opcjonalnie): początkowe fragmenty text, step lub citation, które mają być wyświetlane od razu.

Zapisz zarówno name (spaces/{space}/streamMessages/{stream_message}), jak i nextChunkToken z odpowiedzi na potrzeby kolejnych wywołań.

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"]

Metoda zwraca zasób StreamMessage:

{
  "name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
  "progressText": "Thinking...",
  "markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
  "nextChunkToken": "INITIAL_CHUNK_TOKEN"
}

Dołączanie fragmentów treści

Gdy aplikacja do Google Chat generuje etapy pośrednie lub tokeny tekstowe, wywołaj spaces.streamMessages.appendStreamMessageContent:

  • Przekaż najnowszą wersję nextChunkToken w chunkToken. Każde udane wywołanie zwraca nowy obiekt nextChunkToken, który musisz przekazać w następnym żądaniu aktualizacji lub zatwierdzenia.
  • Fragmenty text są łączone bezpośrednio z poprzednio przesłanym tekstem. W razie potrzeby dodaj spacje lub znaki nowego wiersza między fragmentami.
  • Do listy kroków pośrednich wyświetlanych w komunikacie dodawane są fragmenty step.
  • progressText jest opcjonalny. Jeśli pominiesz parametr progressText lub ustawisz go na "", Google Chat będzie nadal wyświetlać wcześniej ustawioną wartość 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"]

Zastępowanie treści przesyłanych strumieniowo lub tekstu postępu

Jeśli chcesz ponownie zapisać wcześniej przesłane treści – na przykład po uruchomieniu przetwarzania końcowego lub filtra bezpieczeństwa – lub zaktualizować tylko progressText bez dołączania fragmentów, wywołaj spaces.streamMessages.patch:

  • Ustaw updateMask na listę rozdzielonych przecinkami pól do zaktualizowania: progressText, streamContentChunks lub progressText,streamContentChunks.
  • Dołącz najnowszy parametr nextChunkToken do treści żądania StreamMessage i zapisz nowy parametr nextChunkToken zwrócony w odpowiedzi.
  • Gdy parametr streamContentChunks ma wartość updateMask, podane fragmenty zastępują wcześniej przesłane treści, zamiast być do nich dołączane.
  • Gdy progressText jest w updateMask, progressText nie może być puste. Zwykły tekst o długości do 250 znaków.

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"]

Finalizowanie strumienia

Po zakończeniu generowania wywołaj spaces.streamMessages.commit z najnowszym chunkToken, aby sfinalizować strumień w stały zasób Message:

  • W żądaniu zatwierdzenia możesz opcjonalnie uwzględnić końcowe streamContentChunks, w tym typy fragmentów tylko do zatwierdzenia (card, accessoryWidget i citedSource).
  • Jeśli w poprzednich wywołaniach przesłano już niepuste treści, podczas zatwierdzania możesz pominąć streamContentChunks. Ostatnia zatwierdzona wiadomość nie może być pusta.

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']}")

Odrzucanie aktywnego strumienia

Jeśli wystąpi nieodwracalny błąd lub użytkownik anuluje generowanie, a nie chcesz pozostawiać w wątku częściowej odpowiedzi, wywołaj 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()

Jeśli strumień został zatwierdzony w Message, wywołaj spaces.messages.delete, jeśli chcesz usunąć sfinalizowaną wiadomość.

Obsługiwane typy fragmentów treści

Każdy wpis w streamContentChunks określa jedno pole w unii StreamContentChunk. Niektóre typy fragmentów można wysyłać przez cały czas trwania transmisji, a inne są akceptowane tylko w spaces.streamMessages.commit:

Pole fragmentu Obsługiwane metody Pole Message końcowe Uwagi
text create, appendStreamMessageContent, patch, commit text Tekst przyrostowy. Połączono appendStreamMessageContent; zastąpiono patch. Ostateczny zatwierdzony tekst nie może przekraczać 32 000 bajtów.
step create, appendStreamMessageContent, patch, commit elements.steps.steps[] Pośredni etap przetwarzania z użyciem zwykłego tekstu title (wymagany) i opcjonalnego description.
citation create, appendStreamMessageContent, patch, commit elements.citations[] Wstaw w tekście Citation z alfanumerycznym id (mniej niż 63 znaki, dozwolone podkreślenia) i citedSources. Wymaga tagów markupSyntax: "MARKUP_SYNTAX_MARKDOWN" i pasujących tagów <chat-citation data-id="ID">...</chat-citation> w text. Wysłanie citation z istniejącym id zastępuje wcześniejszy cytat.
card Tylko commit cardsV2[] Karta interaktywna lub informacyjna CardWithId.
accessoryWidget Tylko commit accessoryWidgets[] AccessoryWidget przyciski wyświetlane pod wiadomością, np. przyciski do wysyłania opinii.
citedSource Tylko commit elements.citedSources[] CitedSource link w stopce wyświetlany pod wiadomością bez tagów cytatów w tekście.

Obsługa anulowania strumienia przez użytkownika

Gdy strumień jest aktywny, tylko nadawca wiadomości, do której odnosi się strumień (referencedMessage), może kliknąć Zatrzymaj w wiadomości strumieniowej. Gdy użytkownik zatrzyma strumień, Google Chat wysyła do punktu końcowego aplikacji do Google Chat zdarzenie interakcji polecenia systemowego zawierające cancelStream.streamMessage (nazwę zasobu spaces/{space}/streamMessages/{stream_message} zatrzymanego strumienia).

Struktura przychodzącego ładunku JSON zależy od tego, czy tworzysz aplikację Google Chat jako dodatek do Google Workspace, czy za pomocą zdarzeń interakcji interfejsu Chat API:

Dodatki 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"
        }
      }
    }
  }
}

Interfejs API aplikacji Google Chat

{
  "type": "SYSTEM_COMMAND",
  "space": {
    "name": "spaces/SPACE_ID"
  },
  "user": {
    "name": "users/USER_ID"
  },
  "systemCommandMetadata": {
    "cancelStream": {
      "streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
    }
  }
}

Gdy aplikacja do Google Chat otrzyma zdarzenie cancelStream:

  1. Zasygnalizuj pracownikowi działającemu w tle, aby przestał generować kolejne tokeny lub uruchamiać narzędzia podrzędne dla: streamMessage.
  2. Rozwiąż problem z aktywną transmisją, korzystając z jednej z tych metod:
    • Zachowaj częściowe dane wyjściowe: wywołaj spaces.streamMessages.commit z najnowszym chunkToken (opcjonalnie dodając notatkę, np. "\n\n_Generation stopped._").
    • Odrzucenie odpowiedzi: wywołaj spaces.streamMessages.delete, aby nic nie zostało zapisane w wątku.
  3. Zwróć pustą odpowiedź HTTP 200 OK JSON ({}) z modułu obsługi zdarzeń interakcji.

Ograniczenia

Strumień wiadomości ma następujące ograniczenia:

  • Dostępność platformy: strumieniowe przesyłanie wiadomości jest widoczne tylko w Google Chat w przeglądarce. Obsługa Androida i iOS już wkrótce.
  • Przekroczenie limitu czasu strumienia: aktywny strumień StreamMessage wygasa po 10 minutach od utworzenia. Aplikacja Google Chat musi wywołać metodę spaces.streamMessages.commit lub spaces.streamMessages.delete w ciągu 10 minut od rozpoczęcia strumienia.