Griffin31
ENקביעת הדגמה
EN
בלוג

ניסוי Custom GPT: בניית עוזר מייל ל-M365

13 במאי 2025

ניסוי Custom GPT: בניית עוזר מייל ל-M365

בקצרה (TL;DR)

האתגר: הצפת מיילים

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

רציתי פתרון שיאפשר לי לתקשר עם המייל שלי בצורה טבעית ושיחתית יותר. משהו שיבין בדיוק למה אני מתכוון כשאני אומר "show me my emails" (ולא ימשוך הודעות מכל תיקייה בחשבון שלי).

הכירו את Custom GPTs

כש-OpenAI הציגה את היכולת ליצור custom GPTs, ראיתי מיד את הפוטנציאל לבנות עוזר שנועד במיוחד לניהול מייל. Custom GPTs מאפשרים ליצור עוזרי AI ייעודיים עם ידע, יכולות והתנהגויות ספציפיים שמותאמים לתרחישי שימוש מסוימים.

דרישות

היופי ביישום הזה הוא הפשטות שלו. הנה כל מה שצריך כדי לבנות פתרון דומה:

דרישות בסיסיות

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

רישום אפליקציה ב-Azure AD

החלק ה"טכני" היחיד הוא הגדרת החיבור ל-Microsoft:

ה-Custom GPT מטפל בדפוסי האינטראקציה המורכבים, בעיבוד שפה טבעית ובפירוש פקודות המייל, והחיבור הישיר ל-Microsoft Graph API מאפשר לו לגשת למיילים שלכם ולנהל אותם בצורה מאובטחת.

אחרי שהדרישות המינימליות האלה מתמלאות, בואו נראה איך בניתי את ניסוי ה-GPT של עוזר המייל ל-M365.

ניסוי עוזר המייל ל-M365

יצאתי ליצור custom GPT שיעשה את הדברים הבאים:

יכולות מרכזיות

עוזר המייל שלי ל-M365 מבוסס ה-GPT יכול:

ההגדרה הטכנית

העוזר מתחבר ל-Microsoft Graph API עם OAuth scopes מתאימים כדי:

יישמתי כמה אופטימיזציות כדי שיפעל ביעילות:

צלילה לעומק: היישום הטכני

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

זרימת האימות של OAuth

האינטגרציה נשענת על Azure AD OAuth 2.0 כדי להזדהות בצורה מאובטחת מול Microsoft Graph. כך נראית זרימת האימות:

חיבור ה-Custom GPT

ה-custom GPT מתחבר לחשבון ה-Microsoft 365 שלי דרך שכבת API שבניתי. זה דרש:

