בניית סביבות עבודה משותפות לאנשים ולסוכנים

כיצד פרויקטים ב-Perplexity הופכים עבודה ארוכת טווח למתמידה, מקבילית וניתנת למעקב.

מחבריםPerplexity Engineering

לאחרונה השקנו את התכונה פרויקטים, שבה אנשים וסוכנים עובדים יחד על משימות ארוכות טווח ב-Perplexity Computer. כל פרויקט נשען על סביבת עבודה משותפת עם מערכת קבצים שצוברת הקשר לאורך הפעלות, סוכנים ומשתמשים. שיתוף פעולה מרובה משתתפים שנמשך חודשים דורש התמדה, מקביליות ועקיבות:

  • התמדה: העבודה צריכה להישמר מעבר לכל הפעלת סוכן או ארגז חול בודדים, כדי שאותו משתמש או סוכן יוכל להמשיך אותה בעתיד, וכל שותף יוכל להתבסס עליה.
  • מקביליות: לכל אחד מכמה משתמשים באותו פרויקט יכולים להיות כמה סוכנים שמבצעים עריכות במקביל, שמקדמות מצב משותף במקום לדרוס זו את זו.
  • עקיבות: גם משתמשים וגם סוכנים צריכים לראות את ההיסטוריה, כדי שיוכלו לבדוק מי שינה מה מאז ההפעלה האחרונה שלהם בלי לקרוא מחדש את הפרויקט כולו, ולבטל עריכות כשצריך.

האתגרים האלה מוכרים למהנדסי תוכנה. פלטפורמות Git forge צצו לפני עשרות שנים כדי לענות על הדרישות האלה: שירות מאוחסן שעוטף את ניהול הגרסאות ביכולות שיתוף פעולה, כך שתורמים רבים יכולים להתכנס למקור אמת יחיד. מערכת הקבצים שמפעילה את הפרויקטים היא פלטפורמת Git forge כזו, שנבנתה לשיתוף פעולה בין בני אדם לסוכנים ובין סוכנים לסוכנים.

פרויקט אחד: סוכנים אינטראקטיביים, סוכנים מתוזמנים וסוכנים של משתמשים אחרים מתכנסים כולם לאותו מאגר.

ארכיטקטורה

Git כבר מטפל בהתמדה, במקביליות ובעקיבות. הוא מזהה כל קובץ לפי התוכן שלו, כך שקבצים שלא השתנו נשמרים פעם אחת בלבד ועותקים שמורים אף פעם אינם מתיישנים. כל קומיט שומר את כל הקבצים שעודכנו בבת אחת, כך ששותפים לעבודה אף פעם אינם רואים מצב גמור למחצה. כששני כותבים מתנגשים, זה שהפסיד יכול להחיל מחדש את השינויים שלו על הגרסה החדשה ולנסות שוב. שרשרת הקומיטים מראה מי שינה מה ומקלה על שחזור גרסאות קודמות. מה שסוכנים אינם צריכים הוא שאר הרכיבים של פלטפורמת forge שנבנתה לבני אדם, כמו מסכי סקירה, ניהול ענפים והתראות.

הארכיטקטורה שלנו מריצה את כל התהליך בענן ותומכת בכל סוגי עבודת הידע, הרבה מעבר לכתיבת קוד. כל פרויקט ב-Computer מגובה במאגר Git שמוגש על ידי 5 רכיבים:

  • ה-CLI מתחזק עותקי עבודה מקומיים, מסנכרן עדכונים ושולח את השינויים של הסוכן בכל ארגז חול.
  • מישור הבקרה מאתר את מאגר הפרויקט ומנפיק אישורי גישה קצרי מועד כדי להגיע אליו.
  • שירות ה-Git שולח נתוני מאגר לארגזי החול, מקבל מהם שליחות ומחליט איזו שליחה הופכת לקומיט האחרון שהתקבל.
  • אחסון מבוסס AWS S3 שומר את נתוני המאגר באופן עמיד.
  • עיבוד במורד הזרם מחלץ טקסט ומידע שימושי נוסף ממסמכים שמורים ומנגיש אותו כשסוכנים טוענים או מרעננים את הפרויקט.

