Перейти к основному содержимому

Chat API v2

предупреждение

Канал Chat API v2 доступен, только если платформа Agent Platform установлена в контур вашей компании и эта функция включена в конфигурации.

Канал можно подключить только в проектах с движком версии 2.

Chat API v2 — канал для интеграции чата с Agent Platform в ваше приложение, в котором ответ агента приходит в потоковом режиме: клиент видит текст по мере генерации, а не дожидается ответа целиком.

подсказка

Полное описание методов, параметров, форматов ответов и ошибок смотрите в спецификации Chat API v2.

Чем Chat API v2 отличается от Chat API​

Это два разных API с разными адресами и методами. Chat API продолжает работать, переходить на новую версию не обязательно.

Chat APIChat API v2
Адрес/chatapi/{token}/streamapi
ТокенЧасть путиЗаголовок STREAM-API-TOKEN
Ответ агентаЦеликом, когда готовЧастями, по мере генерации
Передача файловНетЕсть
Остановка ответаНетЕсть

Подключение канала​

Добавить канал в проекте можно двумя способами:

  • В проекте перейдите в раздел Интеграции, выберите Подключить → Канал → Chat API v2.
  • На холсте дважды нажмите на триггер Сообщение и выберите Подключить канал → Chat API v2.

Укажите параметры:

  1. Задайте Название канала. Оно будет отображаться в списке интеграций.
  2. Укажите URL обратного вызова, если будете его использовать.
  3. Нажмите Добавить.
примечание

Указывать STREAM-API-TOKEN не нужно, он будет сгенерирован автоматически.

Токен и адрес для доступа к Chat API v2​

После создания канала получить токен и адрес для запросов можно двумя способами:

  • В разделе Интеграции:

    • Чтобы получить токен, нажмите → Редактировать у канала Chat API v2 и скопируйте значение STREAM-API-TOKEN.
    • Чтобы получить адрес, нажмите → Получить вебхук.
  • На холсте нажмите триггер Сообщение, затем:

    • Чтобы получить токен, нажмите → Редактировать у канала Chat API v2 и скопируйте значение STREAM-API-TOKEN.
    • Чтобы получить адрес, Получить вебхук.

Токена в адресе нет: его нужно передавать в заголовке STREAM-API-TOKEN в каждом запросе.

Пример:

STREAM-API-TOKEN: ZaBcdEfG:123tf0bc456708ea48a89ee1b537211141dd0f41b

Токен определяет и аккаунт, и проект, поэтому поля accountId и botId в запросах не обязательны. Если указать в них чужой аккаунт или проект, запрос вернет 404 — по ответу нельзя узнать, существует ли такой проект.

Запрос без заголовка или с неизвестным токеном тоже возвращает 404.

Как устроена потоковая генерация​

Получение ответа на вопрос клиента выполняется в два шага:

  1. Приложение отправляет сообщение методом POST /streamapi/message и сразу получает ответ 202 — генерация к этому моменту еще не началась.
  2. Приложение открывает поток Server-Sent Events (SSE) методом GET /streamapi/resume и читает ответ агента по частям.

Такое разделение позволяет приложению не держать соединение открытым все время ответа: генерация идет на сервере и переживает обрыв связи, а приложение возвращается к ней, когда сможет.

Каждая часть ответа приходит отдельным событием SSE, которое несет JSON-объект в поле data. Дальше такие события называются кадрами.

Отправка сообщения​

POST /streamapi/message принимает сообщение клиента и ставит его в обработку:

{
"clientId": "user-42",
"chatId": "f1c0b1e2-…",
"text": "Какой у меня остаток по счету?",
"clientMessageId": "msg-1001"
}
  • clientId — идентификатор клиента, обязателен.
  • chatId — идентификатор диалога, который нужно продолжить. Если его не указать, откроется новый диалог, а его идентификатор вернется в ответе.
  • clientMessageId — идентификатор сообщения на стороне приложения. Он же служит ключом идемпотентности: повторная отправка с тем же значением не создаст второй запрос к агенту.
  • Вместо текста можно отправить событие — поле event с названием и eventData с полезной нагрузкой.

Сообщение должно нести хотя бы одно из трех: text, event или непустой attachments (см. Передача файлов). Иначе запрос отклоняется с ошибкой 400 chatadapter.streaming.empty_message.

Ответ 202 возвращает questionId — идентификатор вопроса, по которому нужно читать ответ, — и chatId:

{
"questionId": "a7d3…",
"chatId": "f1c0b1e2-…"
}

Ответы 500 и 503 означают, что обработка не началась: отправьте сообщение заново — обязательно с новым clientMessageId.

