CMA:记忆用户偏好

让 Agent 记住并应用用户偏好

🌐 查看英文原文 | 源码 Notebook


构建记住用户的 Agent

大多数 Agent 每次对话都从零开始。客户告诉你的购物助手他们的尺码、预算和避免哪些材质,下次他们回来时 Agent 已经忘记了一切。他们不得不重复自己,体验感觉很通用而不是个性化。

我们刚刚在 Claude Managed Agents 中引入了 Memory 来解决这个问题。把它想象成你的 Agent 为每个客户获得的一个共享笔记本:Claude 在 session 期间记下相关内容,下次同一个客户回来时,这些笔记仍然在那里。设置只需一个简单的 API 调用。

在本指南中,你将为一个零售品牌构建一个示例购物助手。Agent 将在客户第一次访问时了解客户的偏好,将其保存到用户特定的 memory store 中,并在下次访问时自动回忆它们,无需再次告知。

你将构建什么

  • 一个保存一个客户购物偏好的 memory store
  • 一个配置为检查和更新该 store 的购物 Agent
  • 两个独立的 session,演示记忆跨访问保持
  • 从你自己的应用程序检查和种子化记忆的模式

记忆如何工作

当你将 memory store 附加到 session 时,它在 Agent 的环境中以 /mnt/memory/{store-name} 目录出现,Claude 使用其标准文件工具在那里读取和写入文件。你的应用程序通过 REST API 对相同文件具有完全的读写权限,因此你可以用已知事实种子化 store、审计 Agent 写入的内容,或将所有内容导出到你自己的系统。

Beta 功能。 Memory store 是 Claude Managed Agents 公开 beta 的一部分。API 在正式发布之前可能会更改。Python SDK 会自动为 client.beta 下的每个方法添加所需的 anthropic-beta header。

前提条件

  • 具有 Claude Managed Agents beta 访问权限的 Anthropic API 密钥。将其设置为 ANTHROPIC_API_KEY 环境变量。
  • Python 3.11 或更高版本。
  • Anthropic Python SDK。Memory store 方法需要最近的版本:
uv add anthropic
# or: pip install -U anthropic

设置客户端

%%capture
%pip install -q "anthropic>=0.91.0"
import os

from anthropic import Anthropic
from utilities import wait_for_idle_status

MODEL = os.environ.get("COOKBOOK_MODEL", "claude-sonnet-4-6")

client = Anthropic()

对话轮次辅助函数

Managed agent 的 session 是事件驱动的:你发送一条用户消息,然后流式接收事件直到 session 进入空闲状态。为了让本指南后续部分更易读,我们将这个循环封装在一个辅助函数中,打印 Agent 的回复以及它在 /mnt/memory/ 下访问的文件。

def run_turn(session_id: str, user_text: str) -> str:
    """Send one user message and stream the agent's response until it goes idle.

    Returns the agent's full text reply for callers who want it programmatically.
    """
    print(f"\n[user]  {user_text}")
    reply_parts: list[str] = []

    with client.beta.sessions.events.stream(session_id) as stream:
        client.beta.sessions.events.send(
            session_id,
            events=[
                {
                    "type": "user.message",
                    "content": [{"type": "text", "text": user_text}],
                }
            ],
        )
        for event in stream:
            if event.type == "agent.message":
                for block in event.content:
                    if block.type == "text":
                        reply_parts.append(block.text)
                        print(f"[agent] {block.text}")

            elif event.type == "agent.tool_use":
                # Surface reads and writes to the memory mount so you can see
                # the agent checking and updating its memory.
                inp = event.input or {}
                target = inp.get("file_path") or inp.get("command", "")
                if "/mnt/memory/" in str(target):
                    print(f"  [memory] {event.name}: {target}")

            elif event.type == "session.status_idle":
                # Break on any idle reason so an unexpected stop_reason
                # (such as requires_action) cannot hang the loop.
                break

            elif event.type == "session.status_terminated":
                break

    wait_for_idle_status(client, session_id)
    return "".join(reply_parts)

步骤 1:创建 memory store

Memory store 是一个命名的文本文件容器,作用范围限定在你的 workspace 内。在生产部署中,你通常会为每个终端用户创建一个 store,并在自己的数据库中维护用户 ID 到 store ID 的映射。