לכל סוכן יש ארגז חול מבודד משלו. בתוך ארגז החול הזה, הוא עובד עם עותק מקומי של המאגר, שמנוהל על ידי CLI קטן. ה-CLI מבקש ממישור הבקרה לאתר את המאגר, ולאחר מכן מאחזר ושולח שינויים דרך שירות ה-Git. שרת proxy של הסוכנים בודק את הרשאות הגישה של בקשות מארגזי החול לפני שהן מגיעות לשירות ה-Git. שירות ה-Git קורא וכותב מאגרים במערכת קבצים משותפת, ומחליט אם כל שליחה הופכת לקומיט האחרון שהתקבל. מערכת הקבצים הזו מסופקת על ידי AWS S3Files, שהופך אחסון מבוסס S3 לנגיש כקבצים ותיקיות רגילים. האחסון מחולק לתאים מבודדים, ולכל אחד מהם מערכת קבצים ורפליקות של שירות ה-Git משלו. לאחר שקומיט מתקבל, רכיבי worker ברקע מחלצים טקסט ומידע שימושי נוסף ממסמכים שהשתנו, שומרים את התוצאות בנפרד מהיסטוריית המאגר ומנגישים אותן כשסוכנים טוענים או מרעננים את הפרויקט.

בהמשך נסקור את הרכיבים אחד אחד.

ה-CLI פונה למישור הבקרה כדי לאתר את המאגר שלו, ולאחר מכן מאחזר ושולח דרך שירות ה-Git באמצעות שרת ה-proxy של הסוכנים. שירות ה-Git בודק את השליחות ומעדכן את ה-head שהתקבל ואת תיבת היציאה ב-DynamoDB. רפליקות מקומיות בתא משתמשות באחסון S3Files, ורכיבי worker במורד הזרם מחלצים טקסט לצורך יצירת עותקי עבודה וסנכרון.

CLI: עבודה עם קבצים מקומיים בארגזי חול

סוכנים רצים ב-SPACE, פלטפורמת ארגזי החול הפנימית שלנו. בכל ארגז חול יש CLI קטן שמממש את המאגר ושומר אותו מסונכרן. עבור כל יחידת עבודה משמעותית שהסוכן מבצע, ה-CLI שולח את השינויים כקומיט.

בדרך כלל סוכנים נוגעים רק בחלק קטן מקובצי הפרויקט, כך שאין צורך לממש את המאגר כולו מראש. לכן ה-CLI מממש את הקבצים בצורה עצלה: קבצים גדולים ובינאריים שמאוחסנים באמצעות Git Large File Storage (LFS) מגיעים כקובצי מצביע קטנים שמכילים את הגודל האמיתי של האובייקט, כך שהסוכן יכול להיעזר בשם ובגודל כדי להחליט אם כדאי להוריד אותם. במסמכים כמו קובצי PDF, הסוכן קורא קודם את הטקסט המנותח, ומוריד את קובצי המקור רק כשיש בכך צורך.

אפשרות טבעית נוספת הייתה שימוש במערכת קבצים וירטואלית. בדקנו מימוש מבוסס FUSE, אך הוא היה איטי פי 400 בערך בעומסי עבודה עתירי פעולות על מערכת הקבצים בהשוואה לקבצים מקומיים, מאחר שכל בקשה דורשת סבב תקשורת ברשת. בנוסף, בדרך כלל הוא דורש הרשאות מיוחסות מהמארח, כך שכל סוכן היה צריך לרוץ בארגז חול מיוחס.

גודל המצביע ומניפסט הניתוח קובעים: קריאת הטקסט המנותח, הורדת המקור או דילוג.

מישור הבקרה: איתור מאגרים והענקת גישה

