התחילו לעבוד עם Claude Managed Agents באמצעות ה-תיעוד שלנו.
נושא חוזר בבלוג ההנדסה שלנו הוא כיצד לבנות סוכנים יעילים ולתכנן harnesses עבור עבודה ארוכת טווח. קו מנחה משותף בעבודה זו הוא ש-harnesses מקודדים הנחות לגבי מה ש-Claude אינו יכול לעשות לבדו. עם זאת, יש להטיל ספק תכוף בהנחות אלו מכיוון שהן עלולות להתיישן ככל שהמודלים משתפרים.
כדוגמה אחת בלבד, בעבודה קודמת מצאנו ש-Claude Sonnet 4.5 היה מסיים משימות בטרם עת כשהרגיש את מגבלת חלון ההקשר שלו מתקרבת – התנהגות המכונה לעיתים "חרדת הקשר". טיפלנו בכך על ידי הוספת איפוס חלון הקשר ל-harness. אך כששילבנו את אותו ה-harness עם Claude Opus 4.5, גילינו שההתנהגות נעלמה. האיפוסים הפכו למשקולת מיותרת.
אנו מצפים ש-harnesses ימשיכו להתפתח. לכן בנינו את Managed Agents: שירות מארח ב-Claude Platform שמפעיל סוכני AI לטווח ארוך מטעמכם, באמצעות סט קטן של ממשקים שנועדו לשרוד כל יישום ספציפי – כולל אלו שאנו מפעילים כיום.
בניית Managed Agents משמעותה הייתה פתרון בעיה ותיקה בעולם המחשוב: כיצד לתכנן מערכת עבור "תוכניות שטרם נהגו". לפני עשרות שנים, מערכות הפעלה פתרו בעיה זו על ידי וירטואליזציה של חומרה להפשטות – תהליך, קובץ – כלליות מספיק עבור תוכניות שטרם התקיימו. ההפשטות שרדו זמן רב יותר מהחומרה. הפקודה read() אדישה לשאלה אם היא ניגשת לחבילת דיסקים משנות ה-70 או ל-SSD מודרני. ההפשטות העליונות נותרו יציבות בעוד שהיישומים שמתחתן השתנו בחופשיות.
Managed Agents פועלים לפי אותו דפוס. ביצענו וירטואליזציה לרכיבי סוכן: session (יומן המכיל את כל מה שקרה, המיועד להוספה בלבד), harness (הלולאה שקוראת ל-Claude ומנתבת את קריאות הכלים של Claude לתשתית הרלוונטית), ו-sandbox (סביבת הרצה שבה Claude יכול להריץ קוד ולערוך קבצים). גישה זו מאפשרת להחליף את היישום של כל אחד מהם מבלי להפריע לאחרים. אנו נחרצים לגבי צורת הממשקים הללו, אך לא לגבי מה שרץ מאחוריהם.
לא לאמץ "חיית מחמד"
התחלנו על ידי הצבת כל רכיבי הסוכן בקונטיינר יחיד, מה שאומר שה-session, ה-harness של הסוכן וה-sandbox, כולם חלקו סביבה אחת. לגישה זו היו יתרונות, כולל עריכות קבצים שהיו קריאות מערכת ישירות (syscalls), ולא היו גבולות שירותים לתכנון.
אך בקישור הכל לקונטיינר אחד, נתקלנו בבעיית תשתית ישנה: אימצנו "חיית מחמד". באנלוגיית "חיות מחמד מול בקר", "חיית מחמד" היא פרט בעל שם, שמטופל ידנית ולא ניתן להרשות לעצמנו לאבד אותו, בעוד ש"בקר" הם ניתנים להחלפה. במקרה שלנו, השרת הפך ל"חיית המחמד" הזו; אם קונטיינר כשל, ה-session אבד. אם קונטיינר לא הגיב, היינו צריכים "להחיות" אותו.
"החייאת" קונטיינרים משמעותה הייתה ניפוי באגים של sessions תקועים שאינם מגיבים. חלון הגישה היחיד שלנו היה זרם אירועי ה-WebSocket, אך הוא לא יכל לומר לנו היכן התרחשו הכשלים, מה שהוביל לכך שבאג ב-harness, איבוד חבילות בזרם האירועים, או קונטיינר שיורד מהאוויר – כולם הופיעו באופן זהה. כדי להבין מה השתבש, מהנדס נאלץ לפתוח shell בתוך הקונטיינר, אך מכיוון שאותו קונטיינר הכיל לעיתים קרובות גם נתוני משתמש, גישה זו למעשה שללה מאיתנו את היכולת לבצע ניפוי באגים.
בעיה שנייה הייתה שה-harness הניח שכל מה ש-Claude עבד עליו נמצא באותו קונטיינר יחד איתו. כשלקוחות ביקשו לחבר את Claude לענן הפרטי הווירטואלי (VPC) שלהם, הם נאלצו לקשר את הרשת שלהם לשלנו, או להריץ את ה-harness שלנו בסביבה שלהם. הנחה שהוטמעה ב-harness הפכה לבעיה כשביקשנו לחבר אותו לתשתיות שונות.
ניתוק ה"מוח" מה"ידיים"
הפתרון אליו הגענו היה לנתק את מה שכינינו "המוח" (Claude וה-harness שלו) הן מ"הידיים" (סביבות sandbox וכלים המבצעים פעולות) והן מה-"session" (יומן אירועי ה-session). כל אחד מהם הפך לממשק שעשה מעט הנחות לגבי האחרים, וכל אחד יכול היה להיכשל או להיות מוחלף באופן עצמאי.
ה-harness עוזב את הקונטיינר. ניתוק ה"מוח" מה"ידיים" גרם לכך שה-harness לא נשאר בתוך הקונטיינר. הוא קרא לקונטיינר באותו אופן שבו קרא לכל כלי אחר: execute(name, input) → string. הקונטיינר הפך ל"בקר". אם הקונטיינר כשל, ה-harness תפס את הכשל כשגיאת קריאת כלי והעביר אותה בחזרה ל-Claude. אם Claude החליט לנסות שוב, קונטיינר חדש יכול היה לעבור אתחול מחדש עם "מתכון" סטנדרטי: provision({resources}). לא היינו צריכים יותר "להחיות" קונטיינרים שנכשלו.
התאוששות מכשל ב-harness. גם ה-harness הפך ל"בקר". מכיוון שיומן ה-session נמצא מחוץ ל-harness, שום דבר ב-harness לא צריך לשרוד קריסה. כאשר אחד נכשל, ניתן לאתחל חדש באמצעות wake(sessionId), להשתמש ב-getSession(id) כדי לקבל בחזרה את יומן האירועים, ולהמשיך מהאירוע האחרון. במהלך לולאת הסוכן, ה-harness כותב ל-session באמצעות emitEvent(id, event) כדי לשמור תיעוד עמיד של אירועים.
גבולות האבטחה. בתכנון המקובל, כל קוד בלתי מהימן ש-Claude יצר רץ באותו קונטיינר כמו פרטי זיהוי (credentials) – כך ש-prompt injection היה צריך רק לשכנע את Claude לקרוא את סביבתו שלו. ברגע שלתוקף יש את הטוקנים הללו, הוא יכול להפעיל sessions חדשים ובלתי מוגבלים ולהאציל להם עבודה. הגבלת היקף פעולה היא אמצעי הגנה ברור, אך הדבר מקודד הנחה לגבי מה ש-Claude אינו יכול לעשות עם טוקן מוגבל – ו-Claude הופך לחכם יותר ויותר. התיקון המבני היה לוודא שהטוקנים לעולם אינם נגישים מה-sandbox שבו רץ הקוד שנוצר על ידי Claude.
השתמשנו בשני דפוסים כדי להבטיח זאת. אימות (Auth) יכול להיות משולב עם משאב או להיות שמור בכספת מחוץ ל-sandbox. עבור Git, אנו משתמשים בטוקן גישה של כל מאגר (repository) כדי לשבט את המאגר במהלך אתחול ה-sandbox ולחבר אותו ל-remote המקומי של Git. פעולות Git push ו-pull עובדות מתוך ה-sandbox מבלי שהסוכן אי פעם יטפל בטוקן עצמו. עבור כלים מותאמים אישית, אנו תומכים ב-MCP ואוגרים טוקני OAuth בכספת מאובטחת. Claude קורא לכלי MCP דרך פרוקסי ייעודי; פרוקסי זה מקבל טוקן המשויך ל-session. הפרוקסי יכול אז לשלוף את פרטי הזיהוי המתאימים מהכספת ולבצע את הקריאה לשירות החיצוני. ה-harness לעולם אינו מקבל מידע על פרטי זיהוי כלשהם.
ה-session אינו חלון ההקשר של Claude
משימות ארוכות טווח חורגות לעיתים קרובות מאורך חלון ההקשר של Claude, והדרכים הסטנדרטיות לטפל בכך כוללות כולן החלטות בלתי הפיכות לגבי מה לשמור. חקרנו טכניקות אלו בעבודה קודמת על הנדסת חלון הקשר (context engineering). לדוגמה, דחיסה (compaction) מאפשרת ל-Claude לשמור סיכום של חלון ההקשר שלו, וכלי הזיכרון מאפשר ל-Claude לכתוב חלון הקשר לקבצים, מה שמאפשר למידה בין sessions. ניתן לשלב זאת עם קיצוץ חלון הקשר (context trimming), המסיר באופן סלקטיבי טוקנים כגון תוצאות כלים ישנות או בלוקים של חשיבה.
אך החלטות בלתי הפיכות לשמור או להשליך באופן סלקטיבי חלון הקשר עלולות להוביל לכשלים. קשה לדעת אילו טוקנים יידרשו בפניות עתידיות. אם הודעות עוברות טרנספורמציה באמצעות שלב דחיסה, ה-harness מסיר הודעות דחוסות מחלון ההקשר של Claude, ואלו ניתנות לשחזור רק אם הן מאוחסנות. עבודה קודמת בחנה דרכים לטפל בכך על ידי אחסון חלון הקשר כאובייקט שחי מחוץ לחלון ההקשר. לדוגמה, חלון הקשר יכול להיות אובייקט ב-REPL ש-LLM ניגש אליו באופן פרוגרמטי על ידי כתיבת קוד לסינון או חיתוך שלו.
ב-Managed Agents, ה-session מספק יתרון זהה, ומשמש כאובייקט חלון הקשר שחי מחוץ לחלון ההקשר של Claude. אך במקום להיות מאוחסן בתוך ה-sandbox או ה-REPL, חלון ההקשר מאוחסן באופן עמיד ביומן ה-session. הממשק, getEvents(), מאפשר ל"מוח" לחקור את חלון ההקשר על ידי בחירת פרוסות מיקום (positional slices) מזרם האירועים. הממשק יכול לשמש בגמישות, ולאפשר ל"מוח" להמשיך מכל נקודה שבה הפסיק לקרוא לאחרונה, להריץ לאחור כמה אירועים לפני רגע מסוים כדי לראות את ההקדמה, או לקרוא מחדש חלון הקשר לפני פעולה ספציפית.
כל אירועים שנשלפו יכולים גם לעבור טרנספורמציה ב-harness לפני שהם מועברים לחלון ההקשר של Claude. טרנספורמציות אלו יכולות להיות כל מה שה-harness מקודד, כולל ארגון חלון הקשר להשגת שיעור פגיעות גבוה במטמון הפרומפטים (prompt cache hit rate) והנדסת חלון הקשר. הפרדנו את הסוגיות של אחסון חלון הקשר שניתן לשחזור ב-session, וניהול חלון הקשר שרירותי ב-harness, מכיוון שאיננו יכולים לחזות איזו הנדסת חלון הקשר ספציפית תידרש במודלים עתידיים. הממשקים דוחפים את ניהול חלון ההקשר ל-harness, ומבטיחים רק שה-session עמיד וזמין לחקירה.
מוחות רבים, ידיים רבות
מוחות רבים. ניתוק ה"מוח" מה"ידיים" פתר אחת מהתלונות המוקדמות ביותר של לקוחותינו. כאשר צוותים רצו ש-Claude יעבוד מול משאבים ב-VPC שלהם, הדרך היחידה הייתה לקשר את הרשת שלהם לשלנו, מכיוון שהקונטיינר שהחזיק את ה-harness הניח שכל משאב נמצא לידו. ברגע שה-harness לא היה עוד בקונטיינר, הנחה זו נעלמה. לאותו שינוי הייתה תועלת בביצועים. כשהצבנו בתחילה את "המוח" בקונטיינר, זה אמר שמוחות רבים דרשו מספר זהה של קונטיינרים. עבור כל "מוח", שום הסקה לא יכלה לקרות עד שהקונטיינר סופק; כל session שילם את עלות הגדרת הקונטיינר המלאה מראש. כל session, אפילו כאלה שלעולם לא היו נוגעים ב-sandbox, נאלץ לשבט את המאגר (repo), לאתחל את התהליך, לשלוף אירועים ממתינים מהשרתים שלנו.
זמן המת זה מתבטא בזמן עד לטוקן ראשון (time-to-first-token – TTFT), המודד כמה זמן session ממתין בין קבלת עבודה לבין יצירת טוקן התגובה הראשון שלו. TTFT הוא זמן ההשהיה (latency) שהמשתמש מרגיש בצורה החריפה ביותר.
ניתוק ה"מוח" מה"ידיים" פירושו שקונטיינרים מסופקים על ידי "המוח" באמצעות קריאת כלי (execute(name, input) → string) רק אם הם נחוצים. כך ש-session שלא נזקק לקונטיינר מיד לא המתין לו. הסקה יכלה להתחיל ברגע ששכבת התיאום (orchestration layer) שלפה אירועים ממתינים מיומן ה-session. באמצעות ארכיטקטורה זו, מדד ה-p50 TTFT שלנו ירד בכ-60% ומדד ה-p95 ירד ביותר מ-90%. סקיילינג ל"מוחות" רבים פשוט הצריך הפעלה של harnesses מרובים וחסרי מצב (stateless), וחיבורם ל"ידיים" רק במידת הצורך.
ידיים רבות. רצינו גם את היכולת לחבר כל "מוח" לידיים רבות. בפועל, המשמעות היא ש-Claude חייב לבצע חשיבה על סביבות הרצה רבות ולהחליט לאן לשלוח עבודה – משימה קוגניטיבית קשה יותר מאשר פעולה ב-shell יחיד. התחלנו עם "המוח" בקונטיינר בודד מכיוון שמודלים מוקדמים יותר לא היו מסוגלים לכך. ככל שהאינטליגנציה עברה סקיילינג, הקונטיינר היחיד הפך למגבלה במקום זאת: כאשר קונטיינר זה כשל, איבדנו מצב (state) עבור כל "יד" שאליה "המוח" ניגש.
ניתוק ה"מוח" מה"ידיים" הופך כל "יד" לכלי, execute(name, input) → string: שם וקלט נכנסים, ומחרוזת מוחזרת. ממשק זה תומך בכל כלי מותאם אישית, בכל שרת MCP, ובכלים שלנו. ה-harness לא יודע אם ה-sandbox הוא קונטיינר, טלפון, או אמולטור של פוקימון. ומכיוון שאין "יד" המקושרת ל"מוח" כלשהו, "מוחות" יכולים להעביר "ידיים" זה לזה.
מסקנה
האתגר שעמד בפנינו הוא ישן: כיצד לתכנן מערכת עבור "תוכניות שטרם נהגו". מערכות הפעלה שרדו עשרות שנים על ידי וירטואליזציה של החומרה להפשטות כלליות מספיק עבור תוכניות שטרם התקיימו. עם Managed Agents, מטרתנו הייתה לתכנן מערכת שתתאים ל-harnesses, sandboxes או רכיבים אחרים עתידיים סביב Claude.
Managed Agents הוא meta-harness באותה רוח, שאינו נחרץ לגבי ה-harness הספציפי ש-Claude יזדקק לו בעתיד. במקום זאת, זוהי מערכת עם ממשקים כלליים המאפשרים מגוון רחב של harnesses שונים. לדוגמה, Claude Code הוא harness מצוין שאנו משתמשים בו רבות במגוון משימות. הראנו גם ש-harnesses סוכני משימתיים (task-specific agent harnesses) מצטיינים בתחומים צרים. Managed Agents יכולה להתאים לכל אחד מאלה, תוך התאמה לאינטליגנציה של Claude לאורך זמן.
תכנון Meta-harness משמעותו להיות נחרצים לגבי הממשקים סביב Claude: אנו מצפים ש-Claude יזדקק ליכולת לתפעל מצב (state – ה-session) ולבצע חישובים (ה-sandbox). אנו מצפים גם ש-Claude ידרוש את היכולת לעבור סקיילינג ל"מוחות" רבים ו"ידיים" רבות. תכננו את הממשקים כך שניתן יהיה להפעילם באופן אמין ומאובטח לאורך טווח זמן ארוך. אך איננו מניחים הנחות לגבי מספרם או מיקומם של "המוחות" או "הידיים" ש-Claude יזדקק להם.
תודות
נכתב על ידי לאנס מרטין (Lance Martin), גייב צ'אמאי (Gabe Cemaj) ומייקל כהן (Michael Cohen). תודה מיוחדת לצוות ה-Agents API ולג'ייק איטון (Jake Eaton) על תרומתם.



