Как передавать сообщения с помощью Google Chat API

В этом руководстве рассказывается, как постепенно передавать сообщения в Google Chat с помощью ресурса StreamMessage в Chat API. Потоковая передача позволяет приложению Google Chat показывать индикаторы выполнения в реальном времени, промежуточные рассуждения или шаги выполнения инструмента, а также инкрементные текстовые ответы до завершения сообщения.

Потоковая передача предназначена для приложений 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), на которое генерируется ответ, может нажать Остановить, чтобы отменить генерацию. Chat отправляет системную команду отмены в ваше приложение Chat, чтобы вы могли остановить работу серверной части и либо зафиксировать частичный вывод, либо удалить поток.

Требования

Чтобы передавать сообщения в потоковом режиме с помощью Chat API, выполните следующие предварительные требования:

  • Аккаунт Google Workspace Business или Enterprise с доступом к Google Chat.
  • Настройте среду:
  • Добавьте приложение Chat в чат-группу с цепочками (или в прямую переписку, в которой уже есть хотя бы одно сообщение) и получите название ресурса доступного сообщения, на которое нужно ответить (spaces/SPACE_ID/messages/MESSAGE_ID).
  • Чтобы использовать примеры на Python, установите клиентскую библиотеку Google API для Python и 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 (по умолчанию используется синтаксис, настроенный для приложения 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:

  1. Подайте фоновому процессу сигнал прекратить генерировать токены или запускать инструменты для streamMessage.
  2. Решите проблему с активной трансляцией одним из следующих способов:
    • Сохранить частичный вывод. Вызовите spaces.streamMessages.commit с последним chunkToken (при необходимости добавьте примечание, например "\n\n_Generation stopped._").
    • Удалить ответ. Вызовите spaces.streamMessages.delete, чтобы ничего не сохранять в цепочке.
  3. Верните пустой ответ JSON HTTP 200 OK ({}) из обработчика событий взаимодействия.

Ограничения

Для потока сообщений действуют следующие ограничения:

  • Доступность на платформах. Потоковая передача сообщений доступна только в веб-версии Chat. Поддержка Android и iOS будет добавлена в ближайшее время.
  • Время ожидания потока. Активный поток StreamMessage прекращает работу через 10 минут после создания. Приложение Chat должно вызвать spaces.streamMessages.commit или spaces.streamMessages.delete в течение 10 минут после начала трансляции.