موفرو السياق

يعمل موفرو السياق حول كل استدعاء لإضافة سياق قبل التنفيذ ومعالجة البيانات بعد التنفيذ.

Note

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

نمط مضمن

تكوين الموفرين من خلال خيارات المنشئ عند إنشاء عامل. AIContextProvider هي نقطة الامتداد المضمنة لإثراء الذاكرة/السياق.

AIAgent agent = new OpenAIClient("<your_api_key>")
    .GetChatClient(modelName)
    .AsAIAgent(new ChatClientAgentOptions()
    {
        ChatOptions = new() { Instructions = "You are a helpful assistant." },
        AIContextProviders = [
            new MyCustomMemoryProvider()
        ],
    });

AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Remember my name is Alice.", session));

Tip

للحصول على قائمة بالتكاملات التي تم إنشاؤها AIContextProvider مسبقا، راجع عمليات تكامل موفر السياق.

النمط العادي هو تكوين الموفرين من خلال context_providers=[...] عند إنشاء عامل.

InMemoryHistoryProvider هو موفر المحفوظات المضمن المستخدم لذاكرة المحادثة المحلية.

from agent_framework import Agent, InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient

agent = Agent(
    client=OpenAIChatClient(),
    name="MemoryBot",
    instructions="You are a helpful assistant.",
    context_providers=[InMemoryHistoryProvider("memory", load_messages=True)],
)

session = agent.create_session()
await agent.run("Remember that I prefer vegetarian food.", session=session)

RawAgent قد يضيف InMemoryHistoryProvider() تلقائيا مع معرف "in_memory" المصدر الافتراضي في حالات معينة، ولكن إضافته بشكل صريح عندما تريد سلوك ذاكرة محلية محددة.

الذاكرة المدعومة بالملفات عبر جلسات العمل

استخدم FileMemoryProvider عندما يجب أن يقرر النموذج ما يجب تخزينه واسترجاعه من خلال file_memory_* الأدوات. في Python، يستمد حذف scope مجلد العمل من معرف جلسة العمل الحالي، لذلك لا تشارك جلسات العمل المنفصلة ملفات الذاكرة. مرر مستقرا scope، مثل معرف المستخدم، لمشاركة ملفات الذاكرة نفسها عبر جلسات العمل، واختر تنفيذا AgentFileStore للتخزين الاحتياطي.

# 1. Create the file store the provider will use to persist memory files.
#    Here we use a file-system backed store rooted at a local
#    ``agent-file-memory`` folder, but any AgentFileStore implementation can
#    be used, e.g. InMemoryAgentFileStore or a custom blob-backed store.
memory_root = Path(__file__).parent / "agent-file-memory"
store = FileSystemAgentFileStore(memory_root)

# 2. Create the FileMemoryProvider over that store.
#    The ``scope`` determines the scope and lifetime of the memories:
#    - A stable scope, like the per-user one below, gives durable memories
#      shared by every session for that user. That is what allows the second
#      conversation further down to recall what the user said in the first.
#    - Omitting ``scope`` (the default) isolates memories to a single session
#      (the working folder is derived from the session id).
file_memory_provider = FileMemoryProvider(store, scope=f"users/{USER_ID}")

# 3. Attach the provider to the agent so it gets the file_memory_* tools.
agent = Agent(
    client=client,
    name="TravelAssistant",
    instructions=(
        "You are a helpful travel assistant. Remember what the user tells you about "
        "themselves so that you can give better recommendations later."
    ),
    context_providers=[file_memory_provider],
)

تكوين الموفرين من خلال agent.Config.ContextProviders عند إنشاء عامل. يقوم موفرو السياق بإدخال سياق إضافي قبل تشغيل كل عامل ويمكن أن تستمر الحالة بعد كل تشغيل.

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Config: agent.Config{
        ContextProviders: []agent.ContextProvider{provider},
    },
})

استخدام موفري السياق مع Harness Agent

تقوم الأنماط اليدوية أعلاه بإرفاق الموفرين الذين تختارهم فقط. يقوم Harness Agent بتجميع مجموعة موفر مرتب عند إنشائه. استخدم خيارات بناء كل SDK لتعطيل أو استبدال الإعدادات الافتراضية وإلحاق موفرين إضافيين.

