סוכן תשובות לתורמים · AfifAI
מקרה בוחן · הנדסת AI · 2026

ממפתח אחד מקובע לסוכן Copilot שכל עמותה יכולה להתקין

מגייסי תרומות מבקשים מ־⁠Copilot ב־⁠Outlook או ב־⁠Teams לנסח תשובה לתורם, ומקבלים מייל אישי שנבנה מהתרומות וההערות האמיתיות של אותו תורם ב־⁠CRM. לקחתי את הסוכן מפיילוט ללקוח אחד לתכנון מרובה טננטים: חבילת Marketplace אחת ו־⁠backend אחד, וכל ארגון מחבר את מפתח ה־⁠CRM שלו.

TypeScriptM365 Copilot declarative agentCustom engine agentEntra ID SSOAzure Table StorageAzure OpenAI gpt-4oZodVitest
לקוח
ספקית CRM אמריקאית לגיוס תרומות בעמותות; פיילוט עם ארגון זכויות אדם בינלאומי (חסוי)
התפקיד שלי
ראש צוות, ארכיטקט ומפתח בפועל
שלב
פיילוט חי; עבודת המפתחות מרובת הטננטים נבנתה (שלבים ⁦0–4⁩), אמצע 2026
סטטוס
פיילוט חי
במבט אחד
N עד 1פריסות שנדרשות כדי לשרת N ארגונים: backend משותף אחד וחבילת Marketplace אחת
כ־⁠5 דקותלקליטת ארגון, במקום ≈יום של בניית חבילה ו־⁠backend ידנית (הערכה)
96טסטי יחידה ב־⁠8 סוויטות, שרצים עם lint, טיפוסים, build ו־⁠evals בכל push
0עובדות על תורמים שהמודל כותב. התבניות מכניסות placeholders, והשכתוב משנה רק טון ואורך
3ממשקי Microsoft 365 על ליבת סוכן אחת: Copilot ב־⁠Outlook, Teams / Copilot Chat, תוסף Outlook
⁦24–39⁩ימי פיתוח בהערכה שלי לעבודה מרובת הטננטים, שנמסרה ב־⁠5 שלבים ועוד הקשחה
AES‑256הצפנת GCM לכל מפתח שמור, שמפוענח רק בזיכרון ואף פעם לא נרשם בלוג

מספרים שמסומנים ”הערכה” הם הערכות הנדסיות שצריך לאמת מול הספקית. כל השאר נספרו מהקוד, מהטסטים וממסמכי הפרויקט.

הבעיה

כל לקוח היה צריך עותק משלו

הגרסה הראשונה שירתה ארגון פיילוט אחד, ומפתח ה־⁠API של ה־⁠CRM היה הגדרה אחת ב־⁠backend שנקראת בעלייה. כשספקית ה־⁠CRM בדקה את ההתקנה, היא מצאה את התוצאה המתבקשת: חבילה אחת היא backend אחד, ו־⁠backend אחד הוא מפתח אחד שמשותף לכל מי שמתקין.

לכן רישום Marketplace בשירות עצמי היה מחייב build ו־⁠backend מאורח נפרדים לכל לקוח. זה לא היה מקובל, וגם לא מפתח אחד משותף לכל הארגונים.

מה הובלתי

תכנון, הערכה ובנייה

  • בניתי עם הצוות את ליבת הסוכן ואת שני ממשקי ה־⁠Copilot: חיפוש תורם, תבניות פנייה, שכתוב מבוסס נתונים וסכמות פלט.
  • הובלתי את פגישת ה־⁠multi-tenant עם הספקית, ואחריה כתבתי את תכנון הפתרון, את אפשרויות מדיניות הקליטה ואת הערכת המאמץ (⁦24–39⁩ ימי פיתוח, 5 שלבים).
  • מימשתי את שכבת הטננטים: חילוץ זהות, מאגר מפתחות מוצפן לכל טננט, cache ל־⁠resolver, API למפתחות מנהל, כרטיס קליטה בצ׳אט, Entra SSO לממשק ה־⁠Outlook ופאנל ”Connect” בתוסף.
  • הקמתי את שער האיכות (lint, פורמט, בדיקת טיפוסים, 96 טסטים, build ו־⁠eval harness) שרץ ב־⁠CI בכל push.
