Skip to content

Memory & Context Management with Claude Sonnet 4.5 #143

@Pines-Cheng

Description

@Pines-Cheng

Claude Sonnet 4.5 的记忆与上下文管理

学习如何使用 Claude 的记忆工具和上下文编辑功能构建能够跨对话学习和改进的 AI 智能体。

目录

  1. 简介:为什么记忆很重要
  2. 使用场景
  3. 快速入门示例
  4. 工作原理
  5. 代码审查助手演示
  6. 实际应用
  7. 最佳实践

设置

VSCode 用户

# 1. 创建虚拟环境
python -m venv .venv

# 2. 激活虚拟环境
source .venv/bin/activate  # macOS/Linux
# 或者: .venv\Scripts\activate  # Windows

# 3. 安装依赖
pip install -r requirements.txt

# 4. 在 VSCode 中:选择 .venv 作为内核(右上角)

API 密钥

cp .env.example .env
# 编辑 .env 并添加你的 ANTHROPIC_API_KEY

从这里获取你的 API 密钥:https://console.anthropic.com/

1. 简介:为什么记忆很重要 {#introduction}

本教程演示了Effective context engineering for AI agents中描述的上下文工程模式的实际实现。该文章涵盖了为什么上下文是有限资源、注意力预算如何工作,以及构建有效智能体的策略——这些技术你将在这里看到实际应用。

问题

大型语言模型具有有限的上下文窗口(Claude 4 为 200k 个令牌)。虽然这看起来很大,但会出现几个挑战:

  • 上下文限制:长对话或复杂任务可能超出可用上下文
  • 计算成本:处理大型上下文很昂贵 - 注意力机制呈二次方扩展
  • 重复模式:跨对话的相似任务每次都需要重新解释上下文
  • 信息丢失:当上下文填满时,早期的重要信息会丢失

解决方案

Claude Sonnet 4.5 引入了两个强大的功能:

  1. 记忆工具 (memory_20250818):启用跨对话学习

    • Claude 可以记录学到的内容以供将来参考
    • 基于文件的系统,位于 /memories 目录下
    • 客户端实现让你完全控制
  2. 上下文编辑 (clear_tool_uses_20250919):自动管理上下文

    • 当上下文增长过大时清除旧的工具结果
    • 保留最近的上下文同时保存记忆
    • 可配置的触发器和保留策略

好处

构建随时间在特定任务上变得更好的 AI 智能体:

  • 会话 1:Claude 解决问题,记录模式
  • 会话 2:Claude 立即应用学到的模式(更快!)
  • 长会话:上下文编辑保持对话可管理

可以把它想象成给 Claude 一个笔记本来记笔记并回顾——就像人类做的那样。

2. 使用场景 {#use-cases}

记忆和上下文管理启用了强大的新工作流程:

🔍 代码审查助手

  • 从过去的审查中学习调试模式
  • 在未来会话中立即识别相似的错误
  • 构建团队特定的代码质量知识
  • 生产就绪:与 claude-code-action 集成进行 GitHub PR 审查

📚 研究助手

  • 在多个会话中积累主题知识
  • 连接不同研究线程的见解
  • 维护参考书目和来源跟踪

💬 客户支持机器人

  • 学习用户偏好和沟通风格
  • 记住常见问题和解决方案
  • 从交互中构建产品知识库

📊 数据分析助手

  • 记住数据集模式和异常
  • 存储有效的分析技术
  • 随时间构建领域特定见解

支持的模型:Claude Opus 4 (claude-opus-4-20250514)、Claude Opus 4.1 (claude-opus-4-1-20250805)、Claude Sonnet 4 (claude-sonnet-4-20250514) 和 Claude Sonnet 4.5 (claude-sonnet-4-5-20250929)

本教程专注于代码审查助手,因为它清楚地演示了记忆(学习模式)和上下文编辑(处理长审查)。

3. 快速入门示例 {#quick-start}

让我们通过简单的示例看看记忆和上下文管理的实际应用。

设置

首先,安装依赖并配置你的环境:

# 安装所需包
# 选项 1:从 requirements.txt
# %pip install -q -r requirements.txt

# 选项 2:直接安装
%pip install -q anthropic python-dotenv ipykernel

⚠️ 重要:在此目录中创建 .env 文件:

# 复制 .env.example 到 .env 并添加你的 API 密钥
cp .env.example .env

然后编辑 .env 添加你从 https://console.anthropic.com/ 获得的 Anthropic API 密钥

import os
from typing import Any, cast

from anthropic import Anthropic
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

API_KEY = os.getenv("ANTHROPIC_API_KEY")
MODEL = os.getenv("ANTHROPIC_MODEL")

if not API_KEY:
    raise ValueError(
        "未找到 ANTHROPIC_API_KEY。"
        "复制 .env.example 到 .env 并添加你的 API 密钥。"
    )

if not MODEL:
    raise ValueError(
        "未找到 ANTHROPIC_MODEL。"
        "复制 .env.example 到 .env 并设置模型。"
    )

MODEL = cast(str, MODEL)

client = Anthropic(api_key=API_KEY)

print("✓ API 密钥已加载")
print(f"✓ 使用模型:{MODEL}")

示例 1:基本记忆使用

让我们看看 Claude 如何使用记忆来存储信息以供将来参考。

辅助函数

这些示例使用来自 demo_helpers.py 的辅助函数:

  • run_conversation_loop():处理 API 对话循环

    • 调用启用了记忆工具的 Claude API
    • 执行工具使用(记忆操作)
    • 继续直到 Claude 停止使用工具
    • 返回最终响应
  • run_conversation_turn():单轮对话(在示例 3 中使用)

    • 与上面相同,但在一次 API 调用后返回
    • 当你需要细粒度控制时很有用
  • print_context_management_info():显示上下文清理统计

    • 显示节省的令牌、清除的工具使用
    • 帮助可视化上下文编辑何时触发

⚠️ 记忆清除说明

以下单元格清除所有记忆文件,为此演示提供干净的起点。这对于多次运行笔记本以查看一致结果很有用。

在生产应用中,你应该仔细考虑是否清除所有记忆,因为这会永久删除学到的模式。考虑使用选择性删除或将记忆组织到项目特定的目录中。

# 导入辅助函数
from memory_demo.demo_helpers import run_conversation_loop, run_conversation_turn, print_context_management_info
from memory_tool import MemoryToolHandler

# 初始化
client = Anthropic(api_key=API_KEY)
memory = MemoryToolHandler(base_path="./demo_memory")

# 清除任何现有记忆以重新开始
print("🧹 清除之前的记忆...")
memory.clear_all_memory()
print("✓ 记忆已清除\n")

# 加载带有竞态条件错误的示例代码
with open("memory_demo/sample_code/web_scraper_v1.py", "r") as f:
    code_to_review = f.read()

messages = [
    {
        "role": "user",
        "content": f"我正在审查一个多线程网络爬虫,它有时返回的结果比预期少。计数在运行间不一致。你能找到问题吗?\n\n```python\n{code_to_review}\n```"
    }
]

print("=" * 60)
print("📝 会话 1:从错误中学习")
print("=" * 60)

# 运行对话循环
response = run_conversation_loop(
    client=client,
    model=MODEL,
    messages=messages,
    memory_handler=memory,
    system="你是一个代码审查员。",
    max_tokens=2048,
    max_turns=5,
    verbose=True
)

print("\n" + "=" * 60)
print("✅ 会话 1 完成!")
print("=" * 60)

发生了什么?

  1. Claude 检查了它的记忆(首次运行时为空)
  2. 识别了错误:竞态条件 - 多个线程在没有同步的情况下修改共享状态(self.resultsself.failed_urls
  3. 在记忆中存储了并发模式以供将来参考

现在让我们看看魔法 - Claude 在新对话中应用这个学到的模式:

示例 2:跨对话学习

开始一个全新的对话 - 记忆持续存在!

# 新对话(空消息)
# 加载具有类似并发问题的 API 客户端代码
with open("memory_demo/sample_code/api_client_v1.py", "r") as f:
    code_to_review = f.read()

messages = [
    {
        "role": "user",
        "content": f"审查这个 API 客户端代码:\n\n```python\n{code_to_review}\n```"
    }
]

print("=" * 60)
print("🚀 会话 2:应用学到的模式")
print("=" * 60)

# 运行对话循环
response = run_conversation_loop(
    client=client,
    model=MODEL,
    messages=messages,
    memory_handler=memory,
    system="你是一个代码审查员。",
    max_tokens=2048,
    max_turns=5,
    verbose=True
)

print("\n" + "=" * 60)
print("✅ 会话 2 完成!")
print("=" * 60)

注意区别:

  • Claude 立即检查记忆并找到了线程安全/并发模式
  • 立即识别了异步代码中的类似问题,无需重新学习
  • 响应更快,因为它应用了关于共享可变状态的存储知识

这就是跨对话学习的实际应用!

示例 3:在保留记忆的同时清除上下文

在有许多代码文件的长审查会话中会发生什么?

  • 上下文被之前审查的工具结果填满
  • 但记忆(学到的模式)必须持续存在!

让我们触发上下文编辑来看看 Claude 如何自动管理这个。

配置说明: 我们使用 clear_at_least: 50 个令牌,因为记忆工具操作的结果很小(每个约 50-150 个令牌)。在生产环境中,对于更大的工具结果(如网络搜索或代码执行),你会使用更高的值,如 3000-5000 个令牌。

# 配置上下文管理以便演示时积极清除
CONTEXT_MANAGEMENT = {
    "edits": [
        {
            "type": "clear_tool_uses_20250919",
            "trigger": {"type": "input_tokens", "value": 5000},  # 更低的阈值以更快触发清除
            "keep": {"type": "tool_uses", "value": 1},  # 只保留最后一个工具使用
            "clear_at_least": {"type": "input_tokens", "value": 50}
        }
    ]
}

# 从之前的会话继续 - 记忆持续存在!
# 添加多个代码审查以建立上下文

print("=" * 60)
print("📚 会话 3:带上下文清除的长审查会话")
print("=" * 60)
print()

# 审查 1:数据处理器(更大的文件)
with open("memory_demo/sample_code/data_processor_v1.py", "r") as f:
    data_processor_code = f.read()

messages.extend([
    {
        "role": "user",
        "content": f"审查这个数据处理器:\n\n```python\n{data_processor_code}\n```"
    }
])

print("📝 审查 1:数据处理器")
response = run_conversation_turn(
    client=client,
    model=MODEL,
    messages=messages,
    memory_handler=memory,
    system="你是一个代码审查员。",
    context_management=CONTEXT_MANAGEMENT,
    max_tokens=2048,
    verbose=True
)

# 将响应添加到消息中
messages.append({"role": "assistant", "content": response[1]})
if response[2]:
    messages.append({"role": "user", "content": response[2]})

print(f"  📊 输入令牌:{response[0].usage.input_tokens:,}")
context_cleared, saved = print_context_management_info(response[0])
print()

# 审查 2:添加 SQL 代码
with open("memory_demo/sample_code/sql_query_builder.py", "r") as f:
    sql_code = f.read()

messages.extend([
    {
        "role": "user",
        "content": f"审查这个 SQL 查询构建器:\n\n```python\n{sql_code}\n```"
    }
])

print("📝 审查 2:SQL 查询构建器")
response = run_conversation_turn(
    client=client,
    model=MODEL,
    messages=messages,
    memory_handler=memory,
    system="你是一个代码审查员。",
    context_management=CONTEXT_MANAGEMENT,
    max_tokens=2048,
    verbose=True
)

messages.append({"role": "assistant", "content": response[1]})
if response[2]:
    messages.append({"role": "user", "content": response[2]})

print(f"  📊 输入令牌:{response[0].usage.input_tokens:,}")
context_cleared, saved = print_context_management_info(response[0])
print()

print("=" * 60)
print("✅ 会话 3 完成!")
print("=" * 60)

刚才发生了什么?

随着多次审查期间上下文的增长:

  1. 上下文清除自动触发,当输入令牌超过 5,000 时
  2. 旧的工具结果被移除 - 清除了 2 个工具使用,每次节省约 66 个令牌
  3. 记忆文件保持完整 - Claude 仍然可以查询学到的模式
  4. 令牌使用继续增长,但由于清除而增长速度较慢

这演示了关键好处:

  • 短期记忆(带工具结果的对话上下文)→ 清除以节省空间
  • 长期记忆/memories 中存储的模式)→ 跨会话持续存在

为什么令牌节省这么少? 记忆工具操作返回紧凑的结果(文件路径、成功消息)。str_replace 操作只返回"文件编辑成功"加上元数据。在具有更大工具结果(返回完整文章的网络搜索、具有长输出的代码执行)的生产用例中,上下文清除将节省数千个令牌。

让我们验证记忆在清除后仍然存在:

# 验证记忆在上下文清除后持续存在
import os

print("📂 demo_memory/ 中的记忆文件:")
print()

for root, dirs, files in os.walk("./demo_memory"):
    # 计算显示的相对路径
    level = root.replace("./demo_memory", "").count(os.sep)
    indent = "  " * level
    folder_name = os.path.basename(root) or "demo_memory"
    print(f"{indent}{folder_name}/")
    
    sub_indent = "  " * (level + 1)
    for file in files:
        file_path = os.path.join(root, file)
        size = os.path.getsize(file_path)
        print(f"{sub_indent}├── {file} ({size} 字节)")

print()
print("✅ 尽管上下文清除,所有学到的模式都得到保留!")

4. 工作原理 {#how-it-works}

记忆工具架构

记忆工具是客户端的 - 你控制存储。Claude 进行工具调用,你的应用程序执行它们。

记忆工具命令

命令 描述 示例
view 显示目录或文件内容 {"command": "view", "path": "/memories"}
create 创建或覆盖文件 {"command": "create", "path": "/memories/notes.md", "file_text": "..."}
str_replace 替换文件中的文本 {"command": "str_replace", "path": "...", "old_str": "...", "new_str": "..."}
insert 在行号处插入文本 {"command": "insert", "path": "...", "insert_line": 2, "insert_text": "..."}
delete 删除文件或目录 {"command": "delete", "path": "/memories/old.txt"}
rename 重命名或移动文件 {"command": "rename", "old_path": "...", "new_path": "..."}

查看 memory_tool.py 了解包含路径验证和安全措施的完整实现。

理解演示代码

来自 code_review_demo.py 的关键实现细节:

class CodeReviewAssistant:
    def __init__(self, memory_storage_path="./memory_storage"):
        self.client = Anthropic(api_key=API_KEY)
        self.memory_handler = MemoryToolHandler(base_path=memory_storage_path)
        self.messages = []
    
    def review_code(self, code, filename, description=""):
        # 1. 添加用户消息
        self.messages.append({...})
        
        # 2. 带工具执行的对话循环
        while True:
            response = self.client.beta.messages.create(
                model=MODEL,
                system=self._create_system_prompt(),
                messages=self.messages,
                tools=[{"type": "memory_20250818", "name": "memory"}],
                betas=["context-management-2025-06-27"],
                context_management=CONTEXT_MANAGEMENT
            )
            
            # 3. 执行工具使用
            tool_results = []
            for content in response.content:
                if content.type == "tool_use":
                    result = self._execute_tool_use(content)
                    tool_results.append({...})
            
            # 4. 如果有工具使用则继续,否则完成
            if tool_results:
                self.messages.append({"role": "user", "content": tool_results})
            else:
                break

关键模式:在有工具使用时持续调用 API,执行它们并将结果反馈回去。

Claude 实际学到了什么

这就是记忆强大的原因 - 语义模式识别,而不仅仅是语法:

会话 1:基于线程的网络爬虫

# 错误:竞态条件
class WebScraper:
    def __init__(self):
        self.results = []  # 共享状态!
    
    def scrape_urls(self, urls):
        with ThreadPoolExecutor() as executor:
            for future in as_completed(futures):
                self.results.append(future.result())  # 竞态!

Claude 在记忆中存储的内容(示例文件:/memories/concurrency_patterns/thread_safety.md):

当 Claude 遇到这种模式时,它将以下见解存储到其记忆文件中:

  • 症状:并发操作中的不一致结果
  • 原因:多个线程修改的共享可变状态(列表/字典)
  • 解决方案:使用锁、线程安全数据结构或返回结果
  • 红旗:线程回调中的实例变量、未使用的锁、计数器增量

会话 2:异步 API 客户端(新对话!)

Claude 首先检查记忆,找到线程安全模式,然后:

  1. 识别异步代码中的类似模式(协程也可以交错)
  2. 立即应用解决方案(无需重新学习)
  3. 解释并参考存储的知识
# Claude 立即发现这个:
async def fetch_all(self, endpoints):
    for coro in asyncio.as_completed(tasks):
        self.responses.append(await coro)  # 相同模式!

为什么这很重要:

  • 语法检查器完全错过竞态条件
  • Claude 学习架构模式并跨上下文应用它们
  • 跨语言:模式也适用于 Go、Java、Rust 并发
  • 变得更好:每次审查都会添加到知识库中

示例代码文件

演示使用这些示例文件(都有并发/线程安全错误):

  • memory_demo/sample_code/web_scraper_v1.py - 竞态条件:线程修改共享状态
  • memory_demo/sample_code/api_client_v1.py - 异步上下文中的类似并发错误
  • memory_demo/sample_code/data_processor_v1.py - 长会话演示的多个并发问题

让我们看看其中一个:

memory_demo/sample_code/web_scraper_v1.py

"""
具有竞态条件错误的并发网络爬虫。
多个线程在没有同步的情况下修改共享状态。
"""

import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List, Dict

import requests


class WebScraper:
    """并发获取多个 URL 的网络爬虫。"""

    def __init__(self, max_workers: int = 10):
        self.max_workers = max_workers
        self.results = []  # 错误:多个线程访问的共享可变状态!
        self.failed_urls = []  # 错误:另一个竞态条件!

    def fetch_url(self, url: str) -> Dict[str, any]:
        """获取单个 URL 并返回结果。"""
        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            return {
                "url": url,
                "status": response.status_code,
                "content_length": len(response.content),
            }
        except requests.exceptions.RequestException as e:
            return {"url": url, "error": str(e)}

    def scrape_urls(self, urls: List[str]) -> List[Dict[str, any]]:
        """
        并发爬取多个 URL。

        错误:self.results 被多个线程访问而没有锁定!
        这会导致结果丢失或损坏的竞态条件。
        """
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            futures = [executor.submit(self.fetch_url, url) for url in urls]

            for future in as_completed(futures):
                result = future.result()

                # 竞态条件:多个线程同时向 self.results 追加
                if "error" in result:
                    self.failed_urls.append(result["url"])  # 竞态条件
                else:
                    self.results.append(result)  # 竞态条件

        return self.results

错误:多个线程在没有锁定的情况下修改 self.resultsself.failed_urls

Claude 将:

  1. 识别竞态条件
  2. /memories/concurrency_patterns/thread_safety.md 中存储模式
  3. 在会话 2 中将此并发模式应用于异步代码

演示概述

我们构建了一个完整的代码审查助手。实现在 memory_demo/code_review_demo.py 中。

运行交互式演示:

python memory_demo/code_review_demo.py

演示展示了:

  1. 会话 1:审查带有错误的 Python 代码 → Claude 学习模式
  2. 会话 2:审查类似代码(新对话)→ Claude 应用模式
  3. 会话 3:长审查会话 → 上下文编辑保持可管理性

7. 最佳实践与安全 {#best-practices}

记忆管理

应该做的:

  • ✅ 存储任务相关模式,而不是对话历史
  • ✅ 使用清晰的目录结构组织
  • ✅ 使用描述性文件名
  • ✅ 定期审查和清理记忆

不应该做的:

  • ❌ 存储敏感信息(密码、API 密钥、PII)
  • ❌ 让记忆无限增长
  • ❌ 不加选择地存储所有内容

安全:路径遍历保护

关键:始终验证路径以防止目录遍历攻击。查看 memory_tool.py 了解实现。

安全:记忆中毒

⚠️ 关键风险:记忆文件被读回到 Claude 的上下文中,使它们成为提示注入的潜在载体。

缓解策略:

  1. 内容清理:在存储前过滤危险模式
  2. 记忆范围隔离:按用户/按项目隔离
  3. 记忆审计:记录和扫描所有记忆操作
  4. 提示工程:指示 Claude 忽略记忆中的指令

查看 memory_tool.py 了解完整的安全实现,以及 tests/ 中的测试。

下一步

资源

反馈

记忆和上下文管理目前处于测试阶段。分享你的反馈以帮助我们改进!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions