次の方法で共有


ランタイム コンテキスト

ランタイム コンテキストにより、ミドルウェアは現在の実行環境と要求に関する情報にアクセスできます。 これにより、セッションごとの構成、ユーザー固有の動作、ランタイム条件に基づく動的ミドルウェアの動作などのパターンが可能になります。

C# では、ランタイム コンテキストは次の 3 つの主要なサーフェスを通過します。

  • AgentRunOptions.AdditionalProperties ミドルウェアとツールが読み取ることができる実行ごとのキー値メタデータ。
  • FunctionInvocationContext 関数呼び出しミドルウェア内のツール呼び出し引数を検査および変更する場合。
  • AgentSession.StateBag は、会話内の複数の実行にわたって保持される共有状態の場合に使用します。

適合する最も狭いサーフェスを使用します。 実行ごとのメタデータは AdditionalPropertiesに属し、永続的な会話状態はセッションの StateBagに属し、ツール引数操作は関数呼び出しミドルウェアに属します。

ヒント

ミドルウェア スコープがランタイム コンテキストへのアクセスにどのように影響するかについては、「 エージェントと実行スコープ 」ページを参照してください。

適切なランタイム サーフェスを選択する

利用シーン API サーフェス からアクセス
複数の実行で会話の状態またはデータを共有する AgentSession.StateBag session.StateBag実行ミドルウェア、ツールでのAIAgent.CurrentRunContext?.Session
実行ごとのメタデータをミドルウェアまたはツールに渡す AgentRunOptions.AdditionalProperties options.AdditionalProperties実行ミドルウェア、ツールでのAIAgent.CurrentRunContext?.RunOptions
ミドルウェア内のツール呼び出し引数を検査または変更する FunctionInvocationContext 関数呼び出しミドルウェアコールバック

を使用して実行ごとの値を渡す AgentRunOptions

AgentRunOptionsAdditionalPropertiesを使用して、実行ごとのキー値データをアタッチします。 関数呼び出しミドルウェアでは、これらの値をツール引数に転送できます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

[Description("Send an email to the specified address.")]
static string SendEmail(
    [Description("Recipient email address.")] string address,
    [Description("User ID of the sender.")] string userId,
    [Description("Tenant name.")] string tenant = "default")
{
    return $"Queued email for {address} from {userId} ({tenant})";
}

// Function invocation middleware that injects per-run values into tool arguments
async ValueTask<object?> InjectRunContext(
    AIAgent agent,
    FunctionInvocationContext context,
    Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
    CancellationToken cancellationToken)
{
    var runOptions = AIAgent.CurrentRunContext?.RunOptions;
    if (runOptions?.AdditionalProperties is { } props)
    {
        if (props.TryGetValue("user_id", out var userId))
        {
            context.Arguments["userId"] = userId;
        }

        if (props.TryGetValue("tenant", out var tenant))
        {
            context.Arguments["tenant"] = tenant;
        }
    }

    return await next(context, cancellationToken);
}

AIAgent baseAgent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
        .AsAIAgent(
            model: "gpt-4o-mini",
            instructions: "Send email updates.",
            tools: [AIFunctionFactory.Create(SendEmail)]);

var agent = baseAgent
    .AsBuilder()
        .Use(InjectRunContext)
    .Build();

var response = await agent.RunAsync(
    "Email the launch update to finance@example.com",
    options: new AgentRunOptions
    {
        AdditionalProperties = new AdditionalPropertiesDictionary
        {
            ["user_id"] = "user-123",
            ["tenant"] = "contoso",
        }
    });

Console.WriteLine(response);

Warnung

DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。

ミドルウェアは、アンビエント AIAgent.CurrentRunContextを介してAgentRunOptions.AdditionalPropertiesから実行ごとの値を読み取り、ツールの実行前にツールのFunctionInvocationContext.Argumentsに挿入します。

関数呼び出しミドルウェアがコンテキストを受け取る

関数呼び出しミドルウェアは、 FunctionInvocationContext を使用して、ツールの引数の検査または変更、結果のインターセプト、またはツールの実行全体のスキップを行います。

using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

async ValueTask<object?> EnrichToolContext(
    AIAgent agent,
    FunctionInvocationContext context,
    Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
    CancellationToken cancellationToken)
{
    if (!context.Arguments.ContainsKey("tenant"))
    {
        context.Arguments["tenant"] = "contoso";
    }

    if (!context.Arguments.ContainsKey("requestSource"))
    {
        context.Arguments["requestSource"] = "middleware";
    }

    return await next(context, cancellationToken);
}

AIAgent baseAgent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
        .AsAIAgent(
            model: "gpt-4o-mini",
            instructions: "Send email updates.",
            tools: [AIFunctionFactory.Create(SendEmail)]);

var agent = baseAgent
    .AsBuilder()
        .Use(EnrichToolContext)
    .Build();

