رفتن به محتوا

کار با نشست‌ها

یک نشست (session) همان تاریخچه‌ی مکالمه‌ای است که SDK در حینِ کارِ ایجنتت انباشته می‌کند. شاملِ پرامپتت، هر فراخوانیِ ابزاری که ایجنت انجام داده، هر نتیجه‌ی ابزار و هر پاسخ است. SDK آن را به‌صورت خودکار روی دیسک می‌نویسد تا بتوانی بعداً به آن برگردی.

بازگشت به یک نشست یعنی ایجنت کانتکستِ کامل از قبل را دارد: فایل‌هایی که قبلاً خوانده، تحلیلی که قبلاً انجام داده، تصمیماتی که قبلاً گرفته. می‌توانی یک سوالِ پیگیرانه بپرسی، از یک وقفه بازیابی شوی، یا برای امتحانِ رویکردی متفاوت انشعاب بگیری.

این راهنما پوشش می‌دهد که چطور رویکردِ درست را برای اپلیکیشنت انتخاب کنی، چه واسط‌هایی از SDK نشست‌ها را به‌صورت خودکار دنبال می‌کنند، چطور session IDها را بگیری و resume و fork را به‌صورتِ دستی استفاده کنی، و درباره‌ی از سرگیریِ نشست‌ها میانِ میزبان‌ها چه باید بدانی.

اینکه به چقدر مدیریتِ نشست نیاز داری به شکلِ اپلیکیشنت بستگی دارد. مدیریتِ نشست وقتی به میان می‌آید که چند پرامپت می‌فرستی که باید کانتکست را به اشتراک بگذارند. درونِ یک فراخوانیِ query()، ایجنت از قبل هر چند نوبت که لازم باشد می‌گیرد، و پرامپت‌های دسترسی و AskUserQuestion درونِ حلقه مدیریت می‌شوند (فراخوانی را تمام نمی‌کنند).

چیزی که می‌سازیچه چیزی استفاده کن
وظیفه‌ی تک‌شات: یک پرامپت، بدونِ پیگیریهیچ چیزِ اضافی. یک فراخوانیِ query() کافی است.
چتِ چندنوبتی در یک فرایندClaudeSDKClient (Python) یا continue: true (TypeScript). SDK نشست را بدونِ مدیریتِ ID برایت دنبال می‌کند.
ادامه دادن از جایی که رها کردی پس از ری‌استارتِ فرایندcontinue_conversation=True (Python) / continue: true (TypeScript). جدیدترین نشستِ آن دایرکتوری را از سر می‌گیرد، بدونِ نیاز به ID.
از سرگیریِ یک نشستِ گذشته‌ی مشخص (نه جدیدترین)session ID را بگیر و به resume پاس بده.
امتحانِ یک رویکردِ جایگزین بدونِ از دست دادنِ اصلینشست را fork کن.
وظیفه‌ی بدونِ حالت، نمی‌خواهی چیزی روی دیسک نوشته شود (فقط TypeScript)persistSession: false را تنظیم کن. نشست فقط در حافظه برای مدتِ فراخوانی وجود دارد. Python همیشه روی دیسک پایدار می‌کند.

ادامه (continue)، از سرگیری (resume) و fork، فیلدهای گزینه‌ای هستند که روی query() تنظیم می‌کنی (ClaudeAgentOptions در Python، Options در TypeScript).

ادامه و از سرگیری هر دو یک نشستِ موجود را برمی‌دارند و به آن اضافه می‌کنند. تفاوت در نحوه‌ی پیدا کردنِ آن نشست است:

  • ادامه جدیدترین نشستِ دایرکتوریِ فعلی را پیدا می‌کند. چیزی را دنبال نمی‌کنی. وقتی اپت در هر زمان یک مکالمه را اجرا می‌کند خوب کار می‌کند.
  • از سرگیری یک session IDِ مشخص می‌گیرد. تو ID را دنبال می‌کنی. وقتی چند نشست داری لازم است (مثلاً یکی به ازای هر کاربر در یک اپِ چندکاربره) یا می‌خواهی به نشستی برگردی که جدیدترین نیست.