מישור הבקרה מאתר היכן כל מאגר מאוחסן ומעניק גישה אליו. כשנוצר מאגר, מישור הבקרה משייך אותו לתא אחסון – יחידת אחסון מבודדת עם רפליקות משלה של שירות ה-Git. כשסוכן מתחיל לעבוד על פרויקט, ה-CLI שואל את מישור הבקרה היכן המאגר מאוחסן. מישור הבקרה מחזיר את המיקום ואישורי גישה זמניים לשירות ה-Git. מישור הבקרה אף פעם אינו קורא או כותב נתוני מאגר. שירות ה-Git הוא שמחליט מה נכנס למאגר.

שירות ה-Git: העברת נתונים ותיאום עדכונים

שירות ה-Git מבצע פעולות Git ומעביר את תוכן המאגר ואת היסטוריית הגרסאות בין ארגזי החול לבין האחסון. זוהי מעטפת דקה סביב פקודות השרת של Git עצמו. השירות תומך בגילוי refs כדי לומר ללקוח היכן נמצא ה-head של המאגר, ב-upload-pack לשכפול ולאחזור, וב-receive-pack לקבלת אובייקטים חדשים ובקשה לקדם את ה-head של המאגר. בנוסף, הוא משרת את ה-API של Git LFS: עבור קבצים גדולים ובינאריים, הוא מנפיק קישורים קצרי מועד כדי שארגזי החול יוכלו להעלות את הקבצים האלה ל-S3 ולהוריד אותם ממנו ישירות.

התחלנו עם שרת forge מוכן מהמדף, כדי שנוכל לבנות אב-טיפוס של ממשקי הסוכן והמשתמש במקביל לבניית פלטפורמת forge קלת משקל משלנו, שמתמקדת רק בפעולות ה-Git שאנחנו צריכים.

השירות מורכב מצי של רפליקות, שכל אחת מהן היא מופע פועל של אותו שרת. תא מקבץ אחסון משותף יחד עם קבוצת רפליקות והשירותים התומכים בהן. כך, רפליקות בתוך תא יכולות לגשת לאותה מערכת קבצים משותפת, במקום לתחזק עותקים עצמאיים של כל מאגר.

האחסון המשותף הזה מאפשר לרפליקות להישאר חסרות מצב (stateless). כל רפליקה בתא יכולה לשרת כל אחד מהמאגרים שלו, כך שהחלפה או הוספה של רפליקות אינה מחייבת העברת נתוני מאגר בין שרתים. בקשות למאגר מסוים מגיעות בדרך כלל לאותה רפליקה כדי לשמור על התועלת של המטמונים שלה, אבל המאגר אינו תלוי בכך שהמופע הספציפי הזה יישאר זמין.

שלב הקבלה בודק את האינווריאנטים של המאגר כדי לוודא שקומיט שהתקבל מייצג מצב תקין של המאגר. אנחנו מגדירים חוזה מפורש לכל מאגר, כך שבדיקות מכניות יכולות למנוע ממנו לגדול יותר מדי. קבצים גדולים וקבצים בינאריים, כמו קובצי PDF ותמונות, נשמרים באמצעות Git LFS ולא כ-blobs גולמיים של Git. הדחיסה (compaction) שומרת על זמינות ההיסטוריה האחרונה ובמקביל מגבילה את גודל המאגר. לאחר שליחה מוצלחת, ה-CLI שואל את שירות ה-Git אם המאגר זקוק לדחיסה. אם מספר הקומיטים השמורים חורג מסף מסוים, שירות ה-Git שומר רק קבוצה קטנה יותר של הקומיטים האחרונים. ההיסטוריה הישנה מוחלפת בתמונת מצב התחלתית אחת, והקומיטים האחרונים מוחלים מחדש כדי לעדכן את מזהי הקומיטים.

הדחיסה שומרת את הקומיטים האחרונים, מקפלת היסטוריה ישנה לשורש יחיד של תמונת מצב ומחילה מחדש את הקומיטים האחרונים עם מזהים חדשים; ה-head החדש שומר על אותם קבצים.