Чтение ответа​

GET /streamapi/resume открывает поток SSE с ответом агента. Обязательные параметры — chatId и clientId:

GET /streamapi/resume?chatId=f1c0b1e2-…&clientId=user-42&questionId=a7d3…

Если questionId не указать, канал отдаст поток последнего запроса в диалоге.

Каждый кадр несет общие поля: type — тип кадра, messageId — идентификатор запроса, sequence — позицию кадра в потоке, offset — позицию для переподключения.

Типы кадров​

typeЧто означает
deltaОчередная порция ответа в поле text
blockГотовый ответ целиком: тип в поле blockType — text, image или file
statusСлужебное сообщение о том, чем занят агент: tool_running, tool_finished, awaiting_input, queued
doneОтвет завершен. Поле turnStatus — completed или awaiting_input
errorОтвет прерван. Причина — в поле code

Кадры done и error терминальные: после любого из них поток закрывается.

В одном ответе могут быть и delta, и block. Кадры delta порождает только блок Агент: текст, который генерирует LLM, передается по мере готовности. Функции Reactions порождают кадры block:

  • sendText() и sendHtml() дают blockType: text;
  • sendImage() — image;
  • sendFile(), sendVideo() и sendAudio() — file;
  • sendLocation() и sendRawRequest() в этом канале кадра не создают.

Если приложение открывает GET /streamapi/resume уже после завершения генерации, ответ агента возвращается не кадрами delta, а одним block, собранным из истории.

Кадр status с kind: queued приходит, когда в диалоге уже обрабатываются другие сообщения и это стоит в очереди. Поле queuePosition показывает, сколько запросов впереди; значение 1 означает, что следующим начнется этот. Переподключаться не нужно — генерация придет в то же соединение.

Правило совместимости​

Набор типов кадров и их полей может пополняться. Приложение должно пропускать кадр с незнакомым type и незнакомые поля внутри кадра, а не завершать работу с ошибкой. То же относится к значениям kind в кадре status и к типам событий в уведомлениях.

Переподключение после обрыва​

Генерация идет на сервере и не прекращается, когда приложение теряет соединение. Чтобы продолжить с места обрыва, передайте offset последнего полученного кадра в повторном запросе:

GET /streamapi/resume?chatId=f1c0b1e2-…&clientId=user-42&questionId=a7d3…&offset=1759512345678-4

Канал отдаст кадры, созданные после этой позиции. Если значение offset не выдавалось этим API, запрос отклоняется с ошибкой 400 chatadapter.streaming.invalid_offset.

offset принадлежит конкретному запросу, а не диалогу: если в диалоге обрабатывается несколько сообщений сразу, приложение хранит свою позицию для каждой пары chatId и questionId.

Переподключаться нужно не после каждой ошибки. Код в кадре error говорит, что делать:

codeЧто делать
stream_interruptedПереподключиться с последним offset: ответ, возможно, еще генерируется
turn_finishedНе переподключаться: ответ завершен, он лежит в истории
chat_taken_overНе переподключаться: диалог открыт в другом соединении

Один диалог — одно подключение​

Диалог одновременно открыт только в одном месте. Когда приложение открывает поток, диалог закрепляется за его подключением, а соединение, которое держало диалог раньше, закрывается кадром error с кодом chat_taken_over.

Приложение, получившее chat_taken_over, должно показать, что диалог открыт в другом месте, и остановиться. Если вместо этого переподключиться, два подключения начнут отбирать диалог друг у друга.

Если одно окно приложения читает несколько ответов в одном диалоге, передавайте во всех запросах на чтение ответа одинаковый параметр connectionId.

Остановка ответа​

POST /streamapi/cancel останавливает генерацию:

{
"chatId": "f1c0b1e2-…",
"clientId": "user-42",
"questionId": "a7d3…",
"readUpToSequence": 17
}

Ответ 200 означает, что генерация остановлена, 204 — что она уже успела завершиться.

Если questionId не указать, остановятся все запросы, которые сейчас обрабатываются в диалоге.

Необязательное поле readUpToSequence сообщает, до какого кадра приложение успело показать ответ. Канал запомнит эту границу, и в истории останется только показанная часть — после перезагрузки клиент не увидит текст, которого не видел раньше. Такой ответ помечается в истории полем interrupted.

Остановленный ответ не отправляется на URL обратного вызова.

Передача файлов​

