Nachrichten mit der Google Chat API streamen

In diesem Leitfaden wird beschrieben, wie Sie Nachrichten in Google Chat mithilfe der Ressource StreamMessage in der Chat API schrittweise streamen. Mit Streaming kann Ihre Google Chat-App Live-Fortschrittsanzeigen, Zwischenschritte beim Schlussfolgern oder bei der Toolausführung und inkrementelle Textantworten anzeigen, bevor eine Nachricht fertiggestellt wird.

Streaming ist für Chat-Apps gedacht, die auf generativer KI oder lang laufenden Backend-Workflows basieren, bei denen das Generieren einer vollständigen Antwort mehrere Sekunden oder Minuten dauert.

Funktionsweise des Nachrichtenstreamings

Eine Streamingnachricht durchläuft einen aktiven Streamingstatus, bevor sie zu einer Standardressource vom Typ Message in einem Gruppenbereich wird:

  1. Stream starten (spaces.streamMessages.create): Ihre Chat-App erstellt einen StreamMessage in einem Gruppenbereich mit Threads und verweist auf die Nachricht zur Nutzerinteraktion, auf die sie antwortet. Die API zeigt im Thread eine Live-Fortschrittsanzeige an und gibt einen StreamMessage-Ressourcennamen und eine erste nextChunkToken zurück.
  2. Stream aktualisieren (appendStreamMessageContent oder patch): Während Ihre Chat-App Ausgaben generiert, sendet sie sequenzielle Updates mit dem neuesten nextChunkToken:
    • Rufen Sie spaces.streamMessages.appendStreamMessageContent auf, um neue Textblöcke zu verketten, Zwischenschritte anzuhängen, Inline-Zitate hinzuzufügen oder progressText zu aktualisieren.
    • Rufen Sie spaces.streamMessages.patch auf, um die gestreamten Inhaltsblöcke zu ersetzen, oder aktualisieren Sie progressText direkt.
  3. Stream abschließen oder verwerfen (commit oder delete):
    • Rufen Sie spaces.streamMessages.commit auf, um den Stream abzuschließen und in eine permanente Message-Ressource zu konvertieren. Sie können beim Übernehmen auch Elemente anhängen, die nur für die Übernahme bestimmt sind, z. B. Karten, Zubehör-Widgets und Quellen in der Fußzeile.
    • Rufen Sie spaces.streamMessages.delete auf, um einen aktiven Stream zu verwerfen, ohne eine Nachricht im Gruppenbereich zu speichern.

Während ein Stream aktiv ist, kann nur der Absender der referenzierten Nachricht (referencedMessage), auf die gestreamt wird, auf Beenden klicken, um die Generierung abzubrechen. Google Chat sendet ein Systembefehlsereignis zum Abbrechen an Ihre Chat-App, damit Sie die Backend-Arbeit beenden und entweder die Teilausgabe übernehmen oder den Stream löschen können.

Vorbereitung

Wenn Sie Nachrichten mit der Chat API streamen möchten, müssen Sie die folgenden Voraussetzungen erfüllen:

  • Ein Google Workspace-Konto für Unternehmen mit Zugriff auf Google Chat.
  • Richten Sie Ihre Umgebung ein:
  • Fügen Sie Ihre Chat-App einem Chatbereich mit Thread (oder einer 1:1-Direktnachricht, die bereits mindestens eine Nachricht enthält) hinzu und rufen Sie den Ressourcennamen einer zugänglichen Nachricht ab, auf die Sie antworten möchten (spaces/SPACE_ID/messages/MESSAGE_ID).
  • Installieren Sie für Python-Beispiele die Google API-Clientbibliothek für Python und google-auth und initialisieren Sie den Chat API-Dienstclient:

    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)
    

Stream starten