ミドルウェアは関数呼び出しコンテキストを受け取り、 next 呼び出してパイプラインを続行します。 nextを呼び出す前にcontext.Argumentsを変更すると、ツールに更新された値が表示されます。

共有ランタイム状態に AgentSession.StateBag を使用する

using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

[Description("Store the specified topic in session state.")]
static string RememberTopic(
    [Description("Topic to remember.")] string topic)
{
    var session = AIAgent.CurrentRunContext?.Session;
    if (session is null)
    {
        return "No session available.";
    }

    session.StateBag.SetValue("topic", topic);
    return $"Stored '{topic}' in session state.";
}

AIAgent agent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
        .AsAIAgent(
            model: "gpt-4o-mini",
            instructions: "Remember important topics.",
            tools: [AIFunctionFactory.Create(RememberTopic)]);

var session = await agent.CreateSessionAsync();
await agent.RunAsync("Remember that the budget review is on Friday.", session: session);
Console.WriteLine(session.StateBag.GetValue<string>("topic"));

session:を使用してセッションを明示的に渡し、AIAgent.CurrentRunContext?.Session経由でツールからアクセスします。 StateBagは、同じセッション内の実行間で保持される、タイプ セーフでスレッド セーフなストレージを提供します。

ミドルウェアとツール間でセッション状態を共有する

実行ミドルウェアはセッションの StateBagの読み取りと書き込みを行うことができます。すべての変更は、同じ要求で実行されている関数呼び出しミドルウェアとツールに表示されます。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

// Run middleware that stamps the session with request metadata
async Task<AgentResponse> StampRequestMetadata(
    IEnumerable<ChatMessage> messages,
    AgentSession? session,
    AgentRunOptions? options,
    AIAgent innerAgent,
    CancellationToken cancellationToken)
{
    if (session is not null && options?.AdditionalProperties is { } props)
    {
        if (props.TryGetValue("request_id", out var requestId))
        {
            session.StateBag.SetValue("requestId", requestId?.ToString());
        }
    }

    return await innerAgent.RunAsync(messages, session, options, cancellationToken);
}

AIAgent baseAgent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
        .AsAIAgent(
            model: "gpt-4o-mini",
            instructions: "You are a helpful assistant.");

var agent = baseAgent
    .AsBuilder()
        .Use(runFunc: StampRequestMetadata, runStreamingFunc: null)
    .Build();

var session = await agent.CreateSessionAsync();
await agent.RunAsync(
    "Hello!",
    session: session,
    options: new AgentRunOptions
    {
        AdditionalProperties = new AdditionalPropertiesDictionary
        {
            ["request_id"] = "req-abc-123",
        }
    });

Console.WriteLine(session.StateBag.GetValue<string>("requestId"));

実行ミドルウェアは、セッションをパラメーターとして直接受け取ります。 タイプ セーフ アクセスには、 StateBag.SetValueGetValue を使用します。 実行ミドルウェア フェーズ中に格納された値は、 AIAgent.CurrentRunContext?.Sessionを介してツールと関数呼び出しミドルウェアで使用できます。

Python ランタイム コンテキストは、次の 3 つのパブリック サーフェスに分割されます。

  • session= 会話の状態と履歴。
  • function_invocation_kwargs= ツールまたは関数ミドルウェアのみが表示する必要がある値。
  • client_kwargs= チャット クライアント固有のデータまたはクライアント ミドルウェアの構成。

データに適合する最小のサーフェスを使用します。 これにより、ツールの入力が明示的に維持され、クライアントのみのメタデータがツールの実行にリークすることを回避できます。

ヒント

function_invocation_kwargsは、任意のパブリック **kwargsagent.run()またはget_response()に渡す古いパターンの代わりとして扱います。

適切なランタイム バケットを選択する

利用シーン API サーフェス からアクセス
会話の状態、サービス セッション ID、または履歴を共有する session= ctx.sessionAgentContext.session
ランタイム値を渡すツールまたは関数ミドルウェアのみが必要 function_invocation_kwargs= FunctionInvocationContext.kwargs
クライアント固有のランタイム値またはクライアント ミドルウェアの構成を渡す client_kwargs= カスタム get_response(..., client_kwargs=...) 実装

ツールのみのランタイム値を渡す

from typing import Annotated

from agent_framework import FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient


@tool(approval_mode="never_require")
def send_email(
    address: Annotated[str, "Recipient email address."],
    ctx: FunctionInvocationContext,
) -> str:
    user_id = ctx.kwargs["user_id"]
    tenant = ctx.kwargs.get("tenant", "default")
    return f"Queued email for {address} from {user_id} ({tenant})"


agent = OpenAIChatClient().as_agent(
    name="Notifier",
    instructions="Send email updates.",
    tools=[send_email],
)

response = await agent.run(
    "Email the launch update to finance@example.com",
    function_invocation_kwargs={
        "user_id": "user-123",
        "tenant": "contoso",
    },
)

