Agent Framework Python 1.12.1でステートレスtool call再生を修正|原因と対処法

Microsoft Agent Framework for Pythonで、store=Falseなどのステートレス実行を使っていると、推論モデルが生成したtool callを次のターンで再生できず、HTTP 400になることがありました。

結論からいうと、この問題はMicrosoft Agent Framework for Pythonリリース1.12.1で修正されています。agent-framework-coreだけでなく、agent-framework-foundry、agent-framework-foundry-hosting、agent-framework-openaiにまたがる修正です。該当する環境では、関連パッケージを整合した状態へ更新し、更新前に保存したセッションやチェックポイントも含めて再検証する必要があります。(GitHub)

目次

Agent Framework Python 1.12.1でステートレスtool call再生を修正

Microsoft Agent Framework for Pythonの1.12.1では、推論項目と対になったツール呼び出し、いわゆるreasoning-paired tool callをステートレス環境で再生する処理が修正されました。

影響対象としてリリースノートに記載されているのは、次の4パッケージです。

パッケージ主な役割今回の修正との関係
agent-framework-coreメッセージ、コンテンツ、ワークフローなどの共通基盤推論IDや暗号化された推論データを失わずに保持する
agent-framework-openaiOpenAI Responses APIとの変換・通信ステートレス実行時に推論項目とtool callを正しく再構築する
agent-framework-foundryMicrosoft Foundryのエージェント連携複数エージェント間の履歴再生を正常化する
agent-framework-foundry-hostingFoundry Agent Server上でのホスティングホスト経由でも推論メタデータを保持して入出力する

特に問題になっていたのは、クライアント側で実行する通常のfunction_callと、Foundry側で処理されるmcp_callです。1.12.1では両方が修正対象になっています。(GitHub)

問題が発生するステートレス実行とは

Agent Frameworkの会話継続方法は、大きく分けるとステートフルとステートレスの2種類があります。

実行方法会話履歴の保持場所次のリクエストで必要な処理
ステートフルOpenAIやFoundryなどのサービス側previous_response_idや会話IDで前回の状態を参照する
ステートレスアプリケーション側必要な会話履歴やtool call情報をリクエスト内で再送する

サービス側の会話保存を利用する場合、前回の推論項目やtool callはサービス側に残っています。そのため、クライアントが同じ項目をもう一度送ると、重複IDとして拒否される可能性があります。

一方、store=Falseでサービス側に履歴を保存しない場合は、クライアント側が前回までの状態を復元しなければなりません。推論モデルを使う場合、単なるテキスト履歴だけでなく、推論項目、tool call、tool resultの対応関係も維持する必要があります。1.12.1の実装では、サービス側の継続情報がないときにreasoning.encrypted_contentを取得対象へ自動追加する処理が入っています。

reasoning-paired tool callとは

推論モデルの応答では、ツール呼び出しが独立した項目として生成されるとは限りません。次のように、推論項目とtool callがひとまとまりとして扱われる場合があります。

reasoning
  └─ function_call または mcp_call
       └─ function_call_output または MCPの実行結果

ここでいうreasoningは、アプリケーションが読むための完全な思考過程を意味するものではありません。Responses APIが会話を継続するために使用する、IDや暗号化された不透明な推論データを含む項目です。

この組み合わせでは、tool callだけを次のリクエストへ送ることはできません。tool callが特定のreasoning項目に関連付けられている場合、関連するreasoning項目も一緒に渡す必要があります。

修正前にHTTP 400が発生した理由

修正前のステートレス再生では、Agent Frameworkが履歴からtext_reasoningを取り除く一方で、関連するfunction_callやmcp_callを残すことがありました。

処理の流れは次のようになります。

前回の正常な応答
reasoning + tool call + tool result

次のリクエストを作成
tool call + tool result
※reasoningだけが欠落

Foundry側で検証
「必要なreasoning項目が存在しない」と判断

HTTP 400

実際に報告された問題では、mcp_callに必要なreasoning項目が含まれていないため、Azure側がリクエストをHTTP 400で拒否していました。

この現象は毎回発生するわけではありません。推論モデルがreasoningとtool callを対にした形式で出力した場合にだけ表面化するため、開発環境では再現しにくくても、会話回数やtool call回数が増える本番環境では発生確率が高くなります。(GitHub)

