+180 XP

Automatiser le travail récurrent avec la stack OpenAI

Ce brief concurrentiel hebdomadaire que vous rédigez chaque lundi matin, celui où vous parcourez cinq blogs de concurrents, résumez les changements de prix et collez le tout dans un email, peut tourner tout seul pendant que vous dormez. L'astuce : cesser de voir ChatGPT comme un endroit où l'on se rend et commencer à voir l'API OpenAI comme un composant que l'on planifie. Cette leçon transforme un chat répété en pipeline autonome, avec les garde-fous qui l'empêchent de devenir coûteux ou embarrassant.

Deux façons d'automatiser : dans le produit ou via l'API

Avant d'écrire du code, sachez qu'OpenAI propose une option no-code. Dans ChatGPT, les tâches planifiées permettent de demander à l'assistant d'exécuter un prompt selon une fréquence récurrente et de vous notifier. C'est réellement utile pour des rappels personnels et des digests légers. Voir Scheduled tasks in ChatGPT.

Mais les tâches planifiées vivent dans votre compte ChatGPT. Elles ne peuvent pas facilement committer dans un repo, écrire dans votre data warehouse ou envoyer du mail via le fournisseur transactionnel de votre entreprise. Dès que vous avez besoin d'une vraie intégration, de gestion de versions ou d'un compte de service non rattaché à votre login personnel, vous passez à l'API plus un scheduler externe.

La règle de décision :

  • Une personne, sortie légère, notify-me : tâches planifiées ChatGPT.
  • Sortie partagée, systèmes réels, exécutions auditables : API + cron ou CI.

Le reste de cette leçon porte sur la seconde voie.

Le pipeline, en quatre étapes

Notre exemple : chaque lundi à 6h, récupérer le contenu récent des concurrents, produire un brief structuré, le mettre en forme dans un email et l'envoyer.

Un job planifié n'est que quatre étapes collées ensemble :

  1. Collecter les entrées (flux RSS, quelques URL, le brief de la semaine dernière).
  2. Générer le brief via l'API.
  3. Valider la sortie par rapport à un schéma.
  4. Livrer (email, Slack, un fichier committé).

Le modèle ne prend en charge que l'étape 2. Traiter collecte, validation et livraison comme du code ordinaire (pas des appels au modèle) est ce qui rend le pipeline économique et prévisible.

Pourquoi les structured outputs ne sont pas négociables ici

Dans un chat, vous lisez de la prose et vous passez à autre chose. Dans un pipeline, l'étape suivante est du code qui doit *parser* le résultat. Si le modèle renvoie des formes légèrement différentes d'une semaine à l'autre, votre moteur de rendu d'email casse.

Utilisez les Structured Outputs pour forcer le modèle à renvoyer du JSON conforme à un schéma que vous définissez. Ce n'est pas du prompt-begging (« merci de renvoyer du JSON ») ; l'API contraint la génération à votre schéma. Lire Structured Outputs.

Construire le générateur de brief

Voici l'appel principal avec la Responses API, l'interface principale actuelle d'OpenAI pour les nouveaux développements. Notez le bloc text.format qui fixe la réponse à un schéma.

python
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

SCHEMA = {
    "type": "object",
    "properties": {
        "summary": {"type": "string"},
        "items": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "competitor": {"type": "string"},
                    "headline": {"type": "string"},
                    "why_it_matters": {"type": "string"},
                    "source_url": {"type": "string"},
                },
                "required": ["competitor", "headline", "why_it_matters", "source_url"],
                "additionalProperties": False,
            },
        },
    },
    "required": ["summary", "items"],
    "additionalProperties": False,
}

def build_brief(gathered_text: str) -> dict:
    resp = client.responses.create(
        model="gpt-4.1-mini",
        input=[
            {"role": "system", "content": "You are a competitive analyst. "
             "Only use facts present in the provided sources. If a claim is "
             "not supported, omit it. Never invent pricing or dates."},
            {"role": "user", "content": gathered_text},
        ],
        text={"format": {"type": "json_schema", "name": "brief",
                         "schema": SCHEMA, "strict": True}},
    )
    return resp.output_parsed

Trois choix délibérés :

  • Un modèle petit et peu cher (gpt-4.1-mini ici) gère très bien la synthèse. Réservez les modèles frontier aux tâches qui ont réellement besoin de raisonnement. C'est votre principal levier de coût.
  • `strict": True` garantit que le JSON valide, donc l'étape 4 peut faire le rendu sans parsing défensif.
  • Le system prompt interdit l'invention. Les jobs récurrents tournent sans supervision : un prix concurrent halluciné peut rester dans une boîte de réception de dirigeant avant que quiconque le remarque.

Collecter les entrées sans une fenêtre de contexte gigantesque

