Compartilhar via


Contexto de runtime

O contexto de runtime fornece ao middleware acesso a informações sobre o ambiente de execução e a solicitação atuais. Isso permite padrões como configuração por sessão, comportamento específico do usuário e comportamento de middleware dinâmico com base em condições de runtime.

No C#, o contexto de runtime flui por três superfícies principais:

  • AgentRunOptions.AdditionalProperties para metadados chave-valor por execução que middleware e ferramentas podem ler.
  • FunctionInvocationContext para inspecionar e modificar argumentos de chamada de ferramenta dentro do middleware de invocação de função.
  • AgentSession.StateBag para o estado compartilhado que persiste entre execuções dentro de uma conversa.

Use a superfície mais estreita que se ajusta. Os metadados por execução pertencem, AdditionalPropertieso estado de conversa persistente pertence à sessão StateBage a manipulação de argumento de ferramenta pertence ao middleware de invocação de função.

Dica

Consulte a página Agente vs Executar Escopo para obter informações sobre como o escopo do middleware afeta o acesso ao contexto de runtime.

Escolha a superfície de runtime certa

Caso de uso Superfície da API Acessado a partir de
Compartilhar o estado ou os dados da conversa entre execuções AgentSession.StateBag session.StateBag no middleware de execução, AIAgent.CurrentRunContext?.Session em ferramentas
Passar metadados por execução para middleware ou ferramentas AgentRunOptions.AdditionalProperties options.AdditionalProperties no middleware de execução, AIAgent.CurrentRunContext?.RunOptions em ferramentas
Inspecionar ou modificar argumentos de chamada de ferramenta no middleware FunctionInvocationContext Retorno de chamada de middleware de invocação de função

Passar valores por execução por meio de AgentRunOptions

AgentRunOptions Use AdditionalProperties para anexar dados chave-valor por execução. O middleware de invocação de função pode encaminhar esses valores para argumentos de ferramenta.

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

Aviso

DefaultAzureCredential é conveniente para o desenvolvimento, mas requer uma consideração cuidadosa na produção. Em produção, considere o uso de uma credencial específica (por exemplo, ManagedIdentityCredential) para evitar problemas de latência, investigação de credenciais não intencionais e possíveis riscos de segurança de mecanismos de fallback.

O middleware lê valores AgentRunOptions.AdditionalProperties por execução por meio do ambiente AIAgent.CurrentRunContext e os injeta na ferramenta antes da execução da FunctionInvocationContext.Arguments ferramenta.

O middleware de invocação de função recebe contexto

O middleware de invocação de função usa FunctionInvocationContext para inspecionar ou modificar argumentos de ferramenta, interceptar resultados ou ignorar totalmente a execução da ferramenta.

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

O middleware recebe o contexto de invocação de função e chama next para continuar o pipeline. Mutar context.Arguments antes de chamar nexte a ferramenta verá os valores atualizados.

Usar AgentSession.StateBag para o estado de runtime compartilhado

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

Passe a sessão explicitamente e session: acesse-a das ferramentas por meio AIAgent.CurrentRunContext?.Sessionde . O StateBag fornece armazenamento com segurança de tipo e thread que persiste entre execuções na mesma sessão.

Compartilhar o estado da sessão entre middleware e ferramentas

O middleware de execução pode ler e gravar a sessão StateBage todas as alterações ficam visíveis para o middleware de invocação de função e as ferramentas em execução na mesma solicitação.

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

O middleware de execução recebe a sessão diretamente como um parâmetro. Use StateBag.SetValue e GetValue para acesso com segurança de tipo. Todos os valores armazenados durante a fase de middleware de execução estão disponíveis para ferramentas e middleware de invocação de função por meio de AIAgent.CurrentRunContext?.Session.

O contexto de runtime do Python é dividido entre três superfícies públicas:

  • session= para o estado e o histórico da conversa.
  • function_invocation_kwargs= para valores que somente ferramentas ou middleware de função devem ver.
  • client_kwargs= para dados específicos do cliente de chat ou configuração de middleware do cliente.

Use a menor superfície que se ajusta aos dados. Isso mantém as entradas da ferramenta explícitas e evita o vazamento de metadados somente do cliente na execução da ferramenta.

Dica

Trate function_invocation_kwargs como a substituição do antigo padrão de passar público **kwargs arbitrário para agent.run() ou get_response().

Escolher o bucket de runtime certo

Caso de uso Superfície da API Acessado a partir de
Compartilhar estado da conversa, IDs de sessão de serviço ou histórico session= ctx.session, AgentContext.session
Passar valores de runtime somente ferramentas ou middleware de função precisa function_invocation_kwargs= FunctionInvocationContext.kwargs
Passar valores de runtime específicos do cliente ou configuração de middleware do cliente client_kwargs= implementações personalizadas get_response(..., client_kwargs=...)

Passar valores de runtime somente para ferramentas

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)

Use ctx.kwargs dentro da ferramenta em vez de declarar o cobertor **kwargs na ferramenta que pode ser chamada. As ferramentas herdadas **kwargs ainda funcionam para compatibilidade, mas serão removidas antes da GA.

Qualquer parâmetro anotado como FunctionInvocationContext é tratado como o parâmetro de contexto de runtime injetado, independentemente de seu nome, e não é exposto no esquema JSON mostrado ao modelo. Se você fornecer um modelo de esquema/entrada explícito, um parâmetro sem anotação simples nomeado ctx também será reconhecido como o parâmetro de contexto injetado.

Se o valor for um estado de ferramenta de longa duração ou uma dependência em vez de dados por invocação, mantenha-o em uma instância de classe de ferramenta em vez de passá-lo.function_invocation_kwargs Para esse padrão, consulte Criar uma classe com várias ferramentas de função.

O middleware de função recebe o mesmo contexto

O middleware de função usa o mesmo FunctionInvocationContext objeto que as ferramentas recebem. Isso significa que o middleware pode inspecionarcontext.arguments, context.kwargse context.sessioncontext.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],
)

O contrato de middleware usa call_next() sem argumentos. Mutar context.kwargs antes de chamá-lo, e a ferramenta selecionada vê esses valores por meio de sua injeção FunctionInvocationContext.

Usar session= para o estado de runtime compartilhado

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

Passe a sessão explicitamente com session= e leia-a de ctx.session. O acesso à sessão não precisa mais percorrer kwargs de runtime.

Compartilhar o estado da sessão com agentes delegados

Quando um agente é exposto como uma ferramenta por meio as_tool()de kwargs de função de runtime que já fluem por ctx.kwargs. Adicione propagate_session=True somente quando o subagente deve compartilhar o chamador 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,
)

Com propagate_session=True, o agente delegado vê o mesmo ctx.session estado que o chamador. Deixe-o False isolar o agente filho em sua própria sessão.

Clientes e agentes de chat personalizados

Se você implementar métodos públicos run() ou get_response() personalizados, adicione os buckets de runtime explícitos à assinatura.

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

Use function_invocation_kwargs para fluxos de invocação de ferramentas e client_kwargs para comportamento específico do cliente. Passar valores específicos do cliente diretamente pelo público **kwargs é apenas um caminho de compatibilidade e deve ser tratado como preterido. Da mesma forma, definir novas ferramentas com **kwargs a compatibilidade somente migração é consumir dados de runtime por meio do objeto de contexto injetado.

Próximas etapas