תיעוד 131
עבודה עם sessions
כיצד sessions שומרים את היסטוריית השיחה של הסוכן, ומתי להשתמש ב-continue, ב-resume וב-fork כדי לחזור להרצה קודמת.
session הוא היסטוריית השיחה שה-SDK צובר בזמן שהסוכן שלכם עובד. הוא מכיל את ההנחיה (prompt) שלכם, כל קריאה לכלי שהסוכן ביצע, כל תוצאת כלי וכל תגובה. ה-SDK כותב אותו לדיסק באופן אוטומטי כדי שתוכלו לחזור אליו מאוחר יותר.
חזרה אל session פירושה שלסוכן יש הקשר מלא מלפני כן: קבצים שהוא כבר קרא, ניתוח שהוא כבר ביצע, החלטות שהוא כבר קיבל. אתם יכולים לשאול שאלת המשך, להתאושש מהפרעה, או להתפצל כדי לנסות גישה שונה.
הערה:
sessionsשומרים את השיחה, לא את מערכת הקבצים. כדי ליצור תמונת מצב (snapshot) ולבטל שינויים בקבצים שהסוכן ביצע, השתמשו ב-file checkpointing.
מדריך זה מסביר כיצד לבחור את הגישה הנכונה ליישום שלכם, את ממשקי ה-SDK שעוקבים אחרי sessions באופן אוטומטי, כיצד ללכוד מזהי session ולהשתמש ב-resume וב-fork באופן ידני, ומה כדאי לדעת על חידוש sessions בין מארחים (hosts).
#בחירת גישה
היקף הטיפול ב-session הדרוש לכם תלוי במבנה היישום שלכם. ניהול session נכנס לתמונה כאשר אתם שולחים מספר הנחיות שאמורות לחלוק הקשר משותף. בתוך קריאת query() בודדת, הסוכן כבר לוקח כמה תורות (turns) שהוא צריך, ובקשות הרשאה ו-AskUserQuestion מטופלות בתוך הלולאה (הן אינן מסיימות את הקריאה).
| מה אתם בונים | במה להשתמש |
|---|---|
| משימה חד-פעמית: הנחיה בודדת, ללא המשך | שום דבר נוסף. קריאת query() אחת מטפלת בזה. |
| שיחה מרובת תורות בתהליך יחיד | ClaudeSDKClient (ב-Python) או continue: true (ב-TypeScript). ה-SDK עוקב אחר ה-session עבורכם ללא ניהול מזהים. |
| המשך מהמקום שבו עצרתם לאחר הפעלה מחדש של תהליך | continue_conversation=True (ב-Python) / continue: true (ב-TypeScript). מחדש את ה-session העדכני ביותר בספריה, ללא צורך במזהה. |
חידוש session מסוים מהעבר (לא העדכני ביותר) | לכדו את ה-session ID והעבירו אותו אל resume. |
| ניסיון גישה חלופית מבלי לאבד את המקור | בצעו fork ל-session. |
| משימה ללא מצב (Stateless), כשאינכם רוצים שדבר ייכתב לדיסק | הגדירו persistSession: false (ב-TypeScript בלבד). ה-session קיים בזיכרון בלבד למשך הקריאה. ב-Python, הגדירו את CLAUDE_CODE_SKIP_PROMPT_HISTORY באפשרות ה-env כדי למנוע כתיבת תמלילים (transcripts) במקום זאת. |
#Continue, resume ו-fork
Continue, resume ו-fork הם שדות אפשרויות שאתם מגדירים ב-query() (ClaudeAgentOptions ב-Python, Options ב-TypeScript).
גם continue וגם resume ממשיכים session קיים ומוסיפים אליו. ההבדל הוא באופן שבו הם מוצאים את אותו session:
- Continue מוצא את ה-
sessionהעדכני ביותר בספריה הנוכחית. אינכם עוקבים אחר שום דבר. פועל היטב כאשר היישום שלכם מריץ שיחה אחת בכל פעם. - Resume מקבל מזהה
session IDספציפי. אתם עוקבים אחר המזהה. נדרש כאשר יש לכם מספרsessions(למשל, אחד לכל משתמש ביישום מרובה משתמשים) או כאשר אתם רוצים לחזור לאחד שאינו העדכני ביותר.
Fork שונה: הוא יוצר session חדש שמתחיל עם עותק של היסטוריית המקור. המקור נשאר ללא שינוי. השתמשו ב-fork כדי לנסות כיוון אחר תוך שמירה על האפשרות לחזור לאחור.
#ניהול sessions אוטומטי
שני ה-SDKs מציעים ממשק שעוקב אחר מצב ה-session עבורכם בין קריאות, כך שאינכם צריכים להעביר מזהים ידנית. השתמשו בהם עבור שיחות מרובות תורות בתוך תהליך יחיד.
#Python: ClaudeSDKClient
ClaudeSDKClient מטפל במזהי session ID באופן פנימי. כל קריאה ל-client.query() ממשיכה אוטומטית את אותו session. קראו ל-client.receive_response() כדי לבצע איטרציה על ההודעות עבור השאילתה הנוכחית. השתמשו בלקוח כ-async context manager כך שהקמת החיבור וסגירתו יטופלו עבורכם, או קראו ל-connect() ול-disconnect() ידנית.
דוגמה זו מריצה שתי שאילתות מול אותו client. הראשונה מבקשת מהסוכן לנתח מודול, והשנייה מבקשת ממנו לבצע refactoring לאותו מודול. מכיוון ששתי הקריאות עוברות דרך אותו מופע לקוח, לשאילתה השנייה יש הקשר מלא מהראשונה ללא 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())כל שאילתה מדפיסה את תגובת הטקסט של הסוכן ואחריה שורת סטטוס מתוך הודעת התוצאה, כגון [done: success, cost: $0.0042].
עיינו ב-Python SDK reference לפרטים על מתי להשתמש ב-ClaudeSDKClient לעומת פונקציית query() העצמאית.
#TypeScript: continue: true
ל-TypeScript SDK אין אובייקט לקוח שמחזיק session כמו ClaudeSDKClient של Python. במקום זאת, העבירו continue: true בכל קריאת query() עוקבת וה-SDK ימשיך את ה-session העדכני ביותר בספריה הנוכחית. אין צורך במעקב אחר מזהה.
דוגמה זו מבצעת שתי קריאות query() נפרדות. הראשונה יוצרת session חדש, והשנייה מגדירה continue: true, מה שמורה ל-SDK למצוא ולחדש את ה-session העדכני ביותר בדיסק. לסוכן יש הקשר מלא מהקריאה הראשונה:
import { query } from "@anthropic-ai/claude-agent-sdk";
// First query: creates a new session
try {
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);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the follow-up query below still runs.
console.error(`Session ended with an error: ${error}`);
}
// 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);
}
}הערה: ממשק ה-API הניסיוני V2 session API, שסיפק את
createSession()עם תבנית שלsend/stream, הוסר ב-TypeScript Agent SDK גרסה 0.3.142. השתמשו בפונקציהquery()ובאפשרויות ה-sessionהמתוארות בדף זה במקום זאת.
#שימוש באפשרויות session עם query()
#לכידת ה-session ID
פעולות resume ו-fork דורשות session ID. קראו אותו מתוך השדה session_id בהודעת התוצאה (ResultMessage ב-Python, SDKResultMessage ב-TypeScript), אשר קיים בכל תוצאה ללא תלות בהצלחה או בשגיאה. ב-TypeScript המזהה זמין גם מוקדם יותר כשדה ישיר בהודעת SystemMessage של ה-init, וב-Python הוא מקונן בתוך SystemMessage.data.
#Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
session_id = None
try:
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)
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the loop above already captured session_id;
# connection or process failures yield no result message, so session_id stays None.
print(f"Session ended with an error: {error}")
print(f"Session ID: {session_id}")
return session_id
session_id = asyncio.run(main())#TypeScript
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
try {
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);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the loop above already captured sessionId;
// connection or process failures yield no result message, so sessionId stays undefined.
console.error(`Session ended with an error: ${error}`);
}
console.log(`Session ID: ${sessionId}`);כאשר השאילתה מסתיימת, הסקריפט מדפיס את תגובת הסוכן ואחריה שורה כגון Session ID: 5b3f2c1a-8d4e-4f6b-9a7c-2e1d0f9b8a6c. בסעיפים הבאים תעבירו מזהה זה ל-resume.
#חידוש לפי מזהה (Resume by ID)
העבירו session ID אל resume כדי לחזור לאותו session ספציפי. הסוכן ממשיך עם הקשר מלא מהמקום שבו ה-session עצר. סיבות נפוצות לחידוש:
- מעקב אחר משימה שהושלמה. הסוכן כבר ניתח משהו, וכעת אתם רוצים שהוא יפעל על סמך אותו ניתוח מבלי לקרוא מחדש קבצים.
- התאוששות ממגבלה. ההרצה הראשונה הסתיימה עם
error_max_turnsאוerror_max_budget_usd(ראו Handle the result), חדשו עם מגבלה גבוהה יותר. בקריאתquery()חד-פעמית ה-SDK זורק שגיאה לאחר הפקת תוצאת שגיאה זו, לכן תפסו את השגיאה לפני החידוש. - הפעלה מחדש של התהליך שלכם. לכדתם את המזהה לפני הכיבוי ואתם רוצים לשחזר את השיחה.
דוגמה זו מחדשת את ה-session מ-לכידת ה-session ID באמצעות הנחיית המשך. מכיוון שאתם מחדשים, לסוכן כבר יש את הניתוח הקודם בהקשר:
#Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
session_id = "..."
# The ID you captured in the previous example
async def main():
# 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)
asyncio.run(main())#TypeScript
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);
}
}אתם אמורים לראות תגובה שמתבססת על הניתוח המוקדם יותר במקום להתחיל מחדש. זה מאשר שהסוכן חידש את ה-session כאשר ההקשר הקודם שלו נותר שלם.
טיפ: Claude Code שומר sessions תחת
~/.claude/projects/<encoded-cwd>/*.jsonl. אם הגדרתם את משתנה הסביבהCLAUDE_CONFIG_DIR, חפשו תחת$CLAUDE_CONFIG_DIR/projects/במקום זאת.כדי למצוא את ספריית ה-
sessionשלכם, החליפו כל תו שאינו אלפאנומרי בנתיב המוחלט של ספריית העבודה בתו-: הנתיב/Users/me/projהופך ל--Users-me-proj. עבור ספריית עבודה שהשם המומר שלה עולה על 200 תווים, Claude Code מקצר את השם ומוסיף גיבוב (hash), לכן התאימו את 200 התווים הראשונים של השם המומר בעת רישום תכולתprojects/.אם הגדרתם את
CLAUDE_CODE_PROJECT_DIR_NAMEלצדCLAUDE_CONFIG_DIR, חפשו תחת אותו שם ב-projects/במקום זאת. דורש את TypeScript Agent SDK בגרסה v0.3.234 ומעלה, או את Python Agent SDK בגרסה v0.2.140 ומעלה.אתם יכולים לחדש מכל ספריית עבודה:
- חיפוש בין ספריות (Cross-directory lookup): Claude Code מחפש מעבר לספריית הפרויקט הנוכחית כדי למצוא את המזהה, ראו Resume a session עבור סדר החיפוש המדויק וכיצד מטופלים עותקים כפולים.
- אותה מכונה בלבד: קובץ ה-
sessionעדיין חייב להתקיים במכונה הנוכחית.לפני גרסה v2.1.223, החיפוש הוגבל לספריית הפרויקט הנוכחית ול-git worktrees שלה, וגרסאות SDK שכוללות CLI ישן יותר עדיין מתנהגות כך.
כדי לחדש sessions בין מכונות או בסביבות serverless, בצעו שיקוף (mirror) של תמלילים לאחסון משותף באמצעות מתאם SessionStore.
#ביצוע Fork כדי לבחון חלופות
ביצוע fork יוצר session חדש שמתחיל עם עותק של היסטוריית המקור אך מתפצל מאותה נקודה. ה-fork מקבל session ID משלו, בעוד שהמזהה וההיסטוריה של המקור נשארים ללא שינוי. בסופו של דבר יש לכם שני sessions עצמאיים שתוכלו לחדש בנפרד.
הערה: ביצוע fork מפצל את היסטוריית השיחה, לא את מערכת הקבצים. אם סוכן מפוצל עורך קבצים, השינויים הללו אמיתיים וגלויים לכל
sessionשעובד באותה ספריה. כדי לפצל ולבטל שינויים בקבצים, השתמשו ב-file checkpointing.
דוגמה זו מתבססת על לכידת ה-session ID: כבר ניתחתם מודול אימות ב-session_id ואתם רוצים לבחון את OAuth2 מבלי לאבד את השרשור שהתמקד ב-JWT. הבלוק הראשון מבצע fork ל-session ולוכד את מזהה ה-fork (בשם forked_id), והבלוק השני מחדש את ה-session_id המקורי כדי להמשיך במסלול ה-JWT. כעת יש לכם שני מזהי session שמצביעים על שתי היסטוריות נפרדות:
#Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
session_id = "..."
# The ID you captured in the previous example
async def main():
# Fork: branch from session_id into a new session
forked_id = None
try:
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)
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, forked_id was already captured by the
# loop above; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
print(f"Forked session: {forked_id}")
# Original session is untouched; resuming it continues the JWT thread
try:
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)
except Exception as error:
# A single-shot query() raises after yielding an error result.
print(f"Session ended with an error: {error}")
asyncio.run(main())#TypeScript
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;
try {
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);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, forkedId was already captured by the loop
// above; connection or process failures yield no result message.
console.error(`Session ended with an error: ${error}`);
}
console.log(`Forked session: ${forkedId}`);
// Original session is untouched; resuming it continues the JWT thread
try {
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);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result.
console.error(`Session ended with an error: ${error}`);
}אתם אמורים לראות ש-forkedId שונה ממזהה ה-session המקורי. חידוש ה-session המקורי עדיין ממשיך את שרשור ה-JWT, מה שמאשר שה-fork לא שינה את היסטוריית המקור.
#חידוש בין מארחים (hosts)
קובצי session הם מקומיים למכונה שיצרה אותם. כדי לחדש session במארח שונה (סביבות CI, מכולות ארעיות, סביבות serverless), בחרו בגישה המתאימה:
- העברת מאגר sessions (Session store): חברו מתאם
sessionStore/session_storeכך שה-SDK ישקף תמלילים למערכת הקצה (backend) שלכם ומארח אחר יוכל לחדש אותם. מפתח החיפוש במאגר נגזר מספריית העבודה, לכן חדשו מתוךcwdהתואם לזה של ההרצה המקורית. - העברת קובץ ה-session: שמרו את
~/.claude/projects/<encoded-cwd>/<session-id>.jsonlמההרצה הראשונה ושחזרו אותו בתוך ספרייה כלשהי תחת~/.claude/projects/במארח החדש לפני קריאה ל-resume. Claude Code מחפש מעבר לספריית הפרויקט הנוכחית כדי למצוא את המזהה, ראו Resume a session עבור סדר החיפוש המדויק וכיצד מטופלים עותקים כפולים. לפני גרסה v2.1.223, החיפוש הוגבל לספריית הפרויקט הנוכחית ול-git worktrees שלה, וגרסאות SDK שכוללות CLI ישן יותר עדיין מתנהגות כך. - אל תסתמכו על חידוש session: לכדו את התוצאות הדרושות לכם (פלט ניתוח, החלטות, קובצי diff) כמצב יישום (application state) והעבירו אותן לתוך ההנחיה של
sessionחדש. גישה זו לעיתים קרובות יציבה יותר מאשר העברת קובצי תמליל ממקום למקום.
שני ה-SDKs חושפים פונקציות למניית sessions בדיסק ולקריאת ההודעות שלהם: listSessions() ו-getSessionMessages() ב-TypeScript, ו-list_sessions() ו-get_session_messages() ב-Python. השתמשו בהן כדי לבנות רכיבי בחירת session מותאמים אישית, לוגיקת ניקוי, או מציגי תמלילים.
שני ה-SDKs חושפים גם פונקציות לאיתור ושינוי של sessions בודדים: get_session_info(), rename_session() ו-tag_session() ב-Python, ו-getSessionInfo(), renameSession() ו-tagSession() ב-TypeScript. השתמשו בהן כדי לארגן sessions לפי תגיות או לתת להם כותרות קריאות לבני אדם.
#משאבים קשורים
- כיצד פועלת לולאת הסוכן (How the agent loop works): הבנת תורות, הודעות וצבירת הקשר בתוך
session. - נקודות ביקורת לקבצים (File checkpointing): יצירת תמונת מצב ושחזור שינויים בקבצים שהסוכן ביצע בתוך
session. ClaudeAgentOptionsב-Python: עזר מלא לאפשרויותsessionעבור Python.Optionsב-TypeScript: עזר מלא לאפשרויותsessionעבור TypeScript.