שלב סנכרון פשוט מטפל בכותבים מקבילים. הוא עוקב אחר הקומיט האחרון שהתקבל, או ה-head. כשהוא מקבל שליחה, הוא מאשר אותה רק אם היא נבנתה על בסיס ה-head הזה. אם לא, הוא שולח תגובה מובנית מסוג stale-base (בסיס מיושן); לאחר מכן הצד המפסיד צריך להחיל מחדש את השינוי שלו על גבי ה-head החדש ולשלוח שוב. התכנון הזה מבטיח שסוכנים מקבילים לא ידרסו בשקט זה את עבודתו של זה, בלי לגרום להאטות משמעותיות. בתהליכי העבודה שלנו בסביבת הייצור, פחות מ-2% מהשליחות נדחות כמיושנות. בסך הכול, זמן ההשהיה החציוני של שליחה הוא פחות מ-1.7 שניות, וזמן ההשהיה באחוזון ה-99 (p99) הוא פחות מ-8 שניות.

שני סוכנים שולחים במקביל; השליחה של אחד מהם מתקבלת, והשני מקבל תגובת stale-base, מחיל מחדש את השינוי על ה-head החדש ושולח שוב.

שירות ה-Git גם מתעד כל שינוי שהתקבל. עבור כל כתיבה שהתקבלה, הוא מוסיף רשומה לתיבת יציאה (outbox) – יומן שמפרט את המאגר, את הקומיט החדש, את המשתמש שביצע את השינוי ואת הנתיבים שהשתנו. שירותים במורד הזרם, כמו המנתח, קוראים את היומן הזה כדי למצוא עבודה חדשה.

יחד, הרכיבים האלה יוצרים שכבת ניהול מאגרים, בלי החלקים שסוכנים אינם זקוקים להם. אין צורך ב-pull requests, כי אף אדם אינו סוקר כל שינוי. במקום זאת, שירות ה-Git בודק את החוזה בזמן השליחה, הסוכן שהפסיד במרוץ מחיל מחדש את השינוי שלו, והיסטוריית הקומיטים מתעדת מי שינה מה. בדיקות דטרמיניסטיות שומרות על כל מאגר בגבולות החוזה שלו, במקום להסתמך על מפעיל אנושי שינהל את הגידול.

אחסון: שמירה על עמידות העבודה

שכבת האחסון שומרת את נתוני המאגר באופן עמיד, בנפרד מארגזי החול ומהרפליקות של שירות ה-Git. המאגרים נמצאים ב-AWS S3Files, שחושף אחסון מבוסס S3 דרך ממשק של מערכת קבצים ש-Git רגיל יכול להשתמש בו בלי שינויים. כל אחד מהם הוא מאגר Git חשוף (bare) במערכת הקבצים המשותפת, שמכיל את האובייקטים וההפניות של המאגר ולא עותק עבודה.

ההפרדה בין האחסון למשאבי החישוב מאפשרת לארגזי החול להישאר זמניים. כל סוכן מקבל ארגז חול משלו, וסוכנים רבים יכולים לעבוד על אותו פרויקט בלי לחלוק מכונה. אם ארגז חול קורס, רק עבודה שעדיין לא נשלחה נמצאת בסיכון.

לחלופין, יכולנו לתת לכל פרויקט ארגז חול ארוך חיים שכל הסוכנים שלו חולקים, כך שקובצי הפרויקט היו נשמרים בדיסק של אותה מכונה. עם זאת, הדבר היה קושר את משאבי החישוב של הסוכנים למקום שבו המכונה פועלת, ומחייב להתאים את גודלה לתקופות העמוסות ביותר שלה. בנוסף, היא הייתה צריכה להישאר תקינה במשך חודשים, כשהגיבויים והשחזור מנוהלים כחלק ממערכת האחסון של הפרויקט.

