Skip to main content

Краткий ответ

При выполнении многоэтапного вызова функций (function calling) моделями Gemini 3 и более поздними (gemini-4-argon, gemini-3.8-flash, gemini-3.1-pro-preview и т. д.) возникают два типа ошибок 400, связанных с подписью рассуждений (thoughtSignature), и для них требуются разные решения: Если вы выполняете вызовы через OpenAI-совместимый эндпоинт /v1/chat/completions, к вам это не относится: шлюз сам управляет подписями, и стандартных сообщений tool_calls / tool будет вполне достаточно.

Как различить эти две ошибки

Отсутствующая подпись

В сообщении указывается функция и её индекс в массиве parts. Это строгое правило, установленное провайдером модели для семейства Gemini 3: каждая часть functionCall в текущей реплике должна содержать свою подпись. При отсутствии подписи запрос завершается ошибкой 400 на любом маршруте, и повторные попытки не помогут.

Недействительная или поврежденная подпись

Поле присутствует, но не проходит валидацию. Подпись представляет собой зашифрованный blob-объект, который может проверить только создавший его маршрут. Если в ходе диалога меняется маршрут, модель, ключ или группа, ранее созданные подписи могут перестать приниматься.

Почему это происходит

Когда включены tools, модели Gemini 3 прикрепляют зашифрованный thoughtSignature к той же части, что и каждый functionCall, фиксируя состояние рассуждения на этом шаге. При следующем запросе модели он необходим для продолжения цепочки рассуждений, поэтому провайдер требует отправлять его обратно без изменений.

Отсутствует: клиент пересобрал часть

Самая частая причина. Кастомный фреймворк или middleware при сборке истории сохраняет только name и args из functionCall и отбрасывает соседний thoughtSignature; либо чанки при потоковой передаче собираются заново без объединения с подписью.

Отсутствует: история была сжата или перезаписана

Клиент сократил или суммаризировал контекст либо объединил несколько частей в одну, из-за чего подпись была удалена вместе с ними.

Недействительна: маршрут изменился в ходе диалога

Первые реплики обрабатывались маршрутом A, а последующие попали на маршрут B, который не распознает подписи маршрута A. Переключение может произойти из-за смены вами группы или ключа либо в результате автоматического переключения при сбое.

Недействительна: модель изменилась в ходе диалога

Начало на gemini-3.8-flash и переключение на gemini-4-argon в рамках одного диалога. Подписи не переносятся между моделями.
При параллельных вызовах функций модель прикрепляет подпись только к первой части functionCall; у остальных ее обоснованно нет, поэтому не создавайте ее искусственно. При последовательных вызовах (несколько шагов в рамках одной реплики) каждый шаг содержит подпись, и каждую из них необходимо возвращать.

Исправление 1: отсутствует подпись, измените способ воспроизведения истории клиентом

1

Передавайте весь ответ модели обратно дословно

При использовании официального google-genai SDK просто используйте contents.append(response.candidates[0].content); не извлекайте только поле function_call. При отправке прямых REST-запросов реплика модели в contents второго запроса должна выглядеть следующим образом:
thoughtSignature и functionCall — это два поля одной и той же части, а не две разные части.
2

Проверьте, не удаляет ли поле фреймворк или middleware

При работе с LangChain, кастомным фреймворком агентов или слоем трансляции OpenAI-в-Gemini выведите фактическое тело второго запроса и убедитесь, что thoughtSignature все еще присутствует. position N в тексте ошибки — это parts[N]; проверяйте сразу там.
3

Не полагайтесь на то, что некоторые модели пропускают такие запросы

Модели и маршруты различаются по строгости проверки отсутствующих подписей: одни пропускают их, другие возвращают 400. Если один и тот же код всегда работал на gemini-3.8-flash, но падает на gemini-4-argon, вероятнее всего, подпись никогда не отправлялась и ранее просто допускалась. Возврат подписей в соответствии с требованиями провайдера работает стабильно на всех моделях.

Решение 2: недействительная подпись, повторите отправку или начните заново

1

Повторите отправку один раз без изменений

Когда шлюз APIYI получает ошибку 400 из-за подписи, он автоматически повторяет попытку по другому маршруту, поэтому обычная повторная отправка часто завершается успешно. Никаких изменений вносить не требуется.
2

Все еще 400: удалите подписи из реплик истории и отправьте повторно

Удалите поле thoughtSignature из частей каждой реплики role: "model" в истории, затем отправьте запрос повторно. Вы потеряете контекст рассуждения этих реплик, и качество ответа может незначительно снизиться, но диалог можно будет продолжить. Удаляйте подписи только из реплик истории, но ни в коем случае не из текущей.
3

Либо начните новый диалог

Самый простой и надежный вариант. В новом диалоге каждая подпись создается по одному и тому же маршруту с самой первой реплики.
4

Одна модель, один ключ на диалог

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

Отсутствие обработки подписи на OpenAI-совместимом эндпоинте

При вызове Gemini через /v1/chat/completions сообщения tool_calls не содержат поля подписи; шлюз сохраняет и восстанавливает его во время преобразования. В наших тестах на gemini-4-argon двухшаговые диалоги успешно работали для всех трёх форматов: одиночный вызов, два параллельных вызова и текст плюс вызов. Просто возвращайте сообщения tool_calls и tool стандартным для OpenAI способом.
Клиентам, таким как Codex, которые по умолчанию используют /v1/responses, необходимо переключиться на протокол чата, поскольку модели Gemini пока не поддерживают этот эндпоинт. См. Генерация текста Gemini 4 Argon.

Часто задаваемые вопросы

Нет. Возврат подписей — это требование провайдера для всего семейства Gemini 3; маршруты некоторых моделей просто менее строги к их отсутствию. Переход на строго валидирующую модель вскрывает проблему, но первопричина по-прежнему заключается в клиенте, который вообще не отправлял подпись.
Нет. Подпись действительна только на том маршруте и в контексте того диалога, где она была создана. Копирование ее в другой диалог приведет к ошибке Invalid.
В той же части, что и functionCall, внутри чанка, который ее содержит. При повторной сборке stream объедините это поле вместе с functionCall в итоговый ответ модели, прежде чем возвращать его в историю.
Не строго обязательно. Провайдер жестко валидирует подписи только в частях functionCall; отсутствие подписи в текстовой реплике не приводит к ошибке 400, однако ее сохранение помогает непрерывности рассуждения, поэтому в любом случае возвращайте весь ответ дословно.

Связанные документы

Руководство по Gemini Function Calling

Полный двухшаговый пример, включая возврат сигнатур

Генерация текста в Gemini 4 Argon

Примечания к многошаговым tool-call и настройка клиента

Как исправить ошибку Claude Invalid Thinking Signature?

Тот же класс проблем на стороне Claude

Руководство по многошаговым диалогам

Как каждый провайдер ожидает передачу истории