AI Agents · درس

بناء وكيل مساعد بسيط للبريد الإلكتروني

من البداية إلى النهاية: قراءة صندوق الوارد ← التلخيص ← إعداد الرد ← انتظار الموافقة

الدرس 4 من 413 خطوة

بناء وكيل مساعد بسيط للبريد الإلكتروني درس مجاني في AI Agents على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Agents، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Agents 4 دروس في المجموع.

بنية وكيل مساعد البريد الإلكتروني

يتبع وكيل مساعد البريد الإلكتروني مسارًا قياسيًا: الجلب ← التصنيف ← اتخاذ القرار ← إعداد المسودة ← الموافقة ← الإرسال. يقرأ الوكيل رسائل البريد غير المقروءة، ويستخدم نموذجًا لغويًا كبيرًا لتصنيف الردود وإعداد مسوداتها، ثم ينتظر موافقة بشرية قبل الإرسال. ويمنع هذا التصميم الذي يتضمن الإنسان ضمن الحلقة وقوع أخطاء مكلفة نتيجة إرسال البريد الإلكتروني باستقلالية كاملة.

# Email Assistant Pipeline:
#
# 1. FETCH: Pull unread emails from Gmail API
# 2. CLASSIFY: LLM labels each email
#    - 'action_needed': requires a reply
#    - 'fyi': informational, no reply needed
#    - 'spam': should be archived
# 3. DRAFT: LLM generates reply for 'action_needed' emails
# 4. APPROVE: Human reviews drafts in Gmail UI
# 5. SEND: Agent sends approved drafts
#
# Tools: Gmail API, Anthropic/OpenAI API, json, base64

print('Email assistant pipeline: fetch -> classify -> draft -> approve -> send')

تعريف أدوات الوكيل

نظّم الوكيل باستخدام تعريفات واضحة للأدوات. كل أداة هي دالة Python ذات مسؤولية محددة. وتجعل هذه الوحداتية الوكيل قابلًا للاختبار وتصحيح الأخطاء وسهل التوسعة بإمكانات جديدة.

class EmailAssistantTools:
    def __init__(self, gmail_service, llm_client):
        self.gmail = gmail_service
        self.llm = llm_client

    def fetch_unread(self, max_emails=10):
        '''Fetch unread emails from inbox.'''
        pass

    def classify_email(self, subject, body, sender):
        '''Ask LLM to classify: action_needed / fyi / spam.'''
        pass

    def draft_reply(self, subject, body, sender, context):
        '''Ask LLM to draft a reply to an email.'''
        pass

    def create_draft(self, message_id, reply_text):
        '''Save draft reply in Gmail for human review.'''
        pass

    def send_approved_drafts(self):
        '''Send all drafts marked as approved.'''
        pass

print('Tool-based architecture enables testing each step independently')

الخطوة 1: جلب رسائل البريد وتحليلها

تجلب الأداة الأولى رسائل البريد غير المقروءة وتستخرج الحقول الأساسية التي يحتاج إليها النموذج اللغوي الكبير: الموضوع والمرسل والنص العادي للرسالة. اجعل النص قصيرًا — اقتصِره على أول 2000 حرف للبقاء ضمن حدود سياق النموذج اللغوي الكبير وتقليل تكاليف واجهة برمجة التطبيقات.

import base64

def fetch_emails_for_classification(gmail_service, max_emails=10):
    messages_list = gmail_service.users().messages().list(
        userId='me',
        q='is:unread label:inbox',
        maxResults=max_emails
    ).execute().get('messages', [])

    emails = []
    for ref in messages_list:
        msg = gmail_service.users().messages().get(
            userId='me', id=ref['id'], format='full'
        ).execute()

        headers = {h['name'].lower(): h['value']
                   for h in msg['payload'].get('headers', [])}
        body = extract_plain_text_body(msg)[:2000]  # trim for LLM

        emails.append({
            'id': msg['id'],
            'thread_id': msg['threadId'],
            'from': headers.get('from', ''),
            'subject': headers.get('subject', '(no subject)'),
            'body': body
        })

    print(f'Fetched {len(emails)} unread emails')
    return emails

الخطوة 2: التصنيف باستخدام LLM

أرسل كل رسالة بريد إلى نموذج لغوي كبير لتصنيفها. استخدم موجهًا منظمًا يطلب من النموذج إرجاع كائن JSON يتضمن الفئة والتبرير. اطلب إخراج JSON صراحةً، فهو أكثر موثوقية من تحليل النص الحر.

import json
import anthropic
import os

client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