Fork متفاوت است: یک نشستِ جدید می‌سازد که با یک کپی از تاریخچه‌ی اصلی شروع می‌شود. نسخه‌ی اصلی بدونِ تغییر می‌ماند. از fork برای امتحانِ مسیری متفاوت استفاده کن، در حالی که گزینه‌ی بازگشت را حفظ می‌کنی.

هر دو SDK واسطی ارائه می‌دهند که وضعیتِ نشست را میانِ فراخوانی‌ها برایت دنبال می‌کند، پس IDها را به‌صورت دستی این‌طرف‌وآن‌طرف نمی‌بری. این‌ها را برای مکالمه‌های چندنوبتی درونِ یک فرایند استفاده کن.

ClaudeSDKClient session IDها را به‌صورت داخلی مدیریت می‌کند. هر فراخوانیِ client.query() به‌صورت خودکار همان نشست را ادامه می‌دهد. client.receive_response() را فرا بخوان تا روی پیام‌های query فعلی پیمایش کنی. کلاینت را به‌عنوانِ یک async context manager استفاده کن تا برپایی و برچیدنِ اتصال برایت مدیریت شود، یا connect() و disconnect() را به‌صورت دستی فرا بخوان.

این مثال دو query را در برابرِ همان client اجرا می‌کند. اولی از ایجنت می‌خواهد یک ماژول را تحلیل کند؛ دومی از آن می‌خواهد همان ماژول را بازنویسی (refactor) کند. چون هر دو فراخوانی از همان نمونه‌ی client می‌گذرند، query دوم بدونِ هیچ 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())

برای جزئیاتِ اینکه کِی از ClaudeSDKClient در برابرِ تابعِ مستقلِ query() استفاده کنی، مرجعِ SDKِ Python را ببین.

SDKِ TypeScript یک آبجکتِ کلاینتِ نگه‌دارنده‌ی نشست مثلِ ClaudeSDKClient در Python ندارد. در عوض، روی هر فراخوانیِ بعدیِ query() مقدارِ continue: true را پاس بده و SDK جدیدترین نشستِ دایرکتوریِ فعلی را برمی‌دارد. نیازی به دنبال کردنِ ID نیست.

این مثال دو فراخوانیِ مجزای query() انجام می‌دهد. اولی یک نشستِ تازه می‌سازد؛ دومی continue: true را تنظیم می‌کند، که به SDK می‌گوید جدیدترین نشستِ روی دیسک را پیدا و از سر بگیرد. ایجنت کانتکستِ کامل از فراخوانیِ اول را دارد:

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);
}
}

استفاده از گزینه‌های نشست با query()

Section titled “استفاده از گزینه‌های نشست با query()”