HarnessAgentتمكين TodoProviderو AgentModeProviderFileMemoryProviderو و AgentSkillsProvider بشكل افتراضي. ويلحق موفري الخدمة من HarnessAgentOptions.AIContextProviders بعد تلك المضمنات.

HarnessAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    AIContextProviders = [new MyCustomMemoryProvider()],
    DisableAgentSkillsProvider = true,
});

استخدم DisableTodoProviderو DisableAgentModeProviderDisableFileMemoryو و لإزالة DisableAgentSkillsProvider الإعدادات الافتراضية. قم بتكوين الوضع والمهارات مع AgentModeProviderOptions و AgentSkillsSource؛ استبدال تخزين ذاكرة الملفات ب FileMemoryStore. الوصول إلى الملفات هو الاشتراك من خلال FileAccessStore و FileAccessProviderOptions، وتفويض الخلفية هو الاشتراك من خلال BackgroundAgents و BackgroundAgentsProviderOptions. AsHarnessAgent(options) واقبل new HarnessAgent(chatClient, options) نفس HarnessAgentOptions.

create_harness_agent طلب موفر المحفوظات أولا، ثم الضغط بعد التشغيل عند التمكين، متبوعا بموفري المهام ووضع ذاكرة الملفات. تكون ذاكرة الملف قيد التشغيل بشكل افتراضي؛ المهارات والوصول إلى الملفات ووكلاء الخلفية سياق shell هي الاشتراك. يتم إلحاق الموفرين الذين يمرون context_providers= بآخر.

agent = create_harness_agent(
    client,
    context_providers=[UserPreferenceProvider()],
    disable_mode=True,
    skills_paths=["./skills"],
)

استخدم history_providerو todo_providerو mode_provider لاستبدال هذه الإعدادات الافتراضية ب disable_modedisable_todoو و disable_file_memory كتعطيلات. استخدم file_memory_store لاستبدال المخزن الافتراضي {cwd}/agent-file-memory . قم بتمكين الموفرين الاختياريين باستخدام file_access_store، skills_provider أو skills_paths، background_agentsو، ومعلمات shell_executorالإعداد ذات الصلة الخاصة بهم لتكوين الأذونات والإرشادات وسلوك البيئة.

Harness Agent غير متوفر حاليا في Go SDK. أضف موفري السياق بشكل صريح من خلال agent.Config.ContextProviders.

موفر السياق المخصص

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

الفئة الأساسية لموفري السياق هي Microsoft.Agents.AI.AIContextProvider. يشارك موفرو السياق في مسار العامل، ولديهم القدرة على المساهمة في رسائل إدخال العامل أو تجاوزها ويمكنهم استخراج المعلومات من الرسائل الجديدة. AIContextProvider لديه أساليب ظاهرية مختلفة يمكن تجاوزها لتنفيذ موفر السياق المخصص الخاص بك. راجع خيارات التنفيذ المختلفة أدناه لمزيد من المعلومات حول ما يجب تجاوزه.

AIContextProvider الحالة

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

بدلا من ذلك، يمكن تخزين أي قيم محددة AIContextProvider لجلسة العمل، مثل معرفات الذاكرة أو الرسائل أو أي شيء آخر ذي صلة في AgentSession حد ذاته. يتم تمرير كافة الأساليب الظاهرية على AIContextProvider مرجع إلى الحالي AIAgent و AgentSession.

لتمكين تخزين الحالة المصنفة AgentSessionبسهولة في ، يتم توفير فئة أداة مساعدة:

// First define a type containing the properties to store in state
internal class MyCustomState
{
    public string? MemoryId { get; set; }
}

// Create the helper
var sessionStateHelper = new ProviderSessionState<MyCustomState>(
    // stateInitializer is called when there is no state in the session for this AIContextProvider yet
    stateInitializer: currentSession => new MyCustomState() { MemoryId = Guid.NewGuid().ToString() },
    // The key under which to store state in the session for this provider. Make sure it does not clash with the keys of other providers.
    stateKey: this.GetType().Name,
    // An optional jsonSerializerOptions to control the serialization/deserialization of the custom state object
    jsonSerializerOptions: myJsonSerializerOptions);