def classify_email(subject, sender, body):
    prompt = (
        'Classify this email as exactly one of: action_needed, fyi, spam.\n'
        'Return JSON only: {"category": "...", "reason": "..."}\n\n'
        f'From: {sender}\n'
        f'Subject: {subject}\n\n'
        f'Body:\n{body[:1500]}'
    )

    response = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=200,
        messages=[{'role': 'user', 'content': prompt}]
    )

    try:
        result = json.loads(response.content[0].text)
        return result.get('category', 'fyi'), result.get('reason', '')
    except json.JSONDecodeError:
        return 'fyi', 'Could not parse LLM response'

الخطوة 3: إنشاء مسودات الردود

بالنسبة إلى الرسائل المصنفة على أنها action_needed، اطلب من النموذج اللغوي الكبير إنشاء مسودة رد. قدّم سياقًا عن دور الوكيل ونبرته. اطلب ردًا مهنيًا وموجزًا، وأدرج عنصرًا نائبًا لأي معلومات يحتاج الإنسان إلى تعبئتها.

def draft_reply(subject, sender, body, agent_context):
    prompt = (
        'You are an email assistant drafting a professional reply.\n'
        'Guidelines:\n'
        '- Be concise and professional\n'
        '- Use [FILL IN] for any info you don\'t know\n'
        '- Start with a greeting, end with a sign-off\n\n'
        f'Context about the recipient\'s work: {agent_context}\n\n'
        f'Original email from {sender}:\n'
        f'Subject: {subject}\n\n'
        f'{body[:1500]}\n\n'
        'Draft a reply:'
    )

    response = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=500,
        messages=[{'role': 'user', 'content': prompt}]
    )

    return response.content[0].text.strip()

الخطوة 4: حفظ المسودات للمراجعة البشرية

لا ترسل رسائل البريد الإلكتروني التي أنشأها الذكاء الاصطناعي من دون مراجعة بشرية. احفظها كـمسودات في Gmail حتى يتمكن الإنسان من فتح Gmail ومراجعتها وتحريرها عند الحاجة ثم إرسالها. وتتشابه واجهة برمجة تطبيقات إنشاء المسودة مع واجهة الإرسال، مع استخدام drafts().create() فقط.

import base64
from email.mime.text import MIMEText

def save_draft_reply(gmail_service, original_message, reply_text):
    sender_header = next(
        (h['value'] for h in original_message['payload'].get('headers', [])
         if h['name'].lower() == 'from'), ''
    )
    subject = next(
        (h['value'] for h in original_message['payload'].get('headers', [])
         if h['name'].lower() == 'subject'), ''
    )
    msg_id_header = next(
        (h['value'] for h in original_message['payload'].get('headers', [])
         if h['name'].lower() == 'message-id'), ''
    )

    mime_msg = MIMEText(reply_text, 'plain', 'utf-8')
    mime_msg['To'] = sender_header
    mime_msg['Subject'] = 'Re: ' + subject
    mime_msg['In-Reply-To'] = msg_id_header
    mime_msg['References'] = msg_id_header

    raw = base64.urlsafe_b64encode(mime_msg.as_bytes()).decode()
    draft = gmail_service.users().drafts().create(
        userId='me',
        body={'message': {'raw': raw, 'threadId': original_message['threadId']}}
    ).execute()

    print(f'Draft saved: {draft["id"]}')
    return draft['id']

# --- demo: minimal stand-in for the Gmail API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeDrafts:
    def create(self, userId, body):
        return _Exec({'id': 'r9000abc'})

class _FakeUsers:
    def drafts(self):
        return _FakeDrafts()

class _FakeGmailService:
    def users(self):
        return _FakeUsers()

original_message = {
    'threadId': 'thread_1',
    'payload': {'headers': [
        {'name': 'From', 'value': 'customer@example.com'},
        {'name': 'Subject', 'value': 'Question about my order'},
        {'name': 'Message-ID', 'value': '<abc123@mail.example.com>'}
    ]}
}
save_draft_reply(_FakeGmailService(), original_message, 'Thanks for reaching out, we will look into it.')

الخطوة 5: وضع علامة على الرسائل المعالَجة

بعد معالجة رسالة بريد إلكتروني (تصنيفها وإنشاء مسودة أو أرشفتها)، ضع عليها علامة لمنع معالجتها مرة أخرى. أضف تصنيفًا مخصصًا مثل AgentProcessed وأزل تصنيف UNREAD. أنشئ التصنيف مرة واحدة إذا لم يكن موجودًا.

