はじめに
Gemini 3シリーズを使ってツール呼び出し(関数呼び出し)を実装したとき、突然 400 Bad Request が返ってきた経験はありませんか?
InvalidArgument: 400 Function call `tool_name` in the `1.` content block is missing a `thought_signature`.
このエラーはGemini 3から導入された Thought Signatures(思考シグネチャ) を正しく処理していないために発生します。LangChain、Google ADK、n8n、Windmill、Pipecatなど多数のフレームワークでも同様のIssueが報告されており、Gemini 3を使う開発者が共通して直面する問題です。
本記事では、Thought Signaturesの仕組みと実装方法を、公式ドキュメントと公開情報をもとに解説します。
この記事で学べること
- Thought Signaturesとは何か、なぜGemini 3で必要になったか
- 400エラーが発生するパターンと回避方法
- Python公式SDKを使ったときの自動処理と、手動実装の方法
- 逐次・並列関数呼び出しでのシグネチャの扱い方
-
thinking_levelとの組み合わせ時の注意点
対象読者
- Gemini 3(Flash / 3.1 Pro等)のAPIで関数呼び出しを実装している開発者
- 既存のGemini 2.xコードをGemini 3に移行しようとしているエンジニア
- LangChain等のフレームワーク経由でGemini 3を使っている方
TL;DR
- Gemini 3では、関数呼び出しの応答に
thoughtSignatureフィールドが付与される - 次のターンで会話履歴を送るとき、この署名を含めないと 400エラー
- 公式PythonSDK(
google-genai)のchat機能を使えば自動処理される - 手動で会話履歴を管理する場合は、
thought_signatureを必ずそのまま引き継ぐこと -
thinking_level: minimalに設定しても Thought Signatures の送付は必須
Thought Signaturesとは
Thought Signatures(思考シグネチャ)は、モデルの内部的な思考プロセスを 暗号化された表現 として保持し、マルチターンの会話で推論の文脈を維持するための仕組みです。
Gemini 2.xまでは、関数呼び出しの会話履歴は単純に functionCall と functionResponse のやり取りでした。Gemini 3では、モデルが関数を呼び出す前に内部で行う推論過程(思考)を「シグネチャ」として返し、次の呼び出しでそれを受け取ることで、モデルが前の推論を引き継いだ状態 で処理を継続できます。
なぜGemini 3で必要になったのか
Gemini 3は「Dynamic Thinking(動的思考)」がモデルに組み込まれており、関数呼び出しを含むすべての応答で内部推論が動作しています。このとき思考の文脈(シグネチャ)を次のターンに渡さないと、モデルは「途中から文脈なしで作業を再開する」状態になり、整合性が崩れます。
Googleはこの整合性の問題を防ぐため、APIレベルでシグネチャの検証を 強制 することにしました。関数呼び出しの場合、シグネチャ欠落は即座に400エラーになります。
400エラーの発生パターン
Googleの公式ドキュメントによると、バリデーションの厳密さはコンテンツの種類によって異なります。
| コンテンツ種別 | シグネチャ欠落時の挙動 |
|---|---|
| 関数呼び出し(functionCall) | 400エラー(厳密に強制) |
| テキスト生成・チャット | エラーなし(ただし推論品質が低下) |
| 画像生成・編集 | 400エラー(厳密に強制) |
つまり、関数呼び出しを使う場合は必ずシグネチャを引き継ぐ 必要があります。
よくある誤実装パターン
# NG: functionCallをそのまま辞書に変換して再構成すると thoughtSignature が失われる
history.append({
"role": "model",
"parts": [{
"functionCall": {
"name": response.candidates[0].content.parts[0].function_call.name,
"args": dict(response.candidates[0].content.parts[0].function_call.args)
}
# ← thoughtSignature が含まれていない!
}]
})
この実装では thoughtSignature フィールドが手動で除外されてしまうため、次のターンで400エラーになります。
Python実装
方法1: 公式SDKの chat 機能を使う(推奨)
google-genai パッケージの chat セッションを使うと、Thought Signaturesは 自動的に管理 されます。
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
# ツール定義
def get_weather(city: str) -> str:
"""指定した都市の天気情報を取得する"""
# 実際の実装ではAPIを呼び出す
return f"{city}の現在の天気: 晴れ、気温 22℃"
tools = [get_weather]
# chatセッション作成 — Thought Signaturesは自動処理
chat = client.chats.create(
model="gemini-3.1-pro-preview",
config=types.GenerateContentConfig(
tools=tools,
thinking_config=types.ThinkingConfig(
thinking_level="medium"
)
)
)
# 1ターン目: モデルがget_weatherを呼び出す
response = chat.send_message("東京の天気を教えて")
# chatセッションが自動で:
# 1. functionCallに付いたthoughtSignatureを保持
# 2. functionResponseを送るとき自動で引き継ぐ
# 3. 最終回答を返す
print(response.text)
公式SDKの
chat.send_message()を使う場合、Thought Signaturesの管理は完全自動です。手動で何か対処する必要はありません。
方法2: 手動で会話履歴を管理する
generate_content() を直接呼び出して会話履歴を自分で管理する場合は、モデルの応答をそのままの形で履歴に追加 することが重要です。
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
def get_weather(city: str) -> str:
return f"{city}の天気: 晴れ、22℃"
# 会話履歴
messages = []
# ユーザーの質問を追加
messages.append(
types.Content(role="user", parts=[types.Part(text="東京の天気を教えて")])
)
# 1回目のAPI呼び出し
response = client.models.generate_content(
model="gemini-3.1-pro-preview",
contents=messages,
config=types.GenerateContentConfig(
tools=[get_weather],
thinking_config=types.ThinkingConfig(thinking_level="medium")
)
)
# ⭐ポイント: モデルの応答をそのままの形で履歴に追加する
# response.candidates[0].contentには thoughtSignature が含まれている
messages.append(response.candidates[0].content)
# 関数呼び出しがある場合は実行
for part in response.candidates[0].content.parts:
if part.function_call:
fn_name = part.function_call.name
fn_args = dict(part.function_call.args)
# 関数を実行
result = get_weather(**fn_args)
# 関数の結果を会話に追加
messages.append(
types.Content(
role="user",
parts=[types.Part(
function_response=types.FunctionResponse(
name=fn_name,
response={"result": result}
)
)]
)
)
# 2回目のAPI呼び出し(thoughtSignatureは messages に含まれている)
final_response = client.models.generate_content(
model="gemini-3.1-pro-preview",
contents=messages,
config=types.GenerateContentConfig(
tools=[get_weather],
thinking_config=types.ThinkingConfig(thinking_level="medium")
)
)
print(final_response.text)
response.candidates[0].contentをそのまま使わずに、functionCallのフィールドだけを取り出して辞書を再構成するとthoughtSignatureが失われます。必ず元のcontentオブジェクトを履歴に追加してください。
逐次・並列関数呼び出し時のルール
Googleの公式ドキュメントでは、Thought Signaturesの取り扱いについて以下のルールが定められています。
逐次関数呼び出し(Sequential)
モデルが複数の関数を 順番に 呼び出す場合、各ステップの最初の functionCall パーツにシグネチャを含める 必要があります。
ターン1: ユーザー質問
ターン2(モデル): functionCall_A(thoughtSignature_A付き)
ターン3(ユーザー): functionResponse_A
ターン4(モデル): functionCall_B(thoughtSignature_B付き) ← 各ステップに必要
ターン5(ユーザー): functionResponse_B
ターン6(モデル): 最終回答
並列関数呼び出し(Parallel)
モデルが複数の関数を 同時に 呼び出す場合、最初の functionCall パーツのみにシグネチャを含める だけで十分です。
ターン2(モデル):
functionCall_A(thoughtSignature付き) ← シグネチャはここだけ
functionCall_B(シグネチャなし) ← 2番目以降は不要
SDKの chat 機能を使えばこの違いも自動的に処理されます。
thinking_level との組み合わせ
thinking_level を設定してもThought Signaturesの送付が省略できるわけではありません。
| thinking_level | Thought Signaturesの送付 |
|---|---|
high(デフォルト) |
必須 |
medium |
必須 |
low |
必須 |
minimal |
必須(注意点) |
minimalでもThought Signaturesは必須です。
公式ドキュメントには「Circulation of thought signatures is required even when thinking level is set to minimal for Gemini 3 Flash.」と明記されています。thinking_levelとthought_signatureは独立した概念であり、前者はモデルの推論の深さ、後者は文脈の引き継ぎを管理します。
また、旧来の thinking_budget パラメータはGemini 2.5系のパラメータです。Gemini 3では thinking_level を使うよう移行してください。公式ドキュメントによると、Gemini 3で thinking_budget を使うと予期しない動作になる可能性があります。
# Gemini 2.5系(旧): thinking_budget を使用
config = types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(
thinking_budget=10000
)
)
# Gemini 3系(新): thinking_level を使用
config = types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(
thinking_level="high" # minimal / low / medium / high
)
)
よくあるエラーと対処法
エラー1: missing a thought_signature
InvalidArgument: 400 Function call `fn_name` in the `1.` content block is missing a `thought_signature`.
原因: 会話履歴を手動で構築する際に、functionCall から thoughtSignature フィールドを除外してしまっている
対処: response.candidates[0].content をそのまま履歴に追加する(辞書への変換を避ける)
エラー2: thinking_budget の挙動変化
Gemini 2.5系で使っていた thinking_budget パラメータをGemini 3モデルに対して使い続けると、予期しない動作が発生する可能性があります。エラーにはならない場合もありますが、推論品質が低下したりレスポンスが不安定になることがあります。
対処: thinking_budget を削除し、Gemini 3では thinking_level(minimal / low / medium / high)に移行する
エラー3: フレームワーク統合での問題
LangChain、n8n、Pipecat等のフレームワーク経由でGemini 3を使っている場合、フレームワーク側がThought Signaturesに対応していないことで400エラーが発生することがあります。
確認事項:
- 使用しているフレームワークの最新バージョンへアップデート
- リリースノートやIssueでGemini 3対応の記載を確認
- 対応していない場合、公式SDK直接使用を検討する
Gemini 3 Proからの移行(本日3/9が移行期限)
Gemini 3 Pro Preview(gemini-3-pro-preview)は 2026年3月9日 にシャットダウンされます。現在このモデルを使っている場合は、gemini-3.1-pro-preview への移行が必要です。
# Before: Gemini 3 Pro Preview(本日シャットダウン)
model = "gemini-3-pro-preview"
# After: Gemini 3.1 Pro Preview(現行推奨モデル)
model = "gemini-3.1-pro-preview"
移行時にThought Signaturesの実装も見直す良い機会です。SDKの chat 機能への切り替えを検討してみてください。
まとめ
- Gemini 3の関数呼び出しではThought Signaturesが必須。含めないと400エラーになる
-
最も簡単な解決策は公式SDKの
chatセッションを使うこと。自動管理されるため実装コストがゼロ - 手動実装する場合は
response.candidates[0].contentをそのまま履歴に追加し、辞書への変換で削ぎ落とさないようにする -
thinking_level: minimalでもThought Signaturesの送付は必須 - Gemini 3では
thinking_budget(Gemini 2.5系)ではなくthinking_levelを使うこと
フレームワーク経由でGemini 3を使っている場合は、フレームワーク側の対応状況を確認し、未対応であれば公式SDKへの移行を検討してください。