ארגז חול אחד ארוך חיים שסוכני הפרויקט חולקים, לעומת ארגזי חול קצרי חיים לכל סוכן שמגיעים לאחסון משותף עמיד דרך רפליקות חסרות מצב של שירות ה-Git.

עיבוד במורד הזרם: הפיכת מסמכים לקריאים עבור סוכנים

הרכיב העיקרי במורד הזרם הוא צינור ניתוח שמחלץ טקסט ומטא-נתונים ממסמכים שנוספו או עודכנו, כדי שסוכנים יוכלו לקרוא בקלות את קובצי הפרויקט. רכיב worker של המנתח צורך עדכונים מתור זרם השינויים, גוזר את הקבצים שהשתנו מהמטא-נתונים של הקומיט ומאחזר את הבייטים הגולמיים באמצעות הגיבוב של התוכן. לאחר מכן הוא מנתח את המסמך כדי להפיק את הטקסט המחולץ ואת המטא-נתונים של המסמך.

פלטי הניתוח נשמרים מחוץ למאגר, באחסון נפרד שהמפתח בו הוא הגיבוב של מסמך המקור. התוצרים האלה אף פעם אינם נשמרים כקומיט בחזרה במאגר, כי הדבר היה מזהם את ההיסטוריה שלא לצורך ומגדיל את מספר הכתיבות המקבילות. במקום זאת, פלטי הניתוח מגיעים לארגז החול דרך יצירת עותק העבודה והסנכרון. כל עותק עבודה כולל תיקייה שמורה שבה ה-CLI משתמש לצורך מימוש התוצרים. הניתוח מתבצע באופן אסינכרוני, כך שרכיב worker של המנתח שפועל ברקע אינו חוסם יצירת עותקי עבודה.

כיצד סוכנים משתמשים בסביבת העבודה

סוכנים חוקרים את גרסת המאגר שנטענה לארגז החול באמצעות כלים סטנדרטיים של מערכת הקבצים. במקום לפעול לפי צינור קבוע של חיפוש ואז קריאה, הם מציגים את תוכן התיקיות, מחפשים בתוכן הקבצים באמצעות grep, קוראים קטעים רלוונטיים ומשתמשים במה שמצאו כדי להחליט היכן לחפש בהמשך.

בדומה ל-Computer, גם Search משתמש באותה גישה באמצעות כלי הקבצים הסטנדרטיים שלו. סוכן חקירה נפרד מחפש בפרויקט במסגרת תקציב מוגבל יותר ומחזיר נתיבי קבצים רלוונטיים וקטעים תומכים. לאחר מכן מודל המענה קורא את הקבצים שנבחרו ומפיק את התשובה. שימוש חוזר בארגז חול קיים של הפרויקט מקצר את זמן ההכנה: התחברות לעותק עבודה חם נמשכת כ-2.9 שניות, לעומת כ-8 שניות בהתחלה קרה.

ארגזי חול של Computer, סוכן המשנה לחקירה של Ask, כותבי ידע ברקע ורכיבי worker של המנתח – כולם קוראים מאותו מאגר ומאותה תיבת יציאה וכותבים אליהם.

טקסט מנותח מאפשר לחפש במסמכים גדולים יותר, כמו קובצי PDF וקובצי Office, באמצעות אותם כלים. הסוכן בודק קודם מניפסט – רשימה שמראה לאילו מסמכים יש טקסט מחולץ זמין – ואז מחפש בטקסט הזה וקורא אותו כקבצים רגילים. הוא מוריד את המסמך המקורי רק כשהטקסט אינו מספיק, למשל כדי לבחון איורים, פריסה או נתונים מוטמעים.