Python 1.12.1で変更された処理

暗号化された推論データを取得する

ステートレス実行では、Agent Frameworkがincludeへ次の値を追加します。

"reasoning.encrypted_content"

これにより、サービス側に会話を保存しなくても、次のターンでreasoning項目を再構築するための不透明なデータを取得できます。

アプリケーション側で同じ値をすでに設定している場合は、重複して追加されません。

推論IDとprotected_dataを保持する

取得した暗号化データは、Agent FrameworkのContentにあるprotected_dataへ保存されます。

また、ストリーミング応答の結合やシリアライズを行っても、reasoning項目のIDとprotected_dataが失われないように処理されています。これにより、次のターンで元のreasoning項目を識別できます。

reasoning項目を再構築して一度だけ送信する

ステートレス実行時には、履歴内のtext_reasoningがプロバイダーのreasoning IDごとにまとめられます。

その後、次の情報を含むreasoning項目として再構築されます。

type
id
summary
encrypted_content
status

同じreasoning IDが複数のContentへ分割されていても、送信時には一つのreasoning項目へまとめられます。tool callとtool resultも対応関係を保ったまま再生されます。

再構築できない履歴を送信前に拒否する

1.12.1では、必要な暗号化推論データが欠落している場合、壊れたリクエストをサービスへ送信する前にChatClientInvalidRequestExceptionを発生させます。

エラーメッセージには、ステートレス再生で推論グループを再構築できないことや、次の対処方法が示されます。

  • サービス側の会話継続を利用する
  • reasoningとtool callのグループをまとめて除外する
  • 欠落した履歴を使用せず、新しいセッションを開始する

HTTP 400になるまで処理を進めるのではなく、アプリケーション側で原因を切り分けやすくなった点も重要です。

実行中のtool loopは維持される

修正では、過去の履歴として再生するtool callと、現在実行中のtool loopが区別されています。

そのため、正常に進行しているクライアント側の関数呼び出しまで削除されるわけではありません。非推論モデルのtool call履歴についても、従来の挙動を維持する方針になっています。(GitHub)

パッケージごとの具体的な影響

agent-framework-core

agent-framework-coreでは、推論コンテンツを結合するときにIDとprotected_dataを保持できるようになっています。

ストリーミング結果やチェックポイントからメッセージを復元した場合でも、暗号化された推論データを次の処理へ引き継ぐための基盤部分です。

agent-framework-openai

今回の修正で中心的な役割を持つパッケージです。

主に次の処理を担当します。

  • ステートレスかステートフルかを判定する
  • ステートレス時にreasoning.encrypted_contentを要求する
  • 保存された推論コンテンツをreasoning項目へ再構築する
  • function_callとfunction_call_outputを正しく再送する
  • mcp_callとその結果を正しくまとめる
  • 再構築不可能な履歴を送信前に検出する

OpenAI Responses APIを直接利用する構成だけでなく、Foundry統合から内部的にOpenAIクライアントを利用する構成にも関係します。

agent-framework-foundry

FoundryAgentやWorkflowBuilderを使った複数エージェント構成では、あるエージェントの応答が別のエージェントへ渡されます。

この境界でreasoning項目だけが失われると、次のエージェントが過去のtool callを再生した際にHTTP 400になる可能性があります。1.12.1では、単一エージェントだけでなく、エージェント間の履歴転送を含む回帰テストが追加されています。(GitHub)

agent-framework-foundry-hosting

Foundry Hostingでは、Responses形式の入力をAgent FrameworkのMessageへ変換し、実行結果を再びResponses形式へ戻します。

1.12.1相当の実装では、reasoning項目を変換するときにIDと暗号化データをContentへ保存し、出力時にもencrypted_contentを含むreasoning項目として復元します。

「1.12.1」は全パッケージ共通のバージョンではない

注意したいのは、python-1.12.1がリリース全体のタグであり、すべてのサブパッケージが1.12.1になっているわけではない点です。

リリース時点の主なバージョンは次のとおりです。

配布パッケージ修正を含むリリース時点のバージョン
agent-framework1.12.1
agent-framework-core1.12.1
agent-framework-openai1.11.0
agent-framework-foundry1.10.3
agent-framework-foundry-hosting1.0.0b260722

