В этом руководстве рассказывается, как постепенно передавать сообщения в Google Chat с помощью ресурса StreamMessage в Chat API. Потоковая передача позволяет приложению Google Chat показывать индикаторы выполнения в реальном времени, промежуточные рассуждения или шаги выполнения инструмента, а также инкрементные текстовые ответы до завершения сообщения.
Потоковая передача предназначена для приложений 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 Workspace Business или Enterprise с доступом к Google Chat.
- Настройте среду:
- Создайте проект Google Cloud.
- Включите и настройте Google Chat API, указав название, значок и описание приложения Chat.
- Создайте учетные данные сервисного аккаунта и сохраните их в файле
credentials.json. Для потоковой передачи сообщений требуется аутентификация приложения с областью авторизацииhttps://www.googleapis.com/auth/chat.bot. Аутентификация пользователя не поддерживается.
- Добавьте приложение Chat в чат-группу с цепочками (или в прямую переписку, в которой уже есть хотя бы одно сообщение) и получите название ресурса доступного сообщения, на которое нужно ответить (
spaces/SPACE_ID/messages/MESSAGE_ID). Чтобы использовать примеры на Python, установите клиентскую библиотеку Google API для Python и
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(по умолчанию используется синтаксис, настроенный для приложения Chat, еслиMARKUP_SYNTAX_UNSPECIFIED). Установите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. Каждый успешный вызов возвращает новый токен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. - Включите последний токен
nextChunkTokenв тело запросаStreamMessageи запишите новый токен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"]
Как завершить трансляцию
Когда генерация завершится, вызовите spaces.streamMessages.commit с последним chunkToken, чтобы преобразовать поток в постоянный ресурс 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. Требуются теги markupSyntax: "MARKUP_SYNTAX_MARKDOWN" и соответствующие им теги <chat-citation data-id="ID">...</chat-citation> в файле text. Если отправить citation с существующим id, предыдущая цитата будет перезаписана. |
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. - Решите проблему с активной трансляцией одним из следующих способов:
- Сохранить частичный вывод. Вызовите
spaces.streamMessages.commitс последнимchunkToken(при необходимости добавьте примечание, например"\n\n_Generation stopped._"). - Удалить ответ. Вызовите
spaces.streamMessages.delete, чтобы ничего не сохранять в цепочке.
- Сохранить частичный вывод. Вызовите
- Верните пустой ответ JSON HTTP
200 OK({}) из обработчика событий взаимодействия.
Ограничения
Для потока сообщений действуют следующие ограничения:
- Доступность на платформах. Потоковая передача сообщений доступна только в веб-версии Chat. Поддержка Android и iOS будет добавлена в ближайшее время.
- Время ожидания потока. Активный поток
StreamMessageпрекращает работу через 10 минут после создания. Приложение Chat должно вызватьspaces.streamMessages.commitилиspaces.streamMessages.deleteв течение 10 минут после начала трансляции.