Python: Add FileMemoryProvider context provider sample (#7428)
* Add FileMemoryProvider sample * Address PR comments
This commit is contained in:
@@ -11,6 +11,7 @@ These samples demonstrate how to use context providers to enrich agent conversat
|
||||
| [`agent_mode_provider.py`](agent_mode_provider.py) | Use the built-in `AgentModeProvider` to track and switch an agent's operating mode at runtime. An interactive loop with a `/mode` slash command demonstrating the built-in `plan`/`execute` modes and custom modes. |
|
||||
| [`cross_session_observer.py`](cross_session_observer.py) | Detect injected context messages whose origins differ from the current session, via the `Message.additional_properties["_attribution"]["origin_session_ids"]` field. Self-contained — no LLM credentials required. |
|
||||
| [`azure_ai_foundry_memory.py`](azure_ai_foundry_memory.py) | Use `FoundryMemoryProvider` to add semantic memory — automatically retrieves, searches, and stores memories via Microsoft Foundry. |
|
||||
| [`file_memory_provider.py`](file_memory_provider.py) | Use the built-in `FileMemoryProvider` with `FileSystemAgentFileStore` to give an agent tools for storing and recalling memories as files, and configure the `scope` so memories persist and are recalled across separate sessions. |
|
||||
| [`file_access_data_processing/`](file_access_data_processing/) | Use `FileAccessProvider` with `FileSystemAgentFileStore` to give an agent read/write/search access to a folder of CSV data files. See its own [README](file_access_data_processing/README.md). |
|
||||
| [`azure_ai_search/`](azure_ai_search/) | Retrieval Augmented Generation (RAG) with Azure AI Search in semantic and agentic modes. See its own [README](azure_ai_search/README.md). |
|
||||
| [`azure_content_understanding/`](azure_content_understanding/) | Analyze documents, images, audio, and video with Azure Content Understanding and inject the extracted content into agent context. |
|
||||
@@ -45,6 +46,11 @@ These samples demonstrate how to use context providers to enrich agent conversat
|
||||
- `AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME`: Embedding model deployment name (e.g., `text-embedding-ada-002`)
|
||||
- Azure CLI authentication (`az login`)
|
||||
|
||||
**For `file_memory_provider.py`:**
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Microsoft Foundry project endpoint
|
||||
- `FOUNDRY_MODEL`: Chat model deployment name
|
||||
- Azure CLI authentication (`az login`)
|
||||
|
||||
**For `file_access_data_processing/`:**
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Microsoft Foundry project endpoint
|
||||
- `FOUNDRY_MODEL`: Chat model deployment name
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import Agent, AgentSession, FileMemoryProvider, FileSystemAgentFileStore
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load python/.env (python-dotenv walks up from this file by default). Pass
|
||||
# override=True so values from .env take precedence over any pre-existing OS
|
||||
# environment variables — without this, OS-level values silently win.
|
||||
load_dotenv(override=True)
|
||||
|
||||
"""
|
||||
Sample: give an agent file-based memory with ``FileMemoryProvider``.
|
||||
|
||||
This sample shows how to attach :class:`FileMemoryProvider` directly to a plain
|
||||
``Agent`` via ``context_providers``, and how to control the *scope* of the
|
||||
memories so they persist and can be recalled across separate sessions.
|
||||
|
||||
``FileMemoryProvider`` is a :class:`~agent_framework.ContextProvider` that
|
||||
exposes a set of ``file_memory_*`` tools (write, read, delete, ls, grep,
|
||||
replace, replace_lines) to the agent, letting the model decide what to remember
|
||||
and when to recall it. Each memory is stored as an individual file in an
|
||||
:class:`~agent_framework.AgentFileStore`; here we use a
|
||||
:class:`~agent_framework.FileSystemAgentFileStore` so the memories are written
|
||||
to real files on disk. Because the files live outside the conversation, the
|
||||
agent can recall them in a later session, even after the original chat history
|
||||
is gone.
|
||||
|
||||
Controlling scope:
|
||||
The provider isolates memories per session by default (the working folder is
|
||||
derived from the session id). Passing an explicit ``scope`` (for example a
|
||||
user id) instead groups memories under a stable working folder that every
|
||||
session for that user shares — which is what lets the second conversation
|
||||
below recall what the user said in the first.
|
||||
|
||||
Prerequisites:
|
||||
- ``FOUNDRY_PROJECT_ENDPOINT``: Your Microsoft Foundry project endpoint.
|
||||
- ``FOUNDRY_MODEL``: Chat model deployment name.
|
||||
- Run ``az login`` before executing the sample.
|
||||
"""
|
||||
|
||||
# The id of the user we are storing memories for. It is used below as the
|
||||
# provider's scope so that each user gets their own memory folder.
|
||||
USER_ID = "UID1"
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
# <create_file_memory_provider>
|
||||
# 1. Create the file store the provider will use to persist memory files.
|
||||
# Here we use a file-system backed store rooted at a local
|
||||
# ``agent-file-memory`` folder, but any AgentFileStore implementation can
|
||||
# be used, e.g. InMemoryAgentFileStore or a custom blob-backed store.
|
||||
memory_root = Path(__file__).parent / "agent-file-memory"
|
||||
store = FileSystemAgentFileStore(memory_root)
|
||||
|
||||
# 2. Create the FileMemoryProvider over that store.
|
||||
# The ``scope`` determines the scope and lifetime of the memories:
|
||||
# - A stable scope, like the per-user one below, gives durable memories
|
||||
# shared by every session for that user. That is what allows the second
|
||||
# conversation further down to recall what the user said in the first.
|
||||
# - Omitting ``scope`` (the default) isolates memories to a single session
|
||||
# (the working folder is derived from the session id).
|
||||
file_memory_provider = FileMemoryProvider(store, scope=f"users/{USER_ID}")
|
||||
|
||||
# 3. Attach the provider to the agent so it gets the file_memory_* tools.
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="TravelAssistant",
|
||||
instructions=(
|
||||
"You are a helpful travel assistant. Remember what the user tells you about "
|
||||
"themselves so that you can give better recommendations later."
|
||||
),
|
||||
context_providers=[file_memory_provider],
|
||||
)
|
||||
# </create_file_memory_provider>
|
||||
|
||||
working_folder = memory_root / "users" / USER_ID
|
||||
print(f"Memory files will be written to: {working_folder}\n")
|
||||
|
||||
# 4. First conversation: tell the agent something worth remembering. The
|
||||
# agent should call file_memory_write to store it as a file in the scope.
|
||||
first_session: AgentSession = agent.create_session()
|
||||
print("=== First conversation ===")
|
||||
print(
|
||||
await agent.run(
|
||||
"I'm vegetarian and I always travel with my dog. Please remember this for future trips.",
|
||||
session=first_session,
|
||||
)
|
||||
)
|
||||
print()
|
||||
|
||||
# 5. Show the memory files the agent created on disk.
|
||||
print("=== Memory files on disk ===")
|
||||
if working_folder.exists():
|
||||
for file in sorted(working_folder.iterdir()):
|
||||
if file.is_file():
|
||||
print(file.name)
|
||||
print()
|
||||
|
||||
# 6. Second conversation: a brand new session with no chat history from the
|
||||
# first. The provider injects the memory index (memories.md) into the
|
||||
# agent's context, and the agent calls file_memory_read to recall the
|
||||
# user's preferences with no shared chat history.
|
||||
second_session: AgentSession = agent.create_session()
|
||||
print("=== Second conversation (new session) ===")
|
||||
print(
|
||||
await agent.run(
|
||||
"Suggest a hotel and a restaurant for my trip to Paris next week.",
|
||||
session=second_session,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
"""
|
||||
Sample output (abridged; exact text varies by model):
|
||||
|
||||
Memory files will be written to: .../context_providers/agent-file-memory/users/UID1
|
||||
|
||||
=== First conversation ===
|
||||
Got it — I'll remember that you're vegetarian and always travel with your dog. I've saved
|
||||
this so I can tailor future recommendations for you.
|
||||
|
||||
=== Memory files on disk ===
|
||||
userpreferences.md
|
||||
|
||||
=== Second conversation (new session) ===
|
||||
Since you're vegetarian and travel with your dog, here are some dog-friendly options in Paris:
|
||||
- Hotel: ... (pet-friendly, central) ...
|
||||
- Restaurant: ... (excellent vegetarian menu, welcomes dogs) ...
|
||||
"""
|
||||
Reference in New Issue
Block a user