از سرگیری و fork به یک session ID نیاز دارند. آن را از فیلدِ session_id روی پیامِ نتیجه (ResultMessage در Python، SDKResultMessage در TypeScript) بخوان، که فارغ از موفقیت یا خطا روی هر نتیجه‌ای حاضر است. در TypeScript، این ID زودتر هم به‌عنوانِ یک فیلدِ مستقیم روی SystemMessage اولیه (init) در دسترس است؛ در 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}`);

یک session ID را به resume پاس بده تا به آن نشستِ مشخص برگردی. ایجنت با کانتکستِ کامل از هر جایی که نشست رها شده ادامه می‌دهد. دلایلِ رایج برای از سرگیری:

  • پیگیریِ یک وظیفه‌ی تمام‌شده. ایجنت چیزی را تحلیل کرده؛ حالا می‌خواهی بر اساسِ آن تحلیل اقدام کند بدونِ بازخوانیِ فایل‌ها.
  • بازیابی از یک سقف. اجرای اول با error_max_turns یا error_max_budget_usd پایان یافت (ببین مدیریتِ نتیجه)؛ با سقفی بالاتر از سر بگیر.
  • ری‌استارتِ فرایندت. ID را پیش از خاموشی گرفتی و می‌خواهی مکالمه را بازیابی کنی.

این مثال نشستِ بخشِ گرفتنِ session 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);
}
}

باید پاسخی ببینی که بر اساسِ تحلیلِ پیشین ساخته می‌شود به‌جای اینکه از نو شروع کند. این تأیید می‌کند که ایجنت نشست را با کانتکستِ پیشینش دست‌نخورده از سر گرفته است.

برای از سرگیریِ نشست‌ها میانِ ماشین‌ها یا در محیط‌های serverless، رونوشت‌ها را با یک آداپتورِ SessionStore به ذخیره‌سازیِ مشترک آینه کن.

Fork برای کاوشِ جایگزین‌ها

Section titled “Fork برای کاوشِ جایگزین‌ها”

fork کردن یک نشستِ جدید می‌سازد که با یک کپی از تاریخچه‌ی اصلی شروع می‌شود ولی از آن نقطه واگرا می‌شود. fork، session IDِ خودش را می‌گیرد؛ ID و تاریخچه‌ی اصلی بدونِ تغییر می‌مانند. در نهایت دو نشستِ مستقل داری که می‌توانی جداگانه از سر بگیری.

این مثال بر اساسِ گرفتنِ session ID ساخته می‌شود: تو از قبل یک ماژولِ احرازِ هویت را در session_id تحلیل کرده‌ای و می‌خواهی OAuth2 را کاوش کنی بدونِ از دست دادنِ رشته‌ی متمرکز بر JWT. بلوکِ اول نشست را fork و ID فورک (forked_id) را می‌گیرد؛ بلوکِ دوم session_id اصلی را از سر می‌گیرد تا مسیرِ JWT را ادامه دهد. حالا دو session ID داری که به دو تاریخچه‌ی جداگانه اشاره می‌کنند:

# 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);
}
}

باید ببینی که forkedId با session IDِ اصلی متفاوت است. از سرگیریِ نشستِ اصلی همچنان رشته‌ی JWT را ادامه می‌دهد، که تأیید می‌کند fork، تاریخچه‌ی اصلی را تغییر نداده است.

از سرگیری میانِ میزبان‌ها

Section titled “از سرگیری میانِ میزبان‌ها”

فایل‌های نشست محلیِ ماشینی هستند که آن‌ها را ساخته. برای از سرگیریِ یک نشست روی میزبانی متفاوت (workerهای CI، کانتینرهای زودگذر، serverless)، دو گزینه داری:

  • فایلِ نشست را جابه‌جا کن. ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl را از اجرای اول پایدار کن و پیش از فراخوانیِ resume آن را به همان مسیر روی میزبانِ جدید بازگردان. cwd باید مطابقت داشته باشد.
  • به از سرگیریِ نشست تکیه نکن. نتایجی را که نیاز داری (خروجیِ تحلیل، تصمیم‌ها، diffهای فایل) به‌عنوانِ وضعیتِ اپلیکیشن بگیر و آن‌ها را به پرامپتِ یک نشستِ تازه پاس بده. این اغلب پایدارتر از جابه‌جا کردنِ فایل‌های رونوشت است.

هر دو SDK توابعی برای فهرست کردنِ نشست‌های روی دیسک و خواندنِ پیام‌هایشان عرضه می‌کنند: listSessions() و getSessionMessages() در TypeScript، list_sessions() و get_session_messages() در Python. از آن‌ها برای ساختنِ انتخاب‌گرهای نشستِ سفارشی، منطقِ پاک‌سازی، یا نمایش‌گرهای رونوشت استفاده کن.

هر دو SDK همچنین توابعی برای جست‌وجو و تغییرِ نشست‌های منفرد عرضه می‌کنند: get_session_info()، rename_session() و tag_session() در Python، و getSessionInfo()، renameSession() و tagSession() در TypeScript. از آن‌ها برای سازماندهیِ نشست‌ها بر اساسِ tag یا دادنِ عنوان‌های قابل‌خواندن برای انسان استفاده کن.