Partager via


Contexte d’exécution

Le contexte d’exécution fournit un intergiciel avec accès aux informations relatives à l’environnement d’exécution actuel et à la demande. Cela permet des modèles tels que la configuration par session, le comportement spécifique à l’utilisateur et le comportement du middleware dynamique en fonction des conditions d’exécution.

En C#, le contexte d’exécution transite par trois surfaces principales :

  • AgentRunOptions.AdditionalProperties pour les métadonnées clé-valeur par exécution que les intergiciels et les outils peuvent lire.
  • FunctionInvocationContext pour inspecter et modifier les arguments d’appel d’outil à l’intérieur du middleware d’appel de fonction.
  • AgentSession.StateBag pour l’état partagé qui persiste entre les exécutions au sein d’une conversation.

Utilisez la surface la plus étroite qui s’adapte. Les métadonnées par exécution appartiennent à AdditionalProperties, l’état de conversation persistant appartient à la manipulation de l’argument de la session et la manipulation d’arguments StateBagde fonction appartient au middleware d’appel de fonction.

Conseil / Astuce

Pour plus d’informations sur la façon dont l’étendue d’intergiciel affecte l’accès au contexte d’exécution, consultez la page Agent vs Run Scope .

Choisir la surface d’exécution appropriée

Cas d’utilisation Surface d’API Accès à partir de
Partager l’état de conversation ou les données entre les exécutions AgentSession.StateBag session.StateBag dans l’intergiciel d’exécution, AIAgent.CurrentRunContext?.Session dans les outils
Passer des métadonnées par exécution à des intergiciels ou des outils AgentRunOptions.AdditionalProperties options.AdditionalProperties dans l’intergiciel d’exécution, AIAgent.CurrentRunContext?.RunOptions dans les outils
Inspecter ou modifier des arguments d’appel d’outil dans le middleware FunctionInvocationContext Rappel de l’intergiciel d’appel de fonction

Passer des valeurs par exécution via AgentRunOptions

Utilisez-la AdditionalPropertiesAgentRunOptions pour attacher des données clé-valeur par exécution. L’intergiciel d’appel de fonction peut transférer ces valeurs dans des arguments d’outil.

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);

Avertissement

DefaultAzureCredential est pratique pour le développement, mais nécessite une considération minutieuse en production. En production, envisagez d’utiliser des informations d’identification spécifiques (par exemple ManagedIdentityCredential) pour éviter les problèmes de latence, la détection involontaire des informations d’identification et les risques de sécurité potentiels liés aux mécanismes de secours.

L’intergiciel lit les valeurs AgentRunOptions.AdditionalProperties par exécution via l’environnement ambiant AIAgent.CurrentRunContext et les injecte dans les outils avant l’exécution de l’outil FunctionInvocationContext.Arguments .

L’intergiciel d’appel de fonction reçoit le contexte

L’intergiciel d’appel de fonction utilise FunctionInvocationContext pour inspecter ou modifier les arguments de l’outil, intercepter les résultats ou ignorer entièrement l’exécution de l’outil.

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();

L’intergiciel reçoit le contexte d’appel de fonction et les appels next pour poursuivre le pipeline. Mutez context.Arguments avant d’appeler next, et l’outil voit les valeurs mises à jour.

Utiliser pour l’état AgentSession.StateBag du runtime partagé

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"));

Transmettez explicitement la session et session: accédez-y à partir d’outils via AIAgent.CurrentRunContext?.Session. Il StateBag fournit un stockage thread-safe de type qui persiste entre les exécutions au sein de la même session.

Partager l’état de session entre les intergiciels et les outils

L’intergiciel d’exécution peut lire et écrire les modifications de StateBagla session, et toutes les modifications sont visibles pour les intergiciels d’appel de fonction et les outils s’exécutant dans la même requête.

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"));

Exécuter l’intergiciel reçoit la session directement en tant que paramètre. Utilisez et GetValue pour l’accès StateBag.SetValue sécurisé de type. Toutes les valeurs stockées pendant la phase d’intergiciel d’exécution sont disponibles pour les outils et les intergiciels de fonction via AIAgent.CurrentRunContext?.Session.