def get_or_create_label(gmail_service, label_name):
    labels = gmail_service.users().labels().list(userId='me').execute()
    for label in labels.get('labels', []):
        if label['name'] == label_name:
            return label['id']

    # Create the label
    new_label = gmail_service.users().labels().create(
        userId='me',
        body={
            'name': label_name,
            'labelListVisibility': 'labelShow',
            'messageListVisibility': 'show'
        }
    ).execute()
    print(f'Created label: {label_name}')
    return new_label['id']

def mark_processed(gmail_service, message_id, agent_label_id):
    gmail_service.users().messages().modify(
        userId='me',
        id=message_id,
        body={
            'addLabelIds': [agent_label_id],
            'removeLabelIds': ['UNREAD']
        }
    ).execute()

# --- demo: minimal stand-in for the Gmail API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeUsers:
    def __init__(self):
        self._labels = [{'id': 'Label_1', 'name': 'Processed'}]
    def labels(self):
        return self
    def list(self, userId):
        return _Exec({'labels': self._labels})
    def create(self, userId, body):
        print(f'Created label: {body["name"]}')
        return _Exec({'id': 'Label_2', 'name': body['name']})
    def messages(self):
        return self
    def modify(self, userId, id, body):
        print(f'Marked {id} processed with {body}')
        return _Exec({'id': id})

class _FakeGmailService:
    def users(self):
        return _FakeUsers()

gmail_service = _FakeGmailService()
label_id = get_or_create_label(gmail_service, 'Processed')
print(f'Label id: {label_id}')
new_label_id = get_or_create_label(gmail_service, 'AgentHandled')
mark_processed(gmail_service, 'msg_99', new_label_id)

حلقة التشغيل الكاملة للوكيل

اربط جميع الخطوات في طريقة run() واحدة. يمر الوكيل على رسائل البريد، ويصنّف كل رسالة، وينشئ مسودات لعناصر الإجراءات، ويؤرشف الرسائل المزعجة، ويضع علامة على جميع الرسائل باعتبارها معالَجة. وسجّل ملخصًا في النهاية.

def run_email_agent(gmail_service, agent_context, max_emails=10):
    processed_label = get_or_create_label(gmail_service, 'AgentProcessed')
    emails = fetch_emails_for_classification(gmail_service, max_emails)

    summary = {'action_needed': 0, 'fyi': 0, 'spam': 0, 'drafts_created': 0}

    for email in emails:
        category, reason = classify_email(
            email['subject'], email['from'], email['body']
        )
        summary[category] += 1
        print(f'[{category}] {email["subject"][:60]} - {reason[:50]}')

        if category == 'action_needed':
            # Fetch full message for reply context
            full_msg = gmail_service.users().messages().get(
                userId='me', id=email['id'], format='full'
            ).execute()
            reply = draft_reply(
                email['subject'], email['from'],
                email['body'], agent_context
            )
            save_draft_reply(gmail_service, full_msg, reply)
            summary['drafts_created'] += 1

        mark_processed(gmail_service, email['id'], processed_label)

    print('\nAgent run complete:')
    for k, v in summary.items():
        print(f'  {k}: {v}')
    return summary

معالجة الأخطاء والمرونة

ضع كل خطوة من خطوات معالجة رسائل البريد داخل try/except. يجب ألا يؤدي الفشل في معالجة رسالة واحدة إلى إيقاف الوكيل عن معالجة بقية الرسائل. سجّل الأخطاء مع معرّف الرسالة وموضوعها حتى تتمكن من مراجعتها لاحقًا.

from googleapiclient.errors import HttpError

def process_email_safely(gmail_service, email, agent_context, label_id):
    try:
        category, reason = classify_email(
            email['subject'], email['from'], email['body']
        )

        if category == 'action_needed':
            full_msg = gmail_service.users().messages().get(
                userId='me', id=email['id'], format='full'
            ).execute()
            reply = draft_reply(
                email['subject'], email['from'],
                email['body'], agent_context
            )
            save_draft_reply(gmail_service, full_msg, reply)

        mark_processed(gmail_service, email['id'], label_id)
        return category

    except HttpError as e:
        print(f'Gmail API error on {email["id"]}: {e.resp.status}')
        return 'error'
    except Exception as e:
        print(f'Unexpected error on {email["subject"][:50]}: {e}')
        return 'error'

جدولة تشغيل الوكيل دوريًا