מה בניתי

ליבת סוכן אחת, שלושה ממשקים, מפתח לכל טננט

ארכיטקטורה

המפתח הולך אחרי הזהות, לא אחרי הפריסה

התרשים רחב מהמסך: החליקו הצידה לצפייה בכולו →

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

החלטות תכנון

סוד לכל טננט, ומודל ברצועה קצרה

מפתח לכל טננט, נפתר בכל בקשה

מפתח ה־⁠CRM עבר מסוד של הפריסה לקונפיגורציה בזמן ריצה, לפי מזהה טננט ה־⁠Entra. חבילת Teams היא קובץ סטטי שכולם מורידים, ולכן היא לא יכולה לשאת סוד של לקוח. המחיר: ממשק ה־⁠Outlook היה צריך לעבור מבלי הזדהות ל־⁠Entra SSO, כדי שהקריאות שלו יזהו את הארגון.

קודם תבניות, המודל אופציונלי

טיוטות מתחילות מתבניות דטרמיניסטיות שמכניסות placeholders מפורשים כשחסר מידע. השכתוב ב־⁠gpt-4o משנה רק טון ואורך, חייב לשמר את ה־⁠placeholders כמו שהם, וחוזר לתבנית אם הוא נכשל.

ליבה אחת, הרבה ממשקים

Microsoft תומכת בסוכני custom engine רק ב־⁠Teams וב־⁠Copilot Chat, וב־⁠declarative agents ב־⁠Outlook וב־⁠Office. הרצה של שניהם על backend אחד מגיעה למשתמשים בכל מקום בלי עלות תשתית נוספת. המחיר: שתי חבילות Teams לתחזק.

קליטה פתוחה, השער נשמר בסכמה

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

יומן הנדסי

מה היה צריך לפתור כדי לעבור מלקוח אחד לרבים

בעיות שעלו בבדיקות עם הספקית, ואיך הן נסגרו:

טננטים

את המפתח אפשר היה להגדיר רק פעם אחת

בדיקת ההתקנה של הספקית עצמה הראתה שהגדרת המפתח היחידה ב־⁠backend גורמת לכל המתקינים לחלוק מפתח אחד, או מחייבת build מותאם לכל לקוח.

מאגר מפתחות ו־⁠resolver לכל טננט מאחורי חבילה אחת. מצב תאימות לאחור השאיר את הפיילוט החי בלי שינוי בזמן שהשינוי עלה.

זהות

קריאות מ־⁠Outlook לא נשאו זהות

ה־⁠plugin של ה־⁠declarative agent עבד בלי הזדהות, ולכן ה־⁠backend לא ידע איזה ארגון קורא ולא יכול היה לבחור מפתח.

העברתי את ה־⁠plugin ל־⁠Entra SSO והוספתי middleware לאימות JWT בנתיבי ה־⁠actions, במקום מפתח ה־⁠actions המשותף.

קליטה

מפתח שגוי היה נכשל באמצע טיוטה

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

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

Cache

מפתח שהוחלף חייב להיכנס לתוקף מיד

הסוכנים נשמרים ב־⁠cache לכל טננט לטובת מהירות, ולכן מפתח שהוחלף היה יכול להיות מתעלם ממנו עד restart.

רשומת ה־⁠cache זוכרת איזה מפתח בנה את הסוכן. מפתח אחר בונה אותו מחדש, ו־⁠API הניהול מאפס את הרשומה בכל שינוי.

אבטחה

כל אחד בארגון יכול היה להגדיר את המפתח

