การทำงานกับ Sessions
วิธีที่ sessions จัดเก็บประวัติการสนทนาของ agent และเวลาที่ควรใช้ continue, resume, และ fork เพื่อกลับไปยัง run ก่อนหน้า
Session คือประวัติการสนทนาที่ SDK สะสมไว้ขณะที่ agent ของคุณทำงาน ซึ่งประกอบด้วย prompt ของคุณ, การเรียกใช้ tool ทุกครั้งที่ agent ทำ, ผลลัพธ์ของ tool ทุกรายการ, และทุก response SDK จะเขียนข้อมูลนี้ลงดิสก์โดยอัตโนมัติ เพื่อให้คุณสามารถกลับมาดูได้ในภายหลัง
การกลับมายัง session หมายความว่า agent มี context เต็มรูปแบบจากก่อนหน้า: ไฟล์ที่อ่านไปแล้ว, การวิเคราะห์ที่ทำไปแล้ว, และการตัดสินใจที่ทำไปแล้ว คุณสามารถถามคำถามติดตาม, กู้คืนจากการหยุดชะงัก, หรือแตกกิ่งออกเพื่อลองแนวทางที่แตกต่างออกไปได้
Sessions จัดเก็บ การสนทนา ไม่ใช่ filesystem หากต้องการสร้าง snapshot และย้อนกลับการเปลี่ยนแปลงไฟล์ที่ agent ทำ ให้ใช้ file checkpointing
คู่มือนี้ครอบคลุมวิธีเลือกแนวทางที่เหมาะสมสำหรับแอปพลิเคชันของคุณ, interface ของ SDK ที่ติดตาม sessions โดยอัตโนมัติ, วิธีจับ session ID และใช้ resume กับ fork ด้วยตนเอง, และสิ่งที่ควรรู้เกี่ยวกับการ resume sessions ข้ามโฮสต์
เลือกแนวทาง
ปริมาณ session handling ที่คุณต้องการขึ้นอยู่กับรูปแบบของแอปพลิเคชันของคุณ การจัดการ session จะเข้ามามีบทบาทเมื่อคุณส่ง prompt หลายรายการที่ควรแชร์ context ร่วมกัน ภายในการเรียก query() ครั้งเดียว agent จะรับจำนวน turn ที่ต้องการเอง และ permission prompts กับ AskUserQuestion จะถูกจัดการใน loop (ดูที่นี่)
| สิ่งที่คุณกำลังสร้าง | สิ่งที่ควรใช้ |
|---|---|
| งานแบบ one-shot: prompt เดียว ไม่มี follow-up | ไม่ต้องทำอะไรเพิ่ม การเรียก query() ครั้งเดียวจัดการได้ |
| Multi-turn chat ในกระบวนการเดียว | ClaudeSDKClient (Python) หรือ continue: true (TypeScript) SDK จะติดตาม session ให้โดยไม่ต้องจัดการ ID |
| กลับมาต่อจากที่หยุดหลังจาก process รีสตาร์ท | continue_conversation=True (Python) / continue: true (TypeScript) Resume session ล่าสุดในไดเรกทอรี ไม่ต้องใช้ ID |
| Resume session เฉพาะในอดีต (ไม่ใช่ล่าสุด) | จับ session ID แล้วส่งไปที่ resume |
| ลองแนวทางอื่นโดยไม่สูญเสียต้นฉบับ | Fork session |
| งานแบบ stateless ไม่ต้องการเขียนลงดิสก์ (TypeScript เท่านั้น) | ตั้งค่า persistSession: false Session จะอยู่ในหน่วยความจำตลอดระยะเวลาของการเรียก Python จะ persist ลงดิสก์เสมอ |
Continue, Resume, และ Fork
Continue, Resume, และ Fork คือ option fields ที่คุณตั้งค่าบน query() (ClaudeAgentOptions ใน Python, Options ใน TypeScript)
Continue และ Resume ทั้งคู่เลือก session ที่มีอยู่และเพิ่มเนื้อหาเข้าไป ความแตกต่างคือวิธีที่พวกเขาค้นหา session นั้น:
- Continue ค้นหา session ล่าสุดในไดเรกทอรีปัจจุบัน ไม่ต้องติดตามอะไร เหมาะเมื่อแอปของคุณทำงานการสนทนาหนึ่งครั้งต่อหนึ่งครั้ง
- Resume รับ session ID เฉพาะ คุณต้องติดตาม ID เองด้วย จำเป็นเมื่อคุณมีหลาย session หรือต้องการกลับไปยัง session ที่ไม่ใช่ล่าสุด
Fork แตกต่างออกไป: มันสร้าง session ใหม่ที่เริ่มต้นด้วยสำเนาของประวัติต้นฉบับ ต้นฉบับจะยังคงไม่เปลี่ยนแปลง ใช้ fork เพื่อลองทิศทางที่แตกต่างพร้อมกับยังมีตัวเลือกที่จะกลับมา
การจัดการ Session อัตโนมัติ
ทั้งสอง SDK มี interface ที่ติดตาม session state ให้คุณในการเรียก ดังนั้นคุณไม่ต้องส่ง ID ด้วยตนเอง ใช้สิ่งเหล่านี้สำหรับการสนทนา multi-turn ภายใน process เดียว
Python: ClaudeSDKClient
ClaudeSDKClient จัดการ session ID ภายใน การเรียก client.query() แต่ละครั้งจะ continue session เดิมโดยอัตโนมัติ เรียก client.receive_response() เพื่อวนซ้ำข้อความสำหรับ query ปัจจุบัน ใช้ client เป็น async context manager เพื่อให้จัดการการตั้งค่าและยกเลิกการเชื่อมต่อให้คุณ หรือเรียก connect() และ disconnect() ด้วยตนเอง
ตัวอย่างนี้รัน query สองรายการกับ client เดียวกัน รายการแรกขอให้ agent วิเคราะห์ module; รายการที่สองขอให้ refactor module นั้น เนื่องจากการเรียกทั้งสองผ่าน client instance เดียวกัน query ที่สองจึงมี context เต็มรูปแบบจากรายการแรกโดยไม่ต้องระบุ resume หรือ session ID อย่างชัดเจน:
import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
ResultMessage,
TextBlock,
)
def print_response(message):
"""Print only the human-readable parts of a message."""
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(message, ResultMessage):
cost = (
f"${message.total_cost_usd:.4f}"
if message.total_cost_usd is not None
else "N/A"
)
print(f"[done: {message.subtype}, cost: {cost}]")
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Grep"],
)
async with ClaudeSDKClient(options=options) as client:
# First query: client captures the session ID internally
await client.query("Analyze the auth module")
async for message in client.receive_response():
print_response(message)
# Second query: automatically continues the same session
await client.query("Now refactor it to use JWT")
async for message in client.receive_response():
print_response(message)
asyncio.run(main())
ดู Python SDK reference สำหรับรายละเอียดเกี่ยวกับเวลาที่จะใช้ ClaudeSDKClient เทียบกับ standalone query()
TypeScript: continue: true
TypeScript SDK ไม่มี session-holding client object เหมือน ClaudeSDKClient ของ Python แต่ให้ส่ง continue: true กับการเรียก query() แต่ละครั้งถัดไป และ SDK จะเลือก session ล่าสุดในไดเรกทอรีปัจจุบัน ไม่ต้องติดตาม ID
ตัวอย่างนี้ทำการเรียก query() แยกสองครั้ง ครั้งแรกสร้าง session ใหม่; ครั้งที่สองตั้งค่า continue: true ซึ่งบอก SDK ให้ค้นหาและ resume session ล่าสุดบนดิสก์ Agent มี context เต็มรูปแบบจากการเรียกครั้งแรก:
import { query } from "@anthropic-ai/claude-agent-sdk";
// First query: creates a new session
for await (const message of query({
prompt: "Analyze the auth module",
options: { allowedTools: ["Read", "Glob", "Grep"] }
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
// Second query: continue: true resumes the most recent 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" && message.subtype === "success") {
console.log(message.result);
}
}
V2 session API แบบทดลอง ซึ่งให้ createSession() พร้อมรูปแบบ send / stream ถูกลบออกใน TypeScript Agent SDK 0.3.142 ให้ใช้ฟังก์ชัน query() และ session options ที่อธิบายในหน้านี้แทน
ใช้ Session Options กับ query()
จับ Session ID
Resume และ Fork ต้องการ session ID อ่านจาก field session_id บน result message (ResultMessage ใน Python, SDKResultMessage ใน TypeScript) ซึ่งมีอยู่ในทุก result ไม่ว่าจะสำเร็จหรือเกิดข้อผิดพลาด ใน TypeScript ID ยังสามารถดูได้เร็วขึ้นในฐานะ field โดยตรงบน init SystemMessage; ใน Python จะซ้อนอยู่ใน SystemMessage.data
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
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
if message.subtype == "success":
print(message.result)
print(f"Session ID: {session_id}")
return session_id
session_id = asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
for await (const message of query({
prompt: "Analyze the auth module and suggest improvements",
options: { allowedTools: ["Read", "Glob", "Grep"] }
})) {
if (message.type === "result") {
sessionId = message.session_id;
if (message.subtype === "success") {
console.log(message.result);
}
}
}
console.log(`Session ID: ${sessionId}`);
Resume ด้วย ID
ส่ง session ID ไปที่ resume เพื่อกลับไปยัง session นั้นโดยเฉพาะ Agent จะเลือกต่อด้วย context เต็มรูปแบบจากจุดที่ session หยุดไว้ เหตุผลทั่วไปในการ resume:
- ติดตาม task ที่เสร็จแล้ว Agent วิเคราะห์บางอย่างแล้ว ตอนนี้คุณต้องการให้มันดำเนินการตามการวิเคราะห์นั้นโดยไม่ต้องอ่านไฟล์ใหม่
- กู้คืนจากข้อจำกัด Run แรกจบด้วย
error_max_turnsหรือerror_max_budget_usd; resume ด้วยข้อจำกัดที่สูงกว่า - รีสตาร์ท process ของคุณ คุณจับ ID ก่อนปิดระบบและต้องการกู้คืนการสนทนา
# Earlier session analyzed the code; now build on that analysis
async for message in query(
prompt="Now implement the refactoring you suggested",
options=ClaudeAgentOptions(
resume=session_id,
allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
import { query } from "@anthropic-ai/claude-agent-sdk";
const sessionId = "..."; // The ID you captured in the previous example
// Earlier session analyzed the code; now build on that analysis
for await (const message of query({
prompt: "Now implement the refactoring you suggested",
options: {
resume: sessionId,
allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
หากการเรียก resume ส่งคืน session ใหม่แทนที่จะเป็นประวัติที่คาดหวัง สาเหตุที่พบบ่อยที่สุดคือ cwd ไม่ตรงกัน Sessions จัดเก็บไว้ที่ ~/.claude/projects/<encoded-cwd>/*.jsonl หรือที่ $CLAUDE_CONFIG_DIR/projects/<encoded-cwd>/*.jsonl หากคุณตั้งค่าตัวแปรสภาพแวดล้อม CLAUDE_CONFIG_DIR โดยที่ <encoded-cwd> คือไดเรกทอรีทำงานแบบ absolute ที่แทนที่อักขระที่ไม่ใช่ตัวอักษรและตัวเลขทุกตัวด้วย - หากการเรียก resume ของคุณรันจากไดเรกทอรีอื่น SDK จะค้นหาในที่ผิด
Fork เพื่อสำรวจทางเลือก
การ Fork สร้าง session ใหม่ที่เริ่มต้นด้วยสำเนาของประวัติต้นฉบับแต่แตกออกจากจุดนั้น Fork จะได้ session ID ของตัวเอง; ID และประวัติของต้นฉบับจะยังคงไม่เปลี่ยนแปลง คุณจะได้ session อิสระสองรายการที่สามารถ resume แยกกัน
การ Fork แตก ประวัติการสนทนา ไม่ใช่ filesystem หาก agent ที่ fork แก้ไขไฟล์ การเปลี่ยนแปลงเหล่านั้นจะเป็นจริงและมองเห็นได้ในทุก session ที่ทำงานในไดเรกทอรีเดียวกัน หากต้องการแยกและย้อนกลับการเปลี่ยนแปลงไฟล์ ให้ใช้ file checkpointing
# Fork: branch from session_id into a new session
forked_id = None
async for message in query(
prompt="Instead of JWT, outline how OAuth2 would work for the auth module",
options=ClaudeAgentOptions(
resume=session_id,
fork_session=True,
max_turns=5,
),
):
if isinstance(message, ResultMessage):
forked_id = message.session_id # The fork's ID, distinct from session_id
if message.subtype == "success":
print(message.result)
print(f"Forked session: {forked_id}")
# Original session is untouched; resuming it continues the JWT thread
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(message.result)
import { query } from "@anthropic-ai/claude-agent-sdk";
const sessionId = "..."; // The ID you captured in the previous example
// Fork: branch from sessionId into a new session
let forkedId: string | undefined;
for await (const message of query({
prompt: "Instead of JWT, outline how OAuth2 would work for the auth module",
options: {
resume: sessionId,
forkSession: true,
maxTurns: 5
}
})) {
if (message.type === "system" && message.subtype === "init") {
forkedId = message.session_id; // The fork's ID, distinct from sessionId
}
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
console.log(`Forked session: ${forkedId}`);
// Original session is untouched; resuming it continues the JWT thread
for await (const message of query({
prompt: "Continue with the JWT approach",
options: { resume: sessionId }
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
Resume ข้ามโฮสต์
ไฟล์ session เป็น local สำหรับเครื่องที่สร้างมัน หากต้องการ resume session บนโฮสต์อื่น (CI workers, ephemeral containers, serverless) คุณมีสองตัวเลือก:
- ย้ายไฟล์ session Persist
~/.claude/projects/<encoded-cwd>/<session-id>.jsonlจาก run แรกและกู้คืนไปยัง path เดียวกันบนโฮสต์ใหม่ก่อนเรียกresumecwdต้องตรงกัน - ไม่พึ่งพา session resume จับผลลัพธ์ที่คุณต้องการ (output การวิเคราะห์, การตัดสินใจ, file diffs) เป็น application state และส่งเข้า prompt ของ session ใหม่ มักจะ robust กว่าการส่งไฟล์ transcript ไปรอบ
ทั้งสอง SDK มีฟังก์ชันสำหรับการ enumerate sessions บนดิสก์และอ่านข้อความ: listSessions() และ getSessionMessages() ใน TypeScript, list_sessions() และ get_session_messages() ใน Python ใช้เพื่อสร้าง session picker แบบ custom, logic การล้างข้อมูล, หรือ transcript viewers
ทั้งสอง SDK ยังมีฟังก์ชันสำหรับค้นหาและแก้ไข session แต่ละรายการ: get_session_info(), rename_session(), และ tag_session() ใน Python, และ getSessionInfo(), renameSession(), และ tagSession() ใน TypeScript ใช้เพื่อจัดระเบียบ sessions ด้วย tag หรือให้ชื่อที่อ่านง่าย
แหล่งข้อมูลที่เกี่ยวข้อง
- วิธีที่ agent loop ทำงาน: ทำความเข้าใจ turns, messages, และการสะสม context ภายใน session
- File checkpointing: Snapshot และย้อนกลับการเปลี่ยนแปลงไฟล์ที่ agent ทำภายใน session
- Python
ClaudeAgentOptions: การอ้างอิง session option เต็มรูปแบบสำหรับ Python - TypeScript
Options: การอ้างอิง session option เต็มรูปแบบสำหรับ TypeScript