// Using the helper you can read state:
MyCustomState state = sessionStateHelper.GetOrInitializeState(session);
Console.WriteLine(state.MemoryId);

// And write state:
sessionStateHelper.SaveState(session, state);

تنفيذ بسيط AIContextProvider

عادة ما يتجاوز أبسط AIContextProvider تنفيذ طريقتين:

  • AIContextProvider.ProvideAIContextAsync - تحميل البيانات ذات الصلة وإرجاع إرشادات أو رسائل أو أدوات إضافية.
  • AIContextProvider.StoreAIContextAsync - استخراج أي بيانات ذات صلة من الرسائل الجديدة والمخزن.

فيما يلي مثال على بسيط AIContextProvider يتكامل مع خدمة الذاكرة.

internal sealed class SimpleServiceMemoryProvider : AIContextProvider
{
    private readonly ProviderSessionState<State> _sessionState;
    private readonly ServiceClient _client;

    public SimpleServiceMemoryProvider(ServiceClient client, Func<AgentSession?, State>? stateInitializer = null)
        : base(null, null)
    {
        this._sessionState = new ProviderSessionState<State>(
            stateInitializer ?? (_ => new State()),
            this.GetType().Name);
        this._client = client;
    }

    public override string StateKey => this._sessionState.StateKey;

    protected override ValueTask<AIContext> ProvideAIContextAsync(InvokingContext context, CancellationToken cancellationToken = default)
    {
        var state = this._sessionState.GetOrInitializeState(context.Session);

        if (state.MemoriesId == null)
        {
            // No stored memories yet.
            return new ValueTask<AIContext>(new AIContext());
        }

        // Find memories that match the current user input.
        var memories = this._client.LoadMemories(state.MemoriesId, string.Join("\n", context.AIContext.Messages?.Select(x => x.Text) ?? []));

        // Return a new message that contains the text from any memories that were found.
        return new ValueTask<AIContext>(new AIContext
        {
            Messages = [new ChatMessage(ChatRole.User, "Here are some memories to help answer the user question: " + string.Join("\n", memories.Select(x => x.Text)))]
        });
    }

    protected override async ValueTask StoreAIContextAsync(InvokedContext context, CancellationToken cancellationToken = default)
    {
        var state = this._sessionState.GetOrInitializeState(context.Session);
        // Create a memory container in the service for this session
        // and save the returned id in the session.
        state.MemoriesId ??= this._client.CreateMemoryContainer();
        this._sessionState.SaveState(context.Session, state);

        // Use the service to extract memories from the user input and agent response.
        await this._client.StoreMemoriesAsync(state.MemoriesId, context.RequestMessages.Concat(context.ResponseMessages ?? []), cancellationToken);
    }

    public class State
    {
        public string? MemoriesId { get; set; }
    }
}

التنفيذ المتقدم AIContextProvider

يمكن أن يختار تنفيذ أكثر تقدما تجاوز الطرق التالية:

  • AIContextProvider.InvokingCoreAsync - يتم استدعاؤه قبل أن يستدعي العامل LLM ويسمح بتعديل قائمة رسائل الطلب والأدوات والإرشادات.
  • AIContextProvider.InvokedCoreAsync - يتم استدعاؤه بعد استدعاء العامل ل LLM ويسمح بالوصول إلى جميع رسائل الطلب والاستجابة.

AIContextProvider يوفر تطبيقات أساسية ل InvokingCoreAsync و InvokedCoreAsync.

InvokingCoreAsync يقوم التنفيذ الأساسي بالآتي:

  • تصفية قائمة رسائل الإدخال إلى الرسائل التي تم تمريرها إلى العامل بواسطة المتصل فقط. لاحظ أنه يمكن تجاوز عامل التصفية هذا عبر المعلمة provideInputMessageFilter الموجودة في الدالة AIContextProvider الإنشائية.
  • استدعاءات ProvideAIContextAsync مع رسائل الطلب المصفاة والأدوات والإرشادات الموجودة.
  • ختم كافة الرسائل التي تم إرجاعها بواسطة ProvideAIContextAsync بمعلومات المصدر، مما يشير إلى أن هذه الرسائل تأتي من موفر السياق هذا.
  • يدمج الرسائل والأدوات والإرشادات التي يتم إرجاعها بواسطة ProvideAIContextAsync مع الرسائل الموجودة، لإنتاج الإدخال الذي سيستخدمه العامل. يتم إلحاق الرسائل والأدوات والإرشادات بالرسائل الموجودة.

