Aller au contenu

Sorties structurées : obtenir un JSON fiable à partir d'un modèle de langage

Blog / Publié 

La première version de chaque intégration LLM analyse la prose. Vous demandez "les trois risques principaux, un par ligne", répartis sur les nouvelles lignes, et cela fonctionne jusqu'au jour où le modèle écrit en premier une phrase d'introduction. Des sorties structurées existent pour mettre fin à cette boucle : vous remettez au modèle un schéma JSON, le décodeur est contraint afin qu'il ne puisse pas émettre quoi que ce soit qui le viole, et vous récupérez une charge utile avec les champs que vous avez demandés. Cette garantie est réelle mais plus étroite qu’il n’y paraît, et la différence réside dans l’endroit où vivent les bugs.

Ce que couvre réellement la garantie

Garanties de décodage contraint forme. La réponse sera analysée au format JSON, elle contiendra vos clés, les types correspondront et les champs d'énumération contiendront l'une de vos valeurs répertoriées. C’est une grande amélioration par rapport à la rapidité et à l’espoir.

Cela ne garantit rien contenu. Un champ saisi sous forme de chaîne contiendra une chaîne ; que cette chaîne soit exacte ou inventée pour satisfaire le schéma reste inchangé par rapport à avant. En fait, un schéma strict rend l'invention légèrement plus probable, car un champ obligatoire doit être rempli avec quelque chose, et le modèle n'a aucun moyen de dire "non présent dans la source". Il s'agit de l'échec le plus courant en production : un invoice_number dans un document qui n'en a pas.

Concevoir autour de lui. Autoriser null là où l'absence est possible, ajouter un explicite found: boolean ou un "unknown" enum membre, et ne rendez jamais un champ obligatoire à moins que la réponse n'existe réellement dans chaque entrée.

Concevoir un schéma qu'un modèle peut remplir

Les schémas écrits pour la validation et les schémas écrits pour la génération sont différents. Quelques règles valables pour tous les fournisseurs :

Les fournisseurs limitent également les mots-clés du schéma JSON qu’ils prennent en charge en mode strict : oneOf, les contraintes de modèle, les limites numériques et la récursion sont généralement limitées ou ignorées. Supposons que le schéma limite uniquement la structure et appliquez vous-même vos règles métier après l'analyse.

Validez quand même

Même avec un mode strict, validez chaque réponse avant qu'elle ne touche votre base de données. Le mode strict peut être désactivé par un changement de configuration, un chemin de secours peut atteindre un modèle qui ne le prend pas en charge, une nouvelle tentative réseau peut renvoyer un objet d'erreur et la réponse peut être tronquée par une limite de jeton au milieu de l'objet - ce qui produit un JSON invalide qu'aucun mode schéma n'empêche.

Lorsqu’une charge utile échoue, la question utile est de savoir quel champ et pourquoi. Le Validateur de schéma JSON prend le document et le schéma et rapporte une ligne par problème, avec le chemin du pointeur JSON, le mot-clé qui a échoué et la valeur réellement trouvée. C'est exactement le formulaire que vous souhaitez pour deux raisons : réparer votre schéma pendant le développement et renvoyer une nouvelle tentative - un rapport de validation échoué collé dans un message de suivi est une invite de réparation remarquablement efficace, puisque le modèle peut voir précisément ce qui ne va pas.

Limitez le nombre de tentatives à une ou deux. Un modèle qui échoue deux fois à un schéma se voit généralement demander quelque chose que l'entrée ne contient pas, et la troisième tentative l'inventera.

D'un exemple de charge utile au code

La plupart des schémas démarrent sous la forme d'un exemple de réponse que quelqu'un a collé dans une discussion. Transformer cela en code tapé et validé à la main est fastidieux et sujet aux erreurs, alors ne le faites pas.

Collez l'échantillon dans Schéma JSON vers Zod et vous obtenez un schéma Zod plus le type TypeScript déduit. Il déduit par champ plutôt que de tout saisir sous forme de chaîne : les valeurs qui ressemblent à des e-mails, des URL, des UUID ou des dates ISO sont obtenues. .email(), .url(), .uuid() et .datetime() au lieu d'un nu z.string(), les tableaux portent leur type d'élément et un champ dont le type varie selon les occurrences devient une union plutôt que de choisir silencieusement le premier qu'il a vu. Ce dernier comportement mérite d'être nourri délibérément : collez trois ou quatre réponses réelles concaténées, et non une, afin que les champs facultatifs et polymorphes apparaissent sous forme d'unions au lieu d'être verrouillés sur ce que contenait le premier échantillon.

Si vous n'avez besoin que de types et non d'une validation d'exécution (car la charge utile provient d'une source en laquelle vous avez déjà confiance) JSON vers TypeScript produit les interfaces directement, avec des objets imbriqués divisés en sous-interfaces nommées plutôt que des blobs anonymes intégrés.

Gardez la direction de la vérité droite : une définition de schéma, avec tout le reste généré à partir de celle-ci. Un schéma Zod et un schéma JSON gérés séparément seront en désaccord dans un délai d'un mois, et le désaccord apparaîtra comme une erreur d'analyse de production plutôt que comme un échec de test.

Lire ce qui est revenu

Les réponses des modèles arrivent réduites sur une seule ligne, ce qui rend leur observation misérable. Le Formateur JSON les indente et les trie en quelque chose de lisible, et sa validation détecte immédiatement le cas de troncature - une charge utile qui se termine au milieu de la chaîne est la signature d'une limite de jeton d'accès, et le correctif est un plafond de sortie plus élevé ou un schéma plus petit, pas un changement rapide.

Lorsque vous comparez ce que deux modèles ont produit à partir de la même entrée ou que vous vérifiez si une modification rapide a modifié la sortie d'une manière que vous n'aviez pas prévue, comparez les charges utiles formatées plutôt que de lire les deux. Les différences structurelles invisibles dans un mur de JSON (un champ qui est progressivement devenu une chaîne de chiffres au lieu d'un nombre) ressortent immédiatement dans une différence.

Gestion des versions

Les schémas changent et la sortie du modèle change avec eux. Placez un champ de version dans la charge utile dès le premier jour, afin que les enregistrements stockés portent la forme sous laquelle ils ont été écrits. La mise à niveau après avoir obtenu un million de lignes de JSON non balisé est le genre de migration pour laquelle personne ne prévoit de temps.

Tous ces outils s'exécutent localement dans votre navigateur, ce qui est plus important que d'habitude pour ce flux de travail : les exemples de charges utiles que vous transformez en schémas sont généralement de véritables réponses API contenant de vraies données client, et ils ne doivent pas être téléchargés vers un service de formatage pour être embellis.

Écrit par Ján Turský

Créateur de TextArray : création d'outils gratuits axés sur la confidentialité qui s'exécutent entièrement dans votre navigateur.