תיעוד 149
מעקב אחר משימות
מעקב אחר משימות בהפעלות של Agent SDK והצגת ההתקדמות של Claude ביישום שלך מתוך קריאות כלים מובנות
בדגמים המפורטים תחת זמינות דגמים, Claude עוקב אחר עבודה מרובת שלבים ללא רשימת משימות כתובה, ו-Claude Code משמיט את כלי מעקב המשימות מהפעלות כברירת מחדל. אינך זקוק לשום דבר בדף זה כדי ש-Claude יעבוד על משימות מרובות שלבים בדגמים אלה.
בהפעלה שכוללת את כלי מעקב המשימות, Claude מנהל רשימת משימות כתובה, ומעדכן את הסטטוס של כל פריט תוך כדי עבודה. אתה רואה כל שינוי בזרם ההודעות כקריאת כלי מובנית. בחר להפעיל זאת בהפעלה (opt in) רק כאשר היישום שלך קורא את קריאות הכלים הללו, בין אם כדי לתעד פעילות משימות ביומן ובין אם כדי לרנדר תצוגת התקדמות משלו.
#זמינות דגמים
הערה: ב-TypeScript Agent SDK גרסה 0.3.233 ומעלה, או ב-Python Agent SDK גרסה 0.2.139 ומעלה, חלה המגבלה הבאה.
הכלים הבאים אינם זמינים ב-Opus 4.8, Sonnet 5, Fable 5, Mythos 5, או בגרסאות מאוחרות יותר של משפחות אלו, אלא אם תבחר להפעיל אותם במפורש:
TodoWriteTaskCreateTaskGetTaskUpdateTaskListבדגמים אחרים, Claude Code מספק את כלי ה-Task כברירת מחדל ואת
TodoWriteרק כאשר מגדיריםCLAUDE_CODE_ENABLE_TASKS=0.
בדגמים המפורטים, אלא אם תבחר לצרף הפעלה, לא תראה בלוקים של tool_use עבור הכלים בזרם ההודעות. ה-Agent SDK מחיל ברירות מחדל אלה באמצעות הקובץ הבינארי של Claude Code שהוא כולל בתוכו. אם תכוון את pathToClaudeCodeExecutable (TypeScript) או cli_path (Python) להתקנת Claude Code משלך, תקבל את הכלים שאותה התקנה מספקת, תחת ברירות המחדל שלה. כדי לראות את הקבוצה המדויקת בהפעלה פעילה, בדוק אילו כלים זמינים. כדי לצרף הפעלה, בצע אחת מהפעולות הבאות:
- ציין את שמו של אחד הכלים באפשרות
allowedTools(TypeScript) אוallowed_tools(Python) - פרט את הכלים באפשרות
tools, המגבילה את הכלים המובנים של ההפעלה רק לאלה שהיא מציינת. כלול את הכלים הרצויים לצד הכלים המובנים האחרים שבהם אתה משתמש - הגדר
CLAUDE_CODE_ENABLE_TODO_TOOLS=1באפשרותenv, כפי שעושות הדוגמאות בדף זה. ב-TypeScript, האפשרותenvמחליפה את סביבת תהליך המשנה, לכן פזר את...process.envכדי לשמור על משתנים שהורשו. ב-Python, האפשרותenvממוזגת על גבי הסביבה שהורשה
#מחזור חיי משימה
Claude מעביר כל משימה דרך מחזור חיים צפוי:
- נוצרה (Created): Claude מוסיף את המשימה במצב
pendingכאשר הוא מזהה משימה - הופעלה (Activated): Claude מגדיר את המשימה כמצב
in_progressכאשר הוא מתחיל בעבודה - הושלמה (Completed): Claude מסמן אותה כהושלמה כאשר המשימה מסתיימת בהצלחה
- הוסרה (Removed): Claude מוחק משימה שאינו זקוק לה עוד על ידי הגדרת
status: "deleted"בקריאתTaskUpdate
#מתי Claude יוצר משימות
בהפעלה שכוללת את כלי מעקב המשימות, Claude יוצר משימות עבור רוב העבודות מרובות השלבים, כגון:
- משימות מורכבות מרובות שלבים הדורשות שלוש פעולות נפרדות או יותר
- רשימות משימות שסופקו על ידי המשתמש כאשר מוזכרים מספר פריטים
- פעולות ארוכות יותר המפיקות תועלת ממעקב אחר התקדמות
- בקשות מפורשות כאשר משתמשים מבקשים ארגון משימות
Claude עשוי לדלג על משימות עבור בקשות קצרות מאוד או בעלות שלב בודד.
#דוגמאות
לפני הרצת דוגמאות אלה, התקן את Claude Agent SDK על פי מדריך ההתחלה המהירה. כל דוגמה בדף זה חולקת את אותה הגדרת הרשאות ואותה התנהגות יציאה:
- מצב הרשאות: בקשות הדוגמה מבקשות מ-Claude לבצע עבודה אמיתית בפרויקט, לכן כל דוגמה מגדירה
permissionMode: "acceptEdits"(TypeScript) אוpermission_mode="acceptEdits"(Python) כדי לאשר אוטומטית את עריכות הקבצים שהעבודה מייצרת. ראה מצבי הרשאות לחלופות. - מגבלת תורות: כל דוגמה רצה עד שהסוכן מסיים ומניב את הודעת התוצאה הסופית שלו. אם הפעלה מגיעה למגבלת התורות שלה קודם לכן, להודעת תוצאה זו יש את תת הסוג
error_max_turns. בדוק אתsubtypeכדי לזהות סיום זה. - טיפול בשגיאות: דוגמאות אלה משתמשות בקריאות
query()בודדות (single-shot). לאחר החזרת תוצאתerror_max_turns, הפונקציהquery()מעלה שגיאה הכוללת אתReached maximum number of turns. כל דוגמה עוטפת את הלולאה שלה בבלוק try כדי לצאת בצורה נקייה כאשר זה קורה. ראה טיפול בתוצאה עבור תת הסוגים של תוצאות.
הערה: הודעות מערכת המשימות, ובהן
SDKTaskNotificationMessage(TypeScript) אוTaskNotificationMessage(Python), מדווחות על משימות רקע כגון פקודות רקע וסוכני משנה. בזרם ההודעות, אתה רואה פעילות משימות כבלוקים שלtool_useבהודעות ה-assistant.
#ניטור שינויים במשימות
הדוגמה הבאה מאזינה לזרם ה-assistant עבור בלוקים מסוג tool_use של TaskCreate ו-TaskUpdate, ומדפיסה שורת + עם הנושא של כל משימה חדשה ושורת עדכון עם מזהה המשימה והסטטוס החדש של כל שינוי סטטוס. השתמש במבנה זה כאשר אתה רוצה יומן של פעילות משימות במקום תצוגה מרונדרת. שורות ה-+ אינן כוללות את המזהים שהוקצו, לכן יומן זה אינו יכול לקשר עדכונים בחזרה לפעולות היצירה שלהם. כדי לשמור על קשר זה, שמור את המזהים כפי שעושה הדוגמה הצגת התקדמות בזמן אמת.
הקלט המוזרם של tool_use הוא המבנה הגולמי שהמודל פלט. Claude Code מתקן שמות מפתחות מסוימים שהיו קרובים אך לא מדויקים לפני הביצוע, כשהוא ממפה את id או task_id ל-taskId ואת active_form ל-activeForm, אך תיקון זה אינו משתקף בזרם. קרא את שדות הקלט של TaskUpdate בצורה מגננתית, כפי שעושות שתי הדוגמאות בדף זה, במקום להניח שהשם התקני תמיד קיים.
#TypeScript
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({
prompt: "Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
// Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
options: { maxTurns: 15, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
})) {
if (message.type !== "assistant") continue;
for (const block of message.message.content) {
if (block.type !== "tool_use") continue;
if (block.name === "TaskCreate") {
const input = block.input as { subject: string };
console.log(`+ ${input.subject}`);
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result.
console.log(`Session ended with an error: ${error}`);
}#Python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
async def main():
try:
async for message in query(
prompt="Create a static website with a home page, an about page, and a shared stylesheet, and track progress with todos",
# Keeps the Task tools on models where Claude Code otherwise doesn't provide them.
options=ClaudeAgentOptions(max_turns=15, permission_mode="acceptEdits", env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"}),
):
if not isinstance(message, AssistantMessage):
continue
for block in message.content:
if not isinstance(block, ToolUseBlock):
continue
if block.name == "TaskCreate":
print(f"+ {block.input.get('subject', '')}")
elif block.name == "TaskUpdate" and block.input.get("status"):
task_id = (
block.input.get("taskId")
or block.input.get("id")
or block.input.get("task_id")
)
if task_id:
print(f" {task_id} -> {block.input['status']}")
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())#הצגת התקדמות בזמן אמת
הדוגמה הבאה מאזינה לזרם ה-assistant עבור בלוקים מסוג tool_use של TaskCreate ו-TaskUpdate, ושומרת מיפוי של משימות לפי מזהה משימה במחלקה TaskTracker, תוך רינדור מחדש של סיכום התקדמות בכל שינוי. הסיכום סופר משימות שהושלמו ומשימות שנמצאות בתהליך, ומציג את התווית activeForm של כל פריט פעיל במקום ה-subject שלו. השתמש במבנה זה כאשר היישום שלך מנהל תצוגת התקדמות במקום לתעד כל אירוע ביומן.
מזהה המשימה שהוקצה אינו מופיע בקלט של TaskCreate. Claude Code מעביר את הפלט המובנה של כל כלי בהודעת המשתמש שנושאת את בלוק ה-tool_result שלו, בתוך השדה tool_use_result. עבור TaskCreate, אובייקט זה מתועד ב-TypeScript בתור TaskCreateOutput תחת סוגי פלט של כלים, וב-Python השדה הוא מילון רגיל באותו מבנה. רכיב המעקב מתאים כל בלוק tool_result לקריאת ה-tool_use שלו לפי tool_use_id וקורא את task.id מתוך ה-tool_use_result של ההודעה המותאמת. Claude יכול לקרוא את הרשימה בחזרה באמצעות TaskList ואת הפרטים המלאים של משימה אחת באמצעות TaskGet.
#TypeScript
import { query } from "@anthropic-ai/claude-agent-sdk";
type Task = { subject: string; activeForm?: string; status: string };
class TaskTracker {
private tasks = new Map<string, Task>();
private pendingCreates = new Map<string, { subject: string; activeForm?: string }>();
displayProgress() {
if (this.tasks.size === 0) {
console.log("\nProgress: no open tasks\n");
return;
}
const items = [...this.tasks.values()];
const completed = items.filter((t) => t.status === "completed").length;
const inProgress = items.filter((t) => t.status === "in_progress").length;
console.log(`\nProgress: ${completed}/${this.tasks.size} completed`);
console.log(`Currently working on: ${inProgress} task(s)\n`);
for (const [id, task] of this.tasks) {
const icon =
task.status === "completed" ? "✅" : task.status === "in_progress" ? "🔧" : "❌";
const text = task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject;
console.log(`${id}. ${icon} ${text}`);
}
}
handleToolUse(block: { id: string; name: string; input: unknown }) {
if (block.name === "TaskCreate") {
const input = block.input as { subject: string; activeForm?: string; active_form?: string };
this.pendingCreates.set(block.id, {
subject: input.subject,
activeForm: input.activeForm ?? input.active_form,
});
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
activeForm?: string;
active_form?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (!taskId) return;
if (input.status === "deleted") {
this.tasks.delete(taskId);
this.displayProgress();
return;
}
const task = this.tasks.get(taskId);
if (!task) return;
if (input.status) task.status = input.status;
const active = input.activeForm ?? input.active_form;
if (active) task.activeForm = active;
this.displayProgress();
}
}
handleToolResult(block: { tool_use_id: string; is_error?: boolean }, result: unknown) {
const create = this.pendingCreates.get(block.tool_use_id);
if (!create) return;
this.pendingCreates.delete(block.tool_use_id);
if (block.is_error) return;
// The result's user message carries the tool's structured output as
// tool_use_result; for TaskCreate that's TaskCreateOutput,
// { task: { id, subject } }.
const out = result as { task?: { id: string } };
if (!out?.task?.id) return;
this.tasks.set(out.task.id, { ...create, status: "pending" });
this.displayProgress();
}
async trackQuery(prompt: string) {
try {
for await (const message of query({
prompt,
options: { maxTurns: 20, permissionMode: "acceptEdits", env: { ...process.env, CLAUDE_CODE_ENABLE_TODO_TOOLS: "1" } },
})) {
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use") this.handleToolUse(block);
}
}
if (message.type === "user" && Array.isArray(message.message.content)) {
for (const block of message.message.content) {
if (block.type === "tool_result") this.handleToolResult(block, message.tool_use_result);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// such as when the maxTurns limit is hit.
console.log(`Session ended with an error: ${error}`);
}
}
}
// Usage
const tracker = new TaskTracker();
await tracker.trackQuery("Build a complete authentication system with todos");#Python
import asyncio
from claude_agent_sdk import (
query,
ClaudeAgentOptions,
AssistantMessage,
UserMessage,
ToolUseBlock,
ToolResultBlock,
)
class TaskTracker:
def __init__(self):
self.tasks: dict[str, dict] = {}
self.pending_creates: dict[str, dict] = {}
def display_progress(self):
if not self.tasks:
print("\nProgress: no open tasks\n")
return
completed = len([t for t in self.tasks.values() if t["status"] == "completed"])
in_progress = len([t for t in self.tasks.values() if t["status"] == "in_progress"])
print(f"\nProgress: {completed}/{len(self.tasks)} completed")
print(f"Currently working on: {in_progress} task(s)\n")
for task_id, task in self.tasks.items():
icon = (
"✅"
if task["status"] == "completed"
else "🔧"
if task["status"] == "in_progress"
else "❌"
)
text = (
task["activeForm"]
if task["status"] == "in_progress" and task.get("activeForm")
else task["subject"]
)
print(f"{task_id}. {icon} {text}")
def handle_tool_use(self, block: ToolUseBlock):
if block.name == "TaskCreate":
self.pending_creates[block.id] = {
"subject": block.input.get("subject", ""),
"activeForm": block.input.get("activeForm") or block.input.get("active_form"),
}
elif block.name == "TaskUpdate":
task_id = (
block.input.get("taskId")
or block.input.get("id")
or block.input.get("task_id")
)
if not task_id:
return
if block.input.get("status") == "deleted":
self.tasks.pop(task_id, None)
self.display_progress()
return
task = self.tasks.get(task_id)
if not task:
return
if block.input.get("status"):
task["status"] = block.input["status"]
active = block.input.get("activeForm") or block.input.get("active_form")
if active:
task["activeForm"] = active
self.display_progress()
def handle_tool_result(self, block: ToolResultBlock, tool_use_result):
create = self.pending_creates.pop(block.tool_use_id, None)
if create is None or block.is_error:
return
# The result's user message carries the tool's structured output as
# tool_use_result; for TaskCreate that's {"task": {"id": ..., "subject": ...}}.
task = (tool_use_result or {}).get("task") or {}
if not task.get("id"):
return
self.tasks[task["id"]] = {**create, "status": "pending"}
self.display_progress()
async def track_query(self, prompt: str):
try:
async for message in query(
prompt=prompt,
options=ClaudeAgentOptions(
max_turns=20,
permission_mode="acceptEdits",
env={"CLAUDE_CODE_ENABLE_TODO_TOOLS": "1"},
),
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
self.handle_tool_use(block)
if isinstance(message, UserMessage) and isinstance(message.content, list):
for block in message.content:
if isinstance(block, ToolResultBlock):
self.handle_tool_result(block, message.tool_use_result)
except Exception as error:
# A single-shot query() raises after yielding an error result,
# such as when the max_turns limit is hit.
print(f"Session ended with an error: {error}")
# Usage
async def main():
tracker = TaskTracker()
await tracker.track_query("Build a complete authentication system with todos")
asyncio.run(main())#תיעוד קשור
- תיעוד הפניה של Agent SDK: TypeScript: האפשרויות, הטיפוסים וסכמות הכלים עבור ה-SDK של TypeScript, כולל סוגי הקלט והפלט של כלי ה-Task
- תיעוד הפניה של Agent SDK: Python: האפשרויות, הטיפוסים ותיעוד הכלים עבור ה-SDK של Python
- קלט מוזרם: שני מצבי הקלט, ומתי להשתמש בקלט מוזרם במקום בקריאות הבודדות שבהן משתמשות דוגמאות אלה
- מתן כלים מותאמים אישית ל-Claude: הגדר כלים משלך באמצעות שרת ה-MCP הפנימי של ה-SDK