客服人員聊天歷史與記憶是關鍵功能,能讓客服人員在對話間維持上下文、記住使用者偏好,並提供個人化體驗。 代理框架提供多種功能以適應不同使用情境,從簡單的記憶體內聊天訊息儲存,到持久化資料庫及專門的記憶體服務。
聊天記錄
Agent Framework 支援多種聊天記錄儲存選項。 可用的選項會因代理程式類型和用於建置代理程式的基礎服務而異。
主要支援的兩種情境是:
- 記憶體儲存:Agent 建立在不支援服務內聊天歷史儲存的服務上(例如 OpenAI 聊天完成)。 預設情況下,Agent Framework 會將完整的聊天歷史存入
AgentSession物件記憶體,但開發者若有需要,也可以提供自訂ChatHistoryProvider實作,將聊天歷史儲存在第三方儲存庫中。 -
服務內儲存:Agent 建立在需要服務內儲存聊天歷史的服務上(例如 Azure AI Foundry Persistent Agents)。 Agent Framework 將遠端聊天記錄的 ID 儲存在物件中
AgentSession,且不支援其他聊天記錄儲存選項。
記憶體內聊天記錄儲存
當使用不支援在服務內儲存聊天歷史的服務時,代理框架預設將聊天歷史存入物件的記憶體 AgentSession 中。 在這種情況下,儲存在會話物件中的完整聊天歷史,加上任何新訊息,都會在每次代理執行時提供給底層服務。 這種設計讓與代理人能自然地進行對話。 呼叫者只提供新用戶訊息,客服人員則只會回覆新的回應。 但客服可以存取完整的對話紀錄,並在生成回應時使用這些紀錄。
當使用 OpenAI 聊天完成作為代理的底層服務時,以下程式碼會產生包含代理執行聊天歷史的會話物件。
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(JokerInstructions, JokerName);
AgentSession session = await agent.GetNewSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
當訊息儲存在記憶體中時,可以從會話中擷取訊息清單,必要時直接操作訊息。
IList<ChatMessage>? messages = session.GetService<IList<ChatMessage>>();
備註
以這種方式從物件擷取訊息 AgentSession ,只有在使用記憶體儲存時才有效。
記憶體儲存的聊天歷史減少
當底層服務不支援服務內儲存時,預設使用的內建功能 InMemoryChatHistoryProvider 可以用縮小器來管理聊天歷史大小。
這有助於避免超出基礎服務的內容大小限制。
可以 InMemoryChatHistoryProvider 採取選擇性 Microsoft.Extensions.AI.IChatReducer 實作來減少聊天記錄的大小。
它還允許您配置調用 reducer 的事件,無論是在將消息添加到聊天歷史記錄之後,還是在返回聊天歷史記錄以進行下一次調用之前。
要使用減速器配置,InMemoryChatHistoryProvider您可以提供一個工廠來為每個新工廠InMemoryChatHistoryProvider構建一個新的AgentSession,並傳遞給它您選擇的減速器。 也可以 InMemoryChatHistoryProvider 傳遞一個選擇性的觸發事件,該事件可以設定為 InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded 或 InMemoryChatHistoryProvider.ChatReducerTriggerEvent.BeforeMessagesRetrieval。
工廠是一個非同步函式,接收上下文物件和消去標記。
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(new ChatClientAgentOptions
{
Name = JokerName,
ChatOptions = new() { Instructions = JokerInstructions },
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(
new InMemoryChatHistoryProvider(
new MessageCountingChatReducer(2),
ctx.SerializedState,
ctx.JsonSerializerOptions,
InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded))
});
備註
只有在使用 .InMemoryChatHistoryProvider 當服務具有服務中的聊天記錄儲存體時,由服務本身來管理聊天記錄的大小。 同樣,當使用第 3 方存儲(見下文)時,由第 3 方存儲解決方案來管理聊天記錄大小。 如果你提供 ChatHistoryProviderFactory 聊天記錄服務,但使用內建聊天記錄儲存的服務,工廠就不會被使用。
推理服務聊天記錄儲存
當使用需要在服務內儲存聊天歷史的服務時,代理框架會將遠端聊天歷史的 ID 儲存在物件中 AgentSession 。
例如,當 OpenAI 回應中 store=true 作為代理的底層服務時,以下程式碼會產生包含服務最後回傳回應 ID 的會話物件。
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetOpenAIResponseClient(modelName)
.AsAIAgent(JokerInstructions, JokerName);
AgentSession session = await agent.GetNewSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
備註
部分服務,例如 OpenAI 回應,支援在服務內儲存聊天歷史(store=true),或在每次呼叫時提供完整聊天歷史(store=false)。 因此,根據服務使用的使用模式,Agent Framework 會預設將完整聊天歷史儲存在記憶體中,或是儲存服務聊天歷史的 ID 參考。
第三方聊天記錄儲存
當使用不支援服務內聊天歷史儲存的服務時,Agent Framework 允許開發者以第三方聊天歷史儲存取代預設的記憶體儲存。 開發人員必須提供基底抽象 ChatHistoryProvider 類別的子類別。
該 ChatHistoryProvider 類定義了用於存儲和檢索聊天消息的接口。 開發人員必須實作 InvokedAsync and InvokingAsync 方法,以便在產生訊息時將訊息新增至遠端存放區,並在叫用基礎服務之前從遠端存放區擷取訊息。
代理程式將在處理使用者查詢時使用傳 InvokingAsync 回的所有訊息。 由實 ChatHistoryProvider 作者確定聊天記錄的大小不會超過基礎服務的上下文視窗。
當實作一個將聊天歷史儲存在遠端商店的自訂 ChatHistoryProvider 時,該會話的聊天記錄應該用該會話唯一的金鑰來儲存。 實作應該 ChatHistoryProvider 會產生此金鑰,並使其保持在其狀態。
ChatHistoryProvider 有一個 Serialize 方法可以覆寫,當會話序列化時將其狀態序列化。 也 ChatHistoryProvider 應該提供建構函式,以 作為 JsonElement 輸入,以支援其狀態的還原序列化。
若要將自訂ChatHistoryProviderChatClientAgent提供給 ,您可以在建立代理程式時使用該ChatHistoryProviderFactory選項。
以下是範例,示範如何將 的ChatHistoryProvider自訂實作傳遞至以 Azure OpenAI 聊天完成為基礎的 。ChatClientAgent
工廠是一個非同步函式,接收上下文物件和消去標記。
AIAgent agent = new AzureOpenAIClient(
new Uri(endpoint),
new AzureCliCredential())
.GetChatClient(deploymentName)
.AsAIAgent(new ChatClientAgentOptions
{
Name = JokerName,
ChatOptions = new() { Instructions = JokerInstructions },
ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(
// Create a new chat history provider for this agent that stores the messages in a custom store.
// Each session must get its own copy of the CustomChatHistoryProvider, since the provider
// also contains the ID that the session is stored under.
new CustomChatHistoryProvider(
vectorStore,
ctx.SerializedState,
ctx.JsonSerializerOptions))
});
小提示
如需如何建立自訂訊息存放區的詳細範例,請參閱在 第三方儲存體中儲存聊天記錄 教學課程。
長期記憶
代理框架允許開發人員提供自訂元件,這些元件可以提取記憶或向代理提供記憶。
若要實作這樣的記憶體元件,開發人員需要將抽象基類子 AIContextProvider 類別化。 這個類別有兩個核心方法, InvokingAsync 以及 InvokedAsync。 覆寫時, InvokedAsync 允許開發人員檢查使用者提供或代理程式產生的所有訊息。
InvokingAsync 允許開發人員為特定代理程式執行注入額外的內容。 可以提供系統說明、附加消息和附加功能。
小提示
如需如何建立自訂記憶體元件的詳細範例,請參閱將 記憶體新增至代理程式 教學課程。
代理會話序列化
能夠在代理程式呼叫之間保存物件 AgentSession 非常重要。 這讓使用者可以向客服提問,並花很長時間才提出後續問題。 這可讓狀態在 AgentSession 服務或應用程式重新啟動後繼續運作。
即使聊天記錄儲存在遠端儲存庫,物件 AgentSession 仍包含指向遠端聊天記錄的 ID。 因此,失去 AgentSession 州名也會導致遠端聊天紀錄的 ID 也隨之消失。
因此,以及 AgentSession 附加到它的任何物件都提供了 Serialize 序列化其狀態的方法。 同時 AIAgent 也提供 DeserializeSessionAsync 一種方法,能從序列化狀態重建會話。 該DeserializeSessionAsync方法會重新建立 agent 與 並設定ChatHistoryProviderAIContextProvider的會話。
// Serialize the session state to a JsonElement, so it can be stored for later use.
JsonElement serializedSessionState = session.Serialize();
// Re-create the session from the JsonElement.
AgentSession resumedSession = await agent.DeserializeSessionAsync(serializedSessionState);
備註
AgentSession 物件可能不只包含聊天歷史,例如上下文提供者也可能在會話物件中儲存狀態。 因此,請務必一律序列化、儲存和還原序列化整個 AgentSession 物件,以確保保留所有狀態。
這很重要
始終將物體視為 AgentSession 不透明的物體,除非您非常確定內部結構。 內容可能不僅因代理類型而異,還因服務類型和配置而異。
警告
將與原始代理不同的代理進行反序列化,或使用配置與原始代理不同的代理,可能會導致錯誤或意外行為。
記憶體類型
代理框架支援多種類型的記憶體以適應不同的用例,包括將聊天記錄作為短期記憶的一部分進行管理,以及提供用於提取、儲存和注入代理的擴展點。
In-Memory 儲存體 (預設)
最簡單的記憶體形式,其中交談歷程記錄會在應用程式執行階段期間儲存在記憶體中。 這是預設行為,不需要額外的設定。
from agent_framework import ChatAgent
from agent_framework.openai import OpenAIChatClient
# Default behavior - uses in-memory storage
agent = ChatAgent(
chat_client=OpenAIChatClient(),
instructions="You are a helpful assistant."
)
# Conversation history is maintained in memory for this thread
thread = agent.get_new_thread()
response = await agent.run("Hello, my name is Alice", thread=thread)
持續性訊息存放區
對於需要跨會話保存對話歷史記錄的應用程序,框架提供了 ChatMessageStore 實現:
內建 ChatMessageStore
可序列化的預設記憶體內實作:
from agent_framework import ChatMessageStore
# Create a custom message store
def create_message_store():
return ChatMessageStore()
agent = ChatAgent(
chat_client=OpenAIChatClient(),
instructions="You are a helpful assistant.",
chat_message_store_factory=create_message_store
)
Redis 訊息存放區
對於需要持續儲存的生產應用程式:
from agent_framework.redis import RedisChatMessageStore
def create_redis_store():
return RedisChatMessageStore(
redis_url="redis://localhost:6379",
thread_id="user_session_123",
max_messages=100 # Keep last 100 messages
)
agent = ChatAgent(
chat_client=OpenAIChatClient(),
instructions="You are a helpful assistant.",
chat_message_store_factory=create_redis_store
)
自訂訊息存放區
您可以實作自己的儲存後端,方法是實作 ChatMessageStoreProtocol:
from agent_framework import ChatMessage, ChatMessageStoreProtocol
from typing import Any
from collections.abc import Sequence
class DatabaseMessageStore(ChatMessageStoreProtocol):
def __init__(self, connection_string: str):
self.connection_string = connection_string
self._messages: list[ChatMessage] = []
async def add_messages(self, messages: Sequence[ChatMessage]) -> None:
"""Add messages to database."""
# Implement database insertion logic
self._messages.extend(messages)
async def list_messages(self) -> list[ChatMessage]:
"""Retrieve messages from database."""
# Implement database query logic
return self._messages
async def serialize(self, **kwargs: Any) -> Any:
"""Serialize store state for persistence."""
return {"connection_string": self.connection_string}
async def update_from_state(self, serialized_store_state: Any, **kwargs: Any) -> None:
"""Update store from serialized state."""
if serialized_store_state:
self.connection_string = serialized_store_state["connection_string"]
小提示
如需如何建立自訂訊息存放區的詳細範例,請參閱在 第三方儲存體中儲存聊天記錄 教學課程。
內容提供者 (動態記憶體)
上下文提供者通過在每次代理呼叫之前注入相關上下文來實現複雜的記憶模式:
基本內容提供者
from agent_framework import ContextProvider, Context, ChatMessage
from collections.abc import MutableSequence
from typing import Any
class UserPreferencesMemory(ContextProvider):
def __init__(self):
self.preferences = {}
async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
"""Provide user preferences before each invocation."""
if self.preferences:
preferences_text = ", ".join([f"{k}: {v}" for k, v in self.preferences.items()])
instructions = f"User preferences: {preferences_text}"
return Context(instructions=instructions)
return Context()
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 and store user preferences from the conversation."""
# Implement preference extraction logic
pass
小提示
如需如何建立自訂記憶體元件的詳細範例,請參閱將 記憶體新增至代理程式 教學課程。
外部記憶體服務
該框架支持與 Mem0 等專用內存服務的集成:
from agent_framework.mem0 import Mem0Provider
# Using Mem0 for advanced memory capabilities
memory_provider = Mem0Provider(
api_key="your-mem0-api-key",
user_id="user_123",
application_id="my_app"
)
agent = ChatAgent(
chat_client=OpenAIChatClient(),
instructions="You are a helpful assistant with memory.",
context_providers=memory_provider
)
執行緒序列化和持久性
架構支援序列化整個執行緒狀態,以便在應用程式重新啟動時持續存在:
import json
# Create agent and thread
agent = ChatAgent(chat_client=OpenAIChatClient())
thread = agent.get_new_thread()
# Have conversation
await agent.run("Hello, my name is Alice", thread=thread)
# Serialize thread state
serialized_thread = await thread.serialize()
# Save to file/database
with open("thread_state.json", "w") as f:
json.dump(serialized_thread, f)
# Later, restore the thread
with open("thread_state.json", "r") as f:
thread_data = json.load(f)
restored_thread = await agent.deserialize_thread(thread_data)
# Continue conversation with full context
await agent.run("What's my name?", thread=restored_thread)