自学教程

Claude Agent SDK 会话持久化

简介

会话持久化,就是把Agent对话状态(消息历史、工具调用上下文、会话元数据)保存到存储介质,支持中断后恢复会话,继续之前的对话与工具调用流程。

Claude SDK 本身不自带持久化存储,messages 消息数组就是会话核心状态。

  • 适用场景:长任务Agent、多轮工具调用、任务断点续跑、用户跨页面继续对话
  • 核心保存对象:完整 messages 消息列表、会话ID、创建时间、token统计、自定义业务字段
  • 不保存:模型内部记忆,模型无内置状态,所有上下文由传入的 messages 数组决定

区分:Claude Code 的检查点(Checkpoint)是内置会话快照;SDK 需要自己实现会话存储。

前置准备

  1. Python3.10+ / Node.js18+
  2. SDK:anthropic / @anthropic-ai/sdk
  3. 存储可选:本地JSON文件(测试)、SQLite / MySQL、Redis(生产)

核心原理

  1. 每次API交互后,把新增消息追加到 messages 数组
  2. 将完整 messages 序列化为 JSON,存入数据库/文件,绑定唯一会话ID
  3. 恢复会话:根据会话ID读取JSON,反序列化得到完整 messages 数组
  4. 将读取后的 messages 传入SDK接口,继续对话,Agent感知完整历史
  5. 工具调用场景:tool_use 和 tool_result 消息块必须完整保存,丢失会导致工具流程断裂

存储方案选型

存储方案适用场景优点缺点
JSON文件本地测试、简单脚本无需额外服务,简单并发差,不适合多用户
SQLite中小规模单机服务轻量,持久化高并发性能一般
MySQL / PostgreSQL正式业务系统稳定,支持查询需要维护数据库
Redis高并发对话、临时会话读写极快,支持过期内存成本,数据易丢失(需持久化配置)

实战1:Python 会话持久化(JSON文件存储)

简易版本:保存会话到本地json文件,实现保存、加载会话

import os
import json
from pathlib import Path
from anthropic import Anthropic

client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
SESSION_DIR = Path("./agent_sessions")
SESSION_DIR.mkdir(exist_ok=True)

# 保存会话
def save_session(session_id: str, messages: list):
    session_data = {
        "session_id": session_id,
        "messages": messages,
        "updated_at": json.dumps({"time": "now"}),
    }
    path = SESSION_DIR / f"{session_id}.json"
    with open(path, "w", encoding="utf-8") as f:
        json.dump(session_data, f, ensure_ascii=False, indent=2)

# 加载会话
def load_session(session_id: str) -> list | None:
    path = SESSION_DIR / f"{session_id}.json"
    if not path.exists():
        return None
    with open(path, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data["messages"]

# 主会话逻辑
def run_session():
    sid = "demo-session-001"
    messages = load_session(sid) or [{"role": "user", "content": "写一个简单Python工具函数"}]

    resp = client.messages.create(
        model="claude-3-5-sonnet-latest",
        max_tokens=1024,
        messages=messages
    )
    # 将模型返回消息追加进上下文
    messages.append(resp.content[0].to_dict())
    print(resp.content[0].text)

    # 持久化保存
    save_session(sid, messages)
    print(f"会话 {sid} 已保存,下次启动可以继续对话")

if __name__ == "__main__":
    run_session()

实战2:TypeScript 会话持久化(文件存储)

import Anthropic from "@anthropic-ai/sdk";
import fs from "fs/promises";
import path from "path";

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});
const SESSION_DIR = path.join(__dirname, "agent_sessions");

async function initDir() {
  await fs.mkdir(SESSION_DIR, { recursive: true });
}

// 保存会话
async function saveSession(sessionId: string, messages: any[]) {
  const filePath = path.join(SESSION_DIR, `${sessionId}.json`);
  const data = {
    sessionId,
    messages,
    updatedAt: new Date().toISOString()
  };
  await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
}

