Déployer un modèle fine-tuné
Mis à jour le 28 juillet 2026
Du développement à la production
Votre modèle fine-tuné est évalué et approuvé. Il est temps de le déployer en production. Cette leçon couvre les bonnes pratiques pour un déploiement fiable, sécurisé et maintenable — c’est-à-dire tout ce qui sépare un modèle qui fonctionne sur votre poste d’un service que vos utilisateurs peuvent solliciter en continu sans que vous soyez de garde.
Commençons par le plus simple : le modèle fine-tuné s’utilise exactement comme n’importe quel modèle OpenAI. La seule différence est le nom du modèle, qui devient un identifiant long incluant votre organisation, votre suffixe et un identifiant de job. Aucun paramètre supplémentaire, aucun point d’entrée particulier.
from openai import OpenAI
client = OpenAI()
# Le modèle fine-tuné a un identifiant unique
MODELE_PRODUCTION = "ft:gpt-5.6-luna:votre-org::mon-assistant-v3:abc12345"
response = client.responses.create(
model=MODELE_PRODUCTION,
input="Bonjour, j'ai un problème avec ma commande #12345."
)
print(response.output_text)
Centraliser la configuration
Cet identifiant a une particularité qui doit gouverner toute votre architecture : il change à chaque nouvelle version de votre modèle. Codé en dur au milieu de votre application, il vous condamnera à un redéploiement complet pour la moindre mise à jour, et à une recherche fastidieuse le jour où il apparaîtra à trois endroits différents. Placez-le dans une variable d’environnement, avec le modèle de base comme valeur de repli : votre application démarrera même si la variable n’est pas renseignée, ce qui évite l’incident du service muet un dimanche soir.
import os
class Config:
"""Configuration centralisée des modèles."""
# Variable d'environnement pour le modèle en production
MODEL_ID = os.environ.get(
"OPENAI_FT_MODEL",
"gpt-5.6-luna" # fallback sur le modèle de base
)
# Prompt système (peut être vide si intégré dans le fine-tuning)
SYSTEM_PROMPT = os.environ.get("SYSTEM_PROMPT", "")
# Paramètres de génération
MAX_TOKENS = int(os.environ.get("MAX_TOKENS", "1024"))
TEMPERATURE = float(os.environ.get("TEMPERATURE", "0.7"))
Le même raisonnement s’applique aux appels eux-mêmes. Plutôt que de disperser des client.responses.create dans votre code métier, encapsulez-les dans un service unique. Vous gagnez un point de passage obligé où brancher plus tard la journalisation, les métriques et le repli, sans rouvrir vingt fichiers. Remarquez comment le service ci-dessous compose un contexte optionnel avec la question, et comment il rattrape toute exception pour la rediriger vers le modèle de base.
from openai import OpenAI
class AssistantService:
"""Service encapsulant les appels au modèle fine-tuné."""
def __init__(self):
self.client = OpenAI()
self.model = Config.MODEL_ID
def repondre(self, question: str, contexte: str = "") -> str:
"""Génère une réponse."""
input_msg = question
if contexte:
input_msg = f"Contexte : {contexte}\n\nQuestion : {question}"
try:
response = self.client.responses.create(
model=self.model,
input=input_msg,
max_output_tokens=Config.MAX_TOKENS,
temperature=Config.TEMPERATURE
)
return response.output_text
except Exception as e:
# Fallback sur le modèle de base en cas d'erreur
return self._fallback(input_msg, str(e))
def _fallback(self, input_msg: str, erreur: str) -> str:
"""Fallback sur le modèle de base."""
print(f"Fallback activé : {erreur}")
response = self.client.responses.create(
model="gpt-5.6-luna",
instructions=Config.SYSTEM_PROMPT,
input=input_msg
)
return response.output_text
Résister aux incidents
Une réponse dégradée vaut mieux qu’une absence de réponse. Si votre modèle fine-tuné est temporairement indisponible, mieux vaut basculer sur le modèle de base — avec le prompt système que vous aviez conservé, précisément pour ce cas — que d’afficher une erreur à l’utilisateur. La fonction suivante enchaîne deux mécanismes complémentaires : d’abord trois tentatives sur le modèle principal, espacées par un délai qui double à chaque échec pour ne pas aggraver une surcharge passagère ; ensuite, seulement si tout a échoué, un appel au modèle de repli. Conservez le drapeau fallback renvoyé dans le résultat, car il constitue la seule trace exploitable de la fréquence à laquelle ce mécanisme se déclenche réellement.
import time
def appel_resilient(
client: OpenAI,
modele_principal: str,
modele_fallback: str,
input_msg: str,
max_retries: int = 3
) -> dict:
"""Appel résilient avec retry et fallback."""
for tentative in range(max_retries):
try:
response = client.responses.create(
model=modele_principal,
input=input_msg,
timeout=30
)
return {
"reponse": response.output_text,
"modele_utilise": modele_principal,
"fallback": False
}
except Exception as e:
print(f"Tentative {tentative + 1} échouée : {e}")
if tentative < max_retries - 1:
time.sleep(2 ** tentative) # backoff exponentiel
# Fallback
try:
response = client.responses.create(
model=modele_fallback,
input=input_msg
)
return {
"reponse": response.output_text,
"modele_utilise": modele_fallback,
"fallback": True
}
except Exception as e:
return {"erreur": str(e), "fallback": True}
Le rate limiting mérite un traitement à part, car il ne s’agit pas d’une panne mais d’une régulation : les modèles fine-tunés partagent les mêmes limites de taux que les modèles de base. Une RateLimitError vous demande simplement de patienter, et l’attente progressive ci-dessous suffit à absorber un pic. Si la boucle atteint son plafond, en revanche, le problème n’est plus conjoncturel et c’est votre quota qu’il faut revoir.
from openai import RateLimitError
import time
def appel_avec_rate_limit(client, modele, input_msg, max_wait=60):
"""Gère le rate limiting avec attente progressive."""
wait = 1
while wait <= max_wait:
try:
return client.responses.create(
model=modele,
input=input_msg
)
except RateLimitError:
print(f"Rate limit atteint, attente {wait}s...")
time.sleep(wait)
wait *= 2
raise Exception("Rate limit persistant — vérifiez vos quotas")
Journaliser ce qui compte
Un journal structuré transforme des incidents opaques en diagnostics rapides. Enregistrez pour chaque appel le modèle réellement utilisé, la volumétrie en entrée et en sortie, la latence et le drapeau de repli. Ces informations suffisent à répondre aux questions qui se posent en pleine situation d’incident : depuis quand la latence a-t-elle dérivé, la hausse des coûts vient-elle du trafic ou de réponses plus longues, et combien d’utilisateurs ont en réalité été servis par le modèle de base.
import logging
import datetime
logger = logging.getLogger("fine_tuning_prod")
def log_appel(
modele: str,
input_msg: str,
output: str,
latence_ms: float,
fallback: bool
):
"""Log structuré pour chaque appel en production."""
logger.info(
"appel_modele",
extra={
"modele": modele,
"input_tokens": len(input_msg.split()),
"output_tokens": len(output.split()),
"latence_ms": latence_ms,
"fallback": fallback,
"timestamp": datetime.datetime.now().isoformat()
}
)
Côté configuration, tout se regroupe dans un fichier d’environnement qui ne doit jamais rejoindre votre dépôt : il contient votre clé d’API, et une clé publiée est une clé compromise.
# .env (ne pas committer)
OPENAI_API_KEY=sk-...
OPENAI_FT_MODEL=ft:gpt-5.6-luna:org::suffix:abc12345
OPENAI_FT_MODEL_FALLBACK=gpt-5.6-luna
SYSTEM_PROMPT="Vous êtes un assistant support client professionnel."
MAX_TOKENS=1024
TEMPERATURE=0.7
Avant d’ouvrir les vannes
Avant d’exposer le service à vos utilisateurs, reprenez une à une les vérifications de cette leçon. Le modèle doit avoir été évalué et approuvé au regard du seuil que vous aviez défini à l’avance ; son nom doit vivre dans une variable d’environnement et nulle part en dur ; un mécanisme de fallback doit être en place. Ces trois points conditionnent tout le reste, puisque sans eux vous mettez en ligne un modèle dont vous ne connaissez ni la qualité mesurée, ni l’adresse exacte, ni le comportement le jour où l’API répond mal.
Contrôlez ensuite l’instrumentation. Le logging doit être activé et enregistrer au minimum le modèle utilisé, la latence et les tokens ; le rate limiting doit être géré plutôt que subi ; les alertes doivent être configurées sur le taux d’erreur et la latence P95, faute de quoi c’est votre premier utilisateur mécontent qui jouera le rôle de sonde. Reste le rollback, qui doit être documenté et testable — un rollback jamais exécuté pour de vrai reste une intention, pas un mécanisme.
Points clés à retenir
- Utilisez des variables d’environnement pour le nom du modèle
- Implémentez toujours un fallback sur le modèle de base
- Loggez chaque appel avec le modèle utilisé et la latence
- Gérez le rate limiting avec un backoff exponentiel
- Testez votre mécanisme de fallback avant de déployer