Передача файла выполняется в два шага:

  1. Приложение загружает файл методом POST /streamapi/files с типом содержимого multipart/form-data, полем file и параметром clientId:

    POST /streamapi/files?clientId=user-42

    В ответ приходит дескриптор файла — ссылка на файл в хранилище и метаданные:

    {
    "url": "https://…",
    "type": "image",
    "mimeType": "image/png",
    "name": "screenshot.png",
    "size": 20481
    }
  2. Приложение отправляет сообщение методом POST /streamapi/message, передавая дескриптор целиком в массиве attachments:

    {
    "clientId": "user-42",
    "text": "Что не так на этом экране?",
    "attachments": [
    {
    "url": "https://…",
    "type": "image",
    "mimeType": "image/png",
    "name": "screenshot.png",
    "size": 20481
    }]
    }

Ограничения:

  • файл не больше 20 Мбайт (иначе ошибка 413 chatadapter.streaming.file_too_large);
  • не больше 20 вложений в одном сообщении;
  • ссылка на вложение не длиннее 2048 символов.

Конфигурация Agent Platform в контуре вашей компании может ограничивать и список допустимых типов файлов — тогда файл другого типа отклоняется с ошибкой 415 chatadapter.streaming.file_type_not_allowed.

предупреждение

До модели доходят только вложения с типом image. Документы агент получает в коде через Context.getMessageContent().files, но в запрос к LLM они не передаются.

Файлы, которые прислал агент, приходят в кадре block с blockType: file в том же формате дескриптора.

История переписки​

Метод GET /streamapi/history возвращает завершенные сообщения диалога — вопросы клиента и ответы агента:

GET /streamapi/history?chatId=f1c0b1e2-…&clientId=user-42&limit=50

Страница содержит все, что нужно, чтобы отобразить переписку заново: файлы приходят дескрипторами в attachments, сообщения без собственного текста описываются полем systemMessageType.

Чтобы получить следующую страницу, передайте в параметре before самое раннее значение timestamp из полученной страницы. Параметр sortTimestamp задает порядок сообщений на странице — DESC (по умолчанию) или ASC — и не меняет их состав.

Список диалогов​

Метод GET /streamapi/conversations возвращает диалоги клиента. Метод нужен приложению, которое показывает клиенту такой список, а не только активный диалог.

Уведомления вне обработки сообщения​

У канала есть второй путь доставки — для ответов, которые появляются не в ответ на сообщение клиента (рассылка, реакция по таймауту).

Метод GET /streamapi/notifications работает по принципу long polling: соединение открыто, пока не появится событие или не истечет время ожидания. Получив ответ, приложение сразу отправляет следующий запрос с новым значением offset.

Событие несет только адрес изменившегося диалога и время, без текста сообщения. Приложение запрашивает текст методом GET /streamapi/history.

typeЧто означает
message_addedВ диалоге появилось новое сообщение
gapПозиция offset устарела и была удалена из журнала: перечитайте историю и продолжайте с offset из этого ответа
chat_taken_overДиалог открыт в другом подключении

Если offset не передан, канал начинает отсчет с текущего момента и событий не возвращает. Ответ 200 с пустым массивом events и прежним offset — это ответ по таймауту, он же сигнал, что соединение живо.

URL обратного вызова​

Если приложение не может держать соединение открытым, укажите в настройках канала URL обратного вызова. Когда ответ агента готов, а запроса на его чтение нет, Agent Platform отправит на этот адрес готовый ответ целиком — с текстом и вложениями. Кадров в таком запросе нет.

Чтобы указать адрес:

  1. Для канала в разделе Интеграции или в триггере Сообщение нажмите → Редактировать.
  2. Введите адрес в поле URL обратного вызова. Нажмите Сохранить.

На этот же адрес приходят уведомления о событиях вне обработки сообщения.

Если доставить запрос не удалось, Agent Platform пробует еще раз — всего не больше трех попыток, с паузой 10 секунд между ними. Успешным считается любой ответ 2xx. Ответ, который так и не доставили, остается в истории.

Тарификация​

Вызов POST /streamapi/message засчитывается как запуск процесса, вызовы остальных методов — нет. Генерация ответа расходует J-токены. Общие правила описаны в разделе Тарификация.

Ограничения текущей версии​

  • Агент на проекте с подключенным Jay Guard в этом канале возвращает ошибку или пустой ответ, не вызвав инструменты.
  • Если соединение SSE закрывается по таймауту, приложение не получает кадр error с кодом stream_interrupted и видит только обрыв соединения без объяснения причины. Ответ при этом не теряется, его можно дочитать методом GET /streamapi/resume.
  • Режим доставки deliveryMode поддерживает только значение token. Режим sentence, который нужен синтезу речи, пока не реализован.