השווינו את הגישה הזו לחיפוש סמנטי על גבי אינדקס, באמצעות 150 שאלות פיננסיות שכל אחת מהן מתייחסת למסמך יחיד מתוך אותו קורפוס. סוכן החקירה השיג ציון של 0.867 באחזור המסמך הנכון ו-0.627 במתן תשובה נכונה, לעומת כ-0.39 וכ-0.37 בחיפוש הסמנטי – שיפור של פי 1.7 בערך בתשובות ופי 2 באחזור, עם זמן השהיה נמוך בכ-25%. סוכן החקירה עובד בעיקר עם טקסט מחולץ, ורק לעיתים רחוקות מוריד את קובצי ה-PDF המקוריים.

חקירה לעומת חיפוש קבצים סמנטי במבחן ההשוואה הפנימי.

תשתית משותפת אחת

הזיכרון המתמיד של Computer משתמש באותה תשתית של מערכת הקבצים. Perplexity Brain, ויקי הידע של Computer, שומר זיכרון במאגרי ידע בעלי היסטוריה ומדיניות משלהם. ההפרדה בין הידע לבין קובצי הפרויקט מבטיחה שעדכוני זיכרון ברקע לא יתחרו בעריכות של אותם קבצים. הפעלה שזקוקה לשניהם טוענת את שני המאגרים.

סוכני רקע קוראים את הידע הקיים, משלבים מידע חדש ושומרים דפים מעודכנים כקומיטים. סוכנים אחרים קוראים את הדפים האלה בעזרת כלי קבצים רגילים, וגם אנשים יכולים לערוך אותם. הכותבים משתפים את עבודתם דרך המאגר במקום לתאם ישירות עם כל קורא. היסטוריית הקומיטים מתעדת מי שינה מה, ותיבת היציאה מדווחת על עדכונים שהתקבלו לשירותים במורד הזרם. אנשים וסוכנים יכולים להתבסס זה על עבודתו של זה, עם תיעוד של האופן שבו הידע התפתח.

סיכום

כשאנשים וסוכנים חולקים פרויקט, סביבת העבודה עושה יותר מאחסון קבצים. היא מספקת מקום משותף לשילוב תרומות ולמעקב אחר מה שהשתנה. Git כבר מספק חלק גדול מהבסיס הזה. אנחנו מוסיפים תיאום ובדיקות אוטומטיות כדי שסוכנים יוכלו לשלוח עבודה בלי שאדם יאשר כל עדכון. התוצאה היא פלטפורמת forge שצומצמה לרכיבים ההכרחיים בלבד, כי שיתוף פעולה בין סוכנים ופיתוח תוכנה עם תורמים מרובים הם, בבסיסם, אותה בעיה.

סוכנים עובדים בארגזי חול עצמאיים וזמניים, ובמקביל תורמים למאגרים עמידים. מגבלות המאגר שומרות על גידול שניתן לנהל, הורדות מפורשות מאפשרות לסוכנים לבחור מה לאחזר, וקבצים רגילים מאפשרים להם לחקור בעזרת כלים מוכרים. עדכונים ברקע, עריכות אינטראקטיביות ומשימות מתוזמנות יכולים להתבסס זה על עבודתו של זה בלי תיאום ישיר.

ככל שיותר סוכנים עובדים באופן אוטונומי בתוך פרויקטים, סביבת העבודה המשותפת שלהם חייבת להישאר אמינה ומובנת לבני אדם. המטרה פשוטה: כל שינוי צריך להחזיק מעמד זמן רב יותר מהסוכן שיצר אותו, להיות זמין לשותף הבא ולהשאיר תיעוד ברור של מה שהשתנה.

ציטוט

BibTeX
@online{perplexity2026BuildingSharedWorkspacesForPeopleAndAgents,
  author = {{Perplexity} and Se Won Jang and Peter Hart and Kartik Kapur and Kevin Lau and Sidhant Rajadnya and Ishbir Singh and Thomas Wang and Jarrod Bryan and Claire Beamer and Jeremy Yang},
  title = {{Building shared workspaces for people and agents}},
  date = {2026-10-12},
  url = {https://www.perplexity.ai/hub/blog/building-shared-workspaces-for-people-and-agents}
}