Zprostředkovatelé kontextu

Zprostředkovatelé kontextu se spouští kolem každého vyvolání, aby před spuštěním a zpracování dat po spuštění přidali kontext.

Note

Seznam předem připravených zprostředkovatelů kontextu, které můžete použít se svým agentem, najdete v tématu Integrace zprostředkovatele kontextu.

Předdefinovaný vzor

Při vytváření agenta nakonfigurujte zprostředkovatele prostřednictvím možností konstruktoru. AIContextProvider je integrovaný bod rozšíření pro rozšiřování paměti a kontextu.

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

Seznam předdefinovaných AIContextProvider implementací najdete v tématu Integrace zprostředkovatele kontextu.

Běžným postupem je konfigurovat poskytovatele prostřednictvím context_providers=[...] při vytváření agenta.

InMemoryHistoryProvider je integrovaný zprostředkovatel historie používaný pro místní konverzační paměť.

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 může automaticky přidat InMemoryHistoryProvider() s výchozím ID "in_memory" zdroje v konkrétních případech, ale přidat ho explicitně, pokud chcete deterministické chování místní paměti.

Paměť s podporou souborů napříč relacemi

Použijte FileMemoryProvider , když by se měl model rozhodnout, co se má ukládat a odvolat prostřednictvím file_memory_* nástrojů. V Python odvozování odvozuje scope pracovní složku z aktuálního ID relace, takže samostatné relace nesdílejí soubory paměti. Předat stabilní scope, například identifikátor uživatele, sdílet stejné paměťové soubory napříč relacemi a zvolit AgentFileStore implementaci pro záložní úložiště.

# 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],
)

Poskytovatele nakonfigurujte prostřednictvím agent.Config.ContextProviders při vytváření agenta. Poskytovatelé kontextu před spuštěním každého agenta vloží další kontext a po každém spuštění můžou zachovat stav.

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

Použití zprostředkovatelů kontextu s využitím agenta

Výše uvedené ruční vzory připojují pouze vámi zvolené poskytovatele. Agent při vytváření sestaví seřazenou sadu zprostředkovatelů. Pomocí možností vytváření jednotlivých sad SDK zakažte nebo nahraďte výchozí hodnoty a připojte další zprostředkovatele.

HarnessAgent TodoProviderpovolí , AgentModeProviderFileMemoryProvider, a AgentSkillsProvider ve výchozím nastavení. Připojí z nich poskytovatele za HarnessAgentOptions.AIContextProviders předdefinované.

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

Použijte DisableTodoProvider, DisableAgentModeProviderDisableFileMemorya DisableAgentSkillsProvider odeberte výchozí hodnoty. Konfigurujte režim a dovednosti pomocí AgentModeProviderOptions a AgentSkillsSourcenahraďte úložiště FileMemoryStorev paměti souborů . Přístup k souborům se přihlašuje prostřednictvím FileAccessStore a FileAccessProviderOptionsdelegování na pozadí se přihlašuje prostřednictvím BackgroundAgents a BackgroundAgentsProviderOptions. AsHarnessAgent(options) a new HarnessAgent(chatClient, options) přijměte totéž HarnessAgentOptions.

create_harness_agent nejprve objednává zprostředkovatele historie, pak po spuštění komprimace, pokud je povolená, následovaná poskytovateli úkolů, režimu a paměti souborů. Paměť souboru je ve výchozím nastavení zapnutá; dovednosti, přístup k souborům, agenti na pozadí a kontext prostředí jsou opt-in. Zprostředkovatelé předané context_providers= jsou připojeni jako poslední.

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

Použijte history_provider, todo_providera mode_provider nahraďte tyto výchozí hodnoty disable_file_memorydisable_modedisable_todoa jako odhlášení. Slouží file_memory_store k nahrazení výchozího {cwd}/agent-file-memory úložiště. Povolte volitelné zprostředkovatele s file_access_storeparametry nastavení nebo background_agentsskills_providerskills_pathsnebo shell_executor, jejich související parametry nastavení konfigurují oprávnění, pokyny a chování prostředí.

V sadě Go SDK v současné době není k dispozici agenta využití. Explicitně přidejte zprostředkovatele kontextu prostřednictvím agent.Config.ContextProviders.

Vlastní zprostředkovatel kontextu

Vlastní zprostředkovatele kontextu použijte v případě, že po spuštění potřebujete vložit dynamické instrukce, zprávy nebo nástroje nebo extrahovat stav.

Základní třída pro zprostředkovatele kontextu je Microsoft.Agents.AI.AIContextProvider. Zprostředkovatelé kontextu se účastní kanálu agenta, mají možnost přispívat do vstupních zpráv agenta nebo je přepsat a mohou extrahovat informace z nových zpráv. AIContextProvider má různé virtuální metody, které můžete přepsat pro implementaci vlastního zprostředkovatele kontextu. Další informace o tom, co je potřeba přepsat, jsou uvedeny v možnostech implementace níže.