print(response.text)

ツール呼び出し可能なツールに対してブランケット ctx.kwargsを宣言する代わりに、ツール内の**kwargsを使用します。 レガシ **kwargs ツールは互換性のために引き続き機能しますが、GA より前に削除されます。

FunctionInvocationContextとして注釈が付けられたパラメーターは、名前に関係なく挿入されたランタイム コンテキスト パラメーターとして扱われ、モデルに表示される JSON スキーマでは公開されません。 明示的なスキーマ/入力モデルを指定した場合、 ctx という名前のプレーンな未指定のパラメーターも、挿入されたコンテキスト パラメーターとして認識されます。

値が呼び出しごとのデータではなく、ツールの有効期間が長いツールの状態または依存関係である場合は、 function_invocation_kwargsを渡すのではなく、ツール クラス インスタンスに保持します。 そのパターンについては、「 複数の関数ツールを使用してクラスを作成する」を参照してください。

関数ミドルウェアが同じコンテキストを受け取る

関数ミドルウェアは、ツールが受け取るのと同じ FunctionInvocationContext オブジェクトを使用します。 つまり、ミドルウェアは context.argumentscontext.kwargscontext.session、および context.resultを検査できます。

from collections.abc import Awaitable, Callable

from agent_framework import FunctionInvocationContext
from agent_framework.openai import OpenAIChatClient


async def enrich_tool_runtime_context(
    context: FunctionInvocationContext,
    call_next: Callable[[], Awaitable[None]],
) -> None:
    context.kwargs.setdefault("tenant", "contoso")
    context.kwargs.setdefault("request_source", "middleware")
    await call_next()


agent = OpenAIChatClient().as_agent(
    name="Notifier",
    instructions="Send email updates.",
    tools=[send_email],
    middleware=[enrich_tool_runtime_context],
)

ミドルウェア コントラクトでは、引数なしで call_next() を使用します。 呼び出す前に context.kwargs を変更すると、選択したツールは、挿入された FunctionInvocationContextを通じてそれらの値を確認します。

共有ランタイム状態に session= を使用する

from typing import Annotated

from agent_framework import FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient


@tool(approval_mode="never_require")
def remember_topic(
    topic: Annotated[str, "Topic to remember."],
    ctx: FunctionInvocationContext,
) -> str:
    if ctx.session is None:
        return "No session available."

    ctx.session.state["topic"] = topic
    return f"Stored {topic!r} in session state."


agent = OpenAIChatClient().as_agent(
    name="MemoryAgent",
    instructions="Remember important topics.",
    tools=[remember_topic],
)

session = agent.create_session()
await agent.run("Remember that the budget review is on Friday.", session=session)
print(session.state["topic"])

session=を使用してセッションを明示的に渡し、ctx.sessionから読み取ります。 セッション アクセスは、ランタイム kwargs を経由する必要がなくなりました。

委任されたエージェントとセッション状態を共有する

エージェントが as_tool()を介してツールとして公開されている場合、ランタイム関数 kwargs は既に ctx.kwargsを通過します。 サブエージェントが呼び出し元のpropagate_session=Trueを共有する必要がある場合にのみ、AgentSessionを追加します。

from agent_framework import FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient


@tool(description="Store findings for later steps.")
def store_findings(findings: str, ctx: FunctionInvocationContext) -> None:
    if ctx.session is not None:
        ctx.session.state["findings"] = findings


client = OpenAIChatClient()

research_agent = client.as_agent(
    name="ResearchAgent",
    instructions="Research the topic and store findings.",
    tools=[store_findings],
)

research_tool = research_agent.as_tool(
    name="research",
    description="Research a topic and store findings.",
    arg_name="query",
    propagate_session=True,
)

propagate_session=Trueでは、委任されたエージェントは呼び出し元と同じctx.session状態を確認します。 子エージェントを独自のセッションで分離するには、 False のままにします。

カスタム チャット クライアントとエージェント

カスタム パブリック run() または get_response() メソッドを実装する場合は、明示的なランタイム バケットをシグネチャに追加します。

from collections.abc import Mapping, Sequence
from typing import Any

from agent_framework import ChatOptions, Message


async def get_response(
    self,
    messages: Sequence[Message],
    *,
    options: ChatOptions[Any] | None = None,
    function_invocation_kwargs: Mapping[str, Any] | None = None,
    client_kwargs: Mapping[str, Any] | None = None,
    **kwargs: Any,
):
    ...

ツール呼び出しフローには function_invocation_kwargs を使用し、クライアント固有の動作には client_kwargs を使用します。 パブリック **kwargs を介してクライアント固有の値を直接渡すことは互換性パスに過ぎず、非推奨として扱う必要があります。 同様に、 **kwargs を使用して新しいツールを定義することは移行のみの互換性であり、代わりに挿入されたコンテキスト オブジェクトを介してランタイム データを使用します。

次のステップ