כרטיס הצ׳אט היה מאפשר לכל חבר צוות להחליף את המפתח של הארגון.

הגבלה למנהלים בלבד דרך רשימת חברי ה־⁠Team (בעלים או מנהל), יחד עם הצפנה במנוחה והסתרה של מפתחות ומיילים של תורמים בלוגים.

CRM API

הגבלות קצב ושגיאות חולפות

טיוטות מרובות נתקלו ב־⁠429 ומדי פעם ב־⁠5xx מה־⁠CRM.

עד 3 ניסיונות חוזרים עם backoff אקספוננציאלי ו־⁠full jitter, כיבוד Retry-After, ורענון טוקן אחד כש־⁠session פג.

אריזה

הוולידטור של Teams דחה את החבילה

גרסה 0.x, כתובת סכמה לא קנונית, מזהה סוכן שאינו GUID, כתובת spec מרוחקת ודגל confirmation לא נתמך נדחו כל אחד בתורו.

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

נסו בעצמכם

מה קורה לבקשה אחת, לכל טננט

הדגמה להמחשה עם טננטים ותורמים סינתטיים. אין בה נתוני לקוח או קוד. כל תרחיש מציג את הבדיקות שה־⁠backend מריץ.

תוצאה

איפה זה עומד

מדדלפניאחרימקור
פריסות עבור N ארגוניםN (אחת לכל לקוח)backend אחד + חבילה אחתנגזר
קליטת ארגוןחבילה ו־⁠backend ידניים, ≈יוםמנהל מדביק מפתח, ≈5 דק׳הערכה
מפתח לכל ארגוןמפתח משותף אחדמפתח מוצפן לכל טננטקוד
זהות בממשק ה־⁠OutlookאיןEntra SSO, JWT מאומתקוד
זיהוי מפתח שגויבטיוטה הראשונה שנכשלתבשמירה, שום דבר לא נשמרקוד
בדיקות אוטומטיות בכל push—96 טסטים + lint, טיפוסים, build, evalsקוד

מקור: קוד = בריפו; נגזר = נובע מהתכנון; הערכה = לאימות מול הספקית.

  • פיילוט חי ב־⁠Copilot ב־⁠Outlook וב־⁠Teams עבור ארגון בינלאומי, מ־⁠backend אחד ב־⁠Azure.
  • ניהול מפתחות מרובה טננטים נבנה בשני הממשקים: מאגר, resolver, API ניהול, קליטה בצ׳אט, Entra SSO ופאנל בתוסף.
  • סקירה למנהלים ותוכנית בנייה מפורטת בשלבים נמסרו לספקית.
  • שער CI שרץ offline מול fixtures מדומים של ה־⁠CRM, כך שלא צריך סודות כדי לבדוק.
  • הבא בתור: טסט אינטגרציה עם מפתח דמו שני, סקירת אבטחה, הקשחה ל־⁠Marketplace ותיקון הרשת הנפרד בין הבוט ל־⁠API.
טכנולוגיות

טכנולוגיות

סוכן
TypeScript, Node.js, Express, Bot Framework, Zod
Copilot
Declarative agent + API plugin (OpenAPI), custom engine agent, תוסף Office.js
AI
Azure OpenAI gpt-4o: מסווג פניות, שכתוב מבוסס נתונים
טננטים ואבטחה
Entra ID SSO, אימות JWT, Azure Table Storage, AES-256-GCM, Key Vault
איכות
Vitest (96 טסטים), ESLint, Prettier, eval harness, GitHub Actions
אירוח
Azure App Service, Azure Bot Service

שיחת אבחון חינם · 30 דקות · ללא התחייבות

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

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

לשיחת אבחון חינםafif.mansour@gmail.com

נפתח מייל מוכן מראש · מענה תוך יום עסקים

ה־AI קורא. הקוד סופר.

כל הפרויקטים