Microsoft Agent FrameworkのGemini連携で、最初のfunction callは成功するのに、ツール実行結果を返した次のモデル呼び出しで400 INVALID_ARGUMENTが発生する場合、まず疑うべきなのがthought_signatureの欠落です。
Agent FrameworkのPythonリリース1.12.1では、Gemini 3がfunction callに付与したthought_signatureを内部メッセージへ保持し、後続ターンでfunction callを再生するときに復元する修正が追加されました。通常の複数ターンのツール呼び出しでは、1.12.1への更新が主要な対策になります。(GitHub)
ただし、1.12.1以上へ更新すれば、すべての経路で完全に解消するとは限りません。2026年8月3日時点では、ツール承認後の再開経路で署名が失われ、同じエラーが起きるという未クローズの報告があります。アップデート後は、通常のツール実行だけでなく、承認、履歴復元、ストリーミングなど、実際に使用する経路ごとの確認が必要です。(GitHub)
Agent Framework 1.12.1がGemini 3のthought_signature再生を修正
Agent FrameworkのPython 1.12.1リリースノートには、Gemini連携に関する修正として、Gemini 3のfunction-call replayでthought_signatureを保持する変更が明記されています。修正はPR #7095で実装され、2026年7月22日にマージされました。(GitHub)
ここでいうfunction-call replayとは、ツールをもう一度実行することではありません。
モデルが出力した過去のfunction callを、ツール実行結果とともに次のAPIリクエストへ再度含める処理を指します。Geminiは、この再送されたfunction callに元のthought_signatureが含まれていることを要求します。
修正前後の違いを整理すると、次のとおりです。
| 確認項目 | 1.12.0以前の問題 | 1.12.1の修正 |
|---|---|---|
| 署名の主な保持先 | Gemini SDKの元のPartに依存 | 内部メッセージのprotected_dataにも退避 |
| function callの再構築 | 名前、引数、IDだけで再構築すると署名が消える | 退避した署名を後続のfunction callへ復元 |
| JSON変換後の再生 | 元のSDKオブジェクトが失われると不安定 | JSON変換を含む往復テストを追加 |
| 公開APIへの影響 | 該当なし | 公開APIの変更なし |
| 壊れた署名データ | 再生できない | 警告を出し、変換処理自体は停止させない |
1.12.1の重要な点は、元のGemini SDKオブジェクトが残っている場合だけでなく、Agent Framework内部でfunction callが再構築された場合にも署名を引き継げるようにしたことです。
thought_signatureとは何か
thought_signatureは、Geminiの思考内容そのものを表すテキストではありません。モデルの内部状態を次のターンへ継続するために付与される、暗号化された不透明な情報です。
Gemini APIでは、署名がfunctionCallを含む各Partのメタデータとして返されることがあります。クライアント側で会話履歴を再送する場合は、受け取った署名を変更せず、そのまま対応するPartへ戻す必要があります。(Google AI for Developers)
function callに含まれる主な情報の役割は、次のように異なります。
| 情報 | 役割 | 再生成してよいか |
|---|---|---|
| function名 | 呼び出すツールの特定 | 履歴から再構築可能 |
| arguments | ツールへ渡す引数 | 履歴から再構築可能 |
| call ID | function callと結果の対応付け | 元のIDを維持する |
| thought_signature | Geminiの推論状態の継続 | 再生成不可。元の値をそのまま維持する |
thought_signatureは、アプリケーションが内容を解釈したり、自前で生成したりするものではありません。
実装では、次の原則を守る必要があります。
- 受信したバイト列を変更せず保持する
- 別のfunction callへ流用しない
- 文字列として加工したり、独自形式へ変換したりしない
- デバッグログには生データではなく、有無やバイト数だけを記録する
- function callの順序や対応関係を崩さない
thought_signatureが失われると何が起きるのか
典型的なエラー発生の流れ
修正前は、次の流れで問題が発生していました。
- Gemini 3が
functionCallとthought_signatureを返す - Agent Frameworkがfunction callを内部形式へ変換する
- ミドルウェアや履歴ストアが、call ID、名前、引数だけを使ってfunction callを再構築する
- 元のGemini SDKの
Partが失われる - 次のAPIリクエストには、署名のない
functionCallが送信される - Gemini APIがリクエストを不正と判断し、
400 INVALID_ARGUMENTを返す
発生時には、次のようなメッセージが記録されます。
Function call is missing a thought_signature in functionCall parts.
400 INVALID_ARGUMENT
元の不具合報告では、最初のモデル応答とfunction callの取得までは成功し、ツールを実行または承認した後の次のステップで失敗することが特徴として挙げられています。(GitHub)
なぜ最初のfunction callは成功するのか
最初のモデル呼び出しでは、Geminiが署名を生成してクライアントへ返す側なので、署名の再送はまだ必要ありません。
問題になるのは、ツール実行結果を返す次のリクエストです。このとき、クライアントは過去のfunction callを会話履歴に含めます。再送されたfunction callに元の署名がなければ、Geminiは推論状態を正しく継続できないため、リクエストを拒否します。
そのため、次の症状がそろっている場合は、thought_signatureの再生処理を優先的に確認します。
- 1回目のモデル呼び出しは成功する
- function callの解析やツール実行も成功する
- ツール結果を返した直後のAPI呼び出しで失敗する
- エラーに
thought_signatureとfunctionCallが含まれる - 通常のテキスト応答では問題が起きない
thinkingを無効化しても解決しない
元の不具合報告では、thinking_budget=0などでthinkingを無効化してもエラーを回避できなかったとされています。
これは、問題の本質が思考量ではなく、Geminiから一度返された署名を後続リクエストへ正しく戻せていないことにあるためです。thinking設定の変更は、署名を落とす履歴変換やミドルウェアの修正にはなりません。(GitHub)
Agent Framework 1.12.1で何が変わったのか
受信時に署名をprotected_dataへ退避する
1.12.1では、Geminiから受信したfunction-call Partにthought_signatureが含まれている場合、署名をBase64形式へ変換し、function callの直前にtext_reasoningコンテンツとして追加します。
署名は、そのtext_reasoningコンテンツのprotected_dataへ格納されます。
概念的には、次の構造です。
Gemini Part
├─ functionCall
└─ thought_signature
↓ Agent Frameworkで解析
text_reasoning
└─ protected_data = Base64(thought_signature)
FunctionCallContent
├─ call_id
├─ name
└─ arguments
元のGemini SDKのPartを保持するだけでなく、Agent Frameworkの汎用的なメッセージ構造にも署名を退避することで、SDKオブジェクトが失われる経路に対応しています。
再生時にfunction callへ署名を戻す
メッセージをGemini API向けの形式へ変換するときは、function callの直前にあるtext_reasoningコンテンツを確認します。
そこにprotected_dataが存在すれば、Base64から元のバイト列へ戻し、対応するfunction-call Partのthought_signatureへ設定します。
text_reasoning(protected_data)
FunctionCallContent
↓ function-call replay
Gemini Part
├─ functionCall
└─ thought_signature
元のraw_representationにすでに署名が含まれている場合は、その値が維持されます。元のPartは残っているものの署名だけがない場合には、protected_dataから復元した値が補完されます。
JSON変換を挟んでも保持できるようテストされている
1.12.1では、次のケースを対象とした単体テストが追加されています。
- 元のGemini
Partから署名を維持できること - 受信した署名が
text_reasoningへ退避されること - メッセージを辞書やJSONへ変換しても署名を保持できること
- 再構築したfunction callへ署名を復元できること
- 署名が存在しない場合は何も追加しないこと
- 不正なBase64データがあっても変換処理全体を停止しないこと
- Geminiからの解析、メッセージ再構築、Geminiへの再送を一連で実行できること
特に重要なのは、単純にSDKオブジェクトをメモリ上で使い回すテストだけでなく、JSON変換を挟んだ往復処理が確認されている点です。セッション保存や履歴の再構築を行うアプリケーションに関係する修正です。
「Python 1.12.1」はPython本体のバージョンではない
リリース名に含まれるpython-1.12.1は、Python言語やCPython本体のバージョンではありません。Microsoft Agent FrameworkのPython実装に付けられたリリースタグです。
また、関連パッケージがすべて1.12.1というバージョンになるわけではありません。
修正が公開された時点の主なパッケージ対応は、次のとおりです。
| パッケージ | 修正公開時のバージョン |
|---|---|
agent-framework | 1.12.1 |
agent-framework-core | 1.12.1 |
agent-framework-gemini | 1.0.0b260722 |
Geminiコネクターを個別に導入している環境で、存在しないagent-framework-gemini==1.12.1を指定しないよう注意してください。Geminiコネクターは独立したベータ版のバージョン体系になっています。
Agent Frameworkを更新する方法
修正が入ったバージョンを固定して確認する
不具合の再現環境で、1.12.1の修正だけを確認したい場合は、次の組み合わせを使用できます。
python -m pip install --upgrade --pre "agent-framework-core==1.12.1" "agent-framework-gemini==1.0.0b260722"
インストール後は、実際に読み込まれるパッケージのバージョンを確認します。
python -c "from importlib.metadata import version; print('core=', version('agent-framework-core')); print('gemini=', version('agent-framework-gemini'))"
仮想環境、Dockerイメージ、実行ユーザーが異なると、更新した場所とは別のパッケージが読み込まれることがあります。pip listの結果だけで判断せず、実際にアプリケーションを起動するPythonで確認することが重要です。
後続版へ更新する場合
現在の互換バージョンへ更新する場合は、次のように実行します。
python -m pip install --upgrade --pre agent-framework-core agent-framework-gemini
agent-framework-geminiはベータ版として配布されているため、プレリリースを取得する際は--preが必要になります。
2026年8月3日時点では、agent-framework 1.13.0やagent-framework-gemini 1.0.0b260730など、1.12.1より後のリリースも公開されています。新規環境では互換性を確認したうえで後続版を使い、1.12.1と1.0.0b260722の組み合わせは修正導入時点の基準として扱うのが現実的です。(PyPI)
ただし、後続版へ更新したという事実だけでは、利用中の承認経路や外部アダプターまで正常に動作する保証にはなりません。更新後の回帰テストを省略しないでください。
修正を確認するためのテスト手順
最小構成で通常のツールループを確認する
まず、承認処理や外部セッションストアを使わない最小構成で確認します。
- 必ずfunction callが発生するプロンプトを送信する
- Geminiから返されたfunction callを取得する
- 指定されたツールを実行する
- function resultを会話へ追加する
- 同じ会話履歴を使って次のモデル呼び出しを実行する
400 INVALID_ARGUMENTが発生せず、最終回答を取得できることを確認する
天気取得や社内データ検索など、結果が短く、毎回同じ形式になるテスト用ツールを用意すると確認しやすくなります。
本番経路ごとの回帰テストを行う
最小構成が成功したら、本番で使う経路を個別にテストします。
| テストケース | 確認内容 | 正常時の結果 |
|---|---|---|
| 通常のfunction call | 1回のツール実行後にモデルを再呼び出す | 最終回答まで完了する |
| 連続するfunction call | 2回以上のツール呼び出しを継続する | 各ターンで署名が維持される |
| JSON保存と復元 | メッセージをJSON化し、読み戻して再開する | 復元後も次のモデル呼び出しが成功する |
| ツール承認 | 承認要求へ応答して処理を再開する | 承認後の再送で400にならない |
| ストリーミング | ストリーム終了後にツール結果を返す | 次のストリームが正常に開始する |
| 外部アダプター | AG-UIや独自ゲートウェイを経由する | 未知のコンテンツが削除されない |
| セッション再開 | プロセス終了後に履歴を読み戻す | function callと署名の対応が維持される |
同じfunction callを使ったテストでも、通常実行と承認後の再開は内部経路が異なる場合があります。通常のツールループが成功しただけで、承認フローも修正済みと判断しないことが重要です。
ログでは署名の有無だけを確認する
トラブル調査のために、thought_signatureの生データをログへ出力する必要はありません。
次の情報があれば、再生処理の確認には十分です。
call_id: call_123
function_name: search_document
thought_signature_present: true
thought_signature_length: 128
protected_data_present: true
確認すべきなのは署名の内容ではなく、対応するfunction callに署名が存在し、次のリクエストまで維持されていることです。
1.12.1でもツール承認後に失敗する可能性がある
2026年7月31日に登録されたIssue #7453では、ツール承認へ応答した後、再生される履歴にfunction callだけが残り、その直前に置かれるはずのprotected_dataを持つコンテンツが失われるケースが報告されています。
報告例では、通常の複数ターンツールループに対する1.12.1の修正は機能する一方、承認応答後の経路では同じ400 INVALID_ARGUMENTが発生しています。2026年8月3日時点で、このIssueは未クローズです。(GitHub)
1.12.1の実装では、署名を保持したtext_reasoningがfunction callの直前にあることを利用して、両者を対応付けています。そのため、ミドルウェアやアダプターが次のような変換を行うと、署名を復元できなくなる可能性があります。
FunctionCallContentだけを取り出して新しい履歴を作るtext_reasoningを不要なコンテンツとして削除する- function callと直前のコンテンツの順序を入れ替える
- 承認待ち状態をDBへ保存する際、一部のフィールドだけを保存する
- 外部プロトコルへ変換するときに
protected_dataを破棄する - 承認後にcall ID、名前、引数だけでfunction callを再生成する
したがって、実務では「1.12.1以上へ更新済み」という確認だけでなく、承認前のメッセージが承認後の再開処理まで完全に維持されているかを確認する必要があります。
更新後も400エラーが出る場合の確認ポイント
実行中のパッケージバージョンを再確認する
最初に、アプリケーションが使用しているPython環境でバージョンを確認します。
python -c "import sys; print(sys.executable)"
python -c "from importlib.metadata import version; print(version('agent-framework-core')); print(version('agent-framework-gemini'))"
次のような環境では、パッケージを更新しても古いバージョンが動き続けることがあります。
- IDEとターミナルで異なる仮想環境を使っている
- Dockerイメージを再ビルドしていない
- キャッシュされたコンテナが残っている
- ワーカーだけ再起動されていない
- systemdやタスクスケジューラーが別のPythonを参照している
- ロックファイルに古いバージョンが固定されている
独自の履歴変換処理を確認する
次に、Agent Frameworkのメッセージを独自形式へ変換している箇所を確認します。
特に、function callの情報を次の項目だけに限定して保存している処理は注意が必要です。
{
"call_id": "call_123",
"name": "search_document",
"arguments": {
"query": "Agent Framework"
}
}
この形式だけでは、thought_signatureを運ぶprotected_dataが保存されません。
Agent Frameworkのメッセージ全体を保存するか、少なくともfunction callに付随する保護データとコンテンツ順序を維持する必要があります。
未知のcontent typeを削除しない
独自ミドルウェアでは、認識できないcontent typeを削除する処理が実装されていることがあります。
1.12.1ではtext_reasoningが署名の内部キャリアとして利用されるため、「画面に表示しない情報だから不要」と判断して削除すると、後続のfunction callが失敗します。
表示対象から除外することと、会話履歴から削除することは分けて考えてください。
- UIには表示しない
- ログには生データを出さない
- ただし内部メッセージと再送履歴には残す
この扱いが必要です。
thinkingの無効化や単純な再試行で回避しない
thinking_budgetを0にする方法は、元の不具合報告でも有効な回避策になっていません。(GitHub)
また、同じ不正な履歴をそのまま再送するだけのリトライも効果がありません。署名が欠落したリクエストを繰り返し送ることになるためです。
再試行する場合は、リクエストを送信する前に次の点を検証します。
- function callに署名が設定されているか
- function callと署名の対応が正しいか
- ツール結果のcall IDが一致しているか
- 会話履歴の順序が変わっていないか
- 承認処理で保護データが削除されていないか
暫定対応ではcall IDとの対応を明示する
上流側の修正を待てない場合、独自アダプター内でcall_idと署名の対応を保持し、function callの再構築時に元の署名を戻す方法が暫定対応として考えられます。
ただし、これはAgent Frameworkの公開APIだけで完結しない可能性があり、内部実装への依存を増やします。実装する場合は、次の条件を満たす必要があります。
- セッションごとに対応表を分離する
- 並列function callを個別に識別する
- 署名のバイト列を変更しない
- 別のcall IDへ署名を流用しない
- セッション終了後に対応表を破棄する
- 生の署名をログや分析基盤へ送らない
- Agent Framework更新時に回帰テストを行う
恒久対応として独自パッチを固定するのではなく、上流Issueの修正状況を確認し、公式実装へ戻せる構成にしておくことが重要です。
症状から原因を切り分ける
| 発生するタイミング | 疑うべき原因 | 優先する対応 |
|---|---|---|
| 最初のAPI呼び出しから失敗する | 認証、モデル名、リージョン、ツール定義 | API設定とエラー全文を確認 |
| function call生成前に失敗する | JSON Schemaや引数定義 | ツールスキーマを簡略化 |
| ツール結果を返した直後に失敗する | thought_signatureの欠落 | バージョンと再生履歴を確認 |
| 承認を使わない場合は成功する | 承認再開経路でのデータ欠落 | 承認ミドルウェアを重点確認 |
| プロセス再起動後だけ失敗する | 履歴保存時のフィールド欠落 | JSON保存形式を確認 |
| 並列呼び出し時だけ失敗する | call IDと署名の対応崩れ | function call単位で追跡 |
| ストリーミング時だけ失敗する | ストリーム統合時のPart欠落 | 完了イベント後の履歴を確認 |
thought_signatureの問題は、function callを含む2回目以降のモデル呼び出しで表面化しやすい不具合です。最初のリクエストから失敗している場合は、モデル設定や認証、ツールスキーマなど、別の原因も並行して確認してください。
まとめ
Microsoft Agent FrameworkのPython 1.12.1では、Gemini 3のfunction-call replay時にthought_signatureが失われる問題への修正が追加されました。
修正後は、Geminiから受信した署名がtext_reasoningのprotected_dataへ退避され、次のモデル呼び出しで対応するfunction callへ戻されます。これにより、元のGemini SDKオブジェクトが失われる通常の履歴再構築でも、署名を維持しやすくなりました。(GitHub)
一方、ツール承認後の再開など、途中のミドルウェアが署名の内部キャリアを削除する経路では、1.12.1でも問題が残る可能性があります。(GitHub)
対応は、次の順序で進めると確実です。
agent-framework-core1.12.1とagent-framework-gemini1.0.0b260722以降を検証環境へ導入する- 通常のfunction callと連続するfunction callを確認する
- JSON保存後の再開を確認する
- ツール承認後の再開を必ず確認する
- 独自ミドルウェアが
text_reasoningやprotected_dataを削除していないか調べる - 問題がなければ本番環境へ反映する
単にパッケージのバージョンを見るのではなく、実際のアプリケーションで使用しているツール実行、承認、セッション復元の各経路を通して確認することが、再発を防ぐ最も重要なポイントです。

コメント