From a7269f26360c03a3ee954479bc947572548f2cba Mon Sep 17 00:00:00 2001 From: Ryan Knight Date: Tue, 17 Feb 2026 20:43:06 -0700 Subject: [PATCH 1/3] Python: Adds sample documentation for two separate Neo4j context providers for retrieval and memory --- agent-framework/agents/rag.md | 51 +++ .../user-guide/agents/agent-memory.md | 418 ++++++++++++++++++ 2 files changed, 469 insertions(+) create mode 100644 agent-framework/user-guide/agents/agent-memory.md diff --git a/agent-framework/agents/rag.md b/agent-framework/agents/rag.md index 5850b501..fdfef7a7 100644 --- a/agent-framework/agents/rag.md +++ b/agent-framework/agents/rag.md @@ -553,6 +553,57 @@ This pattern works with any Semantic Kernel VectorStore connector, including: Each connector provides the same `create_search_function` method that can be bridged to Agent Framework tools, allowing you to choose the vector database that best fits your needs. See [the full list here](/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors). +### Using Neo4j for GraphRAG + +Neo4j offers two separate integrations for Agent Framework, each serving a different purpose. This provider (`agent-framework-neo4j`) is for **GraphRAG** — searching an existing knowledge graph to ground agent responses. For **persistent memory** that learns from conversations and builds a knowledge graph over time, see the [Neo4j Memory Provider](./agent-memory.md#neo4j-memory-provider). + +For knowledge graph scenarios where relationships between entities matter, the Neo4j Context Provider offers GraphRAG. It supports vector, fulltext, and hybrid search modes, with optional graph traversal to enrich results with related entities via custom Cypher queries. + +```python +from agent_framework import Agent +from agent_framework.azure import AzureAIClient +from agent_framework_neo4j import Neo4jContextProvider, Neo4jSettings, AzureAIEmbedder +from azure.identity.aio import AzureCliCredential + +settings = Neo4jSettings() + +neo4j_provider = Neo4jContextProvider( + uri=settings.uri, + username=settings.username, + password=settings.get_password(), + index_name="documentChunks", + index_type="vector", + embedder=AzureAIEmbedder(...), + top_k=5, + retrieval_query=""" + MATCH (node)-[:FROM_DOCUMENT]->(doc:Document) + OPTIONAL MATCH (doc)<-[:FILED]-(company:Company) + RETURN node.text AS text, score, doc.title AS title, company.name AS company + ORDER BY score DESC + """, +) + +async with ( + neo4j_provider, + AzureAIClient(credential=AzureCliCredential(), project_endpoint=project_endpoint) as client, + Agent( + client=client, + instructions="You are a financial analyst assistant.", + context_providers=[neo4j_provider], + ) as agent, +): + session = agent.create_session() + response = await agent.run("What risks does Acme Corp face?", session=session) +``` + +Key features: +- **Index-driven**: Works with any Neo4j vector or fulltext index +- **Graph traversal**: Custom Cypher queries enrich search results with related entities +- **Search modes**: Vector (semantic similarity), fulltext (keyword/BM25), or hybrid (both combined) + +> [!TIP] +> Install with `pip install agent-framework-neo4j`. See the [Neo4j Context Provider repository](https://github.com/neo4j-labs/neo4j-maf-provider) for complete documentation. + ::: zone-end ## Next steps diff --git a/agent-framework/user-guide/agents/agent-memory.md b/agent-framework/user-guide/agents/agent-memory.md new file mode 100644 index 00000000..0958950c --- /dev/null +++ b/agent-framework/user-guide/agents/agent-memory.md @@ -0,0 +1,418 @@ +--- +title: Agent Chat History and Memory +description: Learn how to use chat history and memory with Agent Framework +zone_pivot_groups: programming-languages +author: markwallace +ms.topic: reference +ms.author: markwallace +ms.date: 09/24/2025 +ms.service: agent-framework +--- + +# Agent Chat History and Memory + +Agent chat history and memory are crucial capabilities that allow agents to maintain context across conversations, remember user preferences, and provide personalized experiences. The Agent Framework provides multiple features to suit different use cases, from simple in-memory chat message storage to persistent databases and specialized memory services. + +::: zone pivot="programming-language-csharp" + +## Chat History + +Various chat history storage options are supported by Agent Framework. The available options vary by agent type and the underlying service(s) used to build the agent. + +The two main supported scenarios are: + +- **In-memory storage**: Agent is built on a service that doesn't support in-service storage of chat history (for example, OpenAI Chat Completion). By default, Agent Framework stores the full chat history in-memory in the `AgentThread` object, but developers can provide a custom `ChatMessageStore` implementation to store chat history in a third-party store if required. +- **In-service storage**: Agent is built on a service that requires in-service storage of chat history (for example, Azure AI Foundry Persistent Agents). Agent Framework stores the ID of the remote chat history in the `AgentThread` object, and no other chat history storage options are supported. + +### In-memory chat history storage + +When using a service that doesn't support in-service storage of chat history, Agent Framework defaults to storing chat history in-memory in the `AgentThread` object. In this case, the full chat history that's stored in the thread object, plus any new messages, will be provided to the underlying service on each agent run. This design allows for a natural conversational experience with the agent. The caller only provides the new user message, and the agent only returns new answers. But the agent has access to the full conversation history and will use it when generating its response. + +When using OpenAI Chat Completion as the underlying service for agents, the following code results in the thread object containing the chat history from the agent run. + +```csharp +AIAgent agent = new OpenAIClient("") + .GetChatClient(modelName) + .AsAIAgent(JokerInstructions, JokerName); +AgentThread thread = await agent.GetNewThreadAsync(); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +``` + +Where messages are stored in memory, it's possible to retrieve the list of messages from the thread and manipulate the messages directly if required. + +```csharp +IList? messages = thread.GetService>(); +``` + +> [!NOTE] +> Retrieving messages from the `AgentThread` object in this way only works if in-memory storage is being used. + +#### Chat history reduction with in-memory storage + +The built-in `InMemoryChatMessageStore` that's used by default when the underlying service does not support in-service storage, +can be configured with a reducer to manage the size of the chat history. +This is useful to avoid exceeding the context size limits of the underlying service. + +The `InMemoryChatMessageStore` can take an optional `Microsoft.Extensions.AI.IChatReducer` implementation to reduce the size of the chat history. +It also allows you to configure the event during which the reducer is invoked, either after a message is added to the chat history +or before the chat history is returned for the next invocation. + +To configure the `InMemoryChatMessageStore` with a reducer, you can provide a factory to construct a new `InMemoryChatMessageStore` +for each new `AgentThread` and pass it a reducer of your choice. The `InMemoryChatMessageStore` can also be passed an optional trigger event +which can be set to either `InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded` or `InMemoryChatMessageStore.ChatReducerTriggerEvent.BeforeMessagesRetrieval`. + +The factory is an async function that receives a context object and a cancellation token. + +```csharp +AIAgent agent = new OpenAIClient("") + .GetChatClient(modelName) + .AsAIAgent(new ChatClientAgentOptions + { + Name = JokerName, + ChatOptions = new() { Instructions = JokerInstructions }, + ChatMessageStoreFactory = (ctx, ct) => new ValueTask( + new InMemoryChatMessageStore( + new MessageCountingChatReducer(2), + ctx.SerializedState, + ctx.JsonSerializerOptions, + InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded)) + }); +``` + +> [!NOTE] +> This feature is only supported when using the `InMemoryChatMessageStore`. When a service has in-service chat history storage, it is up to the service itself to manage the size of the chat history. Similarly, when using 3rd party storage (see below), it is up to the 3rd party storage solution to manage the chat history size. If you provide a `ChatMessageStoreFactory` for a message store but you use a service with built-in chat history storage, the factory will not be used. + +### Inference service chat history storage + +When using a service that requires in-service storage of chat history, Agent Framework stores the ID of the remote chat history in the `AgentThread` object. + +For example, when using OpenAI Responses with store=true as the underlying service for agents, the following code will result in the thread object containing the last response ID returned by the service. + +```csharp +AIAgent agent = new OpenAIClient("") + .GetOpenAIResponseClient(modelName) + .AsAIAgent(JokerInstructions, JokerName); +AgentThread thread = await agent.GetNewThreadAsync(); +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); +``` + +> [!NOTE] +> Some services, for example, OpenAI Responses support either in-service storage of chat history (store=true), or providing the full chat history on each invocation (store=false). +> Therefore, depending on the mode that the service is used in, Agent Framework will either default to storing the full chat history in memory, or storing an ID reference to the service stored chat history. + +### Third-party chat history storage + +When using a service that does not support in-service storage of chat history, Agent Framework allows developers to replace the default in-memory storage of chat history with third-party chat history storage. The developer is required to provide a subclass of the base abstract `ChatMessageStore` class. + +The `ChatMessageStore` class defines the interface for storing and retrieving chat messages. Developers must implement the `InvokedAsync` and `InvokingAsync` methods to add messages to the remote store as they are generated, and retrieve messages from the remote store before invoking the underlying service. + +The agent will use all messages returned by `InvokingAsync` when processing a user query. It is up to the implementer of `ChatMessageStore` to ensure that the size of the chat history does not exceed the context window of the underlying service. + +When implementing a custom `ChatMessageStore` which stores chat history in a remote store, the chat history for that thread should be stored under a key that is unique to that thread. The `ChatMessageStore` implementation should generate this key and keep it in its state. `ChatMessageStore` has a `Serialize` method that can be overridden to serialize its state when the thread is serialized. The `ChatMessageStore` should also provide a constructor that takes a as input to support deserialization of its state. + +To supply a custom `ChatMessageStore` to a `ChatClientAgent`, you can use the `ChatMessageStoreFactory` option when creating the agent. +Here is an example showing how to pass the custom implementation of `ChatMessageStore` to a `ChatClientAgent` that is based on Azure OpenAI Chat Completion. + +The factory is an async function that receives a context object and a cancellation token. + +```csharp +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetChatClient(deploymentName) + .AsAIAgent(new ChatClientAgentOptions + { + Name = JokerName, + ChatOptions = new() { Instructions = JokerInstructions }, + ChatMessageStoreFactory = (ctx, ct) => new ValueTask( + // Create a new chat message store for this agent that stores the messages in a custom store. + // Each thread must get its own copy of the CustomMessageStore, since the store + // also contains the ID that the thread is stored under. + new CustomMessageStore( + vectorStore, + ctx.SerializedState, + ctx.JsonSerializerOptions)) + }); +``` + +> [!TIP] +> For a detailed example on how to create a custom message store, see the [Storing Chat History in 3rd Party Storage](../../tutorials/agents/third-party-chat-history-storage.md) tutorial. + +## Long term memory + +The Agent Framework allows developers to provide custom components that can extract memories or provide memories to an agent. + +To implement such a memory component, the developer needs to subclass the `AIContextProvider` abstract base class. This class has two core methods, `InvokingAsync` and `InvokedAsync`. When overridden, `InvokedAsync` allows developers to inspect all messages provided by users or generated by the agent. `InvokingAsync` allows developers to inject additional context for a specific agent run. System instructions, additional messages and additional functions can be provided. + +> [!TIP] +> For a detailed example on how to create a custom memory component, see the [Adding Memory to an Agent](../../tutorials/agents/memory.md) tutorial. + +## AgentThread Serialization + +It is important to be able to persist an `AgentThread` object between agent invocations. This allows for situations where a user might ask a question of the agent, and take a long time to ask follow up questions. This allows the `AgentThread` state to survive service or app restarts. + +Even if the chat history is stored in a remote store, the `AgentThread` object still contains an ID referencing the remote chat history. Losing the `AgentThread` state will therefore result in also losing the ID of the remote chat history. + +The `AgentThread` as well as any objects attached to it, all therefore provide the `Serialize` method to serialize their state. The `AIAgent` also provides a `DeserializeThreadAsync` method that re-creates a thread from the serialized state. The `DeserializeThreadAsync` method re-creates the thread with the `ChatMessageStore` and `AIContextProvider` configured on the agent. + +```csharp +// Serialize the thread state to a JsonElement, so it can be stored for later use. +JsonElement serializedThreadState = thread.Serialize(); + +// Re-create the thread from the JsonElement. +AgentThread resumedThread = await agent.DeserializeThreadAsync(serializedThreadState); +``` + +> [!NOTE] +> `AgentThread` objects may contain more than just chat history, e.g. context providers may also store state in the thread object. Therefore, it is important to always serialize, store and deserialize the entire `AgentThread` object to ensure that all state is preserved. +> [!IMPORTANT] +> Always treat `AgentThread` objects as opaque objects, unless you are very sure of the internals. The contents may vary not just by agent type, but also by service type and configuration. +> [!WARNING] +> Deserializing a thread with a different agent than that which originally created it, or with an agent that has a different configuration than the original agent, might result in errors or unexpected behavior. + +::: zone-end +::: zone pivot="programming-language-python" + +## Memory Types + +The Agent Framework supports several types of memory to accommodate different use cases, including managing chat history as part of short term memory and providing extension points for extracting, storing and injecting long term memories into agents. + +### In-Memory Storage (Default) + +The simplest form of memory where conversation history is stored in memory during the application runtime. This is the default behavior and requires no additional configuration. + +```python +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) +``` + +### Persistent Message Stores +For applications that need to persist conversation history across sessions, the framework provides `ChatMessageStore` implementations: + +#### Built-in ChatMessageStore +The default in-memory implementation that can be serialized: + +```python +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 Message Store +For production applications requiring persistent storage: + +```python +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 +) +``` + +#### Custom Message Store +You can implement your own storage backend by implementing the `ChatMessageStoreProtocol`: + +```python +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"] +``` + +> [!TIP] +> For a detailed example on how to create a custom message store, see the [Storing Chat History in 3rd Party Storage](../../tutorials/agents/third-party-chat-history-storage.md) tutorial. + +### Context Providers (Dynamic Memory) +Context providers enable sophisticated memory patterns by injecting relevant context before each agent invocation: + +#### Basic Context Provider +```python +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 +``` + +> [!TIP] +> For a detailed example on how to create a custom memory component, see the [Adding Memory to an Agent](../../tutorials/agents/memory.md) tutorial. + +#### External Memory Services +The framework supports integration with specialized memory services like Mem0: + +```python +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 +) +``` + +#### Neo4j Memory Provider + +Neo4j offers two separate integrations for Agent Framework, each serving a different purpose. This provider (`neo4j-agent-memory`) is for **persistent memory** — storing and recalling agent interactions, extracting entities, and building a knowledge graph over time. For **GraphRAG** from an existing knowledge graph using vector, fulltext, or hybrid search, see the [Neo4j Context Provider for GraphRAG](./agent-rag.md#using-neo4j-for-graphrag). + +The Neo4j Memory Provider gives agents persistent memory backed by a knowledge graph. Unlike GraphRAG providers that retrieve from static knowledge bases, the memory provider stores and recalls agent interactions, automatically extracting entities and building a knowledge graph over time. + +The provider manages three types of memory: +- **Short-term memory**: Conversation history and recent context +- **Long-term memory**: Entities, preferences, and facts extracted from interactions +- **Reasoning memory**: Past reasoning traces and tool usage patterns + +```python +from agent_framework import Agent +from agent_framework.azure import AzureAIClient +from azure.identity.aio import AzureCliCredential +from neo4j_agent_memory import MemoryClient, MemorySettings +from neo4j_agent_memory.integrations.microsoft_agent import ( + Neo4jMicrosoftMemory, + create_memory_tools, +) + +settings = MemorySettings() +memory_client = MemoryClient(settings) + +async with memory_client: + memory = Neo4jMicrosoftMemory.from_memory_client( + memory_client=memory_client, + session_id="user-123", + ) + tools = create_memory_tools(memory) + + async with ( + AzureAIClient(credential=AzureCliCredential(), project_endpoint=project_endpoint) as client, + Agent( + client=client, + instructions="You are a helpful assistant with persistent memory.", + tools=tools, + context_providers=[memory.context_provider], + ) as agent, + ): + session = agent.create_session() + response = await agent.run("Remember that I prefer window seats on flights.", session=session) +``` + +Key features: +- **Bidirectional**: Automatically retrieves relevant context before invocation and saves new memories after responses +- **Entity extraction**: Builds a knowledge graph from conversations using a multi-stage extraction pipeline +- **Preference learning**: Infers and stores user preferences across sessions +- **Memory tools**: Agents can explicitly search memory, remember preferences, and find entity connections + +> [!TIP] +> Install with `pip install neo4j-agent-memory[microsoft-agent]`. See the [Neo4j Agent Memory repository](https://github.com/neo4j-labs/agent-memory) for complete documentation. + +### Thread Serialization and Persistence +The framework supports serializing entire thread states for persistence across application restarts: + +```python +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) +``` + +::: zone-end + +## Next steps + +> [!div class="nextstepaction"] +> [Agent Tools](./agent-tools.md) From a79eec46d0ee97686419104b098ff83242fbc204 Mon Sep 17 00:00:00 2001 From: Ryan Knight Date: Thu, 26 Feb 2026 14:25:54 -0700 Subject: [PATCH 2/3] adding pypi links --- agent-framework/agents/rag.md | 2 +- agent-framework/user-guide/agents/agent-memory.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent-framework/agents/rag.md b/agent-framework/agents/rag.md index fdfef7a7..b54c22df 100644 --- a/agent-framework/agents/rag.md +++ b/agent-framework/agents/rag.md @@ -602,7 +602,7 @@ Key features: - **Search modes**: Vector (semantic similarity), fulltext (keyword/BM25), or hybrid (both combined) > [!TIP] -> Install with `pip install agent-framework-neo4j`. See the [Neo4j Context Provider repository](https://github.com/neo4j-labs/neo4j-maf-provider) for complete documentation. +> Install with `pip install agent-framework-neo4j`. See the [Neo4j Context Provider repository](https://github.com/neo4j-labs/neo4j-maf-provider) for complete documentation and the [PyPI package page](https://pypi.org/project/agent-framework-neo4j/) for version details. ::: zone-end diff --git a/agent-framework/user-guide/agents/agent-memory.md b/agent-framework/user-guide/agents/agent-memory.md index 0958950c..6cd989b9 100644 --- a/agent-framework/user-guide/agents/agent-memory.md +++ b/agent-framework/user-guide/agents/agent-memory.md @@ -380,7 +380,7 @@ Key features: - **Memory tools**: Agents can explicitly search memory, remember preferences, and find entity connections > [!TIP] -> Install with `pip install neo4j-agent-memory[microsoft-agent]`. See the [Neo4j Agent Memory repository](https://github.com/neo4j-labs/agent-memory) for complete documentation. +> Install with `pip install neo4j-agent-memory[microsoft-agent]`. See the [Neo4j Agent Memory repository](https://github.com/neo4j-labs/agent-memory) for complete documentation and the [PyPI package page](https://pypi.org/project/neo4j-agent-memory/) for version details. ### Thread Serialization and Persistence The framework supports serializing entire thread states for persistence across application restarts: From 96cdd2f8e3d6b116050749a1fdc31a7cd8556d12 Mon Sep 17 00:00:00 2001 From: Ryan Knight Date: Sun, 29 Mar 2026 11:38:37 -0600 Subject: [PATCH 3/3] Move Neo4j integration docs from inline sections to dedicated integration pages --- agent-framework/TOC.yml | 4 + agent-framework/agents/rag.md | 51 +-- agent-framework/integrations/index.md | 2 + .../integrations/neo4j-graphrag.md | 117 +++++ agent-framework/integrations/neo4j-memory.md | 127 ++++++ .../user-guide/agents/agent-memory.md | 418 ------------------ 6 files changed, 251 insertions(+), 468 deletions(-) create mode 100644 agent-framework/integrations/neo4j-graphrag.md create mode 100644 agent-framework/integrations/neo4j-memory.md delete mode 100644 agent-framework/user-guide/agents/agent-memory.md diff --git a/agent-framework/TOC.yml b/agent-framework/TOC.yml index fb24a7d2..323aa1e2 100644 --- a/agent-framework/TOC.yml +++ b/agent-framework/TOC.yml @@ -164,6 +164,10 @@ items: href: integrations/purview.md - name: M365 href: integrations/m365.md + - name: Neo4j GraphRAG Provider + href: integrations/neo4j-graphrag.md + - name: Neo4j Memory Provider + href: integrations/neo4j-memory.md - name: A2A Protocol href: integrations/a2a.md - name: AG-UI Protocol diff --git a/agent-framework/agents/rag.md b/agent-framework/agents/rag.md index b54c22df..7871d279 100644 --- a/agent-framework/agents/rag.md +++ b/agent-framework/agents/rag.md @@ -553,56 +553,7 @@ This pattern works with any Semantic Kernel VectorStore connector, including: Each connector provides the same `create_search_function` method that can be bridged to Agent Framework tools, allowing you to choose the vector database that best fits your needs. See [the full list here](/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors). -### Using Neo4j for GraphRAG - -Neo4j offers two separate integrations for Agent Framework, each serving a different purpose. This provider (`agent-framework-neo4j`) is for **GraphRAG** — searching an existing knowledge graph to ground agent responses. For **persistent memory** that learns from conversations and builds a knowledge graph over time, see the [Neo4j Memory Provider](./agent-memory.md#neo4j-memory-provider). - -For knowledge graph scenarios where relationships between entities matter, the Neo4j Context Provider offers GraphRAG. It supports vector, fulltext, and hybrid search modes, with optional graph traversal to enrich results with related entities via custom Cypher queries. - -```python -from agent_framework import Agent -from agent_framework.azure import AzureAIClient -from agent_framework_neo4j import Neo4jContextProvider, Neo4jSettings, AzureAIEmbedder -from azure.identity.aio import AzureCliCredential - -settings = Neo4jSettings() - -neo4j_provider = Neo4jContextProvider( - uri=settings.uri, - username=settings.username, - password=settings.get_password(), - index_name="documentChunks", - index_type="vector", - embedder=AzureAIEmbedder(...), - top_k=5, - retrieval_query=""" - MATCH (node)-[:FROM_DOCUMENT]->(doc:Document) - OPTIONAL MATCH (doc)<-[:FILED]-(company:Company) - RETURN node.text AS text, score, doc.title AS title, company.name AS company - ORDER BY score DESC - """, -) - -async with ( - neo4j_provider, - AzureAIClient(credential=AzureCliCredential(), project_endpoint=project_endpoint) as client, - Agent( - client=client, - instructions="You are a financial analyst assistant.", - context_providers=[neo4j_provider], - ) as agent, -): - session = agent.create_session() - response = await agent.run("What risks does Acme Corp face?", session=session) -``` - -Key features: -- **Index-driven**: Works with any Neo4j vector or fulltext index -- **Graph traversal**: Custom Cypher queries enrich search results with related entities -- **Search modes**: Vector (semantic similarity), fulltext (keyword/BM25), or hybrid (both combined) - -> [!TIP] -> Install with `pip install agent-framework-neo4j`. See the [Neo4j Context Provider repository](https://github.com/neo4j-labs/neo4j-maf-provider) for complete documentation and the [PyPI package page](https://pypi.org/project/agent-framework-neo4j/) for version details. +For knowledge graph RAG using graph traversal and Cypher queries, see the [Neo4j GraphRAG Provider](../integrations/neo4j-graphrag.md). ::: zone-end diff --git a/agent-framework/integrations/index.md b/agent-framework/integrations/index.md index 5d1fb837..61d77797 100644 --- a/agent-framework/integrations/index.md +++ b/agent-framework/integrations/index.md @@ -72,6 +72,7 @@ Here is a list of existing providers that can be used. | [Mem0 Memory Provider](https://github.com/microsoft/agent-framework/blob/main/python/packages/mem0/agent_framework_mem0/_provider.py) | Preview | | [Redis Provider](https://github.com/microsoft/agent-framework/blob/main/python/packages/redis/agent_framework_redis/_provider.py) | Preview | | [Purview Context Provider](./purview.md) | Preview | +| [Neo4j Memory Provider](./neo4j-memory.md) | Preview | ::: zone-end @@ -94,6 +95,7 @@ Here is a list of existing providers that can be used. | RAG AI Context Provider | Release Status | | ------------------------------------------------------------------ | --------------- | | [Azure AI Search Provider](https://github.com/microsoft/agent-framework/blob/main/python/packages/azure-ai-search/agent_framework_azure_ai_search/_search_provider.py) | Preview | +| [Neo4j GraphRAG Provider](./neo4j-graphrag.md) | Preview | ::: zone-end diff --git a/agent-framework/integrations/neo4j-graphrag.md b/agent-framework/integrations/neo4j-graphrag.md new file mode 100644 index 00000000..2eb6cdaf --- /dev/null +++ b/agent-framework/integrations/neo4j-graphrag.md @@ -0,0 +1,117 @@ +--- +title: Neo4j GraphRAG Context Provider for Agent Framework +description: Learn how to use the Neo4j GraphRAG Context Provider to add knowledge graph retrieval capabilities to your Agent Framework agents +zone_pivot_groups: programming-languages +author: retroryan +ms.topic: article +ms.author: ryanknight +ms.date: 03/29/2026 +ms.service: agent-framework +--- + +# Neo4j GraphRAG Context Provider + +The Neo4j GraphRAG Context Provider adds Retrieval Augmented Generation (RAG) capabilities to Agent Framework agents using a Neo4j knowledge graph. It supports vector, fulltext, and hybrid search modes, with optional graph traversal to enrich results with related entities via custom Cypher queries. + +For knowledge graph scenarios where relationships between entities matter, this provider retrieves relevant subgraphs rather than isolated text chunks, giving agents richer context for generating responses. + +## Why use Neo4j for GraphRAG? + +- **Graph enhanced retrieval**: Standard vector search returns isolated chunks; graph traversal follows connections to surface related entities, giving agents richer context. +- **Flexible search modes**: Combine vector similarity, keyword/BM25, and graph traversal in a single query. +- **Custom retrieval queries**: Cypher queries let you control exactly which relationships to traverse and what context to return. + +> [!NOTE] +> Neo4j offers two separate integrations for Agent Framework. This provider (`agent-framework-neo4j`) is for **GraphRAG** — searching an existing knowledge graph to ground agent responses. For **persistent memory** that learns from conversations and builds a knowledge graph over time, see the [Neo4j Memory Provider](./neo4j-memory.md). + +::: zone pivot="programming-language-csharp" + +This provider is not yet available for C#. See the Python tab for usage examples. + +::: zone-end + +::: zone pivot="programming-language-python" + +## Prerequisites + +- A Neo4j instance (self-hosted or [Neo4j AuraDB](https://neo4j.com/cloud/aura/)) with a vector or fulltext index configured +- An Azure AI Foundry project with a deployed chat model and an embedding model (e.g. `text-embedding-ada-002`) +- Environment variables set: `NEO4J_URI`, `NEO4J_USERNAME`, `NEO4J_PASSWORD`, `AZURE_AI_PROJECT_ENDPOINT` +- Azure CLI credentials configured (`az login`) +- Python 3.10 or later + +## Installation + +```bash +pip install agent-framework-neo4j +``` + +## Usage + +```python +from agent_framework import Agent +from agent_framework.azure import AzureAIClient +from agent_framework_neo4j import Neo4jContextProvider, Neo4jSettings, AzureAISettings, AzureAIEmbedder +from azure.identity.aio import AzureCliCredential + +credential = AzureCliCredential() + +# Reads NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD from environment variables +neo4j_settings = Neo4jSettings() + +# Reads AZURE_AI_PROJECT_ENDPOINT, AZURE_AI_EMBEDDING_NAME from environment variables +azure_settings = AzureAISettings() + +embedder = AzureAIEmbedder( + endpoint=azure_settings.inference_endpoint, + credential=credential, + model=azure_settings.embedding_model, +) + +neo4j_provider = Neo4jContextProvider( + uri=neo4j_settings.uri, + username=neo4j_settings.username, + password=neo4j_settings.get_password(), + index_name=neo4j_settings.vector_index_name, + index_type="vector", + embedder=embedder, + top_k=5, + retrieval_query=""" + MATCH (node)-[:FROM_DOCUMENT]->(doc:Document) + OPTIONAL MATCH (doc)<-[:FILED]-(company:Company) + RETURN node.text AS text, score, doc.title AS title, company.name AS company + ORDER BY score DESC + """, +) + +async with ( + neo4j_provider, + AzureAIClient(credential=credential, project_endpoint=azure_settings.project_endpoint) as client, + Agent( + client=client, + instructions="You are a financial analyst assistant.", + context_providers=[neo4j_provider], + ) as agent, +): + session = agent.create_session() + response = await agent.run("What risks does Acme Corp face?", session=session) +``` + +## Key features + +- **Index-driven**: Works with any Neo4j vector or fulltext index +- **Graph traversal**: Custom Cypher queries enrich search results with related entities +- **Search modes**: Vector (semantic similarity), fulltext (keyword/BM25), or hybrid (both combined) + +## Resources + +- [Neo4j Context Provider repository](https://github.com/neo4j-labs/neo4j-maf-provider) +- [PyPI package page](https://pypi.org/project/agent-framework-neo4j/) +- [Workshop: Neo4j Context Providers for Agent Framework](https://github.com/neo4j-partners/maf-context-providers-lab) + +::: zone-end + +## Next steps + +> [!div class="nextstepaction"] +> [Neo4j Memory Provider](./neo4j-memory.md) diff --git a/agent-framework/integrations/neo4j-memory.md b/agent-framework/integrations/neo4j-memory.md new file mode 100644 index 00000000..6281c527 --- /dev/null +++ b/agent-framework/integrations/neo4j-memory.md @@ -0,0 +1,127 @@ +--- +title: Neo4j Memory Provider for Agent Framework +description: Learn how to use the Neo4j Memory Provider to add persistent knowledge graph memory to your Agent Framework agents +zone_pivot_groups: programming-languages +author: retroryan +ms.topic: article +ms.author: ryanknight +ms.date: 03/29/2026 +ms.service: agent-framework +--- + +# Neo4j Memory Provider + +The Neo4j Memory Provider gives Agent Framework agents persistent memory backed by a knowledge graph. Unlike RAG providers that retrieve from static knowledge bases, the memory provider stores and recalls agent interactions, automatically extracting entities and building a knowledge graph over time. + +The provider manages three types of memory: + +- **Short-term memory**: Conversation history and recent context +- **Long-term memory**: Entities, preferences, and facts extracted from interactions +- **Reasoning memory**: Past reasoning traces and tool usage patterns + +## Why use Neo4j for agent memory? + +- **Knowledge graph persistence**: Memories are stored as connected entities, not flat records, so the agent can reason about relationships between things it remembers. +- **Automatic entity extraction**: Conversations are parsed into structured entities and relationships without manual schema design. +- **Cross-session recall**: Preferences, facts, and reasoning traces persist across sessions and surface automatically via context providers. + +> [!NOTE] +> Neo4j offers two separate integrations for Agent Framework. This provider (`neo4j-agent-memory`) is for **persistent memory** — storing and recalling agent interactions, extracting entities, and building a knowledge graph over time. For **GraphRAG** from an existing knowledge graph using vector, fulltext, or hybrid search, see the [Neo4j GraphRAG Context Provider](./neo4j-graphrag.md). + +::: zone pivot="programming-language-csharp" + +This provider is not yet available for C#. See the Python tab for usage examples. + +::: zone-end + +::: zone pivot="programming-language-python" + +## Prerequisites + +- A Neo4j instance (self-hosted or [Neo4j AuraDB](https://neo4j.com/cloud/aura/)) +- An Azure AI Foundry project with a deployed chat model +- An OpenAI API key or Azure OpenAI deployment (for embeddings and entity extraction) +- Environment variables set: `NEO4J_URI`, `NEO4J_PASSWORD`, `AZURE_AI_PROJECT_ENDPOINT` +- Azure CLI credentials configured (`az login`) +- Python 3.10 or later + +## Installation + +```bash +pip install neo4j-agent-memory[microsoft-agent] +``` + +## Usage + +```python +import os +from pydantic import SecretStr +from agent_framework import Agent +from agent_framework.azure import AzureAIClient +from azure.identity.aio import AzureCliCredential +from neo4j_agent_memory import MemoryClient, MemorySettings +from neo4j_agent_memory.integrations.microsoft_agent import ( + Neo4jMicrosoftMemory, + create_memory_tools, +) + +# MemorySettings accepts nested configuration for Neo4j, embedding, and LLM providers. +# Environment variables use the NAM_ prefix with __ as nested delimiter +# (e.g. NAM_NEO4J__URI, NAM_NEO4J__PASSWORD, NAM_EMBEDDING__MODEL). +settings = MemorySettings( + neo4j={ + "uri": os.environ["NEO4J_URI"], + "username": os.environ.get("NEO4J_USERNAME", "neo4j"), + "password": SecretStr(os.environ["NEO4J_PASSWORD"]), + }, + embedding={ + "provider": "openai", + "model": "text-embedding-3-small", + }, +) + +memory_client = MemoryClient(settings) + +async with memory_client: + memory = Neo4jMicrosoftMemory.from_memory_client( + memory_client=memory_client, + session_id="user-123", + ) + tools = create_memory_tools(memory) + + async with ( + AzureAIClient( + credential=AzureCliCredential(), + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + ) as client, + Agent( + client=client, + instructions="You are a helpful assistant with persistent memory.", + tools=tools, + context_providers=[memory.context_provider], + ) as agent, + ): + session = agent.create_session() + response = await agent.run("Remember that I prefer window seats on flights.", session=session) +``` + +## Key features + +- **Bidirectional**: Automatically retrieves relevant context before invocation and saves new memories after responses +- **Entity extraction**: Builds a knowledge graph from conversations using a multi-stage extraction pipeline +- **Preference learning**: Infers and stores user preferences across sessions +- **Memory tools**: Agents can explicitly search memory, remember preferences, and find entity connections + +## Resources + +- [Neo4j Agent Memory repository](https://github.com/neo4j-labs/agent-memory) +- [PyPI package page](https://pypi.org/project/neo4j-agent-memory/) +- [Sample: Retail Assistant with Neo4j Agent Memory](https://github.com/neo4j-labs/agent-memory/tree/main/examples/microsoft_agent_retail_assistant) +- [Workshop: Neo4j Context Providers for Agent Framework](https://github.com/neo4j-partners/maf-context-providers-lab) + +::: zone-end + +## Next steps + +> [!div class="nextstepaction"] +> [Neo4j GraphRAG Context Provider](./neo4j-graphrag.md) diff --git a/agent-framework/user-guide/agents/agent-memory.md b/agent-framework/user-guide/agents/agent-memory.md deleted file mode 100644 index 6cd989b9..00000000 --- a/agent-framework/user-guide/agents/agent-memory.md +++ /dev/null @@ -1,418 +0,0 @@ ---- -title: Agent Chat History and Memory -description: Learn how to use chat history and memory with Agent Framework -zone_pivot_groups: programming-languages -author: markwallace -ms.topic: reference -ms.author: markwallace -ms.date: 09/24/2025 -ms.service: agent-framework ---- - -# Agent Chat History and Memory - -Agent chat history and memory are crucial capabilities that allow agents to maintain context across conversations, remember user preferences, and provide personalized experiences. The Agent Framework provides multiple features to suit different use cases, from simple in-memory chat message storage to persistent databases and specialized memory services. - -::: zone pivot="programming-language-csharp" - -## Chat History - -Various chat history storage options are supported by Agent Framework. The available options vary by agent type and the underlying service(s) used to build the agent. - -The two main supported scenarios are: - -- **In-memory storage**: Agent is built on a service that doesn't support in-service storage of chat history (for example, OpenAI Chat Completion). By default, Agent Framework stores the full chat history in-memory in the `AgentThread` object, but developers can provide a custom `ChatMessageStore` implementation to store chat history in a third-party store if required. -- **In-service storage**: Agent is built on a service that requires in-service storage of chat history (for example, Azure AI Foundry Persistent Agents). Agent Framework stores the ID of the remote chat history in the `AgentThread` object, and no other chat history storage options are supported. - -### In-memory chat history storage - -When using a service that doesn't support in-service storage of chat history, Agent Framework defaults to storing chat history in-memory in the `AgentThread` object. In this case, the full chat history that's stored in the thread object, plus any new messages, will be provided to the underlying service on each agent run. This design allows for a natural conversational experience with the agent. The caller only provides the new user message, and the agent only returns new answers. But the agent has access to the full conversation history and will use it when generating its response. - -When using OpenAI Chat Completion as the underlying service for agents, the following code results in the thread object containing the chat history from the agent run. - -```csharp -AIAgent agent = new OpenAIClient("") - .GetChatClient(modelName) - .AsAIAgent(JokerInstructions, JokerName); -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); -``` - -Where messages are stored in memory, it's possible to retrieve the list of messages from the thread and manipulate the messages directly if required. - -```csharp -IList? messages = thread.GetService>(); -``` - -> [!NOTE] -> Retrieving messages from the `AgentThread` object in this way only works if in-memory storage is being used. - -#### Chat history reduction with in-memory storage - -The built-in `InMemoryChatMessageStore` that's used by default when the underlying service does not support in-service storage, -can be configured with a reducer to manage the size of the chat history. -This is useful to avoid exceeding the context size limits of the underlying service. - -The `InMemoryChatMessageStore` can take an optional `Microsoft.Extensions.AI.IChatReducer` implementation to reduce the size of the chat history. -It also allows you to configure the event during which the reducer is invoked, either after a message is added to the chat history -or before the chat history is returned for the next invocation. - -To configure the `InMemoryChatMessageStore` with a reducer, you can provide a factory to construct a new `InMemoryChatMessageStore` -for each new `AgentThread` and pass it a reducer of your choice. The `InMemoryChatMessageStore` can also be passed an optional trigger event -which can be set to either `InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded` or `InMemoryChatMessageStore.ChatReducerTriggerEvent.BeforeMessagesRetrieval`. - -The factory is an async function that receives a context object and a cancellation token. - -```csharp -AIAgent agent = new OpenAIClient("") - .GetChatClient(modelName) - .AsAIAgent(new ChatClientAgentOptions - { - Name = JokerName, - ChatOptions = new() { Instructions = JokerInstructions }, - ChatMessageStoreFactory = (ctx, ct) => new ValueTask( - new InMemoryChatMessageStore( - new MessageCountingChatReducer(2), - ctx.SerializedState, - ctx.JsonSerializerOptions, - InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded)) - }); -``` - -> [!NOTE] -> This feature is only supported when using the `InMemoryChatMessageStore`. When a service has in-service chat history storage, it is up to the service itself to manage the size of the chat history. Similarly, when using 3rd party storage (see below), it is up to the 3rd party storage solution to manage the chat history size. If you provide a `ChatMessageStoreFactory` for a message store but you use a service with built-in chat history storage, the factory will not be used. - -### Inference service chat history storage - -When using a service that requires in-service storage of chat history, Agent Framework stores the ID of the remote chat history in the `AgentThread` object. - -For example, when using OpenAI Responses with store=true as the underlying service for agents, the following code will result in the thread object containing the last response ID returned by the service. - -```csharp -AIAgent agent = new OpenAIClient("") - .GetOpenAIResponseClient(modelName) - .AsAIAgent(JokerInstructions, JokerName); -AgentThread thread = await agent.GetNewThreadAsync(); -Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread)); -``` - -> [!NOTE] -> Some services, for example, OpenAI Responses support either in-service storage of chat history (store=true), or providing the full chat history on each invocation (store=false). -> Therefore, depending on the mode that the service is used in, Agent Framework will either default to storing the full chat history in memory, or storing an ID reference to the service stored chat history. - -### Third-party chat history storage - -When using a service that does not support in-service storage of chat history, Agent Framework allows developers to replace the default in-memory storage of chat history with third-party chat history storage. The developer is required to provide a subclass of the base abstract `ChatMessageStore` class. - -The `ChatMessageStore` class defines the interface for storing and retrieving chat messages. Developers must implement the `InvokedAsync` and `InvokingAsync` methods to add messages to the remote store as they are generated, and retrieve messages from the remote store before invoking the underlying service. - -The agent will use all messages returned by `InvokingAsync` when processing a user query. It is up to the implementer of `ChatMessageStore` to ensure that the size of the chat history does not exceed the context window of the underlying service. - -When implementing a custom `ChatMessageStore` which stores chat history in a remote store, the chat history for that thread should be stored under a key that is unique to that thread. The `ChatMessageStore` implementation should generate this key and keep it in its state. `ChatMessageStore` has a `Serialize` method that can be overridden to serialize its state when the thread is serialized. The `ChatMessageStore` should also provide a constructor that takes a as input to support deserialization of its state. - -To supply a custom `ChatMessageStore` to a `ChatClientAgent`, you can use the `ChatMessageStoreFactory` option when creating the agent. -Here is an example showing how to pass the custom implementation of `ChatMessageStore` to a `ChatClientAgent` that is based on Azure OpenAI Chat Completion. - -The factory is an async function that receives a context object and a cancellation token. - -```csharp -AIAgent agent = new AzureOpenAIClient( - new Uri(endpoint), - new AzureCliCredential()) - .GetChatClient(deploymentName) - .AsAIAgent(new ChatClientAgentOptions - { - Name = JokerName, - ChatOptions = new() { Instructions = JokerInstructions }, - ChatMessageStoreFactory = (ctx, ct) => new ValueTask( - // Create a new chat message store for this agent that stores the messages in a custom store. - // Each thread must get its own copy of the CustomMessageStore, since the store - // also contains the ID that the thread is stored under. - new CustomMessageStore( - vectorStore, - ctx.SerializedState, - ctx.JsonSerializerOptions)) - }); -``` - -> [!TIP] -> For a detailed example on how to create a custom message store, see the [Storing Chat History in 3rd Party Storage](../../tutorials/agents/third-party-chat-history-storage.md) tutorial. - -## Long term memory - -The Agent Framework allows developers to provide custom components that can extract memories or provide memories to an agent. - -To implement such a memory component, the developer needs to subclass the `AIContextProvider` abstract base class. This class has two core methods, `InvokingAsync` and `InvokedAsync`. When overridden, `InvokedAsync` allows developers to inspect all messages provided by users or generated by the agent. `InvokingAsync` allows developers to inject additional context for a specific agent run. System instructions, additional messages and additional functions can be provided. - -> [!TIP] -> For a detailed example on how to create a custom memory component, see the [Adding Memory to an Agent](../../tutorials/agents/memory.md) tutorial. - -## AgentThread Serialization - -It is important to be able to persist an `AgentThread` object between agent invocations. This allows for situations where a user might ask a question of the agent, and take a long time to ask follow up questions. This allows the `AgentThread` state to survive service or app restarts. - -Even if the chat history is stored in a remote store, the `AgentThread` object still contains an ID referencing the remote chat history. Losing the `AgentThread` state will therefore result in also losing the ID of the remote chat history. - -The `AgentThread` as well as any objects attached to it, all therefore provide the `Serialize` method to serialize their state. The `AIAgent` also provides a `DeserializeThreadAsync` method that re-creates a thread from the serialized state. The `DeserializeThreadAsync` method re-creates the thread with the `ChatMessageStore` and `AIContextProvider` configured on the agent. - -```csharp -// Serialize the thread state to a JsonElement, so it can be stored for later use. -JsonElement serializedThreadState = thread.Serialize(); - -// Re-create the thread from the JsonElement. -AgentThread resumedThread = await agent.DeserializeThreadAsync(serializedThreadState); -``` - -> [!NOTE] -> `AgentThread` objects may contain more than just chat history, e.g. context providers may also store state in the thread object. Therefore, it is important to always serialize, store and deserialize the entire `AgentThread` object to ensure that all state is preserved. -> [!IMPORTANT] -> Always treat `AgentThread` objects as opaque objects, unless you are very sure of the internals. The contents may vary not just by agent type, but also by service type and configuration. -> [!WARNING] -> Deserializing a thread with a different agent than that which originally created it, or with an agent that has a different configuration than the original agent, might result in errors or unexpected behavior. - -::: zone-end -::: zone pivot="programming-language-python" - -## Memory Types - -The Agent Framework supports several types of memory to accommodate different use cases, including managing chat history as part of short term memory and providing extension points for extracting, storing and injecting long term memories into agents. - -### In-Memory Storage (Default) - -The simplest form of memory where conversation history is stored in memory during the application runtime. This is the default behavior and requires no additional configuration. - -```python -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) -``` - -### Persistent Message Stores -For applications that need to persist conversation history across sessions, the framework provides `ChatMessageStore` implementations: - -#### Built-in ChatMessageStore -The default in-memory implementation that can be serialized: - -```python -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 Message Store -For production applications requiring persistent storage: - -```python -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 -) -``` - -#### Custom Message Store -You can implement your own storage backend by implementing the `ChatMessageStoreProtocol`: - -```python -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"] -``` - -> [!TIP] -> For a detailed example on how to create a custom message store, see the [Storing Chat History in 3rd Party Storage](../../tutorials/agents/third-party-chat-history-storage.md) tutorial. - -### Context Providers (Dynamic Memory) -Context providers enable sophisticated memory patterns by injecting relevant context before each agent invocation: - -#### Basic Context Provider -```python -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 -``` - -> [!TIP] -> For a detailed example on how to create a custom memory component, see the [Adding Memory to an Agent](../../tutorials/agents/memory.md) tutorial. - -#### External Memory Services -The framework supports integration with specialized memory services like Mem0: - -```python -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 -) -``` - -#### Neo4j Memory Provider - -Neo4j offers two separate integrations for Agent Framework, each serving a different purpose. This provider (`neo4j-agent-memory`) is for **persistent memory** — storing and recalling agent interactions, extracting entities, and building a knowledge graph over time. For **GraphRAG** from an existing knowledge graph using vector, fulltext, or hybrid search, see the [Neo4j Context Provider for GraphRAG](./agent-rag.md#using-neo4j-for-graphrag). - -The Neo4j Memory Provider gives agents persistent memory backed by a knowledge graph. Unlike GraphRAG providers that retrieve from static knowledge bases, the memory provider stores and recalls agent interactions, automatically extracting entities and building a knowledge graph over time. - -The provider manages three types of memory: -- **Short-term memory**: Conversation history and recent context -- **Long-term memory**: Entities, preferences, and facts extracted from interactions -- **Reasoning memory**: Past reasoning traces and tool usage patterns - -```python -from agent_framework import Agent -from agent_framework.azure import AzureAIClient -from azure.identity.aio import AzureCliCredential -from neo4j_agent_memory import MemoryClient, MemorySettings -from neo4j_agent_memory.integrations.microsoft_agent import ( - Neo4jMicrosoftMemory, - create_memory_tools, -) - -settings = MemorySettings() -memory_client = MemoryClient(settings) - -async with memory_client: - memory = Neo4jMicrosoftMemory.from_memory_client( - memory_client=memory_client, - session_id="user-123", - ) - tools = create_memory_tools(memory) - - async with ( - AzureAIClient(credential=AzureCliCredential(), project_endpoint=project_endpoint) as client, - Agent( - client=client, - instructions="You are a helpful assistant with persistent memory.", - tools=tools, - context_providers=[memory.context_provider], - ) as agent, - ): - session = agent.create_session() - response = await agent.run("Remember that I prefer window seats on flights.", session=session) -``` - -Key features: -- **Bidirectional**: Automatically retrieves relevant context before invocation and saves new memories after responses -- **Entity extraction**: Builds a knowledge graph from conversations using a multi-stage extraction pipeline -- **Preference learning**: Infers and stores user preferences across sessions -- **Memory tools**: Agents can explicitly search memory, remember preferences, and find entity connections - -> [!TIP] -> Install with `pip install neo4j-agent-memory[microsoft-agent]`. See the [Neo4j Agent Memory repository](https://github.com/neo4j-labs/agent-memory) for complete documentation and the [PyPI package page](https://pypi.org/project/neo4j-agent-memory/) for version details. - -### Thread Serialization and Persistence -The framework supports serializing entire thread states for persistence across application restarts: - -```python -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) -``` - -::: zone-end - -## Next steps - -> [!div class="nextstepaction"] -> [Agent Tools](./agent-tools.md)