<!-- Canonical URL: https://ask.atlascloud.ai/fr/structured-json-output-after-llm-api-migration -->

# Pourquoi les sorties JSON structurées échouent-elles après une migration d’API LLM ?

> Le JSON structuré échoue souvent après une migration d’API LLM parce que l’ancienne et la nouvelle pile appliquent des contrats différents. Comparez les schémas de requête, les modes de réponse, le streaming, les enveloppes d’appels d’outils, les refus et les règles de validation avant d’accuser le prompt.

Une migration ne change pas seulement le nom d’hôte. Elle peut changer le modèle, la sérialisation du SDK, les mots-clés de schéma acceptés, l’ordre des événements de streaming, les valeurs d’échantillonnage par défaut, le comportement de sécurité et l’emplacement exact de la charge générée. Une requête qui semble équivalente dans le code de l’application peut donc représenter un contrat différent sur le réseau.

Le diagnostic le plus rapide consiste à enregistrer une requête en échec et sa réponse non modifiée chez les deux fournisseurs. Comparez les corps HTTP réels, pas les objets d’enveloppe exposés par le SDK. Cette comparaison révèle généralement si la rupture se produit avant la génération, pendant celle-ci, ou lorsque l’application extrait et valide le résultat.

## Séparer les erreurs de syntaxe des erreurs de contrat

« Le JSON est cassé » peut désigner plusieurs défauts différents. Les traiter comme un seul problème conduit à modifier les prompts tout en masquant la cause réelle.

* Une erreur de syntaxe produit un texte qu’un analyseur JSON ne peut pas lire.
* Une erreur de schéma produit un JSON valide avec de mauvaises clés, de mauvais types, une mauvaise imbrication ou de mauvaises valeurs d’énumération.
* Une erreur d’extraction survient lorsque des données structurées valides se trouvent dans un autre champ de la réponse.
* Une erreur de streaming survient lorsque des fragments partiels sont analysés comme des documents complets.
* Un refus ou une troncature remplace l’objet attendu par un message de sécurité ou une charge incomplète.
* Une erreur d’appel d’outil renvoie des arguments dans une enveloppe d’outil alors que l’application attend le texte de l’assistant.

Journalisez ces catégories séparément. Une exception de l’analyseur, un champ obligatoire manquant et un `400` de l’API ne doivent pas partager le même compteur générique de tentatives.

## Comparer les deux contrats de requête

Commencez par une matrice de migration champ par champ. Des noms proches ne désignent pas toujours la même chose.

| Zone du contrat | Ancienne pile | Nouvelle pile | Question à tester |
|---|---|---|---|
| Mode de sortie | Objet JSON | Objet contraint par schéma | Garantit-il seulement la syntaxe ou tout le schéma ? |
| Emplacement du schéma | Paramètres d’outil | Format de réponse | Le schéma est-il associé au bon champ de requête ? |
| Rigueur | Implicite | Indicateur explicite | Le mode strict doit-il être activé ? |
| Mots-clés non pris en charge | Ignorés | Rejetés | Les unions, formats, valeurs par défaut ou références sont-ils acceptés ? |
| Clés supplémentaires | Autorisées | Bloquées | `additionalProperties` est-il traité de façon cohérente ? |
| Streaming | Deltas de texte | Événements typés | Quel événement contient l’objet terminé ? |
| Refus | Texte de l’assistant | Champ dédié | L’extracteur distingue-t-il un refus d’une sortie normale ? |

Capturez la requête sérialisée finale après le traitement du SDK. Les valeurs par défaut ajoutées par l’ancien client peuvent disparaître pendant la migration. L’inverse existe aussi : un nouveau SDK peut ajouter un indicateur strict ou renommer un champ de format de réponse sans provoquer d’erreur évidente dans l’application.

## Réduire le schéma avant de modifier le prompt

Testez d’abord le plus petit schéma possible. Commencez avec une seule chaîne obligatoire, sans branche facultative. Ajoutez ensuite, un à un, des entiers, tableaux, objets imbriqués, énumérations et structures conditionnelles. Cette méthode isole les fonctions non prises en charge bien plus vite que la réécriture répétée d’un prompt de production.

Une base portable peut par exemple ressembler à ceci :

```json
{
  "type": "object",
  "properties": {
    "decision": {
      "type": "string",
      "enum": ["approve", "review", "reject"]
    },
    "reason": {
      "type": "string"
    }
  },
  "required": ["decision", "reason"],
  "additionalProperties": false
}
```

