سياق وقت التشغيل

يوفر سياق وقت التشغيل لبرنامج وسيط إمكانية الوصول إلى معلومات حول بيئة التنفيذ الحالية والطلب. يتيح هذا أنماطا مثل التكوين لكل جلسة، والسلوك الخاص للمستخدم، وسلوك البرنامج الوسيط الديناميكي استنادا إلى شروط وقت التشغيل.

في C#، يتدفق سياق وقت التشغيل عبر ثلاثة أسطح رئيسية:

  • AgentRunOptions.AdditionalProperties لبيانات تعريف قيمة المفتاح لكل تشغيل التي يمكن للبرامج الوسيطة والأدوات قراءتها.
  • FunctionInvocationContext لفحص وتعديل وسيطات استدعاء الأداة داخل البرنامج الوسيط لاستدعاء الدالة.
  • AgentSession.StateBag للحالة المشتركة التي تستمر عبر عمليات التشغيل داخل محادثة.

استخدم أضيق سطح يناسبك. تنتمي بيانات التعريف لكل تشغيل إلى AdditionalProperties، وتنتمي حالة المحادثة الثابتة إلى جلسة العمل StateBag، وتنتمي معالجة وسيطة الأدوات إلى البرنامج الوسيط استدعاء الدالة.

Tip

راجع صفحة Agent vs Run Scope للحصول على معلومات حول كيفية تأثير نطاق البرنامج الوسيط على الوصول إلى سياق وقت التشغيل.

اختيار سطح وقت التشغيل المناسب

حالة الاستخدام سطح API تم الوصول من
مشاركة حالة المحادثة أو البيانات عبر عمليات التشغيل AgentSession.StateBag session.StateBag في تشغيل البرامج الوسيطة، AIAgent.CurrentRunContext?.Session في الأدوات
تمرير بيانات التعريف لكل تشغيل إلى البرامج الوسيطة أو الأدوات AgentRunOptions.AdditionalProperties options.AdditionalProperties في تشغيل البرامج الوسيطة، AIAgent.CurrentRunContext?.RunOptions في الأدوات
فحص وسيطات استدعاء الأداة أو تعديلها في البرامج الوسيطة FunctionInvocationContext استدعاء الدالة رد اتصال البرنامج الوسيط

تمرير القيم لكل تشغيل عبر AgentRunOptions

AgentRunOptions استخدم AdditionalProperties لإرفاق بيانات قيمة المفتاح لكل تشغيل. يمكن للبرنامج الوسيط استدعاء الدالة إعادة توجيه هذه القيم إلى وسيطات الأدوات.

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

تحذير

DefaultAzureCredential مناسب للتنمية ولكنه يتطلب دراسة متأنية في الإنتاج. في الإنتاج، ضع في اعتبارك استخدام بيانات اعتماد محددة (على سبيل المثال، ManagedIdentityCredential) لتجنب مشكلات زمن الانتقال، وبحث بيانات الاعتماد غير المقصودة، والمخاطر الأمنية المحتملة من الآليات الاحتياطية.

يقرأ البرنامج الوسيط القيم لكل تشغيل من AgentRunOptions.AdditionalProperties خلال المحيط AIAgent.CurrentRunContext ويدخلها في الأداة 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 لمتابعة البنية الأساسية لبرنامج ربط العمليات التجارية. قم بالتحول context.Arguments قبل استدعاء next، وترى الأداة القيم المحدثة.

استخدام 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.SetValue و GetValue للوصول الآمن للنوع. تتوفر أي قيم مخزنة أثناء مرحلة تشغيل البرامج الوسيطة إلى الأدوات والبرامج الوسيطة لاستدعاء الوظائف عبر AIAgent.CurrentRunContext?.Session.

يتم تقسيم سياق وقت التشغيل Python عبر ثلاثة أسطح عامة:

  • session= لحالة المحادثة والمحفوظات.
  • function_invocation_kwargs= للقيم التي يجب أن تشاهدها الأدوات أو البرامج الوسيطة الوظيفية فقط.
  • client_kwargs= للبيانات الخاصة بالعميل أو تكوين البرنامج الوسيط للعميل.

استخدم أصغر سطح يناسب البيانات. يحافظ هذا على إدخالات الأداة بشكل صريح ويتجنب تسريب بيانات التعريف الخاصة بالعميل فقط إلى تنفيذ الأداة.

Tip

تعامل كحل function_invocation_kwargs بديل للنمط القديم لتمرير العامة **kwargs التعسفية إلى agent.run() أو get_response().

اختر مستودع وقت التشغيل المناسب

حالة الاستخدام سطح API تم الوصول من
مشاركة حالة المحادثة أو معرفات جلسة الخدمة أو المحفوظات session= ctx.session، AgentContext.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 على الأداة القابلة للاستدعاء. يتم رفض وسيطات الكلمة الأساسية لوقت التشغيل غير المتوقعة؛ يجب أن تستهلك الأدوات الجديدة بيانات وقت التشغيل من خلال FunctionInvocationContext.

يتم التعامل مع أي معلمة مشروحة كما FunctionInvocationContext يتم التعامل معها كمعلمة سياق وقت التشغيل التي تم إدخالها، بغض النظر عن اسمها، ولا يتم كشفها في مخطط JSON الموضح للنموذج. إذا قمت بتوفير مخطط/نموذج إدخال صريح، يتم التعرف أيضا على معلمة عادية غير موضحة باسم ctx كمعلمة سياق تم إدخالها.

إذا كانت القيمة هي حالة أداة طويلة الأمد أو تبعية بدلا من بيانات لكل استدعاء، فاحتفظ بها على مثيل فئة أداة بدلا من تمريرها من خلال function_invocation_kwargs. لهذا النمط، راجع إنشاء فئة باستخدام أدوات وظائف متعددة.

تتلقى البرامج الوسيطة للدالة السياق نفسه

يستخدم البرنامج الوسيط للدالة نفس FunctionInvocationContext الكائن الذي تتلقاه الأدوات. وهذا يعني أن البرامج الوسيطة يمكنها فحص context.argumentsو context.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 لعزل العامل التابع في جلسته الخاصة.

عملاء ووكلاء الدردشة المخصصة

إذا قمت بتطبيق أساليب أو get_response() عامة run() مخصصة، أضف مستودعات وقت التشغيل الصريحة إلى التوقيع.

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 هو توافق الترحيل فقط — استهلاك بيانات وقت التشغيل من خلال كائن السياق الذي تم إدخاله بدلا من ذلك.

يمرر Go سياق وقت التشغيل من خلال context.Context القيم والمكتبة agent.Option . يمكن لبرنامج الوسيط فحص الخيارات مع agent.GetOption وإضافة خيارات لكل تشغيل قبل استدعاء next.

runtimeContext := agent.MiddlewareFunc(func(next agent.RunFunc, ctx context.Context, messages []*message.Message, options ...agent.Option) iter.Seq2[*agent.ResponseUpdate, error] {
    session, _ := agent.GetOption(options, agent.WithSession)
    if session != nil {
        options = append(options, agent.WithInstructions("Use the active session context."))
    }
    return next(ctx, messages, options...)
})

session, err := a.CreateSession(ctx)
resp, err := a.RunText(ctx, "Hello", agent.WithSession(session)).Collect()

الخطوات التالية