// 加载会话
async function loadSession(sessionId: string) {
  try {
    const filePath = path.join(SESSION_DIR, `${sessionId}.json`);
    const raw = await fs.readFile(filePath, "utf-8");
    return JSON.parse(raw).messages;
  } catch {
    return null;
  }
}

async function main() {
  await initDir();
  const sid = "demo-session-001";
  const messages = await loadSession(sid) || [{ role: "user", content: "写一个JS工具函数" }];

  const res = await anthropic.messages.create({
    model: "claude-3-5-sonnet-latest",
    max_tokens: 1024,
    messages
  });
  messages.push(res.content[0]);
  console.log(res.content[0].text);
  await saveSession(sid, messages);
  console.log(`会话 ${sid} 已保存`);
}

main();

工具调用场景持久化重点

工具调用会话,tool_use 和 tool_result 消息块必须完整保存,缺一不可。
完整消息结构示例:

[
  {"role":"user","content":"获取当前时间"},
  {"role":"assistant","content":[{"type":"tool_use","id":"tu_xxx","name":"get_current_time","input":{}}]},
  {"role":"user","content":[{"type":"tool_result","tool_use_id":"tu_xxx","content":"2026-10-11"}]},
]

丢失任意一条 tool_use / tool_result,恢复会话后工具流程直接断裂。

Token 裁剪策略(重要)

会话持续累积,messages 会越来越长,超出模型上下文窗口,导致报错。两种方案:

  1. 截断策略:保留最近N轮消息,丢弃最早历史(简单,丢失早期信息)
  2. 摘要压缩策略:定期调用模型,把早期对话压缩成一段摘要,替换原始历史,节省token(推荐长会话)

摘要压缩示例逻辑:

把前面历史对话交给模型,生成对话摘要,用摘要替换旧消息列表,保留最近几轮原始消息。

生产数据库表设计(MySQL示例)

CREATE TABLE agent_session (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  session_id VARCHAR(64) NOT NULL UNIQUE COMMENT '会话唯一ID',
  user_id VARCHAR(64),
  messages JSON NOT NULL COMMENT '完整消息数组',
  total_input_tokens INT DEFAULT 0,
  total_output_tokens INT DEFAULT 0,
  status TINYINT COMMENT '会话状态:0运行,1完成,2暂停',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

最佳实践

  1. 会话ID设计:使用UUID作为session_id,避免ID冲突
  2. 过期清理:给会话设置TTL,长时间未访问自动归档/删除,节省存储与token
  3. 消息追加原则:每次模型返回、每次tool_result,都要追加并保存,不要覆盖旧消息
  4. 备份机制:重要会话做备份,防止存储损坏丢失上下文
  5. 并发锁:多进程同时读写同一会话,增加锁控制,防止消息覆盖
  6. 会话隔离:不同用户、不同任务会话完全隔离,禁止混用messages数组

常见问题

Q:恢复会话后Agent会忘记之前的内容?

A:只要完整保存所有 messages,Agent就能读取全部历史;如果消息丢失,就会丢失对应上下文。模型本身没有持久记忆,全部依赖传入消息数组。

Q:会话保存后,token开销会不会持续变大?

A:是的,每一轮对话都会增加消息长度。长会话必须做消息压缩或者截断,防止超过模型最大上下文限制。

Q:子代理并行任务,子代理会话需要持久化吗?

A:按需。子代理会话独立于主代理;如果子任务允许断点恢复,单独持久化每个子代理的messages;一次性任务可以不保存。

Q:流式输出如何持久化?

A:流式只是分片返回内容,等完整消息块接收完成之后,再追加保存到会话,不要保存不完整的增量块。

小结

会话持久化核心就是持久化存储完整 messages 消息数组。测试用JSON文件,生产推荐MySQL/Redis。工具调用会话必须完整保存tool_use与tool_result块;长会话需要做消息压缩,防止上下文超限。

0 条笔记