Si elle fonctionne, le transport et le chemin d’application de base sont corrects. Si le schéma de production échoue après l’ajout d’une fonction, vous disposez d’une question précise de compatibilité plutôt que d’un vague problème de qualité du modèle.

## Vérifier où se trouve la charge structurée

Les migrations de fournisseur cassent souvent le code d’extraction. Une pile peut renvoyer le JSON dans un message de l’assistant, une autre dans une propriété déjà analysée, et une troisième sous forme d’arguments d’outil convertis en chaîne. Le streaming peut exposer une suite d’événements typés au lieu de deltas de texte ordinaires.

Créez un résultat interne normalisé avant que le reste de l’application ne voie la réponse :

```ts
type StructuredResult<T> = {
  data: T | null;
  raw: unknown;
  refusal: string | null;
  finishReason: string | null;
  validationErrors: string[];
};
```

Conservez `raw` pendant la période de migration. C’est la preuve nécessaire pour distinguer un défaut de sortie du modèle d’un défaut de l’adaptateur. Supprimez les données sensibles avant de stocker les journaux et définissez une durée de conservation adaptée à votre application.

## Ne pas analyser un flux inachevé

Un document JSON reste généralement invalide jusqu’à l’arrivée de son dernier octet. L’analyse de chaque delta de texte produit des erreurs normales qui ressemblent à des incidents de production. Mettez en mémoire tampon la sortie complète, utilisez un analyseur JSON incrémental pris en charge par le fournisseur, ou consommez un événement documenté représentant la réponse structurée finalisée.

Le test de streaming doit couvrir davantage que le parcours idéal :

* chaînes séparées entre plusieurs fragments ;
* guillemets échappés et caractères Unicode ;
* tableaux contenant de nombreux éléments ;
* déconnexion avant l’accolade fermante ;
* événement de refus ;
* objet final valide suivi de métadonnées d’usage ;
* arguments d’outil livrés dans plusieurs deltas.

Traitez une interruption réseau comme une génération incomplète, pas comme une sortie mal formée du modèle. Une nouvelle tentative peut être sûre pour une classification en lecture seule, mais pas nécessairement si le résultat déclenche un effet externe.

## Valider deux fois, mais réparer avec prudence

Même si le fournisseur applique un schéma, votre application doit encore valider le résultat normalisé à sa frontière de confiance. Cela la protège des erreurs d’adaptateur, des régressions de fonctionnalités et des hypothèses que le fournisseur n’a jamais garanties.

Une séquence pratique est la suivante :

1. Confirmez la réussite de la requête HTTP.
2. Détectez explicitement un refus, une troncature ou la fin d’un appel d’outil.
3. Extrayez la charge structurée complète.
4. Analysez-la une seule fois.
5. Validez-la avec la version du schéma de l’application.
6. Rejetez les valeurs inconnues ou dangereuses avant la logique métier.

Évitez une boucle illimitée de « réparation du JSON ». Un modèle de réparation peut modifier le sens tout en rendant la syntaxe valide. Si la réparation est acceptable, limitez-la à une tentative, fournissez les erreurs de validation, conservez la sortie originale et validez à nouveau l’objet réparé. Pour une action à fort impact, envoyez le cas en révision.

## Créer une suite de rejeu pour la migration

Avant de rediriger le trafic de production, constituez un corpus compact à partir de formes réelles de requêtes. Retirez les données personnelles ou confidentielles, puis ajoutez des cas qui exercent les limites du contrat.

| Groupe de cas | Éléments à inclure | Condition de réussite |
|---|---|---|
| Normal | Entrées courtes courantes | Objet valide avec les valeurs attendues |
| Champs facultatifs | Variantes avec et sans champs | Le comportement obligatoire et facultatif respecte le contrat |
| Ambigu | Entrée contradictoire ou incomplète | Objet valide avec un état d’incertitude autorisé |
| Contexte long | Proche du maximum normal de l’application | Aucune troncature et extraction valide |
| Adversarial | Texte demandant au modèle d’ignorer le schéma | Le schéma reste intact |
| Refus | Requête interdite ou dangereuse | Le refus est détecté sans être confondu avec une erreur d’analyse |
| Streaming | Limites de fragments et déconnexions | Seules les charges terminées atteignent la validation |

Exécutez les deux fournisseurs avec des paramètres stables et enregistrez le taux de validité, l’accord sémantique, le taux de refus, le taux de troncature, la latence et le nombre de nouvelles tentatives. L’égalité sémantique exacte n’est pas toujours attendue, mais la validité du schéma doit être une condition stricte.