Le contexte d’exécution Python est divisé sur trois surfaces publiques :

  • session= pour l’état et l’historique des conversations.
  • function_invocation_kwargs= pour les valeurs que seuls les outils ou les intergiciels de fonction doivent voir.
  • client_kwargs= pour la configuration des intergiciels client ou des données spécifiques au client.

Utilisez la plus petite surface qui correspond aux données. Cela permet de conserver les entrées d’outils explicites et d’éviter la fuite de métadonnées client uniquement dans l’exécution de l’outil.

Conseil / Astuce

Traitez function_invocation_kwargs comme le remplacement de l’ancien modèle de passage arbitraire public **kwargs à agent.run() ou get_response().

Choisir le compartiment d’exécution approprié

Cas d’utilisation Surface d’API Accès à partir de
Partager l’état de conversation, les ID de session de service ou l’historique session= ctx.session, AgentContext.session
Transmettre des valeurs d’exécution uniquement aux outils ou intergiciels de fonction function_invocation_kwargs= FunctionInvocationContext.kwargs
Passer des valeurs d’exécution spécifiques au client ou à la configuration du middleware client client_kwargs= implémentations personnalisées get_response(..., client_kwargs=...)

Passer des valeurs d’exécution d’outil uniquement

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)

Utilisez ctx.kwargs à l’intérieur de l’outil au lieu de déclarer une couverture **kwargs sur l’outil pouvant être appelé. Les outils hérités **kwargs fonctionnent toujours pour la compatibilité, mais seront supprimés avant la disponibilité générale.

Tout paramètre annoté comme FunctionInvocationContext étant traité comme le paramètre de contexte d’exécution injecté, quel que soit son nom et n’est pas exposé dans le schéma JSON affiché au modèle. Si vous fournissez un modèle de schéma/d’entrée explicite, un paramètre non annoté brut nommé ctx est également reconnu comme paramètre de contexte injecté.

Si la valeur est un état d’outil de longue durée ou une dépendance plutôt que des données par appel, conservez-la sur une instance de classe d’outil au lieu de la function_invocation_kwargstransmettre. Pour ce modèle, consultez Créer une classe avec plusieurs outils de fonction.

L’intergiciel de fonction reçoit le même contexte

L’intergiciel de fonction utilise le même FunctionInvocationContext objet que celui reçu par les outils. Cela signifie que le middleware peut inspecter context.arguments, context.kwargs, context.sessionet 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],
)

Le contrat d’intergiciel utilise call_next() sans argument. Mutez context.kwargs avant de l’appeler, et l’outil sélectionné voit ces valeurs par le biais de son injecté FunctionInvocationContext.

Utiliser pour l’état session= du runtime partagé

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"])

Passez la session explicitement avec session= et lisez-la à partir de ctx.session. L’accès à la session n’a plus besoin de voyager via les kwargs d’exécution.

Partager l’état de session avec des agents délégués

Lorsqu’un agent est exposé en tant qu’outil via as_tool(), les kwargs de fonction d’exécution transitent déjà par ctx.kwargs. Ajoutez propagate_session=True uniquement lorsque le sous-agent doit partager l’appelant 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,
)

Avec propagate_session=True, l’agent délégué voit le même ctx.session état que l’appelant. Laissez-le False isoler l’agent enfant dans sa propre session.

Clients et agents de conversation personnalisés

Si vous implémentez des méthodes publiques ou run() publiques get_response() personnalisées, ajoutez les compartiments d’exécution explicites à la signature.

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,
):
    ...

Utiliser function_invocation_kwargs pour les flux d’appel d’outils et client_kwargs pour le comportement spécifique au client. La transmission de valeurs spécifiques au client directement par le biais d’un chemin public **kwargs est un chemin de compatibilité et doit être traitée comme déconseillée. De même, la définition de nouveaux outils avec une **kwargs compatibilité de migration uniquement consiste à consommer des données d’exécution via l’objet de contexte injecté à la place.

Prochaines étapes