From c3522ac8c6c4be516e9b475df7328356e634b7a9 Mon Sep 17 00:00:00 2001 From: Tao Chen Date: Wed, 13 May 2026 17:12:20 -0700 Subject: [PATCH] Done: Foundry Memory --- .../responses/10_foundry_memory/.env.example | 7 ++--- .../responses/10_foundry_memory/README.md | 26 +++++++--------- .../10_foundry_memory/agent.manifest.yaml | 11 ++----- .../responses/10_foundry_memory/agent.yaml | 6 ++-- .../responses/10_foundry_memory/main.py | 17 +++++------ .../provision_memory_store.py | 30 +++++++++++++------ 6 files changed, 46 insertions(+), 51 deletions(-) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/.env.example index 41009a5570..cc120c08ba 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/.env.example +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/.env.example @@ -1,9 +1,6 @@ FOUNDRY_PROJECT_ENDPOINT="..." AZURE_AI_MODEL_DEPLOYMENT_NAME="..." # Embedding model deployment (only needed by provision_memory_store.py). -AZURE_OPENAI_EMBEDDING_MODEL="text-embedding-3-small" +AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME="text-embedding-3-small" # Name of the Foundry Memory Store the agent should read/write to. -FOUNDRY_MEMORY_STORE_NAME="agent_framework_memory" -# Optional scope (e.g., user id) used to isolate memories. Falls back to the -# per-session id when unset, which limits memories to a single session. -FOUNDRY_MEMORY_SCOPE="user_123" +MEMORY_STORE_NAME="agent_framework_memory" \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/README.md index 98254f212c..b0c4d62e0d 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/README.md @@ -44,8 +44,8 @@ From this directory, with the venv activated and `az login` done: ```bash export FOUNDRY_PROJECT_ENDPOINT="https://.services.ai.azure.com/api/projects/" export AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4.1-mini" -export AZURE_OPENAI_EMBEDDING_MODEL="text-embedding-3-small" -export FOUNDRY_MEMORY_STORE_NAME="agent_framework_memory" +export AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME="text-embedding-3-small" +export MEMORY_STORE_NAME="agent_framework_memory" python provision_memory_store.py ``` @@ -54,8 +54,8 @@ Or in PowerShell: ```powershell $env:FOUNDRY_PROJECT_ENDPOINT="https://.services.ai.azure.com/api/projects/" $env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4.1-mini" -$env:AZURE_OPENAI_EMBEDDING_MODEL="text-embedding-3-small" -$env:FOUNDRY_MEMORY_STORE_NAME="agent_framework_memory" +$env:AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME="text-embedding-3-small" +$env:MEMORY_STORE_NAME="agent_framework_memory" python provision_memory_store.py ``` @@ -75,16 +75,13 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# In addition to the standard environment variables, this sample requires: ```bash -export FOUNDRY_MEMORY_STORE_NAME="agent_framework_memory" -# Optional — defaults to "user_123" in main.py if unset. -export FOUNDRY_MEMORY_SCOPE="user_123" +export MEMORY_STORE_NAME="agent_framework_memory" ``` Or in PowerShell: ```powershell -$env:FOUNDRY_MEMORY_STORE_NAME="agent_framework_memory" -$env:FOUNDRY_MEMORY_SCOPE="user_123" +$env:MEMORY_STORE_NAME="agent_framework_memory" ``` You can also place these in a `.env` file next to `main.py` — see [`.env.example`](.env.example). @@ -93,7 +90,9 @@ You can also place these in a `.env` file next to `main.py` — see [`.env.examp > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. -Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. The first request seeds a memory; subsequent requests (especially in new sessions) should be able to recall it because memories are scoped to `FOUNDRY_MEMORY_SCOPE`, not to a particular session. +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. The first request seeds a memory; subsequent requests (especially in new sessions) should be able to recall it because memories are persisted across Foundry Hosted Agents sessions. + +> In this sample, the memory is scoped to the user by specifying `scope="{{$userId}}"`, thus memories are isolated across different users but shared across different sessions from the same user. ```bash # 1. Tell the agent something to remember. @@ -108,17 +107,14 @@ curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What do you remember about my preferences?"}' ``` -The agent's answers to the follow-up turns should reflect the preferences shared in the first turn, even though `default_options={"store": False}` is set (so the Responses service is not persisting conversation history) — the recall is coming exclusively from the Foundry Memory Store. - ## Deploying the Agent to Foundry To host the agent on Foundry, follow the instructions in the [Deploying the Agent to Foundry](../../README.md#deploying-the-agent-to-foundry) section of the README in the parent directory. -When deploying, make sure `FOUNDRY_MEMORY_STORE_NAME` and `FOUNDRY_MEMORY_SCOPE` are set in your `azd` environment so they get injected into the hosted container per [`agent.manifest.yaml`](agent.manifest.yaml): +When deploying, make sure `MEMORY_STORE_NAME` and `FOUNDRY_MEMORY_SCOPE` are set in your `azd` environment so they get injected into the hosted container per [`agent.manifest.yaml`](agent.manifest.yaml): ```bash -azd env set FOUNDRY_MEMORY_STORE_NAME "agent_framework_memory" -azd env set FOUNDRY_MEMORY_SCOPE "user_123" +azd env set MEMORY_STORE_NAME "agent_framework_memory" ``` If these are not set, running `azd ai agent init -m ` will prompt you to enter them interactively. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.manifest.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.manifest.yaml index 97333dd70d..42979d092d 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.manifest.yaml +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.manifest.yaml @@ -20,18 +20,13 @@ template: environment_variables: - name: AZURE_AI_MODEL_DEPLOYMENT_NAME value: "{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}" - - name: FOUNDRY_MEMORY_STORE_NAME - value: "{{FOUNDRY_MEMORY_STORE_NAME}}" - - name: FOUNDRY_MEMORY_SCOPE - value: "{{FOUNDRY_MEMORY_SCOPE}}" + - name: MEMORY_STORE_NAME + value: "{{MEMORY_STORE_NAME}}" parameters: properties: - - name: FOUNDRY_MEMORY_STORE_NAME + - name: MEMORY_STORE_NAME secret: false description: The name of the pre-provisioned Foundry Memory Store the agent will use (e.g., agent_framework_memory) - - name: FOUNDRY_MEMORY_SCOPE - secret: false - description: Scope (e.g., user id) used to isolate memories within the store (e.g., user_123) resources: - kind: model id: gpt-4.1-mini diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.yaml index 47960c2c07..502a4c7904 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.yaml +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/agent.yaml @@ -10,7 +10,5 @@ resources: environment_variables: - name: AZURE_AI_MODEL_DEPLOYMENT_NAME value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME} - - name: FOUNDRY_MEMORY_STORE_NAME - value: ${FOUNDRY_MEMORY_STORE_NAME} - - name: FOUNDRY_MEMORY_SCOPE - value: ${FOUNDRY_MEMORY_SCOPE} + - name: MEMORY_STORE_NAME + value: ${MEMORY_STORE_NAME} diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/main.py index 1053611caf..43ce3a4467 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/main.py +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/main.py @@ -6,7 +6,7 @@ This agent uses :class:`FoundryMemoryProvider` to give an otherwise stateless hosted agent persistent, semantic memory backed by an Azure AI Foundry Memory Store. The store itself is provisioned once via ``provision_memory_store.py`` and its name is passed in through the -``FOUNDRY_MEMORY_STORE_NAME`` environment variable. +``MEMORY_STORE_NAME`` environment variable. Unlike the standalone ``azure_ai_foundry_memory.py`` sample, here we construct the :class:`FoundryChatClient` first and then reuse its underlying @@ -41,15 +41,12 @@ async def main() -> None: # constructing a second one for the memory provider. memory_provider = FoundryMemoryProvider( project_client=client.project_client, - memory_store_name=os.environ["FOUNDRY_MEMORY_STORE_NAME"], - # Scope namespaces memories (e.g., per end-user). When unset, the - # provider falls back to the session id, which limits memories to a - # single session. - scope=os.environ.get("FOUNDRY_MEMORY_SCOPE", "user_123"), - # In production, leave update_delay at its default to batch updates and - # reduce cost. We use 0 here so memories are written immediately, which - # makes the sample easier to demo. - update_delay=0, + memory_store_name=os.environ["MEMORY_STORE_NAME"], + # Scope memories by user id, so each user that interacts with the agent + # has their own isolated memories in the store (assuming those users are + # granted access). `{{userId}}` is a special placeholder that the hosting + # infrastructure will replace with the actual user id at runtime. + scope="{{$userId}}", ) agent = Agent( diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/provision_memory_store.py b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/provision_memory_store.py index 0df3c3ecda..fc402800ab 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/provision_memory_store.py +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/10_foundry_memory/provision_memory_store.py @@ -2,7 +2,7 @@ """Provision the Azure AI Foundry Memory Store used by this sample. -Creates the memory store named by ``FOUNDRY_MEMORY_STORE_NAME`` if it does not +Creates the memory store named by ``MEMORY_STORE_NAME`` if it does not already exist. The store is configured with the user-profile capability so the agent can remember stable facts about a user across sessions; chat-summary is disabled to keep the demo focused on durable preferences. Safe to re-run: if a @@ -14,10 +14,10 @@ Usage (from this directory, with the venv activated and ``az login`` done): Required env vars (also read from a local ``.env`` file if present): - FOUNDRY_PROJECT_ENDPOINT e.g. https://.services.ai.azure.com/api/projects/ - AZURE_AI_MODEL_DEPLOYMENT_NAME Chat model deployment used by the memory store - AZURE_OPENAI_EMBEDDING_MODEL Embedding model deployment used by the memory store - FOUNDRY_MEMORY_STORE_NAME Name of the memory store to create + FOUNDRY_PROJECT_ENDPOINT e.g. https://.services.ai.azure.com/api/projects/ + AZURE_AI_MODEL_DEPLOYMENT_NAME Chat model deployment used by the memory store + AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME Embedding model deployment used by the memory store + MEMORY_STORE_NAME Name of the memory store to create Your identity needs ``Azure AI User`` on the Foundry project scope. """ @@ -34,14 +34,14 @@ from azure.core.exceptions import ResourceNotFoundError from azure.identity.aio import DefaultAzureCredential from dotenv import load_dotenv +load_dotenv() + async def main() -> None: - load_dotenv() - endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] - memory_store_name = os.environ["FOUNDRY_MEMORY_STORE_NAME"] + memory_store_name = os.environ["MEMORY_STORE_NAME"] chat_model = os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"] - embedding_model = os.environ["AZURE_OPENAI_EMBEDDING_MODEL"] + embedding_model = os.environ["AZURE_AI_EMBEDDING_MODEL_DEPLOYMENT_NAME"] async with ( DefaultAzureCredential() as credential, @@ -73,6 +73,18 @@ async def main() -> None: ) print(f"Created memory store '{created.name}' (id={created.id}).") + # Verify the store actually exists on the service by reading it back. + # ``create`` returns the requested definition, but a follow-up ``get`` + # confirms the store is persisted and reachable for the agent at runtime. + try: + verified = await project.beta.memory_stores.get(name=memory_store_name) + except ResourceNotFoundError as exc: + raise RuntimeError( + f"Memory store '{memory_store_name}' was not found after creation; " + "the service may not have persisted it." + ) from exc + print(f"Verified memory store '{verified.name}' is available on the service (id={verified.id}).") + if __name__ == "__main__": asyncio.run(main())