Aller au contenu principal

Background mode

Mis à jour le 28 juillet 2026

Une troisième voie entre le synchrone et le lot

Le background mode de la Responses API occupe une place précise dans votre outillage. L’appel synchrone vous donne la réponse tout de suite, mais vous oblige à maintenir la connexion ouverte pendant toute la génération. La Batch API traite des milliers de requêtes à moitié prix, mais dans une fenêtre qui se compte en heures. Entre les deux, le background mode traite une requête individuelle, au tarif standard, sans connexion ouverte : vous envoyez la demande, vous recevez un identifiant, et vous revenez chercher le résultat quand il vous convient.

Ce compromis vise un cas précis : la requête unique qui prend trop longtemps pour un appel bloquant, mais qu’on ne peut pas faire attendre le lendemain. C’est exactement la situation d’un modèle en raisonnement élevé sur un document complexe.

Lancer et récupérer

L’activation tient à un seul paramètre. La réponse retournée immédiatement ne contient pas de texte : elle porte un identifiant et un statut, généralement queued ou in_progress.

import openai

client = openai.OpenAI()

# Lancer la requête en background
response = client.responses.create(
    model="gpt-5.6-sol",  # Idéal pour les tâches lentes (raisonnement élevé)
    input="Analysez les implications économiques de la régulation "
          "européenne sur l'IA pour les PME françaises. "
          "Proposez un plan d'action en 10 points.",
    background=True,  # Mode arrière-plan
)

print(f"ID de la réponse : {response.id}")
print(f"Statut : {response.status}")  # "queued" ou "in_progress"

Cet identifiant est votre seul lien avec le travail en cours : persistez-le avant toute autre chose. Le perdre, c’est payer une génération dont vous ne récupérerez jamais le résultat. La récupération consiste ensuite à interroger l’API jusqu’à ce que le statut devienne terminal, en traitant distinctement l’échec et l’annulation, qui n’ont pas les mêmes conséquences côté métier.

import time

def attendre_reponse(response_id: str, intervalle: int = 5) -> str:
    """Attend qu'une réponse en background soit terminée."""
    while True:
        response = client.responses.retrieve(response_id)

        match response.status:
            case "completed":
                return response.output_text
            case "failed":
                raise RuntimeError(
                    f"La requête a échoué : {response.error}"
                )
            case "cancelled":
                raise RuntimeError("La requête a été annulée.")
            case _:
                print(f"Statut : {response.status}...")
                time.sleep(intervalle)

Le cas des raisonnements longs

Un modèle comme GPT-5.6 Sol en raisonnement élevé peut prendre de trente secondes à deux minutes avant de produire son premier mot visible. Sur un appel synchrone, ces durées se heurtent aux timeouts par défaut des clients HTTP, des proxys et des passerelles. Le background mode supprime le problème à la racine, et permet accessoirement de lancer plusieurs analyses lourdes sans occuper autant de connexions.

async def analyse_approfondie(document: str) -> str:
    """Lance une analyse avec GPT-5.6 Sol sans bloquer."""
    response = client.responses.create(
        model="gpt-5.6-sol",
        input=f"Effectuez une analyse juridique approfondie de ce contrat. "
              f"Identifiez tous les risques potentiels, les clauses "
              f"problématiques et proposez des modifications.\n\n{document}",
        background=True,
    )
    return response.id

# Lancer plusieurs analyses en parallèle
ids_taches = []
for doc in documents:
    tache_id = await analyse_approfondie(doc)
    ids_taches.append(tache_id)

# Récupérer les résultats plus tard
resultats = {}
for tache_id in ids_taches:
    resultats[tache_id] = attendre_reponse(tache_id)

Le pattern se complète naturellement d’un webhook lorsque l’appelant est un autre service. Votre endpoint lance la requête, retourne l’identifiant, et confie à une tâche de fond le soin de surveiller la complétion puis de notifier. L’appelant n’interroge rien : il est prévenu.

from fastapi import FastAPI, BackgroundTasks
import httpx

app = FastAPI()

@app.post("/api/analyse-longue")
async def lancer_analyse(requete: dict, background_tasks: BackgroundTasks):
    """Lance une analyse et notifie par webhook quand c'est fini."""
    response = client.responses.create(
        model="gpt-5.6-sol",
        input=requete["prompt"],
        background=True,
    )

    # Surveiller en arrière-plan
    background_tasks.add_task(
        surveiller_et_notifier,
        response_id=response.id,
        webhook_url=requete["webhook_url"],
    )

    return {"response_id": response.id, "statut": "lancé"}

async def surveiller_et_notifier(response_id: str, webhook_url: str):
    """Surveille une requête background et notifie par webhook."""
    resultat = attendre_reponse(response_id)

    async with httpx.AsyncClient() as http:
        await http.post(webhook_url, json={
            "response_id": response_id,
            "résultat": resultat,
        })

Choisir le bon mode

La règle de décision se formule en deux questions. Combien de requêtes ? Et l’utilisateur attend-il ? Au-delà de la centaine de requêtes sans exigence d’immédiateté, la Batch API l’emporte par son prix : résultat en heures, moitié moins cher. Si quelqu’un attend devant son écran, l’appel synchrone reste le seul choix acceptable, à prix standard. Le background mode occupe le reste de l’espace : peu de requêtes, résultat en minutes, prix standard. Cette logique gagne à être écrite une fois pour toutes plutôt que rejouée à chaque nouveau service.

def choisir_mode(
    nb_requetes: int,
    urgence: str,
    budget: str,
) -> str:
    """Recommande le mode d'exécution optimal."""
    if nb_requetes > 100 and urgence != "immediat":
        return "batch"
    elif urgence == "immediat":
        return "synchrone"
    else:
        return "background"

Prévoyez enfin une porte de sortie. Une requête en arrière-plan qui dépasse largement sa durée attendue immobilise du budget et retarde le reste de votre pipeline ; l’API permet de l’annuler explicitement. Fixez ce plafond au double de la durée observée en pratique, pas à la durée moyenne, sous peine d’annuler des requêtes qui allaient aboutir.

def annuler_si_trop_long(response_id: str, timeout: int = 120):
    """Annule une requête si elle dépasse le timeout."""
    debut = time.time()

    while time.time() - debut < timeout:
        response = client.responses.retrieve(response_id)
        if response.status == "completed":
            return response.output_text
        time.sleep(5)

    # Timeout dépassé — annuler
    client.responses.cancel(response_id)
    raise TimeoutError(
        f"Requête {response_id} annulée après {timeout}s."
    )

Points clés à retenir

  • Le background mode lance des requêtes sans maintenir de connexion ouverte
  • Idéal pour les réglages de raisonnement élevés (GPT-5.6 Sol) et les tâches longues
  • Combinez-le avec un webhook pour une architecture événementielle
  • Persistez l’identifiant de réponse : c’est votre seul lien avec le travail en cours
  • Pour du traitement en masse à bas coût, préférez la Batch API