تقوم InvokedCoreAsync القاعدة بالآتي:

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

من الممكن تجاوز هذه الأساليب لتنفيذ AIContextProvider، ولكن هذا يتطلب من المنفذ تنفيذ الوظيفة الأساسية نفسها حسب الاقتضاء. وفيما يلي مثال على مثل هذا التنفيذ.

internal sealed class AdvancedServiceMemoryProvider : AIContextProvider
{
    private readonly ProviderSessionState<State> _sessionState;
    private readonly ServiceClient _client;

    public AdvancedServiceMemoryProvider(ServiceClient client, Func<AgentSession?, State>? stateInitializer = null)
        : base(null, null)
    {
        this._sessionState = new ProviderSessionState<State>(
            stateInitializer ?? (_ => new State()),
            this.GetType().Name);
        this._client = client;
    }

    public override string StateKey => this._sessionState.StateKey;

    protected override async ValueTask<AIContext> InvokingCoreAsync(InvokingContext context, CancellationToken cancellationToken = default)
    {
        var state = this._sessionState.GetOrInitializeState(context.Session);

        if (state.MemoriesId == null)
        {
            // No stored memories yet.
            return new AIContext();
        }

        // We only want to search for memories based on user input, and exclude chat history or other AI context provider messages.
        var filteredInputMessages = context.AIContext.Messages?.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External);

        // Find memories that match the current user input.
        var memories = this._client.LoadMemories(state.MemoriesId, string.Join("\n", filteredInputMessages?.Select(x => x.Text) ?? []));

        // Create a message for the memories, and stamp it to indicate where it came from.
        var memoryMessages =
            [new ChatMessage(ChatRole.User, "Here are some memories to help answer the user question: " + string.Join("\n", memories.Select(x => x.Text)))]
            .Select(m => m.WithAgentRequestMessageSource(AgentRequestMessageSourceType.AIContextProvider, this.GetType().FullName!));

        // Return a new merged AIContext.
        return new AIContext
        {
            Instructions = context.AIContext.Instructions,
            Messages = context.AIContext.Messages.Concat(memoryMessages),
            Tools = context.AIContext.Tools
        };
    }

    protected override async ValueTask InvokedCoreAsync(InvokedContext context, CancellationToken cancellationToken = default)
    {
        if (context.InvokeException is not null)
        {
            return;
        }

        var state = this._sessionState.GetOrInitializeState(context.Session);
        // Create a memory container in the service for this session
        // and save the returned id in the session.
        state.MemoriesId ??= this._client.CreateMemoryContainer();
        this._sessionState.SaveState(context.Session, state);

        // We only want to store memories based on user input and agent output, and exclude messages from chat history or other AI context providers to avoid feedback loops.
        var filteredRequestMessages = context.RequestMessages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External);

        // Use the service to extract memories from the user input and agent response.
        await this._client.StoreMemoriesAsync(state.MemoriesId, filteredRequestMessages.Concat(context.ResponseMessages ?? []), cancellationToken);
    }

    public class State
    {
        public string? MemoriesId { get; set; }
    }
}
from typing import Any

from agent_framework import AgentSession, ContextProvider, SessionContext


class UserPreferenceProvider(ContextProvider):
    def __init__(self) -> None:
        super().__init__("user-preferences")

    async def before_run(
        self,
        *,
        agent: Any,
        session: AgentSession,
        context: SessionContext,
        state: dict[str, Any],
    ) -> None:
        if favorite := state.get("favorite_food"):
            context.extend_instructions(self.source_id, f"User's favorite food is {favorite}.")

    async def after_run(
        self,
        *,
        agent: Any,
        session: AgentSession,
        context: SessionContext,
        state: dict[str, Any],
    ) -> None:
        for message in context.input_messages:
            text = (message.text or "") if hasattr(message, "text") else ""
            if isinstance(text, str) and "favorite food is" in text.lower():
                state["favorite_food"] = text.split("favorite food is", 1)[1].strip().rstrip(".")