Ne collez pas cinq blogs entiers dans le prompt pour payer des tokens dont vous n'avez pas besoin. Récupérez chaque flux, ne gardez que les entrées plus récentes que votre dernière exécution et coupez chaque article à son chapeau. Votre gathered_text devient un digest compact et daté. Moins de contexte signifie moins de coût, des exécutions plus rapides et moins d'occasions pour le modèle de s'égarer.

C'est aussi là que vous stockez l'état : écrivez last_run.json après chaque exécution réussie afin que la semaine suivante ne traite que les *nouveaux* éléments. Un pipeline qui résume les mêmes articles chaque semaine est à la fois inutilement coûteux et bruyant.

Livraison : rendre, puis envoyer

Gardez le modèle totalement à l'écart de la livraison. Prenez le JSON validé et faites le rendu avec un template, puis envoyez via ce que votre organisation utilise déjà.

python
def render_email(brief: dict) -> str:
    lines = [f"<p>{brief['summary']}</p>", "<ul>"]
    for item in brief["items"]:
        lines.append(
            f"<li><b>{item['competitor']}:</b> {item['headline']} "
            f"&mdash; {item['why_it_matters']} "
            f"<a href='{item['source_url']}'>source</a></li>"
        )
    lines.append("</ul>")
    return "".join(lines)

Envoyez via le SDK de votre fournisseur d'email transactionnel ou via SMTP. L'idée : **le modèle a produit des *faits structurés*, et du code déterministe a produit l'*artefact***. Si la mise en page de l'email doit changer, vous modifiez un template, pas un prompt.

La planification

Maintenant, rendez-le récurrent. Deux options propres.

Option A : cron sur un petit serveur

Sur n'importe quelle machine Linux allumée en permanence, crontab -e :

bash
# Lundi 06:00, écrire les logs, ne jamais laisser un crash passer en silence
0 6 * * 1 cd /opt/briefs && /opt/briefs/.venv/bin/python run.py >> /var/log/brief.log 2>&1 || curl -s "$ALERT_WEBHOOK" -d "brief job failed"

Simple, mais vous gérez le serveur, le fichier de secrets et l'uptime.

Option B : GitHub Actions (recommandé pour la plupart des équipes)

Les runners d'intégration continue sont gratuits pour des jobs planifiés légers, les secrets sont gérés et chaque exécution est loguée. Ici, CI signifie simplement un environnement hébergé qui exécute votre script sur un déclencheur.

yaml
name: weekly-brief
on:
  schedule:
    - cron: "0 6 * * 1"   # Mondays 06:00 UTC
  workflow_dispatch: {}    # lets you run it manually to test

jobs:
  brief:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt
      - run: python run.py
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          EMAIL_API_KEY: ${{ secrets.EMAIL_API_KEY }}

workflow_dispatch est la ligne sous-estimée : elle vous donne un bouton « Run workflow » pour tester tout le pipeline à la demande sans attendre lundi. Les secrets vivent dans les paramètres chiffrés du repo, jamais dans le code.

Schedule Python Scripts with GitHub Actions

Watch on YouTube

Des garde-fous pour rester sûr et économique

Un job non supervisé qui appelle une API payante et envoie des emails à votre équipe a besoin de limites. Fixez-les avant la mise en production, pas après une facture surprise.

Garde-fous de coût

  • Fixez une limite d'usage dans le dashboard de l'API pour qu'une boucle incontrôlée ne vide pas votre compte. Voir Usage limits.
  • Plafonnez les tokens de sortie avec max_output_tokens. Un brief n'a pas besoin de 4 000 tokens.
  • Utilisez un projet et une clé API dédiés à ce job. Des clés par projet permettent de voir exactement ce que coûte ce pipeline et de la révoquer indépendamment.
  • Choisissez le modèle le moins cher qui passe votre eval. Retestez de temps en temps ; les modèles bon marché continuent de progresser.

Garde-fous d'exactitude

  • Ancrez le modèle uniquement dans les sources récupérées, et exigez une source_url par élément (le schéma l'impose déjà). Un élément sans source est un signal d'alerte que votre code peut écarter.
  • Ajoutez un contrôle de cohérence entre validation et livraison : si items est vide, envoyez « aucun changement notable cette semaine » plutôt qu'une coquille vide, et si la liste est suspicieusement longue, signalez-la pour relecture.

Garde-fous opérationnels

  • Échouez bruyamment. Un job cron silencieux qui a cessé de tourner il y a trois semaines est pire que pas de job du tout. Pinguez un webhook en cas d'échec (voir l'exemple cron).
  • Réessayez les erreurs transitoires avec backoff. Les coupures réseau et les rate limits arrivent ; encapsulez l'appel API dans un retry avec quelques tentatives.
  • Loguez la consommation de tokens et le coût de chaque exécution, même en cas de succès, pour repérer les dérives.

Vérification des acquis