AIContextProvider Stav

instance AIContextProvider je připojena k agentovi a stejná instance bude použita pro všechny relace. To znamená, že AIContextProvider v instanci poskytovatele by se neměl ukládat žádný konkrétní stav relace. Může AIContextProvider mít odkaz na klienta služby paměti v poli, ale neměl by mít ID pro konkrétní sadu pamětí v poli.

AIContextProvider může místo toho ukládat jakékoli hodnoty specifické pro relaci, jako jsou ID paměti, zprávy nebo cokoli jiného, co je relevantní pro AgentSession. Všem virtuálním metodám AIContextProvider se předává odkaz na aktuální AIAgent a AgentSession.

Chcete-li snadno ukládat stav s určeným typem v AgentSession, je k dispozici pomocná třída:

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

Jednoduchá AIContextProvider implementace

Nejjednodušší AIContextProvider implementace by obvykle přepsala dvě metody:

  • AIContextProvider.ProvideAIContextAsync – Načtěte relevantní data a vraťte další pokyny, zprávy nebo nástroje.
  • AIContextProvider.StoreAIContextAsync – extrahuje všechna relevantní data z nových zpráv a úložiště.

Tady je příklad jednoduché AIContextProvider integrace se službou paměti.

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

Pokročilá AIContextProvider implementace

Pokročilejší implementace by se mohla rozhodnout přepsat následující metody:

  • AIContextProvider.InvokingCoreAsync – Volá se před vyvoláním LLM a umožňuje provést úpravu seznamu zpráv požadavků, nástrojů a pokynů.
  • AIContextProvider.InvokedCoreAsync – volá se po vyvolání LLM agentem a umožňuje přístup ke všem zprávám požadavků a odpovědí.

AIContextProvider poskytuje základní implementace InvokingCoreAsync a InvokedCoreAsync.

Základní InvokingCoreAsync implementace provede následující:

  • filtruje vstupní seznam zpráv pouze na zprávy, které byly předány agentovi volajícím. Všimněte si, že tento filtr lze přepsat pomocí provideInputMessageFilter parametru v konstruktoru AIContextProvider .
  • volání ProvideAIContextAsync s filtrovanými požadovanými zprávami, existujícími nástroji a pokyny.
  • označí všechny zprávy vrácené zdrojovými ProvideAIContextAsync informacemi, které označují, že tyto zprávy pocházejí od tohoto zprostředkovatele kontextu.
  • sloučí zprávy, nástroje a pokyny vrácené z ProvideAIContextAsync s těmi stávajícími, aby se vytvořil vstup, který agent použije. Zprávy, nástroje a pokyny se připojují k existujícím.

Základ InvokedCoreAsync dělá následující:

  • zkontroluje, jestli spuštění selhalo, a pokud ano, vrátí se bez dalšího zpracování.
  • filtruje vstupní seznam zpráv pouze na zprávy, které byly předány agentovi volajícím. Všimněte si, že tento filtr lze přepsat pomocí storeInputMessageFilter parametru v konstruktoru AIContextProvider .
  • předá filtrované požadované zprávy a všechny zprávy odpovědi do StoreAIContextAsync úložiště.

Tyto metody je možné přepsat k implementaci AIContextProvider, ale to vyžaduje, aby implementátor sám přiměřeně zajistil základní funkčnost. Tady je příklad takové implementace.

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 a HistoryProvider jsou kanonickými základními třídami Pythonu.

Poskytovatelé kontextu mohou také přidat chatové nebo funkční middleware pro aktuální volání pomocí context.extend_middleware(self.source_id, middleware). Agent tyto doplňky context.get_middleware() slučuje a použije je v daném pořadí poskytovatelů před spuštěním chatovacího klienta.

Dynamický výběr nástrojů

Zprostředkovatelé kontextu mohou přidat nástroje pro aktuální vyvolání pomocí context.extend_tools(self.source_id, tools). Postupné načítání nástrojů během smyčky volání funkcí najdete v ukázce dynamic_tool_exposure sample. Informace o sadách spravovaných nástrojů najdete v tématu Microsoft Sada nástrojů Foundry.

Vlastní zprostředkovatel historie

Zprostředkovatelé historie jsou zprostředkovatelé kontextu specializovaní na načítání a ukládání zpráv.

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

V Pythonu můžete nakonfigurovat více poskytovatelů historie, ale pouze jeden by měl používat load_messages=True. Používejte další zprostředkovatele pro diagnostiku a hodnocení spolu s load_messages=False a store_context_messages=True, tak aby zachytili kontext od jiných poskytovatelů společně se vstupem a výstupem. Pokud potřebujete, aby místní historie přetrvávala kolem každého volání modelu ve smyčce nástroje, přečtěte si Úložiště.

Příklad vzoru:

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

Definujte vlastního zprostředkovatele kontextu pomocí zpětného Provide volání:

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
    },
})

Poskytovatelé kontextu mohou číst a zapisovat stav relace:

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
},

Další kroky