你在此处设置的 description 会在每次附加 store 时渲染到 Agent 的系统提示中,因此用它来告诉 Claude 该 store 的用途。

store = client.beta.memory_stores.create(
    name="Shopper Preferences",
    description=(
        "Personal shopping preferences for a single customer: "
        "sizes, style, budget, favorite brands, and materials to avoid."
    ),
)

print(store.id)  # memstore_01...
memstore_01NKkumXZXY8mEhoRA3xhBvN

步骤 2:定义购物 Agent

每个 managed agent session 都需要一个 agent(模型、系统提示和工具)和一个 environment(Agent 运行的容器)。你可以创建一次并在许多客户和 session 中重复使用。

给 Agent 内置的 agent_toolset,其中包含它用来读写记忆的文件工具。agent_toolset_20260401 类型字符串是工具集的 API 标识符,不是模型别名,因此当新模型发布时你不需要更新它。

environment = client.beta.environments.create(
    name="shopping-demo",
    config={"type": "cloud", "networking": {"type": "limited"}},
)

agent = client.beta.agents.create(
    name="Personal Shopper",
    model=MODEL,
    system=(
        "You are a personal shopping assistant for a retail brand. "
        "Help the customer find products that match their taste and budget, "
        "and remember what you learn about them for future visits."
    ),
    tools=[
        {
            "type": "agent_toolset_20260401",
            "default_config": {
                "enabled": True,
                "permission_policy": {"type": "always_allow"},
            },
        }
    ],
)

print(agent.id)  # agent_01...
agent_011CaMbJoHMUyR5TjeFDtMG9

步骤 3:首次访问,Agent 在此学习偏好

现在启动一个 session,并通过 resources 数组附加 memory store。instructions 字段是针对每次附加的指导,告诉 Claude 在这个特定 session 中如何使用该 store。

因为这是客户的第一次访问,store 是空的。注意输出中的 [memory] 行:Agent 检查了 store,发现还没有内容,然后写入了一个新文件来记录它学到的信息。

memory_resource = {
    "type": "memory_store",
    "memory_store_id": store.id,
    "access": "read_write",
    "instructions": (
        "This customer's personal preferences: sizes, style, budget, and "
        "materials to avoid. Check it at the start of every conversation "
        "and update it whenever you learn something new."
    ),
}

session_one = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": agent.version},
    environment_id=environment.id,
    resources=[memory_resource],
)

# The response tells you where the store is mounted inside the agent's environment.
for resource in session_one.resources:
    if resource.type == "memory_store":
        print(f"Mounted at {resource.mount_path}")
Mounted at /mnt/memory/shopper-preferences
run_turn(
    session_one.id,
    "Hi! I'm looking for a new jacket. A few things about me: I wear a size "
    "medium, I only buy vegan leather (no animal leather please), my budget "
    "is usually under $200, and I love earth tones. What would you suggest?",
)
[user]  Hi! I'm looking for a new jacket. A few things about me: I wear a size medium, I only buy vegan leather (no animal leather please), my budget is usually under $200, and I love earth tones. What would you suggest?
[agent] Let me check if I have any info about you on file, and save your preferences right away!
  [memory] bash: cat /mnt/memory/shopper-preferences 2>/dev/null || echo "No file found"
  [memory] write: /mnt/memory/shopper-preferences
  [memory] write: /mnt/memory/shopper-preferences/preferences.md
[agent] I've saved your preferences for future visits! Now, here are some great **vegan leather jacket picks in earth tones under $200** that I'd suggest:

---

### 🌿 Top Picks for You

#### 1. **Olive Moto Jacket** — ~$110–$150
A classic biker silhouette in a deep **olive green** faux leather. Look for styles with zip-detail sleeves and a fitted waist. Great for casual or edgy looks.
- *Why you'll love it:* Earthy tone, very on-trend, versatile for layering.

#### 2. **Camel/Tan Oversized Blazer-Jacket** — ~$80–$160
A structured vegan leather blazer in **camel or tan** gives a polished, sophisticated vibe. Works dressed up or down.
- *Why you'll love it:* Classic earth tone, medium sizing runs well in this style, timeless investment.

