Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V tomto kurzu se dozvíte, jak do agenta přidat paměť implementací AIContextProvider a připojením k agentu.
Důležité
Ne všechny typy agentů podporují AIContextProvider. Tento krok používá nástroj ChatClientAgent, který skutečně podporuje AIContextProvider.
Požadavky
Informace o požadavcích a instalaci balíčků NuGet naleznete v kroku Vytvoření a spuštění jednoduchého agenta v tomto kurzu.
Vytvořte AIContextProvider
AIContextProvider je abstraktní třída, ze které můžete dědit, a která může být přidružena k objektu AgentThread for a ChatClientAgent.
Umožňuje:
- Před a po vyvolání základní služby odvozování spusťte vlastní logiku.
- Před vyvolání základní služby odvození poskytněte agentu další kontext.
- Zkontrolujte všechny zprávy poskytované agentem a produkované agentem.
Události před a po vyvolání
Třída AIContextProvider má dvě metody, které můžete přepsat tak, aby se spustila vlastní logika před a po vyvolání základní služby odvozování:
-
InvokingAsync– volá se před tím, než agent vyvolá základní inferenční službu. Další kontext pro agenta můžete poskytnout vrácením objektuAIContext. Před vyvoláním základní služby se tento kontext sloučí s existujícím kontextem agenta. Do požadavku je možné zadat pokyny, nástroje a zprávy, které se mají přidat. -
InvokedAsync– volá se po přijetí odpovědi od základní inference služby, kterou agent využívá. Můžete zkontrolovat zprávy požadavků a odpovědí a aktualizovat stav zprostředkovatele kontextu.
Serialization
AIContextProvider instance jsou vytvořeny a připojeny k AgentThread při vytvoření vlákna a když je vlákno obnoveno ze serializovaného stavu.
Instance AIContextProvider může mít svůj vlastní stav, který je potřeba zachovat mezi vyvoláním agenta. Například součást paměti, která si pamatuje informace o uživateli, může mít v rámci svého stavu vzpomínky.
Pokud chcete povolit trvalé podprocesy, musíte implementovat SerializeAsync metodu AIContextProvider třídy. Musíte také zadat konstruktor, který přebírá JsonElement parametr, který lze použít k deserializaci stavu při obnovení vlákna.
Ukázková implementace AIContextProvider
Následující příklad součásti vlastní paměti si pamatuje jméno a věk uživatele a poskytuje ho agentovi před každým vyvoláním.
Nejprve vytvořte třídu modelu, která bude uchovávat vzpomínky.
internal sealed class UserInfo
{
public string? UserName { get; set; }
public int? UserAge { get; set; }
}
Pak můžete implementovat AIContextProvider ke správě paměti.
Následující UserInfoMemory třída obsahuje následující chování:
- Při přidání nových zpráv do vlákna používá
IChatClientke hledání jména a věku uživatele na konci každého spuštění. - Poskytuje agentovi všechny aktuální vzpomínky před každým vyvoláním.
- Pokud nejsou k dispozici žádné vzpomínky, dává agentovi pokyn, aby požádal uživatele o chybějící informace, a neodpovídá na žádné otázky, dokud nebudou informace poskytnuty.
- Implementuje také serializaci, která umožňuje zachování paměti jako součást stavu vlákna.
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
internal sealed class UserInfoMemory : AIContextProvider
{
private readonly IChatClient _chatClient;
public UserInfoMemory(IChatClient chatClient, UserInfo? userInfo = null)
{
this._chatClient = chatClient;
this.UserInfo = userInfo ?? new UserInfo();
}
public UserInfoMemory(IChatClient chatClient, JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null)
{
this._chatClient = chatClient;
this.UserInfo = serializedState.ValueKind == JsonValueKind.Object ?
serializedState.Deserialize<UserInfo>(jsonSerializerOptions)! :
new UserInfo();
}
public UserInfo UserInfo { get; set; }
public override async ValueTask InvokedAsync(
InvokedContext context,
CancellationToken cancellationToken = default)
{
if ((this.UserInfo.UserName is null || this.UserInfo.UserAge is null) && context.RequestMessages.Any(x => x.Role == ChatRole.User))
{
var result = await this._chatClient.GetResponseAsync<UserInfo>(
context.RequestMessages,
new ChatOptions()
{
Instructions = "Extract the user's name and age from the message if present. If not present return nulls."
},
cancellationToken: cancellationToken);
this.UserInfo.UserName ??= result.Result.UserName;
this.UserInfo.UserAge ??= result.Result.UserAge;
}
}
public override ValueTask<AIContext> InvokingAsync(
InvokingContext context,
CancellationToken cancellationToken = default)
{
StringBuilder instructions = new();
instructions
.AppendLine(
this.UserInfo.UserName is null ?
"Ask the user for their name and politely decline to answer any questions until they provide it." :
$"The user's name is {this.UserInfo.UserName}.")
.AppendLine(
this.UserInfo.UserAge is null ?
"Ask the user for their age and politely decline to answer any questions until they provide it." :
$"The user's age is {this.UserInfo.UserAge}.");
return new ValueTask<AIContext>(new AIContext
{
Instructions = instructions.ToString()
});
}
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
{
return JsonSerializer.SerializeToElement(this.UserInfo, jsonSerializerOptions);
}
}
Použití AIContextProvider s agentem
Pokud chcete použít vlastní AIContextProvider, musíte zadat AIContextProviderFactory při vytváření agenta. Tato faktorie umožňuje agentu vytvořit novou instanci požadované AIContextProvider pro každé vlákno.
Při vytváření ChatClientAgent je možné předat objekt ChatClientAgentOptions, který umožňuje přidat AIContextProviderFactory k ostatním možnostem agenta.
using System;
using Azure.AI.OpenAI;
using Azure.Identity;
using OpenAI.Chat;
using OpenAI;
ChatClient chatClient = new AzureOpenAIClient(
new Uri("https://<myresource>.openai.azure.com"),
new AzureCliCredential())
.GetChatClient("gpt-4o-mini");
AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions()
{
Instructions = "You are a friendly assistant. Always address the user by their name.",
AIContextProviderFactory = ctx => new UserInfoMemory(
chatClient.AsIChatClient(),
ctx.SerializedState,
ctx.JsonSerializerOptions)
});
Při vytváření nového vlákna bude AIContextProvider vytvořen a připojen k tomuto vláknu pomocí GetNewThread. Jakmile se paměti extrahují, je proto možné získat přístup ke komponentě paměti prostřednictvím metody vlákna GetService a zkontrolovat paměti.
// Create a new thread for the conversation.
AgentThread thread = agent.GetNewThread();
Console.WriteLine(await agent.RunAsync("Hello, what is the square root of 9?", thread));
Console.WriteLine(await agent.RunAsync("My name is Ruaidhrí", thread));
Console.WriteLine(await agent.RunAsync("I am 20 years old", thread));
// Access the memory component via the thread's GetService method.
var userInfo = thread.GetService<UserInfoMemory>()?.UserInfo;
Console.WriteLine($"MEMORY - User Name: {userInfo?.UserName}");
Console.WriteLine($"MEMORY - User Age: {userInfo?.UserAge}");
V tomto kurzu se dozvíte, jak do agenta přidat paměť implementací ContextProvider a připojením k agentu.
Důležité
Ne všechny typy agentů podporují ContextProvider. Tento krok používá nástroj ChatAgent, který skutečně podporuje ContextProvider.
Požadavky
Informace o požadavcích a instalaci balíčků najdete v části Vytvoření a spuštění jednoduchého agenta v tomto kurzu.
Vytvoření contextprovideru
ContextProvider je abstraktní třída, ze které můžete dědit, a která může být přidružena k AgentThread pro ChatAgent.
Umožňuje:
- Před a po vyvolání základní služby odvozování spusťte vlastní logiku.
- Před vyvolání základní služby odvození poskytněte agentu další kontext.
- Zkontrolujte všechny zprávy poskytované agentem a produkované agentem.
Události před a po vyvolání
Třída ContextProvider má dvě metody, které můžete přepsat tak, aby se spustila vlastní logika před a po vyvolání základní služby odvozování:
-
invoking– volá se před tím, než agent vyvolá základní inferenční službu. Další kontext agenta můžete poskytnout vrácením objektuContext. Před vyvoláním základní služby se tento kontext sloučí s existujícím kontextem agenta. Do požadavku je možné zadat pokyny, nástroje a zprávy, které se mají přidat. -
invoked– volá se po přijetí odpovědi od základní inference služby, kterou agent využívá. Můžete zkontrolovat zprávy požadavků a odpovědí a aktualizovat stav zprostředkovatele kontextu.
Serialization
ContextProvider instance jsou vytvořeny a připojeny k AgentThread při vytvoření vlákna a když je vlákno obnoveno ze serializovaného stavu.
Instance ContextProvider může mít svůj vlastní stav, který je potřeba zachovat mezi vyvoláním agenta. Například součást paměti, která si pamatuje informace o uživateli, může mít v rámci svého stavu vzpomínky.
Chcete-li povolit trvalá vlákna, je nutné implementovat serializaci třídy ContextProvider . Musíte také poskytnout konstruktor, který může obnovit stav ze serializovaných dat při obnovení vlákna.
Ukázková implementace ContextProvider
Následující příklad součásti vlastní paměti si pamatuje jméno a věk uživatele a poskytuje ho agentovi před každým vyvoláním.
Nejprve vytvořte třídu modelu, která bude uchovávat vzpomínky.
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str | None = None
age: int | None = None
Pak můžete implementovat ContextProvider ke správě paměti.
Následující UserInfoMemory třída obsahuje následující chování:
- Používá chatovacího klienta k vyhledání jména a věku uživatele ve zprávách uživatelů při přidání nových zpráv do vlákna na konci každého spuštění.
- Poskytuje agentovi všechny aktuální vzpomínky před každým vyvoláním.
- Pokud nejsou k dispozici žádné vzpomínky, dává agentovi pokyn, aby požádal uživatele o chybějící informace, a neodpovídá na žádné otázky, dokud nebudou informace poskytnuty.
- Implementuje také serializaci, která umožňuje zachování paměti jako součást stavu vlákna.
from agent_framework import ContextProvider, Context, InvokedContext, InvokingContext, ChatAgent, ChatClientProtocol
class UserInfoMemory(ContextProvider):
def __init__(self, chat_client: ChatClientProtocol, user_info: UserInfo | None = None, **kwargs: Any):
"""Create the memory.
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
"""
self._chat_client = chat_client
if user_info:
self.user_info = user_info
elif kwargs:
self.user_info = UserInfo.model_validate(kwargs)
else:
self.user_info = UserInfo()
async def invoked(
self,
request_messages: ChatMessage | Sequence[ChatMessage],
response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
invoke_exception: Exception | None = None,
**kwargs: Any,
) -> None:
"""Extract user information from messages after each agent call."""
# Check if we need to extract user info from user messages
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role.value == "user"]
if (self.user_info.name is None or self.user_info.age is None) and user_messages:
try:
# Use the chat client to extract structured information
result = await self._chat_client.get_response(
messages=request_messages,
chat_options=ChatOptions(
instructions="Extract the user's name and age from the message if present. If not present return nulls.",
response_format=UserInfo,
),
)
# Update user info with extracted data
if result.value:
if self.user_info.name is None and result.value.name:
self.user_info.name = result.value.name
if self.user_info.age is None and result.value.age:
self.user_info.age = result.value.age
except Exception:
pass # Failed to extract, continue without updating
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
"""Provide user information context before each agent call."""
instructions: list[str] = []
if self.user_info.name is None:
instructions.append(
"Ask the user for their name and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's name is {self.user_info.name}.")
if self.user_info.age is None:
instructions.append(
"Ask the user for their age and politely decline to answer any questions until they provide it."
)
else:
instructions.append(f"The user's age is {self.user_info.age}.")
# Return context with additional instructions
return Context(instructions=" ".join(instructions))
def serialize(self) -> str:
"""Serialize the user info for thread persistence."""
return self.user_info.model_dump_json()
Použití ContextProvideru s agentem
Pokud chcete použít vlastní ContextProvider, musíte při vytváření agenta poskytnout instanci ContextProvider .
Při vytváření ChatAgent můžete zadat context_providers parametr pro připojení součásti paměti k agentu.
import asyncio
from agent_framework import ChatAgent
from agent_framework.azure import AzureAIAgentClient
from azure.identity.aio import AzureCliCredential
async def main():
async with AzureCliCredential() as credential:
chat_client = AzureAIAgentClient(async_credential=credential)
# Create the memory provider
memory_provider = UserInfoMemory(chat_client)
# Create the agent with memory
async with ChatAgent(
chat_client=chat_client,
instructions="You are a friendly assistant. Always address the user by their name.",
context_providers=memory_provider,
) as agent:
# Create a new thread for the conversation
thread = agent.get_new_thread()
print(await agent.run("Hello, what is the square root of 9?", thread=thread))
print(await agent.run("My name is Ruaidhrí", thread=thread))
print(await agent.run("I am 20 years old", thread=thread))
# Access the memory component via the thread's context_providers attribute and inspect the memories
user_info_memory = thread.context_provider.providers[0]
if user_info_memory:
print()
print(f"MEMORY - User Name: {user_info_memory.user_info.name}")
print(f"MEMORY - User Age: {user_info_memory.user_info.age}")
if __name__ == "__main__":
asyncio.run(main())