
Session 是 Agent 的记忆,SDK 把每次对话存成本地 JSONL。本篇完整拆解三种 session 模式:continue(自动追踪最近会话)、resume(按 ID 精确恢复)、fork(分叉探索不同路径),附 Python/TypeScript 完整代码示例、CLI 对应命令,以及跨机器迁移的两种策略和四条实践建议。
query(),跑完就跑完了,上下文全消失。下次调用,Agent 是个刚开机的无知状态机。~/.claude/projects/<cwd-encoded>/<session-id>.jsonl<cwd-encoded> 是当前工作目录的绝对路径,把非字母数字字符全部替换为 -。例如工作目录是 /Users/me/proj,就变成 -Users-me-proj。
~/.claude/projects/ 下,每个工作目录单独一个子目录(AI 生成示意图) resume,SDK 会去错误的路径找 session 文件,什么都找不到,然后静默地开启一个新 session。这是跨机器 resume 失败的最常见原因。
| 模式 | 如何定位 session | 适用场景 |
|---|---|---|
| continue | 自动找当前目录最近的 session | 单进程内顺序多轮对话,无需手动管 ID |
| resume | 指定 session ID | 多用户 / 多任务、跨进程恢复、需要回到特定历史 |
| fork | 基于指定 session 分叉,生成新 ID | 探索不同实现路径,保留原分支 |
ClaudeSDKClient,它在内部自动追踪 session ID,你完全不需要手动管理:import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
ResultMessage,
TextBlock,
)
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Grep"],
)
async with ClaudeSDKClient(options=options) as client:
# 第一轮:分析认证模块
await client.query("Analyze the auth module")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
# 第二轮:自动续接,"it" 就指 auth module
await client.query("Now refactor it to use JWT")
async for message in client.receive_response():
if isinstance(message, ResultMessage):
print(f"Done, cost: ${message.total_cost_usd:.4f}")
asyncio.run(main())continue: true,每次后续调用带上这个 option,SDK 找当前目录最近的 session:import { query } from "@anthropic-ai/claude-agent-sdk";
// 第一轮
for await (const message of query({
prompt: "Analyze the auth module",
options: { allowedTools: ["Read", "Glob", "Grep"] }
})) {
if (message.type === "result") console.log(message.result);
}
// 第二轮:continue: true 让 SDK 自动找上一个 session
for await (const message of query({
prompt: "Now refactor it to use JWT",
options: {
continue: true,
allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
}
})) {
if (message.type === "result") console.log(message.result);
}ResultMessage.session_id——每次 query 结束都会有SystemMessage(init 类型)的 session_id 字段,在流的最早期就出现;ResultMessage 末尾也有import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def first_run():
session_id = None
async for message in query(
prompt="Analyze the auth module and suggest improvements",
options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"]),
):
if isinstance(message, ResultMessage):
session_id = message.session_id # 从结果消息取 ID
if message.subtype == "success":
print(message.result)
return session_id
session_id = asyncio.run(first_run())
print(f"Captured session: {session_id}")resume 恢复async def follow_up(session_id: str):
async for message in query(
prompt="Now implement the refactoring you suggested",
options=ClaudeAgentOptions(
resume=session_id, # 精确指向上一个 session
allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(follow_up(session_id))max_turns 限制中断,这一轮调高上限后恢复import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# 假设 session_id 已有一轮分析 auth module 的历史
async def explore_alternatives(session_id: str):
# 分叉:从 session_id 的历史出发,探索 OAuth2 路径
forked_id = None
async for message in query(
prompt="Instead of JWT, implement OAuth2 for the auth module",
options=ClaudeAgentOptions(
resume=session_id,
fork_session=True, # 关键:创建新 session,不修改原 session
),
):
if isinstance(message, ResultMessage):
forked_id = message.session_id # fork 的 ID != session_id
if message.subtype == "success":
print(f"OAuth2 approach: {message.result}")
print(f"Fork ID: {forked_id}")
# 原 session_id 未被修改,继续 JWT 路径
async for message in query(
prompt="Continue with the JWT approach",
options=ClaudeAgentOptions(resume=session_id),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(f"JWT approach: {message.result}")
asyncio.run(explore_alternatives(session_id))# continue:接续当前目录最近的 session
claude -c -p "继续上次的任务"
# resume:按 ID 或名字恢复
claude -r "auth-refactor" "Finish this PR"
claude --resume "550e8400-e29b-41d4-a716-446655440000" "继续分析"
# 给 session 命名(方便 resume 时用名字而不是 UUID)
claude -n "auth-refactor" "开始认证模块重构"
# fork:resume 时加 --fork-session flag
claude --resume abc123 --fork-session
# 禁用持久化(TypeScript SDK 专属,CLI 无效)
claude -p --no-session-persistence "一次性查询,不写磁盘"2~/.claude/projects/<cwd-encoded>/<session-id>.jsonl 从机器 A 复制到机器 B 的相同路径(cwd 必须一致),然后正常 resume。list_sessions() / get_session_messages(),TypeScript 的 listSessions() / getSessionMessages(),可以自建 session 选择器或清理逻辑。ClaudeSDKClient(Python)或 continue: true(TypeScript),不要自己管 ID,出错的可能性最小。ResultMessage.session_id 取 ID 就存数据库,不要依赖「最近一个」的隐式逻辑。resume 失效 90% 是 cwd 不匹配,在脚本里可以用 os.chdir() / process.chdir() 显式指定,或者直接传入 session ID 的完整文件路径。persistSession: false 做一次性无状态查询,不写磁盘,适合高频短任务流水线里不需要回溯的场景。allowed_tools、disallowedTools、权限规则语法,以及如何在自动化脚本里精确控制 Agent 能操作什么、不能操作什么。docs.anthropic.com
docs.anthropic.com
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.