#### 3. **Rust/Cognac Bomber Jacket** — ~$90–$170
A **rust or cognac-toned** vegan leather bomber is a bold earth-tone statement piece with a relaxed, comfortable fit.
- *Why you'll love it:* Rich warm color, bomber silhouettes are flattering in medium, cozy feel.

#### 4. **Terracotta Cropped Jacket** — ~$75–$140
A cropped vegan leather jacket in **terracotta** pairs beautifully with high-waisted jeans or wide-leg trousers.
- *Why you'll love it:* Trendy silhouette, stunning earthy hue, well within budget.

---

### 🛍️ Brands to Check Out
Here are some brands known for quality vegan leather within your budget:
- **ASOS** – Wide range of faux leather styles in various earth tones
- **Free People** – Boho-leaning earth tone options
- **H&M** – Affordable vegan leather jackets in seasonal colors
- **Lulus** – Great selection of trendy vegan styles under $100
- **Noize** – A dedicated vegan outerwear brand, often has earthy tones under $200

---

Do any of these styles appeal to you? I can help narrow it down further — for example, do you prefer a **fitted or relaxed fit**, or a particular style like moto, bomber, or blazer? 😊

步骤 4:检查 Agent 保存了什么

Agent 写入到挂载点的所有内容都是常规的记忆文档,你的应用程序可以通过 API 读取、编辑或删除。使用 view="full" 列出 store 的内容,以便在响应中包含文件内容。

这就是你在产品中构建”我们了解你什么”页面、将记忆同步到你自己的数据库或让人工审查者纠正 Agent 弄错的内容的方式。

page = client.beta.memory_stores.memories.list(
    store.id,
    view="full",
)

for memory in page.data:
    if memory.type == "memory":
        print(f"=== {memory.path} ===")
        print(memory.content)
        print()
=== /preferences.md ===
# Shopper Preferences

## Sizes
- Tops/Jackets: Medium

## Style
- Loves earth tones (browns, tans, olive, terracotta, camel, rust, etc.)
- Interested in jackets

## Budget
- Usually under $200

## Materials
- VEGAN LEATHER ONLY — absolutely no animal leather
- (No other material restrictions noted yet)

## Favorite Brands
- None noted yet

## Other Notes
- First visit; preferences collected 2026-04-23

你应该会看到一个文件(通常类似 /preferences.md),其中包含客户提到的尺码、预算、材质和颜色偏好,按主题组织。Claude 自行选择了文件名和结构。

步骤 5:回访,Agent 自主回忆

这是关键的时刻。创建一个全新的 session 并附加同一个 memory store。客户没有重复任何偏好信息,但 Agent 从记忆中读取了它们并据此调整了推荐。

session_two = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": agent.version},
    environment_id=environment.id,
    resources=[memory_resource],  # same store, new session
)

run_turn(
    session_two.id,
    "Hey, I'm back! I need a bag for work. Any recommendations?",
)
[user]  Hey, I'm back! I need a bag for work. Any recommendations?
[agent] Welcome back! Let me check your preferences before making any recommendations!
  [memory] read: /mnt/memory/shopper-preferences
  [memory] bash: ls /mnt/memory/shopper-preferences/
  [memory] read: /mnt/memory/shopper-preferences/preferences.md
[agent] Great news — I already have your preferences on file! Here's what I kept in mind for you:

- 🎨 **Style:** Earth tones (browns, tans, olive, camel, etc.)
- 💰 **Budget:** Under $200
- 🌱 **Materials:** Vegan leather only — no animal leather

With that in mind, here are some work bag recommendations that would suit you perfectly:

---

### 🛍️ Top Picks for a Work Bag

1. **Structured Tote in Camel Vegan Leather** — A polished, professional look in a warm earth tone. Fits a laptop + daily essentials. Great desk-to-commute bag.

2. **Olive Canvas & Vegan Leather Trim Laptop Bag** — A sleek crossbody/shoulder bag with a relaxed but put-together vibe. The canvas keeps it lightweight.

3. **Tan Vegan Leather Satchel** — A classic work satchel with a top handle and shoulder strap. Timeless silhouette, stays under budget.

4. **Terracotta Faux-Leather Tote** — A bold but grounded earth tone that adds personality to a work outfit without being over the top.

---

