Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A tárterület szabályozza, hogy hol vannak tárolva a beszélgetési előzmények, milyen mennyiségű előzmény töltődik be, és hogyan biztosítható a munkamenetek megbízható folytatása.
Beépített tárolási módok
Az Agent Framework két normál tárolási módot támogat:
| Mode | A tárolt adatok | Tipikus használat |
|---|---|---|
| Helyi munkamenet állapota | Teljes csevegési előzmények AgentSession.state segítségével (például InMemoryHistoryProvider) |
A kiszolgálóoldali beszélgetések megőrzését nem igénylő szolgáltatások |
| Szolgáltatás által felügyelt tároló | Beszélgetés állapota a szolgáltatásban; AgentSession.service_session_id rámutat rá |
Natív állandó beszélgetési támogatással rendelkező szolgáltatások |
Memóriabeli csevegési előzmények tárolója
Ha egy szolgáltatónak nincs szüksége kiszolgálóoldali csevegési előzményekre, az Agent Framework helyben tárolja az előzményeket a munkamenetben, és minden futtatáskor releváns üzeneteket küld.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(instructions: "You are a helpful assistant.", name: "Assistant");
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
// When in-memory chat history storage is used, it's possible to access the chat history
// that is stored in the session via the provider attached to the agent.
var provider = agent.GetService<InMemoryChatHistoryProvider>();
List<ChatMessage>? messages = provider?.GetMessages(session);
from agent_framework import InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient
agent = OpenAIChatClient().as_agent(
name="StorageAgent",
instructions="You are a helpful assistant.",
context_providers=[InMemoryHistoryProvider("memory", load_messages=True)],
)
session = agent.create_session()
await agent.run("Remember that I like Italian food.", session=session)
A Go a helyi csevegési előzményeket a(z) agent.Session fájlban egy agent.HistoryProvider segítségével tárolja. Ha nem konfigurál előzményszolgáltatót, az Agent Framework létrehoz egy alapértelmezett memóriabeli szolgáltatót, amelyet egy explicit helyi munkamenet átadásakor használ. Konfiguráljon egyet explicit módon, ha stabil forrásazonosítót vagy egyéni szűrőket szeretne.
history := agent.NewInMemoryHistoryProvider(agent.InMemoryHistoryProviderConfig{
SourceID: "chat_history",
})
a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "You are a helpful assistant.",
Config: agent.Config{
Name: "StorageAgent",
HistoryProvider: history,
},
})
session, err := a.CreateSession(ctx)
if err != nil {
panic(err)
}
_, err = a.RunText(ctx, "Remember that I like Italian food.", agent.WithSession(session)).Collect()
_, err = a.RunText(ctx, "What kind of food do I like?", agent.WithSession(session)).Collect()
Memóriabeli előzmények méretének csökkentése
Ha az előzmények túl nagyok a modell korlátaihoz, alkalmazzon egy szűkítőt.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(new ChatClientAgentOptions
{
Name = "Assistant",
ChatOptions = new() { Instructions = "You are a helpful assistant." },
ChatHistoryProvider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions
{
ChatReducer = new MessageCountingChatReducer(20)
})
});
Szűrővel HistoryProvider korlátozhatja a következő kérésbe betöltött előzményüzeneteket. Például csak a legutóbbi 20 előzményüzenetet tartsa meg:
history := agent.NewInMemoryHistoryProvider(agent.InMemoryHistoryProviderConfig{
SourceID: "chat_history",
ProvideOutputMessageFilter: func(_ context.Context, messages []*message.Message) ([]*message.Message, error) {
if len(messages) <= 20 {
return messages, nil
}
return messages[len(messages)-20:], nil
},
})
Szemantikai vagy jogkivonat-tudatos csökkentéshez használjon tömörítési stratégiát a futtatás előtt, és ne csak az üzenetszámra támaszkodjon.
Megjegyzés:
A redukciós konfiguráció a memóriaelőzmény-szolgáltatókra vonatkozik. A szolgáltatás által felügyelt előzmények esetében a csökkentési viselkedés szolgáltatói/szolgáltatásspecifikus.
Szolgáltatás által felügyelt tároló
Amikor a szolgáltatás kezeli a beszélgetési előzményeket, a munkamenet egy távoli beszélgetés-azonosítót tárol.
OpenAI-válaszok és -beszélgetések esetén alapértelmezés szerint a szolgáltatásoldali azonosítók, például resp_* átlátszatlanok és conv_* hatóköre a háttér API-kulcsra vagy projektre terjed ki. Ez általában akkor elegendő, ha a kulcs vagy projekt már egy alkalmazásra, felhasználóra vagy bérlőre terjed ki. Ha több végfelhasználónak is üzemeltet egy ügynököt ugyanazzal a háttérkulccsal vagy projekttel, tartsa ezeket az azonosítókat megbízható kiszolgálóoldali tárolóban, képezze le őket a saját munkamenet-azonosítóiból, és ellenőrizze a tulajdonjogot a beszélgetés folytatása előtt.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetOpenAIResponseClient(modelName)
.AsAIAgent(instructions: "You are a helpful assistant.", name: "Assistant");
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
// In this case, since we know we are working with a ChatClientAgent, we can cast
// the AgentSession to a ChatClientAgentSession to retrieve the remote conversation
// identifier.
ChatClientAgentSession typedSession = (ChatClientAgentSession)session;
Console.WriteLine(typedSession.ConversationId);
# Rehydrate when the service already has the conversation state.
session = agent.get_session(service_session_id="<service-conversation-id>")
response = await agent.run("Continue this conversation.", session=session)
A Go a szolgáltatóspecifikus beszélgetési azonosítókat tárolja a következőben session.ServiceID(): . Hozzon létre egy munkamenetet egy meglévő szolgáltatásbeszélgetés-azonosító használatával, ha folytatni szeretné a szolgáltatás által kezelt előzményeket:
session, err := a.CreateSession(ctx, agent.WithServiceID("<service-conversation-id>"))
if err != nil {
panic(err)
}
_, err = a.RunText(ctx, "Continue this conversation.", agent.WithSession(session)).Collect()
Amikor egy szolgáltató futás közben létrehozza vagy frissíti a távoli beszélgetés azonosítót, a munkamenet frissül, és a hívás után megvizsgálhatja:
fmt.Println(session.ServiceID())
A konfigurált helyelőzmény-szolgáltatókat a rendszer kihagyja a szolgáltatás által felügyelt munkamenetekhez, így a szolgáltatás továbbra is a beszélgetési előzmények forrása marad.
Szolgáltatásonkénti hívás helyi előzményeinek megőrzése
Az eszközhívási futtatások több modellhívást is kezdeményezhetnek, mielőtt egyetlen agent.run() művelet befejeződik. Alapértelmezés szerint a helyelőzmény-szolgáltatók a teljes futtatás után megőrzik az adatokat. Ha azt szeretné, hogy a helyi előzmények jobban tükrözik a szolgáltatás által felügyelt beszélgetéseket, állítsa be require_per_service_call_history_persistence=True , hogy az előzményszolgáltatók fussanak az egyes modellhívások között.
from agent_framework import Agent, InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient
agent = Agent(
client=OpenAIChatClient(),
name="StorageAgent",
instructions="You are a helpful assistant.",
context_providers=[InMemoryHistoryProvider("memory", load_messages=True)],
require_per_service_call_history_persistence=True,
)
Important
Ezt a módot csak a keretrendszer által felügyelt helyi előzményekhez használja. Ha a futtatás már egy szolgáltatás által felügyelt beszélgetéshez van kötve (például a session.service_session_id vagy options={"conversation_id": ...} által), az Agent Framework hibát jelez ahelyett, hogy a két adatmegőrzési modellt összekeverné.
Ez a mód különösen akkor hasznos, ha a köztes szoftver azonnal leállhat egy eszközhívás után: a modellalapú hívásonkénti adatmegőrzés a helyi előzményeket a szolgáltatás által felügyelt beszélgetések megtartásához igazítja.
A Go előzménykezelő szolgáltatói egy ügynökhívás előtt és után futnak. Nincs külön szolgáltatásonkénti hívásmegtartási kapcsoló; ha egy eszközhurok több szolgáltatói hívást is indít egy futtatáson belül, a teljes futtatás után őrizze meg a helyi előzményeket, vagy implementáljon egy egyéni szolgáltatót/köztes szoftvert az alkalmazás tárolási igényeihez.
Külső/egyéni tárolási szabvány
Adatbázis-/Redis-/blobalapú előzmények esetén implementáljon egy egyéni előzményszolgáltatót.
Fő útmutató:
- Üzenetek tárolása egy munkamenet-hatókörű kulcs segítségével.
- A visszaadott előzményeket tartsa a modell környezeti korlátai között.
- A szolgáltatóspecifikus azonosítók megőrzése a munkamenet állapotában.
Az előzményszolgáltatók alaposztálya a Microsoft.Agents.AI.ChatHistoryProvider.
Az előzményszolgáltatók részt vesznek az ügynökfolyamatban, közreműködhetnek vagy felülbírálhatnak ügynökbemeneti üzeneteket, és új üzeneteket tárolhatnak.
ChatHistoryProvider különböző virtuális módszerekkel rendelkezik, amelyeket felül lehet bírálni a saját egyéni előzményszolgáltató implementálásához.
Az alábbi megvalósítási lehetőségek bemutatják, hogy mit kell felülbírálni.
ChatHistoryProvider Állapot
Egy ChatHistoryProvider példány egy ügynökhöz van csatolva, és minden munkamenethez ugyanazt a példányt használná.
Ez azt jelenti, hogy a ChatHistoryProvider szolgáltatás nem tárol munkamenet-specifikus állapotot a szolgáltatópéldányban.
Előfordulhat, hogy a ChatHistoryProvider mezőben van egy adatbázis-ügyfélre mutató hivatkozás, de nem tartalmazhat adatbáziskulcsot a csevegési előzményekhez.
Ehelyett a ChatHistoryProvider munkamenet-specifikus értékeket, például adatbáziskulcsokat, üzeneteket vagy bármi mást tárolhat, amely önmagában AgentSession releváns. A virtuális metódusok ChatHistoryProvider mindegyike kap egy hivatkozást az aktuális AIAgent és AgentSession.
A gépelt állapot AgentSessionegyszerű tárolásának engedélyezéséhez a rendszer egy segédprogramosztályt biztosít:
// First define a type containing the properties to store in state
internal class MyCustomState
{
public string? DbKey { get; set; }
}
// Create the helper
var sessionStateHelper = new ProviderSessionState<MyCustomState>(
// stateInitializer is called when there is no state in the session for this ChatHistoryProvider yet
stateInitializer: currentSession => new MyCustomState() { DbKey = 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.DbKey);
// And write state:
sessionStateHelper.SaveState(session, state);
Egyszerű ChatHistoryProvider megvalósítás
A legegyszerűbb ChatHistoryProvider implementáció általában két módszert bírál felül:
- ChatHistoryProvider.ProvideChatHistoryAsync – Töltse be a vonatkozó csevegési előzményeket, és adja vissza a betöltött üzeneteket.
- ChatHistoryProvider.StoreChatHistoryAsync – A kérések és a válaszüzenetek tárolása, amelyek mindegyikének újnak kell lennie.
Íme egy példa egy egyszerűre ChatHistoryProvider , amely a csevegési előzményeket közvetlenül a munkamenet állapotában tárolja.
public sealed class SimpleInMemoryChatHistoryProvider : ChatHistoryProvider
{
private readonly ProviderSessionState<State> _sessionState;
public SimpleInMemoryChatHistoryProvider(
Func<AgentSession?, State>? stateInitializer = null,
string? stateKey = null)
{
this._sessionState = new ProviderSessionState<State>(
stateInitializer ?? (_ => new State()),
stateKey ?? this.GetType().Name);
}
public override string StateKey => this._sessionState.StateKey;
protected override ValueTask<IEnumerable<ChatMessage>> ProvideChatHistoryAsync(InvokingContext context, CancellationToken cancellationToken = default) =>
// return all messages in the session state
new(this._sessionState.GetOrInitializeState(context.Session).Messages);
protected override ValueTask StoreChatHistoryAsync(InvokedContext context, CancellationToken cancellationToken = default)
{
var state = this._sessionState.GetOrInitializeState(context.Session);
// Add both request and response messages to the session state.
var allNewMessages = context.RequestMessages.Concat(context.ResponseMessages ?? []);
state.Messages.AddRange(allNewMessages);
this._sessionState.SaveState(context.Session, state);
return default;
}
public sealed class State
{
[JsonPropertyName("messages")]
public List<ChatMessage> Messages { get; set; } = [];
}
}
Fejlett ChatHistoryProvider megvalósítás
Egy fejlettebb implementáció dönthet úgy, hogy felülbírálja a következő módszereket:
- ChatHistoryProvider.InvokingCoreAsync – Ezt a függvényt az ügynök az LLM meghívása előtt hívja meg, lehetővé téve a kérelem üzenetlistájának módosítását.
- ChatHistoryProvider.InvokedCoreAsync – Azt követően hívja meg, hogy az ügynök meghívta az LLM-et, és lehetővé teszi az összes kérés- és válaszüzenet elérését.
ChatHistoryProvider a InvokingCoreAsync és a InvokedCoreAsync alap implementációit biztosítja.
Az InvokingCoreAsync alap implementáció a következőket végzi el:
- A
ProvideChatHistoryAsynchívás az üzenetek lekérésére szolgál, amelyek a futtatás csevegési előzményeiként használatosak. - Egy nem kötelező szűrőt
FuncprovideOutputMessageFilterfuttat azProvideChatHistoryAsyncáltal visszaadott üzeneteken. Ez a szűrőFuncaChatHistoryProviderkonstruktoron keresztül adható meg. - Az ügynök kérési üzeneteinek létrehozása érdekében egyesíti a hívó által az ügynöknek átadott üzeneteket a
ProvideChatHistoryAsyncáltal visszaadott szűrt üzenetekkel. A csevegőelőzmények előre fel lesznek függve az ügynök bemeneti üzeneteihez. - A csevegési előzményekből származó üzenetek szűrve és forrásinformációkkal ellátva kerülnek visszaadásra, amelyeket az
ProvideChatHistoryAsyncbélyegez meg.
Az InvokedCoreAsync alap a következőket teszi:
- ellenőrzi, hogy a futtatás sikertelen volt-e, és ha igen, további feldolgozás nélkül tér vissza.
- szűri az ügynök kérését tartalmazó üzeneteket, hogy kizárja azokat az üzeneteket, amelyeket egy
ChatHistoryProviderállított elő, mivel csak az új üzeneteket szeretnénk tárolni, nem pedig azokat, amelyeket aChatHistoryProvidereredetileg állított elő. Vegye figyelembe, hogy ezt a szűrőt felülírhatja astoreInputMessageFilterparaméterén keresztül aChatHistoryProviderkonstruktorban. - továbbítja a szűrt kérésüzeneteket és az összes válaszüzenetet a
StoreChatHistoryAsynctárhelyre.
Ezeket a metódusokat felül lehet bírálni egy ChatHistoryProviderimplementáláshoz, de ehhez a implementálónak saját maga kell implementálnia az alapfunkciót.
Íme egy példa egy ilyen megvalósításra.
public sealed class AdvancedInMemoryChatHistoryProvider : ChatHistoryProvider
{
private readonly ProviderSessionState<State> _sessionState;
public AdvancedInMemoryChatHistoryProvider(
Func<AgentSession?, State>? stateInitializer = null,
string? stateKey = null)
{
this._sessionState = new ProviderSessionState<State>(
stateInitializer ?? (_ => new State()),
stateKey ?? this.GetType().Name);
}
public override string StateKey => this._sessionState.StateKey;
protected override ValueTask<IEnumerable<ChatMessage>> InvokingCoreAsync(InvokingContext context, CancellationToken cancellationToken = default)
{
// Retrieve the chat history from the session state.
var chatHistory = this._sessionState.GetOrInitializeState(context.Session).Messages;
// Stamp the messages with this class as the source, so that they can be filtered out later if needed when storing the agent input/output.
var stampedChatHistory = chatHistory.Select(message => message.WithAgentRequestMessageSource(AgentRequestMessageSourceType.ChatHistory, this.GetType().FullName!));
// Merge the original input with the chat history to produce a combined agent input.
return new(stampedChatHistory.Concat(context.RequestMessages));
}
protected override ValueTask InvokedCoreAsync(InvokedContext context, CancellationToken cancellationToken = default)
{
if (context.InvokeException is not null)
{
return default;
}
// Since we are receiving all messages that were contributed earlier, including those from chat history, we need to filter out the messages that came from chat history
// so that we don't store message we already have in storage.
var filteredRequestMessages = context.RequestMessages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory);
var state = this._sessionState.GetOrInitializeState(context.Session);
// Add both request and response messages to the state.
var allNewMessages = filteredRequestMessages.Concat(context.ResponseMessages ?? []);
state.Messages.AddRange(allNewMessages);
this._sessionState.SaveState(context.Session, state);
return default;
}
public sealed class State
{
[JsonPropertyName("messages")]
public List<ChatMessage> Messages { get; set; } = [];
}
}
- Pythonban csak egy előzményszolgáltatónak kellene használnia
load_messages=True.
from agent_framework.openai import OpenAIChatClient
history = DatabaseHistoryProvider(db_client)
agent = OpenAIChatClient().as_agent(
name="StorageAgent",
instructions="You are a helpful assistant.",
context_providers=[history],
)
session = agent.create_session()
await agent.run("Store this conversation.", session=session)
A Go-ban akkor implementálhatja agent.HistoryProvider , ha adatbázis-, Redis-, blob- vagy fájlalapú előzményeket szeretne használni. A agent.NewHistoryProvider által létrehozott alapértelmezett segéd betölti a korábbi üzeneteket a(z) Provide helyről, és tárolja az új kérési és válaszüzeneteket a(z) Store helyen. Tartsa meg a tárkulcsokat a munkamenetben, hogy a szolgáltatópéldány újra felhasználható legyen a munkamenetek között.
import (
"context"
"fmt"
"time"
"github.com/microsoft/agent-framework-go/agent"
"github.com/microsoft/agent-framework-go/message"
)
type MessageStore interface {
LoadMessages(context.Context, string) ([]*message.Message, error)
AppendMessages(context.Context, string, []*message.Message) error
}
func NewDatabaseHistoryProvider(store MessageStore) agent.HistoryProvider {
const stateKey = "database_history.key"
historyKey := func(session *agent.Session) string {
var key string
if ok, _ := session.Get(stateKey, &key); ok && key != "" {
return key
}
key = fmt.Sprintf("history-%d", time.Now().UnixNano())
session.Set(stateKey, key)
return key
}
return agent.NewHistoryProvider(agent.HistoryProviderConfig{
SourceID: "database_history",
Provide: func(ctx context.Context, invoking agent.InvokingContext) ([]*message.Message, error) {
session, _ := agent.GetOption(invoking.Options, agent.WithSession)
if session == nil {
return nil, nil
}
return store.LoadMessages(ctx, historyKey(session))
},
Store: func(ctx context.Context, invoked agent.InvokedContext) error {
session, _ := agent.GetOption(invoked.Options, agent.WithSession)
if session == nil {
return nil
}
allMessages := make([]*message.Message, 0, len(invoked.RequestMessages)+len(invoked.ResponseMessages))
allMessages = append(allMessages, invoked.RequestMessages...)
allMessages = append(allMessages, invoked.ResponseMessages...)
return store.AppendMessages(ctx, historyKey(session), allMessages)
},
})
}
Rendelje hozzá az egyéni szolgáltatót az ügynökhöz:
a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "You are a helpful assistant.",
Config: agent.Config{
Name: "StorageAgent",
HistoryProvider: NewDatabaseHistoryProvider(store),
},
})
Ne egyesítse a konfigurált helyi HistoryProvider beállításokat szolgáltatás által felügyelt munkamenettel. Használjon helyi előzménytárat vagy a szolgáltató távoli beszélgetési állapotát egy adott munkamenethez.
Munkamenetek folytatása újraindítások között
A teljes munkamenet-objektum megőrzése, nem csak az üzenet szövege.
JsonElement serialized = agent.SerializeSession(session);
// Store serialized payload in durable storage.
AgentSession resumed = await agent.DeserializeSessionAsync(serialized);
serialized = session.to_dict()
# Store serialized payload in durable storage.
resumed = AgentSession.from_dict(serialized)
A munkamenetek JSON-szerializálással tárolhatók. Ne csak az agent.Sessionüzenetszöveget vagy az előzménykulcsot tárolja.
data, err := json.Marshal(session)
if err != nil {
panic(err)
}
if err := os.WriteFile("session.json", data, 0o644); err != nil {
panic(err)
}
loaded, err := os.ReadFile("session.json")
if err != nil {
panic(err)
}
var resumed agent.Session
if err := json.Unmarshal(loaded, &resumed); err != nil {
panic(err)
}
_, err = a.RunText(ctx, "Continue this conversation.", agent.WithSession(&resumed)).Collect()
Adatbázis-alapú tárolás esetén szerializálja a munkamenetet []byte , és tárolja az előnyben részesített háttérrendszerrel:
data, _ := json.Marshal(session)
db.Set(sessionID, data)
data, _ := db.Get(sessionID)
var resumed agent.Session
_ = json.Unmarshal(data, &resumed)
Tip
A teljes példáért tekintse meg a harmadik fél munkamenet-tárolási mintáját .
Important
Kezelje AgentSession átlátszatlan állapotobjektumként, és állítsa vissza ugyanazzal az ügynök-/szolgáltatói konfigurációval, amely létrehozta. A szerializált munkameneteket és a szolgáltatásoldali munkamenetazonosítókat megbízható alkalmazásállapotként tárolja. Üzemeltetett vagy több-bérlős alkalmazásokban minden tárolt munkamenetet kössön a hitelesített felhasználóhoz vagy bérlőhöz, mielőtt engedélyezi a folytatást.
Tip
Használjon egy további auditelőzmény-értékelési szolgáltatót (load_messages=False, store_context_messages=True) a gazdagabb kontextus és a bemenet/kimenet rögzítéséhez, anélkül hogy ez befolyásolná az elsődleges előzménytöltést.