Wenn Sie eine Antwort streamen möchten, rufen Sie spaces.streamMessages.create auf und übergeben Sie im Anfragetext ein StreamMessage:

  • referencedMessage (erforderlich): Ressourcenname der Nachricht, die die Antwort ausgelöst hat, formatiert als spaces/{space}/messages/{message}. Die referenzierte Nachricht muss sich im selben Gruppenbereich befinden und für Ihre Chat-App zugänglich sein. In Google Chat wird die gestreamte Antwort im selben Thread wie referencedMessage gepostet.
  • progressText (erforderlich): Nicht leeres Nur-Text-Statuslabel (bis zu 250 Zeichen), das neben der Fortschrittsanzeige angezeigt wird, z. B. "Thinking...".
  • markupSyntax (optional): Syntax für die Textformatierung der Nachricht, z. B. MARKUP_SYNTAX_MARKDOWN oder MARKUP_SYNTAX_CHAT (standardmäßig die konfigurierte Syntax der Chat-App, wenn MARKUP_SYNTAX_UNSPECIFIED). Legen Sie MARKUP_SYNTAX_MARKDOWN fest, wenn Sie Inline-Zitate einfügen möchten.
  • streamContentChunks (optional): Anfängliche text-, step- oder citation-Blöcke, die sofort angezeigt werden sollen.

Speichern Sie sowohl name (spaces/{space}/streamMessages/{stream_message}) als auch nextChunkToken aus der Antwort für nachfolgende Aufrufe.

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

Die Methode gibt eine StreamMessage-Ressource zurück:

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

Inhaltsblöcke anhängen

Wenn Ihre Chat-App Zwischenschritte oder Text-Tokens erzeugt, rufen Sie spaces.streamMessages.appendStreamMessageContent auf:

  • Übergeben Sie die aktuelle nextChunkToken in chunkToken. Bei jedem erfolgreichen Aufruf wird ein neuer nextChunkToken zurückgegeben, den Sie bei der nächsten Aktualisierungs- oder Commit-Anfrage übergeben müssen.
  • text-Chunks werden direkt an zuvor gestreamten Text angehängt. Fügen Sie bei Bedarf Leerzeichen oder Zeilenumbrüche zwischen den Blöcken ein.
  • step Chunks werden an die Liste der Zwischenschritte angehängt, die in der Nachricht angezeigt werden.
  • progressText ist optional. Wenn Sie progressText weglassen oder auf "" setzen, wird in Google Chat weiterhin der zuvor festgelegte progressText angezeigt.

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

Gestreamte Inhalte oder Fortschrittstext ersetzen

Wenn Sie zuvor gestreamte Inhalte neu schreiben müssen, z. B. nach der Ausführung eines Nachbearbeitungs- oder Sicherheitsfilters, oder wenn Sie nur progressText aktualisieren möchten, ohne Chunks anzuhängen, rufen Sie spaces.streamMessages.patch auf:

  • Legen Sie für updateMask eine durch Kommas getrennte Liste von Feldern fest, die aktualisiert werden sollen: progressText, streamContentChunks oder progressText,streamContentChunks.
  • Fügen Sie das neueste nextChunkToken in den StreamMessage-Anfragetext ein und notieren Sie das neue nextChunkToken, das in der Antwort zurückgegeben wird.
  • Wenn streamContentChunks den Wert updateMask hat, ersetzen die bereitgestellten Chunks zuvor gestreamte Inhalte, anstatt sie anzuhängen.
  • Wenn progressText in updateMask ist, muss progressText ein nicht leerer Nur-Text mit bis zu 250 Zeichen sein.

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

Stream abschließen

Wenn die Generierung abgeschlossen ist, rufen Sie spaces.streamMessages.commit mit der neuesten chunkToken auf, um den Stream in eine permanente Message-Ressource umzuwandeln:

  • Sie können optional endgültige streamContentChunks in die Commit-Anfrage aufnehmen, einschließlich Nur-Commit-Chunk-Typen (card, accessoryWidget und citedSource).
  • Wenn Sie bereits nicht leere Inhalte in früheren Aufrufen gestreamt haben, können Sie streamContentChunks beim Commit weglassen. Die endgültige übernommene Nachricht darf nicht leer sein.

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

Aktiven Stream verwerfen

Wenn ein nicht behebbarer Fehler auftritt oder ein Nutzer die Generierung abbricht und Sie keine unvollständige Antwort im Thread hinterlassen möchten, rufen Sie spaces.streamMessages.delete auf:

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()

