0Pricing
AI Agents · Ders

Basit Bir E-posta Asistanı Aracısı Oluşturma

Uçtan uca: gelen kutusunu okuyun → özetleyin → yanıt taslağı hazırlayın → onay bekleyin.

Basit Bir E-posta Asistanı Aracısı Oluşturma, CoddyKit'te ücretsiz bir AI Agents dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Agents öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Agents kursu toplamda 4 dersten oluşur.

E-posta Asistanı Ajanı Mimarisi

Bir e-posta asistanı ajanı standart bir işlem hattını izler: Al → Sınıflandır → Karar Ver → Taslak Oluştur → Onayla → Gönder. Ajan okunmamış e-postaları okur, yanıtları sınıflandırmak ve taslaklarını oluşturmak için bir LLM kullanır, ardından göndermeden önce insan onayını bekler. İnsanı sürece dahil eden bu tasarım, tamamen özerk e-posta gönderiminden kaynaklanabilecek maliyetli hataları önler.

# 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')

Ajan Araçlarını Tanımlama

Ajanı açık araç tanımlarıyla yapılandırın. Her araç, belirli bir sorumluluğa sahip bir Python işlevdir. Bu modüler yapı, ajanı sınanabilir, hataları ayıklanabilir ve yeni yeteneklerle kolayca genişletilebilir hale getirir.

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. Adım: E-postaları Alma ve Ayrıştırma

İlk araç, okunmamış e-postaları alır ve LLM'nin ihtiyaç duyduğu temel alanları çıkarır: konu, gönderen ve düz metin gövdesi. LLM bağlam sınırları içinde kalmak ve API maliyetlerini azaltmak için gövdeyi kısa tutun; ilk 2000 karakterle sınırlayın.

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. Adım: LLM ile Sınıflandırma

Her e-postayı sınıflandırma için bir LLM'ye gönderin. Modelden kategori ve gerekçeyi içeren bir JSON nesnesi döndürmesini isteyen yapılandırılmış bir istem kullanın. JSON çıktısını açıkça isteyin; bu, serbest metni ayrıştırmaktan daha güvenilirdir.

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. Adım: Yanıt Taslakları Oluşturma

action_needed olarak sınıflandırılan e-postalar için LLM'den bir yanıt taslağı oluşturmasını isteyin. Ajanın rolü ve üslubu hakkında bağlam sağlayın. Profesyonel ancak kısa bir yanıt isteyin; insanın doldurması gereken her şey için bir yer tutucu ekleyin.

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. Adım: Taslakları İnsan İncelemesi için Kaydetme

Yapay zekâ tarafından oluşturulan e-postaları insan incelemesi olmadan asla göndermeyin. Bunları Gmail taslakları olarak kaydedin; böylece insan Gmail'i açıp inceleyebilir, gerekirse düzenleyebilir ve gönderebilir. Taslak oluşturma API'si, yalnızca drafts().create() kullanması bakımından gönderme API'siyle aynıdır.

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. Adım: İşlenen E-postaları İşaretleme

Bir e-postayı işledikten sonra (sınıflandırma ve taslak oluşturma veya arşivleme tamamlandığında) yeniden işlenmesini önlemek için işaretleyin. AgentProcessed gibi özel bir etiket ekleyin ve UNREAD etiketini kaldırın. Etiket yoksa onu bir kez oluşturun.

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)

Tam Ajan Çalışma Döngüsü

Tüm adımları tek bir run() yönteminde birleştirin. Ajan e-postalar arasında döner, her birini sınıflandırır, işlem gerektirenler için taslaklar oluşturur, istenmeyen e-postaları arşivler ve tümünü işlenmiş olarak işaretler. Sonunda bir özet kaydı tutun.

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

Hata Yönetimi ve Dayanıklılık

Her e-posta işleme adımını try/except içine alın. Bir e-postadaki hata, ajanın geri kalanını işlemesini asla durdurmamalıdır. Daha sonra inceleyebilmeniz için hataları e-posta ID'si ve konusuyla birlikte günlüğe kaydedin.

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'

Ajanı Düzenli Aralıklarla Çalışacak Şekilde Zamanlama

E-posta ajanını, çalıştırmalar arasında time.sleep() bulunan basit bir döngüyle zamanlanmış şekilde çalıştırın. Üretim ortamında cron, APScheduler veya bir bulut işlevi gibi bir görev zamanlayıcı kullanın. Alıcıların saat dilimlerine saygı göstermek için çalışma dışı saatlerde çalışmayı önleyen bir kontrolü her zaman ekleyin.

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)

Günlük Kaydı ve Denetim İzi

Ajanın işlediği her e-postanın JSON günlüğünü tutun. Bu, ajanın davranışını doğrulamak, yanlış sınıflandırmaları teşhis etmek ve istemleri zaman içinde geliştirmek için inceleyebileceğiniz bir denetim izi oluşturur.

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'))

Hızlı Kontrol: İnsanı Sürece Dahil Etme

E-posta asistanı tasarımı hakkındaki anlayışınızı sınayın.

E-posta Asistanı Ajanı Özeti

Eksiksiz bir e-posta asistanı ajanı işlem hattı oluşturdunuz:

  • Al: Gmail API arama sorgularıyla okunmamış e-postaları alın
  • Sınıflandır: LLM, her e-postayı işlem_gerekli/bilgi/istenmeyen olarak JSON çıktısıyla kategorilere ayırır
  • Taslak oluştur: LLM, profesyonel üslupla ve [FILL IN] yer tutucularıyla yanıt oluşturur
  • Taslağı kaydet: insan incelemesi için Gmail'e kaydedin; otomatik olarak asla göndermeyin
  • İşlendi olarak işaretle: yeniden işlemeyi önlemek için özel etiket ekleyin ve UNREAD etiketini kaldırın
  • Hataları yalıt: bir hatanın toplu işlemi durdurmasını önlemek için her e-postayı try/except içine alın
  • Denetim günlüğü: inceleme ve geliştirme amacıyla her eylemi kaydeden JSONL dosyası

Sıkça Sorulan Sorular

“Basit Bir E-posta Asistanı Aracısı Oluşturma” dersi ücretsiz mi?

Evet — “Basit Bir E-posta Asistanı Aracısı Oluşturma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Agents kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Agents kursu toplamda 4 dersten oluşur.

“Basit Bir E-posta Asistanı Aracısı Oluşturma” dersinde ne öğreneceğim?

Uçtan uca: gelen kutusunu okuyun → özetleyin → yanıt taslağı hazırlayın → onay bekleyin. AI Agents ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

AI Agents öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te AI Agents, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.

“Basit Bir E-posta Asistanı Aracısı Oluşturma” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu AI Agents dersinde kod yazıp çalıştırabilir miyim?

Evet. Her AI Agents dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. API Üzerinden Gmail'e Bağlanma
  2. E-postaları Programlı Olarak Okuma ve Gönderme
  3. Takvim Etkinliği Oluşturma ve Sorgulama
  4. Basit Bir E-posta Asistanı Aracısı Oluşturma
← AI Agents Sayfasına Dön