Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
Ez az oktatóanyag bemutatja, hogyan adhat memóriát egy ügynökhöz egy AIContextProvider implementálásával, amit az ügynökhöz csatolunk.
Fontos
Nem minden ügynöktípus támogatja a AIContextProvider. Ez a lépés egy ChatClientAgent, amely támogatja a AIContextProvider-et.
Előfeltételek
Az előfeltételekről és a NuGet-csomagok telepítéséről lásd az egyszerű ügynök létrehozása és futtatása lépést ebben az oktatóanyagban.
AIContextProvider létrehozása
AIContextProvider egy absztrakt osztály, amelyből örökölhet, és amely társítható a AgentThread egy ChatClientAgent számára.
Lehetővé teszi, hogy:
- Futtassa az egyéni logikát azelőtt és azután, hogy az ügynök meghívja az alapját képező következtetési szolgáltatást.
- Adjon meg további környezetet az ügynöknek, mielőtt meghívja az alapul szolgáló következtetési szolgáltatást.
- Vizsgálja meg az ügynök által biztosított és létrehozott összes üzenetet.
Meghívás előtti és utáni események
Az AIContextProvider osztály két metódussal rendelkezik, amelyeket felülbírálhat az egyéni logika futtatásához, mielőtt az ügynök meghívja az alapul szolgáló következtetési szolgáltatást:
-
InvokingAsync- mielőtt az ügynök meghívja az alapul szolgáló következtetési szolgáltatást. További környezetet adhat meg az ügynöknek egyAIContextobjektum visszaadásával. Ez a környezet az alapul szolgáló szolgáltatás meghívása előtt egyesül az ügynök meglévő környezetével. A kéréshez hozzáadandó utasításokat, eszközöket és üzeneteket is megadhat. -
InvokedAsync- miután az ügynök választ kapott az alapul szolgáló következtetési szolgáltatástól. Megvizsgálhatja a kérés- és válaszüzeneteket, és frissítheti a környezetszolgáltató állapotát.
Szerializáció
AIContextProvider példányok jönnek létre és lesznek csatolva egy AgentThread-hoz, amikor a szál létrejön, és amikor a szál szerializált állapotból újraindul.
Lehet, hogy a AIContextProvider példánynak saját állapota van, amelyet meg kell őrizni különböző meghívások során. Előfordulhat például, hogy egy memóriaösszetevő, amely emlékszik a felhasználóra vonatkozó információkra, az állapot részeként memóriával rendelkezhet.
A szálak megőrzésének engedélyezéséhez implementálnia kell a SerializeAsync osztály AIContextProvider metódusát. Olyan konstruktort is meg kell adnia, amely egy JsonElement paramétert használ, amely a szál folytatásakor az állapot deszerializálására használható.
Minta AIContextProvider-implementáció
Az egyéni memóriaösszetevő alábbi példája megjegyzi a felhasználó nevét és életkorát, és minden egyes meghívás előtt megadja az ügynöknek.
Először hozzon létre egy modellosztályt az emlékek tárolásához.
internal sealed class UserInfo
{
public string? UserName { get; set; }
public int? UserAge { get; set; }
}
Ezután megvalósíthatja az AIContextProvider emlékek kezelését.
Az UserInfoMemory alábbi osztály a következő viselkedést tartalmazza:
-
IChatClientSegítségével megkeresi a felhasználó nevét és életkorát a felhasználói üzenetekben, amikor minden futtatás végén új üzeneteket adnak hozzá a szálhoz. - Minden egyes meghívás előtt az ügynöknek biztosít minden aktuális emléket.
- Ha nem állnak rendelkezésre emlékek, arra utasítja az ügynököt, hogy kérje meg a felhasználót a hiányzó információkért, és ne válaszoljon semmilyen kérdésre, amíg meg nem adja az információt.
- Szerializálást is implementál, hogy lehetővé tegye az emlékek megőrzését a szálállapot részeként.
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);
}
}
Az AIContextProvider használata ügynökkel
Az egyéni AIContextProvider használathoz meg kell adnia egy AIContextProviderFactory az ügynök létrehozásakor. Ez a gyár lehetővé teszi az ügynök számára, hogy minden szálhoz egy új példányt hozzon létre a kívánt AIContextProvider-ből.
Amikor létrehoz egy ChatClientAgent, megadhat egy ChatClientAgentOptions objektumot, amely lehetővé teszi az AIContextProviderFactory biztosítását minden egyéb ügynökbeállítás mellett.
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()
{
ChatOptions = new() { Instructions = "You are a friendly assistant. Always address the user by their name." },
AIContextProviderFactory = ctx => new UserInfoMemory(
chatClient.AsIChatClient(),
ctx.SerializedState,
ctx.JsonSerializerOptions)
});
Új szál létrehozásakor a AIContextProvider a GetNewThread által lesz létrehozva és csatolva a szálhoz. Miután az emlékek kinyerésre kerültek, a GetService metódus segítségével elérhető a memória komponens, és megvizsgálhatók az emlékek.
// 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}");
Ez az oktatóanyag bemutatja, hogyan adhat memóriát egy ügynökhöz úgy, hogy megvalósít egy ContextProvider-t, és csatolja az ügynökhöz.
Fontos
Nem minden ügynöktípus támogatja a ContextProvider. Ez a lépés egy ChatAgent, amely támogatja a ContextProvider-et.
Előfeltételek
Az előfeltételekről és a csomagok telepítéséről lásd a Egyszerű ügynök létrehozása és futtatása lépést ebben az oktatóanyagban.
ContextProvider létrehozása
ContextProvider egy absztrakt osztály, amelytől örökölhet, és amely társítható egy AgentThread egy ChatAgent számára.
Lehetővé teszi, hogy:
- Hajtsa végre az egyéni logikát azelőtt és azután, hogy az ügynök meghívja az alapul szolgáló következtetési szolgáltatást.
- Adjon meg további környezetet az ügynöknek, mielőtt meghívja az alapul szolgáló következtetési szolgáltatást.
- Vizsgálja meg az ügynök által biztosított és létrehozott összes üzenetet.
Meghívás előtti és utáni események
Az ContextProvider osztály két metódussal rendelkezik, amelyeket felülbírálhat az egyéni logika futtatásához, mielőtt az ügynök meghívja az alapul szolgáló következtetési szolgáltatást:
-
invoking- mielőtt az ügynök meghívja az alapul szolgáló következtetési szolgáltatást. További környezetet adhat meg az ügynöknek egyContextobjektum visszaadásával. Ez a környezet az alapul szolgáló szolgáltatás meghívása előtt egyesül az ügynök meglévő környezetével. A kéréshez hozzáadandó utasításokat, eszközöket és üzeneteket is megadhat. -
invoked- miután az ügynök választ kapott az alapul szolgáló következtetési szolgáltatástól. Megvizsgálhatja a kérés- és válaszüzeneteket, és frissítheti a környezetszolgáltató állapotát.
Szerializáció
ContextProvider példányok jönnek létre és lesznek csatolva egy AgentThread-hoz, amikor a szál létrejön, és amikor a szál szerializált állapotból újraindul.
Lehet, hogy a ContextProvider példánynak saját állapota van, amelyet meg kell őrizni különböző meghívások során. Előfordulhat például, hogy egy memóriaösszetevő, amely emlékszik a felhasználóra vonatkozó információkra, az állapot részeként memóriával rendelkezhet.
A szálak megőrzésének engedélyezéséhez szerializálást kell implementálnia az ContextProvider osztályhoz. Olyan konstruktort is meg kell adnia, amely a szál folytatásakor visszaállíthatja az állapotot szerializált adatokból.
Sample ContextProvider implementáció
Az egyéni memóriaösszetevő alábbi példája megjegyzi a felhasználó nevét és életkorát, és minden egyes meghívás előtt megadja az ügynöknek.
Először hozzon létre egy modellosztályt az emlékek tárolásához.
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str | None = None
age: int | None = None
Ezután megvalósíthatja az ContextProvider emlékek kezelését.
Az UserInfoMemory alábbi osztály a következő viselkedést tartalmazza:
- A csevegőügyfél használatával megkeresi a felhasználó nevét és életkorát a felhasználói üzenetekben, amikor minden futtatás végén új üzeneteket adnak hozzá a szálhoz.
- Minden egyes meghívás előtt az ügynöknek biztosít minden aktuális emléket.
- Ha nem állnak rendelkezésre emlékek, arra utasítja az ügynököt, hogy kérje meg a felhasználót a hiányzó információkért, és ne válaszoljon semmilyen kérdésre, amíg meg nem adja az információt.
- Szerializálást is implementál, hogy lehetővé tegye az emlékek megőrzését a szálállapot részeként.
from collections.abc import MutableSequence, Sequence
from typing import Any
from agent_framework import ContextProvider, Context, ChatAgent, ChatClientProtocol, ChatMessage, ChatOptions
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."""
# Ensure request_messages is a list
messages_list = [request_messages] if isinstance(request_messages, ChatMessage) else list(request_messages)
# Check if we need to extract user info from user messages
user_messages = [msg for msg in messages_list if 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=messages_list,
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 and isinstance(result.value, UserInfo):
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()
A ContextProvider használata ügynökkel
Az egyéni ContextProvider használatához meg kell adnia a példányosított ContextProvider az ügynök létrehozásakor.
Létrehozáskor ChatAgent megadhatja a paramétert, context_providers hogy a memóriaösszetevőt az ügynökhöz csatolja.
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(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
if thread.context_provider:
user_info_memory = thread.context_provider.providers[0]
if isinstance(user_info_memory, UserInfoMemory):
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())