たとえば、次の指定は正しくありません。

python -m pip install agent-framework-openai==1.12.1

agent-framework-openaiには独自のバージョン番号が設定されています。個別パッケージを固定している環境では、リリースタグだけを見て全パッケージへ同じ番号を指定しないようにしてください。

Agent Frameworkを1.12.1へ更新する手順

現在のバージョンを確認する

まず、仮想環境を有効にした状態で関連パッケージを確認します。

python -m pip show agent-framework agent-framework-core agent-framework-openai agent-framework-foundry agent-framework-foundry-hosting

インストール済みパッケージを一覧で確認する場合は、次のコマンドも利用できます。

python -m pip freeze

確認すべきポイントは、agent-framework-coreだけが新しく、agent-framework-openaiやagent-framework-foundryが古い状態になっていないかです。

今回の修正は複数パッケージにまたがるため、部分的な更新では問題が残る可能性があります。

メタパッケージを利用している場合

agent-frameworkをまとめて導入している環境では、最小限の変更に抑えるなら1.12.1へ固定します。

python -m pip install --upgrade --upgrade-strategy eager "agent-framework==1.12.1"

--upgrade-strategy eagerを付けるのは、すでにインストールされている古い統合パッケージを、依存条件を満たすという理由だけで残さないためです。

更新後は依存関係を確認します。

python -m pip check

必要なパッケージだけを導入している場合

OpenAIとFoundryを個別に導入している環境では、実際に使用しているパッケージを明示的に更新します。

python -m pip install --upgrade "agent-framework-core==1.12.1" "agent-framework-openai==1.11.0" "agent-framework-foundry==1.10.3"

Foundry Hostingを使用している場合は、ホスティングパッケージも追加します。

python -m pip install --upgrade "agent-framework-foundry-hosting==1.0.0b260722"

Foundry Hostingはこの時点でベータ版です。使用していない環境へ無理に追加する必要はありません。

1.13.0以降へ上げる場合

2026年8月1日時点では、Python向けの新しい安定版として1.13.0も公開されています。1.13.0には1.12.1の修正が含まれますが、ワークフローチェックポイントの再生方法などに破壊的変更が含まれています。

問題修正だけを取り込みたい場合は1.12.1へ固定し、別途1.13.0への移行テストを行う方法が安全です。新規環境で現在の安定版を使う場合も、チェックポイント、承認処理、複数エージェントの回帰テストを実施してください。(GitHub)

更新前のセッションとチェックポイントに注意する

パッケージを更新しても、更新前に保存した会話履歴にreasoning IDや暗号化推論データが含まれていなければ、その履歴を完全に復元できない可能性があります。

これは特に次の構成で注意が必要です。

  • 会話履歴をデータベースへ保存している
  • ワークフローチェックポイントを永続化している
  • 複数のコンテナやワーカー間でセッションを共有している
  • バージョン更新後も既存セッションを継続する
  • 独自シリアライザーでContentをJSON化している

1.12.1の検証処理から判断すると、必要なprotected_dataがない履歴は安全に再生できません。更新後もエラーが続く場合は、新規セッションで再現するかを最初に確認してください。新規セッションでは直る場合、コードではなく保存済み履歴の互換性が原因と考えられます。

修正を確認するスモークテスト

次のコードは、ステートレス設定で1ターン目に関数を呼び出し、2ターン目でその履歴を再利用する最小構成です。

import asyncio
import os

from agent_framework import Agent, AgentSession
from agent_framework.openai import OpenAIChatClient


def lookup_order(order_id: str) -> str:
    """注文状況を取得するテスト用ツール。"""
    return f"{order_id}: 発送済み"


async def main() -> None:
    model = os.environ["OPENAI_MODEL"]

    agent = Agent(
        client=OpenAIChatClient(model=model),
        instructions=(
            "注文状況について回答するときは、"
            "必ずlookup_orderツールを呼び出してください。"
        ),
        tools=[lookup_order],
        default_options={
            "store": False,
            "reasoning": {
                "effort": "low",
                "summary": "auto",
            },
        },
    )

    session = AgentSession()

    first = await agent.run(
        "注文番号A-100の状況を確認してください。",
        session=session,
    )
    print(first)

    second = await agent.run(
        "先ほど確認した内容を一文で説明してください。",
        session=session,
    )
    print(second)


