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 pipelinepipelineL'ensemble des opportunités commerciales actives réparties selon les étapes du processus de vente, avec leur valeur potentielle cumulée et leur probabilité de conclusion.Voir la définition complète → autonome, avec les garde-fousgarde-fousRègles et contrôles qui maintiennent un système d'IA dans des limites sûres, légales et conformes à la marque, en bloquant les sorties et actions hors cadre.Voir la définition complète → qui l'empêchent de devenir coûteux ou embarrassant.
Deux façons d'automatiser : dans le produit ou via l'APIAPIApplication Programming Interface : une interface standardisée qui permet aux applications de communiquer et d'échanger des données sans connaître leur fonctionnement interne respectif.Voir la définition complète →
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 warehousedata warehouseUn référentiel central qui consolide les données de nombreux systèmes sources dans un stockage structuré et optimisé pour les requêtes, conçu pour l'analytique, le reporting et la business intelligence.Voir la définition complète → 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 :
- Collecter les entrées (flux RSS, quelques URL, le brief de la semaine dernière).
- Générer le brief via l'API.
- Valider la sortie par rapport à un schémamaUtiliser un logiciel pour automatiser les tâches et campagnes marketing répétitives, afin de personnaliser à grande échelle sur des canaux comme l'email, le web et le social.Voir la définition complète →.
- 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.
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_parsedTrois choix délibérés :
- Un modèle petit et peu cher (
gpt-4.1-miniici) 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 tokenstokensUn token est l'unité de base de texte que traitent les modèles de langage : le plus souvent un fragment de mot, un mot entier ou un signe de ponctuation, plutôt qu'un simple caractère.Voir la définition complète → 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à.
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"— {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 :
# 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.
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
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_urlpar é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
itemsest 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 ?
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.
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 :
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_dispatchpour 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