文档索引
在此获取完整文档索引: https://code.claude.com/docs/llms.txt 使用此文件发现所有可用页面,然后再进一步探索。
使用检查点回退文件更改
跟踪智能体会话期间的文件更改,并将文件恢复到任何先前状态
文件检查点跟踪智能体会话期间通过 Write、Edit 和 NotebookEdit 工具进行的文件修改,允许您将文件回退到任何先前状态。想试试?跳转到交互式示例。
使用检查点,您可以:
- 撤消不需要的更改,将文件恢复到已知良好状态
- 探索替代方案,恢复到检查点并尝试不同的方法
- 从错误中恢复,当智能体进行了不正确的修改时
只有通过 Write、Edit 和 NotebookEdit 工具进行的更改会被跟踪。通过 Bash 命令(如 echo > file.txt 或 sed -i)进行的更改不会被检查点系统捕获。
检查点的工作原理
当您启用文件检查点时,SDK 会在通过 Write、Edit 或 NotebookEdit 工具修改文件之前创建文件备份。响应流中的用户消息包含一个检查点 UUID,您可以用作恢复点。
检查点与智能体用于修改文件的以下内置工具配合工作:
| 工具 | 描述 |
|---|---|
| Write | 创建新文件或用新内容覆盖现有文件 |
| Edit | 对现有文件的特定部分进行有针对性的编辑 |
| NotebookEdit | 修改 Jupyter 笔记本(.ipynb 文件)中的单元格 |
文件回退将磁盘上的文件恢复到先前状态。它不会回退对话本身。调用 rewindFiles()(TypeScript)或 rewind_files()(Python)后,对话历史和上下文保持不变。
检查点系统跟踪:
- 会话期间创建的文件
- 会话期间修改的文件
- 被修改文件的原始内容
当您回退到检查点时,创建的文件会被删除,修改的文件会恢复到该时间点的内容。
实现检查点
要使用文件检查点,在选项中启用它,从响应流中捕获检查点 UUID,然后在需要恢复时调用 rewindFiles()(TypeScript)或 rewind_files()(Python)。
以下示例展示了完整流程:启用检查点,从响应流中捕获检查点 UUID 和会话 ID,然后稍后恢复会话以回退文件。每个步骤在下面详细解释。
import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
UserMessage,
ResultMessage,
)
async def main():
# Step 1: Enable checkpointing
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits", # Auto-accept file edits without prompting
extra_args={
"replay-user-messages": None
}, # Required to receive checkpoint UUIDs in the response stream
)
checkpoint_id = None
session_id = None
# Run the query and capture checkpoint UUID and session ID
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
# Step 2: Capture checkpoint UUID from the first user message
async for message in client.receive_response():
if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
checkpoint_id = message.uuid
if isinstance(message, ResultMessage) and not session_id:
session_id = message.session_id
# Step 3: Later, rewind by resuming the session with an empty prompt
if checkpoint_id and session_id:
async with ClaudeSDKClient(
ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
await client.query("") # Empty prompt to open the connection
async for message in client.receive_response():
await client.rewind_files(checkpoint_id)
break
print(f"Rewound to checkpoint: {checkpoint_id}")
asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
// Step 1: Enable checkpointing
const opts = {
enableFileCheckpointing: true,
permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting
extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream
};
const response = query({
prompt: "Refactor the authentication module",
options: opts
});
let checkpointId: string | undefined;
let sessionId: string | undefined;
// Step 2: Capture checkpoint UUID from the first user message
for await (const message of response) {
if (message.type === "user" && message.uuid && !checkpointId) {
checkpointId = message.uuid;
}
if ("session_id" in message && !sessionId) {
sessionId = message.session_id;
}
}
// Step 3: Later, rewind by resuming the session with an empty prompt
if (checkpointId && sessionId) {
const rewindQuery = query({
prompt: "", // Empty prompt to open the connection
options: { ...opts, resume: sessionId }
});
for await (const msg of rewindQuery) {
await rewindQuery.rewindFiles(checkpointId);
break;
}
console.log(`Rewound to checkpoint: ${checkpointId}`);
}
}
main();
启用检查点
配置您的 SDK 选项以启用检查点并接收检查点 UUID:
选项 Python TypeScript 描述 启用检查点 enable_file_checkpointing=TrueenableFileCheckpointing: true跟踪文件更改以进行回退 接收检查点 UUID extra_args={"replay-user-messages": None}extraArgs: { 'replay-user-messages': null }必需,以便在流中获取用户消息 UUID options = ClaudeAgentOptions( enable_file_checkpointing=True, permission_mode="acceptEdits", extra_args={"replay-user-messages": None}, ) async with ClaudeSDKClient(options) as client: await client.query("Refactor the authentication module")const response = query({ prompt: "Refactor the authentication module", options: { enableFileCheckpointing: true, permissionMode: "acceptEdits" as const, extraArgs: { "replay-user-messages": null } } });捕获检查点 UUID 和会话 ID
设置
replay-user-messages选项后(如上所示),响应流中的每个用户消息都有一个作为检查点的 UUID。对于大多数用例,捕获第一个用户消息 UUID(
message.uuid);回退到它会将所有文件恢复到原始状态。要存储多个检查点并回退到中间状态,请参阅多个恢复点。捕获会话 ID(
message.session_id)是可选的;您只需要在流完成后稍后回退时才需要它。如果您在处理消息时立即调用rewindFiles()(如在风险操作前设置检查点中的示例),可以跳过捕获会话 ID。checkpoint_id = None session_id = None async for message in client.receive_response(): # Update checkpoint on each user message (keeps the latest) if isinstance(message, UserMessage) and message.uuid: checkpoint_id = message.uuid # Capture session ID from the result message if isinstance(message, ResultMessage): session_id = message.session_idlet checkpointId: string | undefined; let sessionId: string | undefined; for await (const message of response) { // Update checkpoint on each user message (keeps the latest) if (message.type === "user" && message.uuid) { checkpointId = message.uuid; } // Capture session ID from any message that has it if ("session_id" in message) { sessionId = message.session_id; } }回退文件
要在流完成后回退,请使用空提示恢复会话并使用检查点 UUID 调用
rewind_files()(Python)或rewindFiles()(TypeScript)。您也可以在流期间回退;有关该模式,请参阅在风险操作前设置检查点。async with ClaudeSDKClient( ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id) ) as client: await client.query("") # Empty prompt to open the connection async for message in client.receive_response(): await client.rewind_files(checkpoint_id) breakconst rewindQuery = query({ prompt: "", // Empty prompt to open the connection options: { ...opts, resume: sessionId } }); for await (const msg of rewindQuery) { await rewindQuery.rewindFiles(checkpointId); break; }如果您捕获了会话 ID 和检查点 UUID,也可以从 CLI 回退:
claude -p --resume <session-id> --rewind-files <checkpoint-uuid>
常见模式
这些模式展示了根据您的用例捕获和使用检查点 UUID 的不同方式。
在风险操作前设置检查点
此模式仅保留最新的检查点 UUID,在每个智能体轮次之前更新它。如果在处理过程中出现问题,您可以立即回退到上一个安全状态并跳出循环。
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage
async def main():
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits",
extra_args={"replay-user-messages": None},
)
safe_checkpoint = None
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
async for message in client.receive_response():
# Update checkpoint before each agent turn starts
# This overwrites the previous checkpoint. Only keep the latest
if isinstance(message, UserMessage) and message.uuid:
safe_checkpoint = message.uuid
# Decide when to revert based on your own logic
# For example: error detection, validation failure, or user input
if your_revert_condition and safe_checkpoint:
await client.rewind_files(safe_checkpoint)
# Exit the loop after rewinding, files are restored
break
asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
const response = query({
prompt: "Refactor the authentication module",
options: {
enableFileCheckpointing: true,
permissionMode: "acceptEdits" as const,
extraArgs: { "replay-user-messages": null }
}
});
let safeCheckpoint: string | undefined;
for await (const message of response) {
// Update checkpoint before each agent turn starts
// This overwrites the previous checkpoint. Only keep the latest
if (message.type === "user" && message.uuid) {
safeCheckpoint = message.uuid;
}
// Decide when to revert based on your own logic
// For example: error detection, validation failure, or user input
if (yourRevertCondition && safeCheckpoint) {
await response.rewindFiles(safeCheckpoint);
// Exit the loop after rewinding, files are restored
break;
}
}
}
main();
多个恢复点
如果 Claude 跨多轮进行更改,您可能想要回退到特定点而不是全部回退。例如,如果 Claude 在第一轮重构了文件并在第二轮添加了测试,您可能想要保留重构但撤消测试。
此模式将所有检查点 UUID 存储在带有元数据的数组中。会话完成后,您可以回退到任何先前的检查点:
import asyncio
from dataclasses import dataclass
from datetime import datetime
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
UserMessage,
ResultMessage,
)
# Store checkpoint metadata for better tracking
@dataclass
class Checkpoint:
id: str
description: str
timestamp: datetime
async def main():
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits",
extra_args={"replay-user-messages": None},
)
checkpoints = []
session_id = None
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
async for message in client.receive_response():
if isinstance(message, UserMessage) and message.uuid:
checkpoints.append(
Checkpoint(
id=message.uuid,
description=f"After turn {len(checkpoints) + 1}",
timestamp=datetime.now(),
)
)
if isinstance(message, ResultMessage) and not session_id:
session_id = message.session_id
# Later: rewind to any checkpoint by resuming the session
if checkpoints and session_id:
target = checkpoints[0] # Pick any checkpoint
async with ClaudeSDKClient(
ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
await client.query("") # Empty prompt to open the connection
async for message in client.receive_response():
await client.rewind_files(target.id)
break
print(f"Rewound to: {target.description}")
asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
// Store checkpoint metadata for better tracking
interface Checkpoint {
id: string;
description: string;
timestamp: Date;
}
async function main() {
const opts = {
enableFileCheckpointing: true,
permissionMode: "acceptEdits" as const,
extraArgs: { "replay-user-messages": null }
};
const response = query({
prompt: "Refactor the authentication module",
options: opts
});
const checkpoints: Checkpoint[] = [];
let sessionId: string | undefined;
for await (const message of response) {
if (message.type === "user" && message.uuid) {
checkpoints.push({
id: message.uuid,
description: `After turn ${checkpoints.length + 1}`,
timestamp: new Date()
});
}
if ("session_id" in message && !sessionId) {
sessionId = message.session_id;
}
}
// Later: rewind to any checkpoint by resuming the session
if (checkpoints.length > 0 && sessionId) {
const target = checkpoints[0]; // Pick any checkpoint
const rewindQuery = query({
prompt: "", // Empty prompt to open the connection
options: { ...opts, resume: sessionId }
});
for await (const msg of rewindQuery) {
await rewindQuery.rewindFiles(target.id);
break;
}
console.log(`Rewound to: ${target.description}`);
}
}
main();
试试看
此完整示例创建一个小型工具文件,让智能体添加文档注释,向您展示更改,然后询问您是否要回退。
开始之前,请确保您已安装 Claude Agent SDK。
创建测试文件
创建一个名为
utils.py(Python)或utils.ts(TypeScript)的新文件,并粘贴以下代码:def add(a, b): return a + b def subtract(a, b): return a - b def multiply(a, b): return a * b def divide(a, b): if b == 0: raise ValueError("Cannot divide by zero") return a / bexport function add(a: number, b: number): number { return a + b; } export function subtract(a: number, b: number): number { return a - b; } export function multiply(a: number, b: number): number { return a * b; } export function divide(a: number, b: number): number { if (b === 0) { throw new Error("Cannot divide by zero"); } return a / b; }运行交互式示例
在与工具文件相同的目录中创建一个名为
try_checkpointing.py(Python)或try_checkpointing.ts(TypeScript)的新文件,并粘贴以下代码。此脚本要求 Claude 为您的工具文件添加文档注释,然后给您回退和恢复原始文件的选项。
import asyncio from claude_agent_sdk import ( ClaudeSDKClient, ClaudeAgentOptions, UserMessage, ResultMessage, ) async def main(): # Configure the SDK with checkpointing enabled # - enable_file_checkpointing: Track file changes for rewinding # - permission_mode: Auto-accept file edits without prompting # - extra_args: Required to receive user message UUIDs in the stream options = ClaudeAgentOptions( enable_file_checkpointing=True, permission_mode="acceptEdits", extra_args={"replay-user-messages": None}, ) checkpoint_id = None # Store the user message UUID for rewinding session_id = None # Store the session ID for resuming print("Running agent to add doc comments to utils.py...\n") # Run the agent and capture checkpoint data from the response stream async with ClaudeSDKClient(options) as client: await client.query("Add doc comments to utils.py") async for message in client.receive_response(): # Capture the first user message UUID - this is our restore point if isinstance(message, UserMessage) and message.uuid and not checkpoint_id: checkpoint_id = message.uuid # Capture the session ID so we can resume later if isinstance(message, ResultMessage): session_id = message.session_id print("Done! Open utils.py to see the added doc comments.\n") # Ask the user if they want to rewind the changes if checkpoint_id and session_id: response = input("Rewind to remove the doc comments? (y/n): ") if response.lower() == "y": # Resume the session with an empty prompt, then rewind async with ClaudeSDKClient( ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id) ) as client: await client.query("") # Empty prompt opens the connection async for message in client.receive_response(): await client.rewind_files(checkpoint_id) # Restore files break print( "\n✓ File restored! Open utils.py to verify the doc comments are gone." ) else: print("\nKept the modified file.") asyncio.run(main())import { query } from "@anthropic-ai/claude-agent-sdk"; import * as readline from "readline"; async function main() { // Configure the SDK with checkpointing enabled // - enableFileCheckpointing: Track file changes for rewinding // - permissionMode: Auto-accept file edits without prompting // - extraArgs: Required to receive user message UUIDs in the stream const opts = { enableFileCheckpointing: true, permissionMode: "acceptEdits" as const, extraArgs: { "replay-user-messages": null } }; let sessionId: string | undefined; // Store the session ID for resuming let checkpointId: string | undefined; // Store the user message UUID for rewinding console.log("Running agent to add doc comments to utils.ts...\n"); // Run the agent and capture checkpoint data from the response stream const response = query({ prompt: "Add doc comments to utils.ts", options: opts }); for await (const message of response) { // Capture the first user message UUID - this is our restore point if (message.type === "user" && message.uuid && !checkpointId) { checkpointId = message.uuid; } // Capture the session ID so we can resume later if ("session_id" in message) { sessionId = message.session_id; } } console.log("Done! Open utils.ts to see the added doc comments.\n"); // Ask the user if they want to rewind the changes if (checkpointId && sessionId) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise<string>((resolve) => { rl.question("Rewind to remove the doc comments? (y/n): ", resolve); }); rl.close(); if (answer.toLowerCase() === "y") { // Resume the session with an empty prompt, then rewind const rewindQuery = query({ prompt: "", // Empty prompt opens the connection options: { ...opts, resume: sessionId } }); for await (const msg of rewindQuery) { await rewindQuery.rewindFiles(checkpointId); // Restore files break; } console.log("\n✓ File restored! Open utils.ts to verify the doc comments are gone."); } else { console.log("\nKept the modified file."); } } } main();此示例演示了完整的检查点工作流程:
- 启用检查点:使用
enable_file_checkpointing=True和permission_mode="acceptEdits"配置 SDK 以自动批准文件编辑 - 捕获检查点数据:在智能体运行时,存储第一个用户消息 UUID(您的恢复点)和会话 ID
- 提示回退:智能体完成后,检查您的工具文件以查看文档注释,然后决定是否要撤消更改
- 恢复并回退:如果是,使用空提示恢复会话并调用
rewind_files()以恢复原始文件
- 启用检查点:使用
运行示例
从与工具文件相同的目录运行脚本。
Tip在运行脚本之前,在 IDE 或编辑器中打开您的工具文件(
utils.py或utils.ts)。您将看到文件在智能体添加文档注释时实时更新,然后在您选择回退时恢复到原始状态。python try_checkpointing.pynpx tsx try_checkpointing.ts您将看到智能体添加文档注释,然后出现提示询问您是否要回退。如果选择是,文件将恢复到原始状态。
限制
文件检查点有以下限制:
| 限制 | 描述 |
|---|---|
| 仅限 Write/Edit/NotebookEdit | 通过 Bash 命令进行的更改不会被跟踪 |
| 同一会话 | 检查点绑定到创建它们的会话 |
| 仅文件内容 | 创建、移动或删除目录不会被回退撤消 |
| 本地文件 | 远程或网络文件不会被跟踪 |
故障排除
检查点选项未被识别
如果 enableFileCheckpointing 或 rewindFiles() 不可用,您可能使用的是较旧的 SDK 版本。
解决方案:更新到最新的 SDK 版本:
- Python:
pip install --upgrade claude-agent-sdk - TypeScript:
npm install @anthropic-ai/claude-agent-sdk@latest
用户消息没有 UUID
如果 message.uuid 是 undefined 或缺失,您没有接收到检查点 UUID。
原因:未设置 replay-user-messages 选项。
解决方案:在选项中添加 extra_args={"replay-user-messages": None}(Python)或 extraArgs: { 'replay-user-messages': null }(TypeScript)。
"No file checkpoint found for message" 错误
当指定用户消息 UUID 的检查点数据不存在时会发生此错误。
常见原因:
- 原始会话未启用文件检查点(
enable_file_checkpointing或enableFileCheckpointing未设置为true) - 在尝试恢复和回退之前,会话未正确完成
解决方案:确保在原始会话上设置了 enable_file_checkpointing=True(Python)或 enableFileCheckpointing: true(TypeScript),然后使用示例中显示的模式:捕获第一个用户消息 UUID,完整完成会话,然后使用空提示恢复并调用一次 rewindFiles()。
"ProcessTransport is not ready for writing" 错误
当您在完成响应迭代后调用 rewindFiles() 或 rewind_files() 时会发生此错误。当循环完成时,与 CLI 进程的连接会关闭。
解决方案:使用空提示恢复会话,然后在新查询上调用回退:
# Resume session with empty prompt, then rewind
async with ClaudeSDKClient(
ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
await client.query("")
async for message in client.receive_response():
await client.rewind_files(checkpoint_id)
break
// Resume session with empty prompt, then rewind
const rewindQuery = query({
prompt: "",
options: { ...opts, resume: sessionId }
});
for await (const msg of rewindQuery) {
await rewindQuery.rewindFiles(checkpointId);
break;
}
后续步骤
- 会话:了解如何恢复会话,这是在流完成后回退所必需的。涵盖会话 ID、恢复对话和会话分叉。
- 权限:配置 Claude 可以使用哪些工具以及如何批准文件修改。如果您想要更多控制编辑发生的时间,这很有用。
- TypeScript SDK 参考:完整的 API 参考,包括
query()和rewindFiles()方法的所有选项。 - Python SDK 参考:完整的 API 参考,包括
ClaudeAgentOptions和rewind_files()方法的所有选项。