## Isoler la compatibilité derrière un adaptateur

Ne laissez pas les types de réponse d’un fournisseur se propager dans tout le code. Un petit adaptateur doit prendre en charge la construction des requêtes, la vérification des capacités de schéma, l’extraction des réponses, les motifs de fin, les champs d’usage et la normalisation des erreurs. Le reste de l’application doit consommer une seule interface interne.

Une passerelle compatible avec OpenAI telle qu’[Atlas Cloud](https://www.atlascloud.ai/?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=structured-json-output-after-llm-api-migration) peut réduire le travail d’intégration répété lorsqu’une application utilise des modèles de texte, d’image et de vidéo. La compatibilité de la couche de transport est utile, mais elle ne supprime pas les tests au niveau du modèle. Conservez des indicateurs de capacité pour la sortie structurée, les outils, le streaming et les fonctions de schéma, au lieu de supposer que tous les modèles derrière un endpoint compatible se comportent de la même manière.

Versionnez ensemble l’adaptateur et le schéma. Lorsqu’un schéma change, stockez sa version avec la sortie afin que les services en aval sachent quel contrat ils ont reçu.

## Utiliser un déploiement progressif avec des critères d’arrêt clairs

Commencez par le rejeu hors ligne, puis le trafic miroir, et enfin un faible pourcentage de trafic réel. Ne vous fiez pas uniquement au taux de réussite global. Un taux de validité de 99 % peut rester inacceptable si le 1 % d’échecs se concentre sur les paiements, la modération ou l’autorisation.

Définissez les critères d’arrêt avant le test :

* une valeur d’énumération dangereuse atteint la logique métier ;
* le taux de validité du schéma passe sous la référence actuelle ;
* la gestion des refus devient ambiguë ;
* la troncature dépasse le seuil convenu ;
* les nouvelles tentatives font dépasser le budget de coût ou de latence ;
* les réponses brutes ne peuvent pas être rapprochées des sorties normalisées.

Lorsque c’est possible, revenez en arrière par la configuration du routage plutôt que par un déploiement de code en urgence. Gardez l’ancien adaptateur disponible jusqu’à ce que le nouveau chemin ait supporté la charge de pointe et les catégories d’entrée rares.

## Conclusion

Les échecs de JSON structuré après une migration d’API LLM proviennent généralement de contrats incompatibles, d’erreurs d’extraction ou d’une logique de streaming incomplète, et non de l’incapacité du nouveau modèle à suivre des consignes. Comparez les charges réellement transmises, réduisez le schéma, normalisez les réponses, validez à la frontière de l’application et rejouez un corpus représentatif avant de déplacer le trafic. Une API compatible peut simplifier la migration, mais les tests explicites de capacités restent le moyen fiable de protéger le comportement en production.

## FAQ

### Pourquoi le même prompt JSON fonctionne-t-il avec un modèle et échoue-t-il avec un autre ?

La formulation du prompt n’est qu’une couche. Les modèles et fournisseurs diffèrent par leurs modes JSON natifs, leur prise en charge des schémas, leur tokenisation, leur comportement de refus et la rigueur avec laquelle ils respectent les contraintes.

### Le mode JSON est-il identique à l’application d’un JSON Schema ?

Non. Le mode JSON peut seulement garantir une syntaxe JSON valide, alors que l’application d’un schéma contraint aussi les clés, les types, les champs obligatoires et parfois les propriétés supplémentaires.

### Faut-il analyser un JSON diffusé en continu token par token ?

En général, non. Mettez en mémoire tampon la charge structurée complète ou utilisez les événements de streaming structuré documentés par le fournisseur, puis validez après l’événement final.

### Une API compatible avec OpenAI supprime-t-elle le travail de migration ?

Elle peut réduire les modifications de transport et de SDK, mais vous devez toujours vérifier les fonctions de schéma propres au modèle, les motifs de fin, les formats d’appels d’outils et le comportement des erreurs.

### Que faire lorsque la validation échoue ?

Conservez la réponse brute, classez l’échec, ne retentez que les cas transitoires sûrs et limitez toute réparation déterministe par une étape de validation, au lieu d’accepter silencieusement des données mal formées.

### Quel est le test de préproduction le plus rapide ?

Rejouez sur les deux piles un corpus fixe de requêtes normales, ambiguës, adversariales, refusées, longues et diffusées en continu, puis comparez les sorties validées champ par champ.