Note

ContextProviderو HistoryProvider هي الفئات الأساسية Python المتعارف عليه.

يمكن لموفري السياق أيضا إضافة دردشة أو برنامج وسيط دالة للاستدعاء الحالي عن طريق استدعاء context.extend_middleware(self.source_id, middleware). يقوم العامل بتسطيح هذه الإضافات مع context.get_middleware() وتطبيقها بترتيب الموفر قبل استدعاء عميل الدردشة.

اختيار الأدوات الديناميكية

يمكن لموفري السياق إضافة أدوات للادعاء الحالي باستخدام context.extend_tools(self.source_id, tools). لتحميل أداة تدريجية أثناء حلقة استدعاء الوظيفة، راجع نموذج dynamic_tool_exposure. للحصول على حزم الأدوات المدارة، راجع Microsoft مربع أدوات Foundry.

موفر المحفوظات المخصصة

موفرو المحفوظات هم موفرو سياق متخصصون لتحميل/تخزين الرسائل.

from collections.abc import Sequence
from typing import Any

from agent_framework import HistoryProvider, Message


class DatabaseHistoryProvider(HistoryProvider):
    def __init__(self, db: Any) -> None:
        super().__init__("db-history", load_messages=True)
        self._db = db

    async def get_messages(
        self,
        session_id: str | None,
        *,
        state: dict[str, Any] | None = None,
        **kwargs: Any,
    ) -> list[Message]:
        key = (state or {}).get("history_key", session_id or "default")
        rows = await self._db.load_messages(key)
        return [Message.from_dict(row) for row in rows]

    async def save_messages(
        self,
        session_id: str | None,
        messages: Sequence[Message],
        *,
        state: dict[str, Any] | None = None,
        **kwargs: Any,
    ) -> None:
        if not messages:
            return
        if state is not None:
            key = state.setdefault("history_key", session_id or "default")
        else:
            key = session_id or "default"
        await self._db.save_messages(key, [m.to_dict() for m in messages])

Important

في Python، يمكنك تكوين موفري محفوظات متعددين، ولكن يجب أن يستخدم load_messages=Trueموفر واحد فقط . استخدم موفرين إضافيين للتشخيص/التقييمات مع load_messages=False و store_context_messages=True حتى يلتقطوا السياق من موفرين آخرين إلى جانب الإدخال/الإخراج. إذا كنت بحاجة إلى محفوظات محلية للاستمرار حول كل استدعاء نموذج في حلقة أداة، فشاهد التخزين.

مثال على النمط:

primary = DatabaseHistoryProvider(db)
audit = InMemoryHistoryProvider("audit", load_messages=False, store_context_messages=True)
agent = Agent(client=OpenAIChatClient(), context_providers=[primary, audit])

تعريف موفر سياق مخصص مع Provide رد اتصال:

import (
    "context"

    "github.com/microsoft/agent-framework-go/agent"
    "github.com/microsoft/agent-framework-go/message"
)

provider := agent.NewContextProvider(agent.ContextProviderConfig{
    SourceID: "user_memory",
    Provide: func(ctx context.Context, invoking agent.InvokingContext) ([]*message.Message, []agent.Option, error) {
        return nil, []agent.Option{agent.WithInstructions("User prefers short answers.")}, nil
    },
})

يمكن لموفري السياق قراءة حالة جلسة العمل وكتابتها:

Provide: func(ctx context.Context, invoking agent.InvokingContext) ([]*message.Message, []agent.Option, error) {
    session, _ := agent.GetOption(invoking.Options, agent.WithSession)
    var state MyState
    _, _ = session.Get("my_key", &state)
    return nil, nil, nil
},
Store: func(ctx context.Context, invoked agent.InvokedContext) error {
    session, _ := agent.GetOption(invoked.Options, agent.WithSession)
    session.Set("my_key", updatedState)
    return nil
},

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