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 emails2. 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 summaryHata 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
- API Üzerinden Gmail'e Bağlanma
- E-postaları Programlı Olarak Okuma ve Gönderme
- Takvim Etkinliği Oluşturma ve Sorgulama
- Basit Bir E-posta Asistanı Aracısı Oluşturma