Créer un assistant e-mail simple sous forme d’agent
De bout en bout : lire la boîte de réception → résumer → rédiger une réponse → attendre l’approbation.
Créer un assistant e-mail simple sous forme d’agent est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Architecture d’un agent assistant de messagerie
Un agent assistant de messagerie suit une chaîne de traitement standard : Récupérer → Classer → Décider → Rédiger → Approuver → Envoyer. L’agent lit les e-mails non lus, utilise un LLM pour les classer et rédiger des réponses, puis attend l’approbation d’une personne avant de les envoyer. Cette conception avec intervention humaine évite les erreurs coûteuses liées à l’envoi entièrement autonome d’e-mails.
# 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')Définir les outils de l’agent
Structurez l’agent à l’aide de définitions d’outils claires. Chaque outil est une fonction Python qui assume une responsabilité précise. Cette modularité facilite les tests, le débogage et l’ajout de nouvelles fonctionnalités à l’agent.
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')Étape 1 : Récupérer et analyser les e-mails
Le premier outil récupère les e-mails non lus et extrait les champs essentiels dont le LLM a besoin : l’objet, l’expéditeur et le corps en texte brut. Gardez le corps court — tronquez-le aux 2 000 premiers caractères pour respecter les limites de contexte du LLM et réduire les coûts d’API.
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Étape 2 : Classification par LLM
Envoyez chaque e-mail à un LLM pour le classer. Utilisez une instruction structurée demandant au modèle de renvoyer un objet JSON contenant la catégorie et le raisonnement. Demandez explicitement une sortie au format JSON : cette méthode est plus fiable que l’analyse d’un texte libre.
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'Étape 3 : Générer des brouillons de réponse
Pour les e-mails classés comme action_needed, demandez au LLM de générer un brouillon de réponse. Fournissez le contexte concernant le rôle et le ton de l’agent. Demandez une réponse professionnelle, mais concise — ajoutez un espace réservé pour tout élément que la personne devra compléter.
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()Étape 4 : Enregistrer les brouillons pour révision humaine
N’envoyez jamais d’e-mails générés par une IA sans révision humaine. Enregistrez-les comme drafts Gmail afin que la personne puisse ouvrir Gmail, les vérifier, les modifier si nécessaire, puis les envoyer. L’API de création de brouillons est la même que celle d’envoi : il suffit d’utiliser 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.')
Étape 5 : Marquer les e-mails traités
Après avoir traité un e-mail (classé et accompagné d’un brouillon, ou archivé), marquez-le pour éviter de le retraiter. Ajoutez un libellé personnalisé comme AgentProcessed et supprimez le libellé UNREAD. Créez le libellé une seule fois s’il n’existe pas.
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)
Boucle d’exécution complète de l’agent
Reliez toutes les étapes dans une seule méthode run(). L’agent parcourt les e-mails, classe chacun d’eux, crée des brouillons pour les éléments nécessitant une action, archive les courriers indésirables et marque tous les e-mails comme traités. Consignez un récapitulatif à la fin.
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 summaryGestion des erreurs et résilience
Entourez chaque étape de traitement d’un e-mail d’un bloc try/except. L’échec du traitement d’un e-mail ne doit jamais empêcher l’agent de traiter les autres. Consignez les erreurs avec l’ID et l’objet de l’e-mail afin de pouvoir les examiner ultérieurement.
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'Planifier l’exécution périodique de l’agent
Exécutez l’agent de messagerie selon un calendrier en utilisant une simple boucle avec un time.sleep() entre les exécutions. En production, utilisez un planificateur de tâches comme cron, APScheduler ou une fonction cloud. Ajoutez toujours une vérification pour éviter les exécutions en dehors des heures de travail et respecter les fuseaux horaires des destinataires.
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)Journalisation et piste d’audit
Conservez un journal JSON de chaque e-mail traité par l’agent. Vous disposerez ainsi d’une piste d’audit que vous pourrez examiner pour vérifier le comportement de l’agent, diagnostiquer les erreurs de classification et améliorer les instructions au fil du temps.
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'))
Vérification rapide : intervention humaine
Vérifiez votre compréhension de la conception de l’assistant de messagerie.
Récapitulatif de l’agent assistant de messagerie
Vous avez créé une chaîne de traitement complète pour un agent assistant de messagerie :
- Récupération : récupérer les e-mails non lus à l’aide des requêtes de recherche de l’API Gmail
- Classification : le LLM classe chaque e-mail en action_needed/fyi/spam avec une sortie JSON
- Brouillon : le LLM génère une réponse avec un ton professionnel et des espaces réservés [FILL IN]
- Enregistrement du brouillon : l’enregistrer dans Gmail pour révision humaine — ne jamais l’envoyer automatiquement
- Marquage comme traité : ajouter un libellé personnalisé et supprimer UNREAD pour éviter un nouveau traitement
- Isolation des erreurs : entourer chaque e-mail d’un bloc try/except afin qu’un échec n’interrompe pas le traitement du lot
- Journal d’audit : fichier JSONL enregistrant chaque action pour permettre la vérification et l’amélioration
Questions Fréquemment Posées
La leçon « Créer un assistant e-mail simple sous forme d’agent » est-elle gratuite ?
Oui — le texte complet de « Créer un assistant e-mail simple sous forme d’agent » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Créer un assistant e-mail simple sous forme d’agent » ?
De bout en bout : lire la boîte de réception → résumer → rédiger une réponse → attendre l’approbation. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Créer un assistant e-mail simple sous forme d’agent » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Se connecter à Gmail via l’API
- Lire et envoyer des e-mails par programmation
- Créer et interroger des événements d’agenda
- Créer un assistant e-mail simple sous forme d’agent