شغّل وكيل البريد الإلكتروني وفق جدول زمني باستخدام حلقة بسيطة تتضمن time.sleep() بين عمليات التشغيل. وفي بيئة الإنتاج، استخدم مجدول مهام مثل cron أو APScheduler أو دالة سحابية. أضف دائمًا فحصًا لتجنب التشغيل خلال ساعات التوقف احترامًا للمناطق الزمنية للمستلمين.

import time
import datetime

def is_business_hours():
    now = datetime.datetime.now()
    # Mon-Fri, 9am-6pm local time
    return (now.weekday() < 5 and 9 <= now.hour < 18)

def run_scheduler(gmail_service, agent_context,
                  interval_minutes=30, max_emails=20):
    print(f'Email agent started. Checking every {interval_minutes} min.')

    while True:
        if is_business_hours():
            print(f'\n[{datetime.datetime.now().strftime("%H:%M")}] Running agent...')
            try:
                run_email_agent(gmail_service, agent_context, max_emails)
            except Exception as e:
                print(f'Agent run failed: {e}')
        else:
            print('Outside business hours, skipping run')

        time.sleep(interval_minutes * 60)

السجل ومسار التدقيق

احتفظ بسجل JSON لكل رسالة بريد إلكتروني يعالجها الوكيل. ينشئ ذلك مسار تدقيق يمكنك مراجعته للتحقق من سلوك الوكيل وتشخيص أخطاء التصنيف وتحسين الموجهات بمرور الوقت.

import json
import datetime
from pathlib import Path

LOG_FILE = Path('agent_audit.jsonl')

def log_action(email_id, subject, sender, category, action, draft_id=None):
    entry = {
        'timestamp': datetime.datetime.now().isoformat(),
        'email_id': email_id,
        'subject': subject[:100],
        'from': sender,
        'category': category,
        'action': action,
        'draft_id': draft_id
    }
    with open(LOG_FILE, 'a', encoding='utf-8') as f:
        f.write(json.dumps(entry) + '\n')

# Usage in the main loop:
log_action(
    email_id='18abc123',
    subject='Invoice #1234',
    sender='billing@vendor.com',
    category='action_needed',
    action='draft_created',
    draft_id='r9000abc'
)

# --- demo ---
print('Audit log contents:')
print(LOG_FILE.read_text(encoding='utf-8'))

تحقق سريع: الإنسان ضمن الحلقة

اختبر مدى فهمك لتصميم مساعد البريد الإلكتروني.

مراجعة وكيل مساعد البريد الإلكتروني

لقد أنشأت مسارًا متكاملًا لوكيل مساعد البريد الإلكتروني:

  • الجلب: جلب رسائل البريد غير المقروءة باستخدام استعلامات بحث Gmail API
  • التصنيف: يصنّف النموذج اللغوي الكبير كل رسالة على أنها action_needed/fyi/spam ويُخرج النتيجة بتنسيق JSON
  • المسودة: ينشئ النموذج اللغوي الكبير ردًا بنبرة مهنية مع عناصر نائبة [FILL IN]
  • حفظ المسودة: تخزينها في Gmail للمراجعة البشرية — وعدم إرسالها تلقائيًا مطلقًا
  • وضع علامة على الرسائل المعالَجة: إضافة تصنيف مخصص وإزالة UNREAD لمنع إعادة المعالجة
  • عزل الأخطاء: وضع كل رسالة داخل try/except لمنع فشل واحد من إيقاف الدفعة
  • سجل التدقيق: ملف JSONL يسجل كل إجراء للمراجعة والتحسين
البدء مجانًا

تعلم AI Agents مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
60
الدروس
239

الأسئلة الشائعة

هل درس «بناء وكيل مساعد بسيط للبريد الإلكتروني» مجاني؟

نعم — نص درس «بناء وكيل مساعد بسيط للبريد الإلكتروني» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Agents، انتقل إلى CoddyKit PRO. تتضمن دورة AI Agents 4 دروس في المجموع.

ماذا ستتعلم في «بناء وكيل مساعد بسيط للبريد الإلكتروني»؟

من البداية إلى النهاية: قراءة صندوق الوارد ← التلخيص ← إعداد الرد ← انتظار الموافقة تتمرن على AI Agents مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Agents؟

لا تُشترط خبرة سابقة. AI Agents على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «بناء وكيل مساعد بسيط للبريد الإلكتروني»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Agents هذا؟

نعم. كل درس في AI Agents يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الاتصال بـ Gmail عبر API
  2. قراءة رسائل البريد وإرسالها برمجيًا
  3. إنشاء أحداث التقويم والاستعلام عنها
  4. بناء وكيل مساعد بسيط للبريد الإلكتروني
← العودة إلى AI Agents