Microsoft Foundry ホスト型エージェントの永続的な状態ストアは、エージェント コンテナーがクラッシュ、再起動、またはアイドル期間の後に削除されたときに保持する必要がある JSON データのサーバーベースのキー値ストレージを提供します。 これを使用して、フレームワークのチェックポイント、アプリケーションで管理される会話履歴、中間成果物、ユーザー設定を格納します。
エージェントは、状態ストア内の項目を明示的に書き込み、更新します。 セッションや会話とは異なり、プラットフォームはストアに自動的にデータを格納しません。 ストアでは、ユーザーごとの分離、タグ、オプティミスティック コンカレンシー、構成可能な有効期限がサポートされています。
この記事では、データのパーティション分割、呼び出し元 ID の管理、ストアとアイテムの作成とアクセス、サービス制限内での作業を行う方法について説明します。 状態ストアはホストされているエージェントでのみ使用できます。
状態ストアに保持するもの
ホスト型エージェントの計算リソースは一時的です。 コンテナーが再起動されるか、アイドル期間が経過した後にプラットフォームによって削除されると、エージェントは セッションの外部にあるローカル ディスクに書き込むものを失います。 状態ストアは、コンテナーの存続期間を超えて維持されなければならない状態を保持します。
一般的な内容は次のとおりです。
- フレームワークのチェックポイント機能により、LangGraph や Microsoft Agent Framework などの独自に用意したエージェント フレームワークは、それぞれ独自のグラフやワークフローの状態を復元して再開できます。
- エージェントが自身を管理する会話履歴。これは、プラットフォームが履歴を自動的に格納しない Invocations-protocol ケースです。
- 生成された成果物と、後のターンで必要な中間作業。
- ユーザーごとのプロファイル、基本設定、およびその他の有効期間の長いアプリケーション データ。
状態ストアだけがオプションではありません。 データベース、BLOB ストレージ、または別のアプリケーション所有ストアも同じように動作します。 状態ストアでは、そのストレージを自分でプロビジョニングしてセキュリティで保護する必要がなくなります。また、エージェントの ID モデルが自動的に適用されます。
必要に応じて取得できる程度に、値を小さく抑えます。 項目の上限は 1 MB であるため、大きなバイナリは BLOB ストレージに属し、ストアはそれらの参照を保持します。
Tip
実行時間の長いエージェントは、進行状況マーカーと一括チェックポイントを同じ状態ストア内の個別の項目に保持できます。 復旧パターンについては、 実行時間の長いエージェントの状態の管理に関するページを参照してください。
ストア
FoundryStateStore クライアントは、選択した 1 つのストア名にバインドされます。 その名前は、ストアの ID と最も外側のパーティションの両方として機能します。 ストアには、読み取り、書き込み、削除、および一覧表示できるキー付き JSON 項目が保持されます。
- ストア名は ID です。作成後にストア名を変更することはできません。そのため、事前に安定した名前付けスキームを選択してください。
- Get-or-create はストア レベルの操作です。単一の get-or-create 呼び出しは、ストアをフェッチするか、存在しないときに作成します。 作成オプションは最初の作成時にのみ適用されるため、ストアが既に存在する場合はサービスによって無視されます。 アイテム書き込みでは、存在しないストアは作成されません。
- アイテムの有効期間: ストア レベルのアイドル ウィンドウでは、アイテムが削除されます。 既定値は 30 日であり、アイテムの有効期限が切れないようにストアを構成できます。 書き込みによってウィンドウが更新され、読み取りは更新されません。 このオプションは作成時に設定します。
データのパーティション化
ストアには、データをパーティション分割するための 2 つの独立した方法が用意されており、ほとんどのエージェントで必要なものは 1 つだけです。
-
ストア名ごと: 各ストア名がそれぞれ独自のパーティションになります。 名前には
/を含めることができるため、checkpoints/thread-abcやworkflow-state/run-42のように階層区切り記号として使用できます。 項目を読み取るコードに、名前の再構築に使用できるパーティション識別子が常に含まれている場合は、このパーティションを選択します。 - エンド ユーザー別: ユーザー分離を使用して作成されたストアでは、エンド ユーザーごとに項目がパーティション分割されるため、マルチテナント エージェントのユーザー間で 1 つのストア名を安全に共有できます。 同じストア名が複数のユーザーにサービスを提供する場合は、このパーティションを選択します。 このオプションは作成時に設定します。ストアは、コードで渡されたものではなく、リクエストからユーザーを特定します。 詳細については、 呼び出し元 ID を参照してください。
参照パスで再構築できる最も狭いパーティションを優先します。 識別子をエンコードするストア名は、アイテムを読み取るすべての呼び出し元にまだその識別子がある場合にのみ役立ちます。 通常、フレームワーク チェックポイント ローダーで行われているように、ルックアップで項目キーが単独で保持される場合は、ストア名をフラットに保ち、代わりにユーザーの分離でパーティション分割を実行できるようにします。
2 つの軸は構成されますが、種類は分けておきます。ストア名には、スレッド、実行、ワークフロー識別子などのユーザー以外のスコープのみを含める必要があります。
Important
エンド ユーザー識別子をストア名にエンコードしないでください。 ストア名は、ログやエラー メッセージなどの操作画面に表示され、独自のコード ビルドの名前は検証済みの ID ではありません。 ユーザーごとのパーティション分割にはユーザー分離を使用します。そのため、プラットフォームでは、エージェントが提供する値ではなく、確立した呼び出し元からの境界が適用されます。
発信者 ID
状態ストアは、ホスト型エージェントの ID モデルを適用します。
コンテナー プロトコル 2.0.0 では、各要求は、エージェントへのプラットフォーム ルートに呼び出し元を識別するx-agent-foundry-call-id ヘッダーを運び、ストアは、その呼び出し元から動作するエンド ユーザーを解決します。 Foundry SDK は、ストア呼び出し時にヘッダーを自動的に転送するため、ほとんどのエージェントがそれを直接扱うことはありません。
2 つの結果は、それを使用して設計する場合に重要です。
- ユーザー分離は呼び出し ID から派生します。ユーザー分離ストアでは、パーティションは、エージェントが渡すものからではなく、識別されたプラットフォームの呼び出し元から取得されます。 ホストされるエージェントは、それ自体のエンドユーザー識別子を提供しません。 ランタイムが SDK クライアントを使用する代わりに独自の HTTP 要求を発行する場合は、ヘッダーを変更せずに転送し、値を不透明として扱います。
- ローカル実行では、ユーザーの分離がサポートされていません。プラットフォームでは、オフプラットフォームの呼び出し ID は提供されません。 コンテナーをローカルで実行するときに、Python SDK と .NET SDK のどちらもユーザー分離を適用できません。 デプロイされたホステッド エージェントを使用して、ユーザー分離境界をテストします。
通話 ID を後で行う作業に引き継ぐ
アイテム操作は呼び出し元に代わって動作し、ストア操作は動作しません。
| Operation | Scope | ユーザーの行動 |
|---|---|---|
create_item、set_item、get_item、delete_item、list_keys |
バインドされたストア内の項目 | ストアでユーザー分離が使用されている場合に、呼び出し ID から解決される |
| ストアを取得または作成 | ストア自体 | 未解決です。この操作はストア スコープです |
既定では、アイテム操作では、内部で実行される要求の呼び出し ID が使用されます。これは、その要求中に完了する作業に必要な ID です。
要求が期限切れになった作業には、バックグラウンド処理や後のプロセス有効期間で再開するステップなど、継承するアンビエント呼び出し ID がありません。 すべての項目操作は、このケースの明示的な呼び出し ID を受け入れます。 リクエストをまだ保持している間にそれをキャプチャし、作業と一緒に保持して、各項目の操作ごとにそれを渡し返すことで、それらの操作が元の呼び出し元として動作し続けられるようにします。
呼び出し ID は現在の要求の呼び出し元を識別し、ユーザー分離ストア内の項目のみをパーティション分割します。 コンテナーが格納するもの以外のパーティション分割は行われません。 独自のコードが所有するファイル、データベース行、またはキャッシュの場合は、「 共有セッションのマルチプレックス ユーザー」で説明されているように、セッション ID とユーザー ID でキーを設定します。
アイテム
項目は、省略可能な文字列タグを持つキーと JSON 値です。
- 値はアプリケーションの JSON です。ストアは項目の値を解釈しません。 フレームワークまたはドメイン モデルを明示的にシリアル化します。
- タグはフィルター処理用です。タグは単純な文字列ラベルであり、キーを一覧表示するときに AND と一致します。 フィルター処理する必要があるフィールドのみを昇格します。
- リストはキーのみを返します:値が大きい場合でもキーのページは安いので、リストとフェッチは別々の手順です。
-
オプティミスティック コンカレンシー: すべての項目に ETag が含まれます。
If-Matchの前提条件は、カウンターなどの変更可能な項目に対する読み取り/変更/書き込み操作で、失われた更新プログラムの状態が破損する場合に使用します。 前提条件が満たされない場合は、現在の ETag が返されます。 -
追加専用チェックポイントには前提条件は必要ありません。各保存で新しいキーが書き込まれると、書き込みの競合は発生しないため、チェックポイント パスに
If-Match必要はありません。
ストアとアイテムを作成する
次の例では、ユーザー分離ストアを取得または作成し、項目を書き込み、読み取り、タグ別にキーを一覧表示します。 Get-or-create は、エージェントに必要な唯一のストア レベルの呼び出しです。これはストアを取得し、ストアが存在しない場合は、指定したオプションを使って作成します。
from azure.ai.agentserver.core.storage import FoundryStateStore
# Fetch the store, or create it on first use
store = await FoundryStateStore.get_or_create(
"checkpoints/thread-abc",
user_isolation=True,
)
# Write an item; the value is your own application JSON
await store.set_item(
"step-1",
{"done": False, "attempt": 1},
tags={"kind": "checkpoint"},
)
# Read it back
item = await store.get_item("step-1")
if item is not None:
print(item.key, item.value["done"], item.etag)
# List keys by tag, then fetch only the values you need
page = await store.list_keys(tags={"kind": "checkpoint"}, limit=50, order="asc")
for key in page.keys:
print(key.key, key.etag)
エンドポイントを渡さない場合、クライアントは、ホストされるエージェントのプラットフォームが設定する環境からプロジェクト エンドポイントを解決します。
サービスの制限
サービスによってこれらの制限が適用されます。 制限に違反した場合、サービスは 400 Bad Request を返し、エラー メッセージの無効なフィールドに名前を付けます。
| フィールド | 制限 |
|---|---|
| ストア名 | プロジェクトとエージェント内で一意の 1 ~ 128 文字。 |
| 項目キー | ストア内で一意の 1 ~ 128 文字。 |
| 項目の値 | 最大 1 MB のシリアル化された JSON。 |
| ストアまたはアイテムのタグ | 最大 16 エントリ。 キーは 1 ~ 64 文字で、値は最大 256 文字です。 |
| 説明 | 最大 1,024 文字。 |
| リスト ページあたりのキー数 | 1 から 100。既定値は 20 です。 |