Sobald ein Stream in einen Message übertragen wurde, rufen Sie stattdessen spaces.messages.delete auf, wenn Sie die abgeschlossene Nachricht löschen müssen.

Unterstützte Typen von Inhaltsblöcken

Jeder Eintrag in streamContentChunks gibt ein Feld in der StreamContentChunk-Union an. Einige Chunk-Typen können während des gesamten Streams gesendet werden, während andere nur in spaces.streamMessages.commit akzeptiert werden:

Feld „Chunk“ Unterstützte Methoden Feld „Final Message“ Hinweise
text create, appendStreamMessageContent, patch, commit text Inkrementeller Text. Verkettet am appendStreamMessageContent; ersetzt am patch. Der endgültige übernommene Text darf 32.000 Byte nicht überschreiten.
step create, appendStreamMessageContent, patch, commit elements.steps.steps[] Zwischenverarbeitungsschritt mit Nur-Text-title (erforderlich) und optionalem description.
citation create, appendStreamMessageContent, patch, commit elements.citations[] Inline-Citation mit einem alphanumerischen id (unter 63 Zeichen, Unterstriche zulässig) und citedSources. Erfordert markupSyntax: "MARKUP_SYNTAX_MARKDOWN" und übereinstimmende <chat-citation data-id="ID">...</chat-citation>-Tags in text. Wenn Sie eine citation mit einer vorhandenen id senden, wird die frühere Zitation überschrieben.
card Nur commit cardsV2[] Interaktive oder informative CardWithId-Karte.
accessoryWidget Nur commit accessoryWidgets[] AccessoryWidget-Buttons, die unter der Nachricht angezeigt werden, z. B. Feedback-Buttons.
citedSource Nur commit elements.citedSources[] CitedSource-Fußzeilenlink, der unter der Nachricht ohne Inline-Zitattags angezeigt wird.

Vom Nutzer initiierte Stream-Abbrüche verarbeiten

Während ein Stream aktiv ist, kann nur der Absender der referenzierten Nachricht (referencedMessage), auf die gestreamt wird, in der Streaming-Nachricht auf Beenden klicken. Wenn der Nutzer einen Stream beendet, sendet Google Chat ein Interaktionsereignis für einen Systembefehl mit cancelStream.streamMessage (dem spaces/{space}/streamMessages/{stream_message}-Ressourcennamen des beendeten Streams) an den Endpunkt Ihrer Chat-App.

Die Struktur der eingehenden JSON-Nutzlast hängt davon ab, ob Sie Ihre Chat-App als Google Workspace-Add-on oder mit Chat API-Interaktionsereignissen erstellen:

Add-ons für 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"
    }
  }
}

Wenn Ihre Chat-App ein cancelStream-Ereignis empfängt:

  1. Signalisieren Sie Ihrem Hintergrund-Worker, dass er keine weiteren Tokens generieren oder nachgelagerte Tools für streamMessage ausführen soll.
  2. Lösen Sie den aktiven Stream mit einem der folgenden Ansätze auf:
    • Teilausgabe beibehalten: Rufen Sie spaces.streamMessages.commit mit der neuesten chunkToken auf und hängen Sie optional einen Hinweis wie "\n\n_Generation stopped._" an.
    • Antwort verwerfen: Rufen Sie spaces.streamMessages.delete auf, damit nichts im Thread gespeichert wird.
  3. Geben Sie eine leere HTTP-200 OK-JSON-Antwort ({}) von Ihrem Interaktionsereignis-Handler zurück.

Beschränkungen

Für das Nachrichten-Streaming gelten die folgenden Einschränkungen:

  • Plattformverfügbarkeit: Das Nachrichtenstreaming ist nur in der Webversion von Google Chat sichtbar. Die Unterstützung für Android und iOS folgt demnächst.
  • Zeitüberschreitung des Streams: Ein aktiver StreamMessage wird 10 Minuten nach der Erstellung beendet. Ihre Chat-App muss innerhalb von 10 Minuten nach dem Start des Streams spaces.streamMessages.commit oder spaces.streamMessages.delete aufrufen.