Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Поставщики контекста запускаются при каждом вызове для добавления контекста перед исполнением и обработки данных после исполнения.
Замечание
Список предварительно созданных поставщиков контекстов, которые можно использовать с агентом, см. в разделе "Интеграция".
Встроенный шаблон
Настройте поставщиков с помощью параметров конструктора при создании агента.
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));
Подсказка
Список предварительно созданных AIContextProvider реализаций см. в разделе "Интеграция"
Обычно провайдеры настраиваются через context_providers=[...] при создании агента.
InMemoryHistoryProvider — это встроенный поставщик истории, используемый для локальной памяти беседы.
from agent_framework import InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient
agent = OpenAIChatClient().as_agent(
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("memory") в некоторых случаях, но добавьте его явно, если требуется предсказуемое поведение локальной памяти.
Поставщик пользовательского контекста
Используйте настраиваемые поставщики контекстов, если необходимо внедрить динамические инструкции, сообщения или средства или извлечь состояние после выполнения.
Базовый класс для поставщиков контекста — Майкрософт.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(".")
Замечание
ContextProvider и HistoryProvider являются каноническими базовыми классами Python.
BaseContextProvider и BaseHistoryProvider по-прежнему существуют как устаревшие алиасы для совместимости, но новые поставщики должны использовать новые имена.
Поставщики контекста также могут добавлять посредническое ПО для чата или функций для текущего вызова при вызове context.extend_middleware(self.source_id, middleware). Агент выравнивает эти дополнения с помощью context.get_middleware() и применяет их в порядке провайдера перед вызовом клиента чата.
Провайдер пользовательской истории действий
Поставщики истории — это поставщики контекста, специализированные для загрузки и хранения сообщений.
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(self.source_id, {}).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(self.source_id, {}).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])
Это важно
В 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 = OpenAIChatClient().as_agent(context_providers=[primary, audit])