0Pricing
AI Agents · Leçon

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 summary

Gestion 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

  1. Se connecter à Gmail via l’API
  2. Lire et envoyer des e-mails par programmation
  3. Créer et interroger des événements d’agenda
  4. Créer un assistant e-mail simple sous forme d’agent
← Retour à AI Agents