json{
  "paths": {
    "/emails": {
      "get": {
        "parameters": [
          {"name": "folder", "default": "inbox"},
          {"name": "filter", "optional": true},
          {"name": "count", "default": 10}
        ],
        "responses": {
          "200": {
            "description": "List of emails",
            "schema": {"$ref": "#/definitions/EmailList"}
          }
        }
      }
    },
    "/emails/{id}": {
      "get": {
        "parameters": [{"name": "id", "required": true}],
        "responses": {
          "200": {
            "description": "Email details",
            "schema": {"$ref": "#/definitions/Email"}
          }
        }
      }
    },
    "/send": {
      "post": {
        "parameters": [
          {"name": "to", "required": true},
          {"name": "subject", "required": true},
          {"name": "body", "required": true},
          {"name": "attachments", "optional": true}
        ]

שאילתות Microsoft Graph

האינטגרציה משתמשת בשאילתות Microsoft Graph ספציפיות כדי לשלוף ביעילות רק את הנתונים הדרושים:

טיפול בשגיאות ועמידות

כדי להבטיח שהמערכת נשארת אמינה:

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

נספח: משאבי יישום מלאים

למי שרוצה ליישם משהו דומה, הנה המשאבים המלאים שהשתמשתי בהם ביישום שלי, שאפשר להתאים לצרכים שלכם.

הוראות Custom GPT מלאות (אנונימיות)

להלן ההוראות המלאות שנתתי ל-Custom GPT שלי. כדאי להתאים אותן להעדפות האישיות שלכם ולסגנון ניהול המייל שלכם:

# M365 Email Assistant GPT

You are a specialized assistant that helps users interact with their Microsoft 365 email through natural language commands. Your primary purpose is to simplify email management by allowing users to perform common email actions through conversation.

## Core Capabilities
- Read and manage emails efficiently
- Compose and send professional messages
- Organize emails across folders
- Find specific messages quickly
- Handle attachments
- Process email drafts

## Email Management Style
- Be concise and practical in your responses
- Focus on completing email tasks efficiently
- Confirm actions before executing irreversible operations
- Provide clear summaries of actions taken
- Suggest helpful email management tips when relevant

## User Profile
The user is a technology executive. They value clear, concise, and professional email communication.

**Default Signature:**
Best,
[Name]
[Title] | [Company]
[Contact Info] | Schedule a meeting
[Company Tagline]

**Important:** Always confirm recipient, subject, and message body before sending any email.

## User Interaction Approach
- For checking emails: List recent unread messages in the Inbox folder by default
- For sending emails: Guide through recipient, subject, and body conversationally and confirm details
- For finding emails: Ask for key search details (sender, timeframe, subject keywords)
- For organizing emails: Suggest appropriate folder structures

## Smart Folder Search Logic
Apply this search logic based on the query:
- **CRITICAL: When a user mentions "inbox" or asks to "show emails," ONLY search within the specific Inbox folder, not the entire mailbox **- **NEVER show emails from other folders when asked to check the inbox**
- Always prioritize the main Inbox folder unless the user explicitly specifies another folder
- For sent emails → Search Sent Items folder first → Then Inbox folder/All Mail if needed
- For deleted emails → Search Deleted Items folder first → Then Inbox folder/Archive if needed
- For archived emails → Check Archive folder first, then expand
- For unspecified location → Default to Inbox folder only, then escalate as needed only if explicitly requested

## Common Command Interpretation
- "Check my email" → List recent unread Inbox folder messages only
- "Show me emails" → Only display messages from the Inbox folder, not from any other folders
- "Show me emails from [person]" → Filter messages by sender in the Inbox folder only
- "Find emails about [topic]" → Search message content in the Inbox folder only
- "Send an email to [person]" → Initiate new message flow
- "Reply to the email from [person]" → Find most recent message from that sender (in Inbox folder by default) and generate a reply
- "Forward that email to [person]" → Forward the most recently discussed email
- "Move this to [folder]" → Move identified email to specified folder
- "Mark as read/unread" → Update isRead status
- "Delete that email" → Move to deleted items folder
- "Create a new folder called [name]" → Create mail folder

## Email Composition Assistance
- Match the user's tone: clear, efficient, professional, and courteous
- Offer to draft messages based on stated intent
- Include appropriate greetings and closings based on context
- Include the user's signature unless otherwise noted

## Technical Implementation

### Microsoft Graph API Connection
- Uses OAuth flow with scopes: Mail.Read, Mail.ReadWrite, Mail.Send

### Performance Optimization
- Request minimal fields in list operations (id, subject, from, receivedDateTime)
- Limit to 3 messages at a time in initial responses
- Use specific filters to narrow results before retrieving
- Only request full message body when viewing specific email

### Error Handling
- Provide clear explanations when errors occur
- Suggest specific troubleshooting steps
- For large responses, automatically retry with fewer fields

### Folder Management
- Support hierarchical folder navigation (parent/child relationships)
- Verify folder existence before attempting moves
- Provide clear success/failure feedback for all operations
- **When searching or displaying emails from the Inbox, ONLY include messages from the specific Inbox folder, NOT from any other folders**

סכמת OpenAPI מלאה (אנונימית)

להלן סכמת ה-OpenAPI המלאה שהשתמשתי בה לאינטגרציה עם Microsoft Graph API. אפשר להתאים אותה ולהוסיף endpoints נוספים לפי הצורך ביישום שלכם:

yamlopenapi: 3.1.0
info:
  title: Microsoft 365 Email API (Minimal)
  description: Minimal API for interacting with Microsoft 365 Email using Microsoft Graph
  version: 1.5.0
servers:
  - url: https://graph.microsoft.com/v1.0
    description: Microsoft Graph API v1.0 endpoint
paths:
  /me/mailFolders/{folderId}/childFolders:
    get:
      operationId: listChildFolders
      summary: List child folders
      description: Get child folders of a specific mail folder
      parameters:
        - name: folderId
          in: path
          required: true
          description: ID of the parent folder
          schema:
            type: string
        - name: $select
          in: query
          description: Properties to return
          required: false
          schema:
            type: string
            default: "id,displayName,childFolderCount,parentFolderId"
      responses:
        '200':
          description: List of child folders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailFolderCollection'
  /me/messages:
    get:
      operationId: listMessages
      summary: List messages in the user's mailbox
      description: Get messages from the user's mailbox
      parameters:
        - name: $select
          in: query
          description: Properties to return
          required: false
          schema:
            type: string
            default: "id,subject,from,receivedDateTime,isRead"
        - name: $top
          in: query
          description: Maximum number of messages to return
          required: false
          schema:
            type: integer
            default: 3
        - name: $filter
          in: query
          description: Filter criteria
          required: false
          schema:
            type: string
        - name: $orderby
          in: query
          description: Sort criteria
          required: false
          schema:
            type: string
            default: "receivedDateTime desc"
      responses:
        '200':
          description: List of messages
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageCollection'

  /me/mailFolders:
    get:
      operationId: listFolders
      summary: List mail folders
      description: Get all mail folders in the user's mailbox
      parameters:
        - name: $select
          in: query
          description: Properties to return
          required: false
          schema:
            type: string
            default: "id,displayName,childFolderCount,parentFolderId"
      responses:
        '200':
          description: List of mail folders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailFolderCollection'
    post:
      operationId: createFolder
      summary: Create a new mail folder
      description: Create a new mail folder in the user's mailbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - displayName
              properties:
                displayName:
                  type: string
                  description: The display name of the folder
      responses:
        '201':
          description: Folder created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailFolder'

  /me/mailFolders/{folderId}:
    get:
      operationId: getFolder
      summary: Get a mail folder
      description: Get details of a specific mail folder
      parameters:
        - name: folderId
          in: path
          required: true
          description: ID of the folder
          schema:
            type: string
        - name: $select
          in: query
          description: Properties to return
          required: false
          schema:
            type: string
            default: "id,displayName,childFolderCount,parentFolderId"
      responses:
        '200':
          description: Mail folder details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailFolder'
    delete:
      operationId: deleteFolder
      summary: Delete a mail folder
      description: Delete a mail folder
      parameters:
        - name: folderId
          in: path
          required: true
          description: ID of the folder
          schema:
            type: string
      responses:
        '204':
          description: Folder deleted successfully

  /me/messages/{messageId}/move:
    post:
      operationId: moveMessage
      summary: Move a message to a different folder
      description: Move a message from one folder to another
      parameters:
        - name: messageId
          in: path
          required: true
          description: ID of the message to move
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - destinationId
              properties:
                destinationId:
                  type: string
                  description: ID of the destination folder
      responses:
        '204':
          description: Message moved successfully without returning content

  /me/messages/{messageId}/reply:
    post:
      operationId: replyToMessage
      summary: Reply to a message
      description: Reply to a specific email message with original content included
      parameters:
        - name: messageId
          in: path
          required: true
          description: ID of the message to reply to
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - comment
              properties:
                comment:
                  type: string
                  description: The comment to include in the reply, which will be added above the original message content
      responses:
        '202':
          description: Reply accepted

  /me/sendMail:
    post:
      operationId: sendMail
      summary: Send an email
      description: Send an email message
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                message:
                  type: object
                  required:
                    - subject
                    - body
                    - toRecipients
                  properties:
                    subject:
                      type: string
                      description: The subject of the message
                    body:
                      type: object
                      required:
                        - contentType
                        - content
                      properties:
                        contentType:
                          type: string
                          enum: [Text, HTML]

טיפים ליישום

כשמתאימים את המשאבים האלה לשימוש שלכם:

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

פתרון בעיות: לקחים מהשטח

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

כשלי אימות

בעיה: ה-GPT היה מאבד לעיתים קרובות את החיבור ל-Microsoft Graph אחרי כשעה של שימוש, ודרש אימות מחדש. שורש הבעיה: תפוגת הטוקן כברירת מחדל הייתה שעה אחת, וזרימת ה-refresh token לא יושמה כראוי. פתרון: יישמתי מנגנון רענון טוקנים תקין שמזהה אוטומטית טוקנים שפגו ומרענן אותם לפני ביצוע קריאות API. הוספתי בדיקת תקינות טוקן לפני כל בקשה:

פתרון חלופי: עברתי מ-delegated permissions ל-application permissions כדי לבטל לגמרי את בקשות האימות החוזרות. כדי לשמור על האבטחה:

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

בלבול תיקיות

בעיה: כשביקשתי "check my inbox", ה-GPT היה מחפש בכל תיקיות המייל ומחזיר תוצאות מבלבלות. שורש הבעיה: Microsoft Graph API מחפש כברירת מחדל בכל התיקיות אם לא אומרים לו אחרת במפורש. ההוראות של ה-GPT לא היו ספציפיות מספיק לגבי היקף התיקיות. פתרון:

היקפי הרשאות (Permission Scopes)

בעיה: פקודות מסוימות כמו "move this email" נכשלו עם שגיאות 403 Forbidden. שורש הבעיה: הגדרת ה-OAuth scope הראשונית כללה רק Mail.Read ו-Mail.Send, אבל לא Mail.ReadWrite, שנדרש לשינוי הודעות. פתרון: רשמתי מחדש את האפליקציה עם כל ההרשאות הנדרשות ויישמתי פונקציית בדיקת הרשאות:

javascriptfunction checkRequiredPermissions(operation) {
  const permissionMap = {
    'read': ['Mail.Read', 'Mail.ReadWrite'],
    'write': ['Mail.ReadWrite'],
    'send': ['Mail.Send'],
    'move': ['Mail.ReadWrite'],
    'delete': ['Mail.ReadWrite']
  };

  const grantedPermissions = getUserPermissions();
  const requiredPermissions = permissionMap[operation]

הגבלת קצב (Rate Limiting)

בעיה: בשימוש כבד, האינטגרציה הגיעה למגבלות הקצב של Microsoft Graph API. שורש הבעיה: בקשות מהירות רבות בלי ניהול throttling תקין. פתרון: יישמתי מנגנון throttling אדפטיבי:

טיפול בקבצים מצורפים גדולים

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

אתגרי עיבוד שפה טבעית

בעיה: ה-GPT לפעמים לא הבין פקודות מורכבות כמו "forward the second email from John to Sarah with a note saying I'll review this tomorrow". שורש הבעיה: ה-GPT התקשה לפרש פקודות מורכבות עם כמה פעולות ופרמטרים. פתרון: יישמתי מפרש פקודות שמפרק בקשות מורכבות:

javascriptfunction parseEmailCommand(command) {
  // Extract action type (send, forward, reply, etc.)
  const actionMatch = command.match(/^(check|show|send|forward|reply to|find)/i);
  const action = actionMatch ? actionMatch[1].toLowerCase() : null;

  // Extract people mentioned
  const peopleMatches = command.match(/\b(from|to|cc)\s+([A-Za-z\s]+)(?=\s|$|,|\.)/g);
  const people = {};

  if (peopleMatches) {
    peopleMatches.forEach(match => {
      const [type, name] = match.split(/\s+(.+)/);
      people[type] = name.trim();
    });
  }

  // Extract other parameters
  const dateMatch = command.match(/\b(today|yesterday|last week|this month)\b/i);
  const timeframe = dateMatch ? dateMatch[1]

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

פתרון בעיית ה-"Inbox"

אתגר מסוים אחד היה לגרום לעוזר להבין שכשאני אומר "check my inbox" או "show me emails", אני מתכוון דווקא להודעות בתיקיית ה-Inbox שלי, ולא למיילים שפזורים בכל תיבת הדואר שלי.

הפתרון היה להורות ל-GPT במפורש ש:

דוגמאות שימוש מהעולם האמיתי

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

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

יתרונות ותוצאות

מאז שהטמעתי את ה-custom GPT הזה:

שיפורים עתידיים

אני ממשיך לחדד את העוזר עם:

נסו בעצמכם

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

הלקח החשוב ביותר שלמדתי הוא שהוראות ברורות ומפורטות עושות את כל ההבדל ביצירת custom GPT שימושי באמת. פרטים קטנים (כמו הגדרה מדויקת של מה זה "inbox") יכולים לשפר דרמטית את החוויה.

ניסיתם custom GPTs לפרודוקטיביות? אשמח לשמוע על החוויות שלכם בתגובות!

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

רוצים לדעת איפה הטננט שלכם עומד? Griffin31 בודק את הגדרות האבטחה של Microsoft 365 אצלכם באופן רציף ומראה מה לתקן קודם.