1. Selon la leçon, quel est le changement de perspective fondamental nécessaire pour automatiser une tâche récurrente avec la stack OpenAI ?

2. Une équipe a besoin d'un brief automatisé qui committe dans un repo partagé, utilise un compte de service non rattaché à un login personnel et produit des exécutions auditables. Quelle approche la leçon recommande-t-elle ?

3. Dans le pipeline en quatre étapes (Collecter, Générer, Valider, Livrer), pourquoi la leçon insiste-t-elle sur le fait que seule l'étape 2 doit être un appel au modèle ?

CHOIX MULTIPLES

4. Sélectionnez TOUTES les affirmations correctes sur les Structured Outputs telles que décrites dans la leçon.

Sélectionnez toutes les réponses correctes.

CHOIX MULTIPLES

5. Sélectionnez TOUS les scénarios où les tâches planifiées ChatGPT (l'option no-code) sont un choix approprié selon la leçon.

Sélectionnez toutes les réponses correctes.

Passer à l'échelle : quand un seul prompt ne suffit plus

La conception à appel unique ci-dessus est le bon point de départ, et elle couvre une part étonnamment large du travail récurrent. N'ajoutez de la structure que si la tâche en a réellement besoin.

Quand ajouter des tools

Si le job doit décider *quelles* sources récupérer ou appeler des systèmes en direct (une API de pricing, une recherche interne), donnez au modèle le function calling pour qu'il puisse demander ces actions, et que votre code les exécute. Le modèle propose ; votre code dispose. Cela empêche le modèle de toucher à quoi que ce soit que vous n'avez pas explicitement autorisé.

Quand utiliser l'agents SDK

Pour les jobs multi-étapes (collecter, rédiger, critiquer son propre brouillon, réviser, puis mettre en forme), le OpenAI Agents SDK offre une façon propre d'orchestrer étapes, tools et handoffs sans écrire soi-même la boucle de contrôle. Utilisez-le quand votre pipeline comporte une vraie logique de branchement. N'y recourez pas pour résumer un flux : c'est du sur-dimensionnement, et chaque appel de modèle supplémentaire, c'est de la latence et du coût.

Un mot sur l'agent ChatGPT et Codex

L'agent ChatGPT peut naviguer et opérer dans une session pour accomplir des tâches, et Codex peut écrire et exécuter du code pour vous. Les deux sont excellents pour *construire et prototyper* ce pipeline de manière interactive. Mais la version de production d'un job récurrent doit être du code déterministe sous gestion de versions avec un scheduler, pas une session d'agent interactive qu'il faut surveiller. Construisez avec l'agent ; déployez avec l'API.

Assembler le tout

Votre run.py n'est plus que les quatre étapes en séquence :

python
def main():
    state = load_state("last_run.json")
    gathered = gather_sources(feeds, since=state["last_run"])
    if not gathered.strip():
        notify("No new competitor content this week.")
        return
    brief = build_brief(gathered)
    if not brief["items"]:
        notify("No notable changes this week.")
        return
    send_email(render_email(brief))
    save_state("last_run.json", now())

if __name__ == "__main__":
    main()

Lisible, testable et ennuyeux dans le meilleur sens du terme. Le modèle est une ligne au milieu. Tout autour est du code simple et débogable, exactement la propriété que vous voulez pour quelque chose qui tourne sans que vous regardiez.

Points clés

  • Ne confiez au modèle que la génération. Collecter, valider et livrer relèvent du code déterministe. Cette séparation est ce qui rend les jobs planifiés économiques et fiables.
  • Forcez les Structured Outputs avec `strict": True` pour que l'étape suivante puisse parser les résultats sans code défensif ni casse d'une semaine à l'autre.
  • Planifiez avec GitHub Actions pour les équipes : secrets gérés, exécutions loguées et un bouton workflow_dispatch pour tester à la demande. N'utilisez cron que si vous contrôlez la machine.
  • Posez les garde-fous avant la mise en production : une limite d'usage stricte, une clé API par projet, le modèle le moins cher qui passe votre eval, des tokens de sortie plafonnés et un webhook d'échec pour qu'un job mort ne passe jamais inaperçu.
  • Construisez de façon interactive, déployez de façon déterministe. Prototypez avec l'agent ChatGPT ou Codex, mais exécutez le job récurrent comme du code sous gestion de versions ; ne recourez à l'Agents SDK que si la tâche comporte un vrai branchement multi-étapes.

À faire, tiré de cette leçon

Ces actions sont compilées dans le plan d'action du rôle.

  • Rédigez les Tasks planifiées comme des prompts complets et autonomes, en précisant le comportement en semaine creuse
  • Fixez des plafonds de facturation stricts avec des clés API distinctes par environnement
  • Confiez au modèle la seule génération ; collectez, validez et livrez dans le code
Voir le plan d'action complet →