Do any of these styles appeal to you? I can also ask a few quick questions to narrow it down further:

- **Do you prefer a tote, backpack, crossbody, or briefcase-style?**
- **Do you need to carry a laptop, and if so, what size?**
- **Do you lean more structured/professional or relaxed/casual for work?**

在输出中注意,Agent 在回答之前读取了 /mnt/memory/shopper-preferences/,然后推荐了纯素皮革、大地色系且在 200 美元以下的包,即使这条消息中根本没有提到这些偏好。这些偏好从第一个 session 传递了过来。

进一步探索:生产模式

从现有数据种子化 store

如果你已经从客户的账户资料或购买历史中了解了一些信息,你可以在第一次 session 之前将它们写入 store,以便 Agent 在开始时就了解情况。在实际应用中,你会在创建任何 session 之前运行此种子化步骤;它出现在这里(演示之后)只是为了让上面主要的”学习-回忆”流程保持聚焦。

注意: 在 notebook 末尾的清理单元格之前运行此单元格,因为 store 在那里被删除。

# Optional: run before the cleanup cell.
seeded = client.beta.memory_stores.memories.create(
    store.id,
    path="/purchase-history.md",
    content=(
        "## Recent purchases\n"
        "- Canvas tote, olive, $89 (Jan 2026)\n"
        "- Wool beanie, rust, $34 (Dec 2025)\n"
    ),
)
print(f"Seeded {seeded.path}")
Seeded /purchase-history.md

组合按客户和共享 store

一个 session 最多可以附加八个 memory store,每个都有自己的访问级别。一个常见的模式是为每个客户设置一个读写 store,再加上一个所有 session 共享的只读品牌知识 store。

catalog = client.beta.memory_stores.create(
    name="Product Catalog Notes",
    description="Current promotions, sizing guidance, and stock notes.",
)

session = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": agent.version},
    environment_id=environment.id,
    resources=[
        {
            "type": "memory_store",
            "memory_store_id": store.id,
            "access": "read_write",
            "instructions": "This customer's personal preferences.",
        },
        {
            "type": "memory_store",
            "memory_store_id": catalog.id,
            "access": "read_only",
            "instructions": "Brand-wide product guidance. Consult before recommending items.",
        },
    ],
)

# Remember to clean up the catalog store when you are done:
# client.beta.memory_stores.delete(catalog.id)

审计和纠正

每次写入 memory store 都会作为一个不可变版本记录下来,其中包含创建它的 session。使用 client.beta.memory_stores.memory_versions.list(...) 查看历史记录,使用 client.beta.memory_stores.memories.update(...) 纠正 Agent 弄错的文件。完整的 API 接口请参阅 Memory API 参考。

清理

删除你在遵循本指南时创建的资源。

wait_for_idle_status(client, session_one.id)
wait_for_idle_status(client, session_two.id)

client.beta.sessions.archive(session_one.id)
client.beta.sessions.archive(session_two.id)
client.beta.memory_stores.delete(store.id)
client.beta.agents.archive(agent.id)
client.beta.environments.archive(environment.id)

总结

你通过以下方式构建了一个跨访问记住客户的购物 Agent:

  1. 为客户创建一个 memory store
  2. 通过 resources 将其附加到每个 session
  3. 让 Claude 在学习新偏好时读取和写入 /mnt/memory/ 下的文件
  4. 通过 Memory API 从你自己的应用程序检查这些文件

从这里开始,你可以将自己的用户 ID 映射到 memory store ID,从现有客户数据种子化 store,并在其上叠加共享的只读 store 来承载品牌全局知识。

本系列中的其他 notebook

  • CMA_iterate_fix_failing_tests.ipynb — 入门 notebook。通过在失败测试套件上的”做-观察-修”循环介绍 agents、environments、sessions、文件挂载和流式事件循环。
  • CMA_operate_in_production.ipynb — 生产环境设置指南:基于 vault 的 MCP 凭据、用于无需长连接的 HITL 的 session.status_idled webhook,以及资源生命周期 CRUD 操作。

了解更多


Tip

中文读者提示:本章节的代码和输出为英文原文。如需理解具体实现细节,可参考上方中文导读和代码注释。如有疑问,欢迎在 GitHub Issues 讨论。