# 会话、恢复与分叉

在多个回合间保留上下文，保存会话身份并从已完成消息分叉。

## 让后续问题使用前文

`NewSession` 创建一个持续连接。每次 Send 返回本轮响应流，消费到 result 后再发送下一条消息。它适合“先分析，再根据分析修改”的交互，不需要每轮重新描述全部材料。

```go
session, err := client.NewSession(ctx, options)
if err != nil { return err }
defer session.Close(context.Background())
for _, prompt := range []string{
    "阅读 README.md，总结项目结构。",
    "根据刚才的结构，为新同事安排三个阅读步骤。",
} {
    stream, err := session.Send(ctx, prompt)
    if err != nil { return err }
    result, err := stream.Result(ctx)
    if err != nil { return err }
    if result.IsError { return fmt.Errorf("回合失败：%s", result.Subtype) }
    fmt.Println(result.Result)
}
fmt.Println("会话 ID：", session.ID())
```


![恢复原会话与从完成消息创建新分支的区别](/images/docs/sdk-session-branches.svg)

## 保存哪些信息

至少保存 Session ID、归属用户、工作目录、运行时种类和运行时数据位置。恢复时需要找到原有 transcript，而不只是拿到一段 UUID。不要把客户 A 的 Session ID 接到客户 B 的运行环境。

`session.ID()` 在身份尚未确定时可能为空。可以在收到初始化或结果消息后保存实际 ID；若使用显式 Session ID，要确保在宿主的业务范围内唯一。

`WithPersistSession(false)` 用于不持久化的会话。此类任务不应承诺稍后恢复。关闭 Session 是释放连接，不是删除业务任务，也不是把会话文件自动转移到你的数据库。

## 恢复既有会话

```go
session, err := client.ResumeSession(ctx, savedSessionID, options)
if err != nil { return err }
defer session.Close(context.Background())
stream, err := session.Send(ctx, "继续整理上一轮的结论。")
if err != nil { return err }
result, err := stream.Result(ctx)
if err != nil { return err }
if result.IsError { return fmt.Errorf("恢复后的任务失败：%s", result.Subtype) }
```

Resume 使用指定身份恢复上下文。它不会自动重放你自己的审批界面、附件下载状态或业务数据库事务。服务重启前后的任务状态仍由宿主管理。

服务端应优先使用明确 ID，而不是 `WithContinueConversation(true)` 所表示的最近会话。后者容易在共享目录或多个工作进程中选到不属于当前任务的记录。

## 从已完成消息创建分支

```go
branch, err := client.ForkSession(ctx, savedSessionID, completedMessageID, options)
if err != nil { return err }
defer branch.Close(context.Background())
fmt.Println("新会话：", branch.ID())
```

传入源 Session ID 和一个已完成消息的 UUID。分叉会创建独立目标，适合在同一份已有结论上尝试另一种方案。Options 不应同时指定目标 `Session.ID`；bridge 会分配身份。不要使用尚在生成的消息作为分界。

Claude Code 可能在新会话收到第一条用户消息后才持久化目标 transcript，但返回的 Session 已有目标 ID。持久文件是否存在与会话身份是否分配要分别判断。

## 并发与关闭

同一 Session 的输出由一条底层通道承载。宿主应串行组织回合，或建立一个专门的输入与输出调度器，不要让多个 HTTP 请求分别读取它。

一个用户取消当前任务时，使用 Interrupt；用户离开整个会话或工作进程退出时，使用 Close。清理使用独立的短超时 context。跨用户隔离、进程回收和任务队列见[部署](/docs/sdk-hosting)。