if __name__ == "__main__":
    asyncio.run(main())

このテストでは、次の点を確認します。

  • 1ターン目で実際にtool callが発生する
  • store=Falseのまま2ターン目へ進める
  • 2ターン目でHTTP 400にならない
  • 同じツールが意図せず再実行されない
  • ストリーミングと非ストリーミングの両方で成功する

推論モデルは毎回同じ形式を返すとは限りません。1回成功しただけで判断せず、複数回実行してください。実際のアプリケーションが並列tool callを使う場合は、複数のツールを同時に呼ぶケースもテスト対象に含めます。(PyPI)

Foundry環境で実施すべき回帰テスト

単一エージェントのテストだけでは、Foundryのワークフローで発生する問題を十分に確認できません。

テスト対象確認する内容
単一エージェント・複数ターン1ターン目のtool callを2ターン目で正常に再生できる
複数エージェント前段エージェントのreasoningとtool callが後段へ渡る
WorkflowBuilderエージェント境界を越えてもHTTP 400にならない
クライアント側関数function_callとfunction_call_outputの対応が維持される
Hosted MCPmcp_callとreasoningの対応が維持される
並列tool call複数のcallとresultが一つのグループとして保持される
ミドルウェアによる中断承認拒否やポリシー中断後も履歴が破損しない
チェックポイント復元JSON化・保存・復元後もreasoning IDとprotected dataが残る
Foundry HostingホストのResponses入出力でも暗号化推論データが保持される

今回の修正では、WorkflowBuilder、FoundryAgent、AgentExecutor、Responses SDKのシリアライズ、Foundry Hostingを通した回帰テストが追加されています。(GitHub)

更新後もエラーになる場合の切り分け

症状考えられる原因対処
reasoningが必要というHTTP 400関連パッケージの一部が古いpip showでcore・OpenAI・Foundryを確認する
新規セッションでは成功する旧セッションに暗号化推論データがない既存セッションを終了し、新しいセッションへ切り替える
Stateless replay cannot reconstructが出るreasoning IDまたはprotected_dataが欠落している履歴の保存形式とコンパクション処理を確認する
Duplicate item IDが出るサービス側継続と履歴の全量再送を併用しているステートフルかステートレスのどちらかへ統一する
単一エージェントは成功するがワークフローで失敗するエージェント境界のフィルターがreasoningだけ削除しているcontext_filterや独自メッセージ変換を確認する
Hosted MCPだけ失敗するFoundry Hostingパッケージが古い1.0.0b260722以降へ更新する
ときどきしか再現しないモデルが毎回reasoningとtool callを対で生成するとは限らないtool callを強制するテストを複数回実施する

特に独自の履歴フィルターでは、text_reasoningだけを削除しないことが重要です。削除が必要な場合は、関連するtool callとtool resultを含むグループ全体を原子的に扱わなければなりません。

reasoning.encrypted_contentをログへ出さない

reasoning.encrypted_contentやContent.protected_dataは、ステートレス継続に使う不透明なデータです。

暗号化されているからといって、アプリケーションログや監視サービスへ無条件に出力するべきではありません。デバッグ時も次の情報だけを記録する方法が安全です。

  • reasoning IDの有無
  • protected_dataの有無
  • tool call ID
  • tool result ID
  • セッションID
  • HTTPステータス
  • リクエスト相関ID

暗号化データそのものではなく、「存在しているか」「どのIDに対応しているか」を確認してください。

まず関連パッケージの整合と新規セッションで確認する

Agent Frameworkのステートレス実行で推論付きtool callを再生できない問題は、Microsoft Agent Framework for Pythonリリース1.12.1で修正されています。

対処では、agent-framework-coreだけを更新するのではなく、実際に利用しているagent-framework-openai、agent-framework-foundry、agent-framework-foundry-hostingも含めてバージョンを確認してください。

更新後は、最初に新規セッションで2ターン以上のtool callを実行します。新規セッションでは成功し、既存セッションだけが失敗する場合は、更新前に保存されたreasoning IDや暗号化推論データの欠落を疑います。そのうえで、複数エージェント、Hosted MCP、並列tool call、チェックポイント復元までテストするのが確実です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次