- Les réponses des modèles locaux peuvent être contraintes pour respecter un schéma JSON, ce qui réduit la charge de parsing en post-traitement et rend les résultats d’extraction de données plus cohérents
- Les utilisateurs peuvent demander des sorties structurées de la même manière en passant le schéma dans le paramètre
format depuis cURL, Python ou JavaScript
- Avec Pydantic en Python et Zod en JavaScript, il est plus facile de relier au niveau du code la définition du schéma et la validation des réponses
- Les exemples renvoient et valident des informations sur un pays, un texte sur des animaux de compagnie et les résultats d’analyse d’images de
llama3.2-vision dans des champs JSON prédéfinis
- Si vous avez besoin de résultats stables, il est recommandé d’ajouter « return as JSON » dans le prompt et de régler la température à 0
Contraindre les réponses du modèle avec JSON Schema
- Ollama prend en charge les sorties structurées qui permettent de contraindre la sortie du modèle à respecter un format JSON Schema donné
- Les bibliothèques Python et JavaScript d’Ollama ont également été mises à jour pour prendre en charge les sorties structurées
- Cette fonctionnalité peut être utilisée pour les tâches suivantes
- Parser des données depuis des documents
- Extraire des données à partir d’images
- Structurer les réponses des modèles de langage
- Obtenir une fiabilité et une cohérence supérieures au mode JSON
Installation et mode d’appel
- Il faut télécharger la dernière version de Ollama
- La bibliothèque Python peut être mise à jour vers la dernière version avec la commande suivante
pip install -U ollama
- La bibliothèque JavaScript peut être installée avec la commande suivante
npm i ollama
- Pour demander une sortie structurée, il faut transmettre le schéma dans le paramètre
format d’une requête cURL ou via les bibliothèques Python et JavaScript
Transmettre un JSON Schema avec cURL
- L’exemple cURL envoie une requête à
http://localhost:11434/api/chat, avec model défini sur llama3.1 et stream sur false
format contient un schéma d’objet avec name, capital et languages
name : chaîne de caractères
capital : chaîne de caractères
languages : tableau de chaînes de caractères
- Les trois champs sont tous définis comme
required
- La réponse est renvoyée au format du JSON Schema inclus dans la requête
{
"capital": "Ottawa",
"languages": [
"English",
"French"
],
"name": "Canada"
}
Utilisation des bibliothèques Python et JavaScript
-
Python
- La bibliothèque Ollama Python transmet le schéma comme objet JSON dans le paramètre
format
- Le schéma peut être passé sous forme de
dict, et la méthode recommandée consiste à le sérialiser avec model_json_schema() de Pydantic
- L’exemple définit les champs
name, capital et languages dans le modèle Country, puis valide la réponse avec Country.model_validate_json()
country = Country.model_validate_json(response.message.content)
- La sortie d’exemple renvoie
Canada, Ottawa, English et French dans les champs définis
-
JavaScript
- La bibliothèque Ollama JavaScript transmet le schéma comme objet JSON dans le paramètre
format
- Le schéma peut être passé comme
object, et la méthode recommandée consiste à utiliser Zod avec zodToJsonSchema()
- L’exemple définit le schéma
Country comme objet Zod, puis valide le corps de la réponse avec Country.parse() après JSON.parse()
const country = Country.parse(JSON.parse(response.message.content));
Extraction de données depuis du texte et des images
-
Extraction depuis un texte sur des animaux de compagnie
- Les sorties structurées peuvent être utilisées pour extraire les informations nécessaires à partir d’un texte
- L’exemple définit les modèles Pydantic
Pet et PetList afin de renvoyer les informations sur les animaux de compagnie dans une structure JSON
Pet : name, animal, age, color, favorite_toy
PetList : tableau pets
- Le texte d’entrée contient des informations sur deux chats
- Luna : 5 ans, pelage gris, aime la laine
- Loki : 2 ans, noir, aime les tennis balls
- La sortie est validée comme une liste d’objets
Pet conforme au schéma défini
-
Description d’image avec un modèle de vision
- Les sorties structurées peuvent être utilisées avec un modèle de vision
- L’exemple analyse une image avec
llama3.2-vision et renvoie un résultat conforme au schéma ImageDescription
- Le schéma comprend les champs suivants
summary
objects
scene
colors
time_of_day
setting
text_content
- La requête d’exemple demande d’analyser dans l’image les objets, la scène, les couleurs et le texte détectable
- Dans les options,
temperature est réglée à 0 pour obtenir une sortie plus déterministe
- La sortie d’exemple résume une scène de plage avec des palmiers et renvoie sous forme structurée les objets
tree et beach, les couleurs, le moment de la journée et le contexte extérieur
Utilisation de l’API compatible OpenAI
- L’exemple compatible OpenAI configure le client
OpenAI avec base_url="http://localhost:11434/v1" et api_key="ollama"
- Il utilise
client.beta.chat.completions.parse() et transmet le modèle Pydantic PetList dans response_format
- Dans la réponse, il vérifie
completion.choices[0].message et, si parsed est présent, affiche le résultat parsé
- Si
refusal est présent, il affiche la réponse de refus, et openai.LengthFinishReasonError est traité comme un cas où il y a trop de tokens
Réglages pour des sorties stables et feuille de route
- Pour définir le schéma de réponse, l’usage de Pydantic en Python ou de Zod en JavaScript est recommandé
- Il est conseillé d’ajouter « return as JSON » dans le prompt pour aider le modèle à comprendre la demande
- Pour une sortie plus déterministe, réglez la température à 0
- Les plans à venir sont les suivants
- Exposition des logits pour une génération contrôlée
- Amélioration des performances et de la précision des sorties structurées
- Accélération GPU pour l’échantillonnage
- Prise en charge de formats supplémentaires au-delà de JSON Schema
1 commentaires
Avis sur Hacker News
Si vous avez besoin de contraintes de sortie plus fortes, llama.cpp prend en charge GBNF
https://github.com/ggerganov/llama.cpp/blob/master/grammars/...
À première vue, comme les modèles ont vu beaucoup plus de JSON, j’aurais tendance à penser qu’ils produisent mieux du JSON que d’autres formats
C’est une bonne nouvelle
Je me demandais comment formuler un prompt général pour générer des données CSV sans phrases inutiles avant ou après du genre « Here is your data » ou « Please note blah blah » ; c’est agréable de pouvoir désormais définir précisément le format de retour souhaité et transmettre directement la sortie structurée en CSV
Sinon, on peut obtenir une sortie techniquement au format CSV, mais dénuée de sens. Le modèle essayait peut-être en réalité de rédiger une réponse en paragraphes, et l’échantillonneur de tokens a choisi des tokens de faible probabilité que le modèle n’avait pas vraiment envie de produire
\n et en s’arrêtant àÇa fonctionne. J’ai donné la phrase ci-dessous à gemma2:2b et j’ai obtenu le JSON attendu
You have spent 190 at Fresh Mart. Current balance: 5098Le résultat était
{"amount": 190, "balance": 5098, "category": "Shopping", "place": "Fresh Mart"}Vraiment impressionnant. C’était effectivement l’une des fonctionnalités que je voulais
Ce qui rend ollama intéressant, c’est qu’il donne l’impression d’utiliser les LLM comme d’autres programmes UNIX, et donne le sentiment que les LLM ont naturellement leur place dans UNIX
Cela dit, je me demande si quelqu’un a réussi à le faire tourner correctement sur un GPU AMD. J’ai entendu dire que c’était plus difficile, mais j’aimerais soutenir un concurrent quand j’achèterai une carte l’an prochain
Sur un mini-PC équipé d’un 780M, j’ai alloué 16 Go de mémoire partagée via le BIOS et ça tournait plutôt bien
Je me demande quel effet ce type de contraintes a sur la qualité de sortie des LLM
Dans certains cas, si la qualité de sortie est meilleure, je préférerais parser du Markdown ou du texte brut
La première utilise un modèle lourd et le langage naturel pour traiter le raisonnement dans des sections Markdown et fournir une réponse finale en langage naturel. Si possible, avec des en-têtes Markdown pour étiqueter clairement les sections
La seconde utilise un modèle moins cher et plus rapide pour convertir cette réponse en un format de sortie structuré, consommé par les parties non-LLM du pipeline
En gros, le mode schéma JSON sert à créer des frontières nettes autour des parties ambiguës en langage naturel, et le LLM joue le rôle d’un préprocesseur qui capture sa propre sortie dans un format utile
Il faut aussi indiquer le schéma au modèle. Sinon, on rencontre davantage de problèmes étranges de tokenisation
Par exemple, si le schéma attend la clé JSON
"foobarbaz"et que la tokenisation BPE standard est["foobar", "baz"], les masques de tokens produits par les bibliothèques actuelles de sortie contrainte peuvent autoriser le modèle à choisir entre"f","foo"et"foobar". Si le modèle choisit"foo", la contrainte force ensuite des tokens comme"bar"et"baz". Le modèle voit alors["foo", "bar", "baz"]au lieu de["foobar", "baz"], ce qui le perturbe [0]Si le prompt indique au modèle que
"foobarbaz"est l’une des clés du schéma, il préférera généralement"foobar"à"foo"[0] Dans les modèles récents, ces tokens sont liés à cause de la normalisation, mais ce ne sont pas les mêmes
Cela dépend beaucoup de la probabilité que le LLM et le prompt produisent naturellement une réponse JSON. Plus on force le LLM, moins il a de chances de produire une entrée normale
Avec les petits modèles, on atteint plus vite les limites de l’espace où il existe une capacité prédictive significative, et la sortie commence à ressembler davantage à du bruit aléatoire
Ce n’est pas une mesure rigoureuse, plutôt une impression après avoir passé beaucoup de temps sur plusieurs projets LLM. Je n’ai pas encore utilisé ces outils précis, mais ollama permettait déjà auparavant de garantir une sortie JSON avec une méthode qui semblait similaire, et j’ai aussi travaillé avec un partenaire sur quelque chose ressemblant à jsonformer pour oobabooga, un autre outil d’exécution LLM
J’espère que ce type d’évolution permettra aussi une génération structurée générale, pas limitée au JSON
Il est facile de brûler beaucoup de tokens, mais si ce qu’on cherche à faire justifie ce coût, on peut aller assez loin. Même si ce n’est pas la qualité absolument maximale, un outil qui permet d’obtenir 95 % du résultat sans effort mérite clairement sa place dans la boîte à outils
Je me demande si on peut l’utiliser avec n’importe quel modèle pris en charge
Sur mon matériel, je ne peux faire tourner de manière fiable que des modèles 1B à 3B, d’où ma question
Avec les petits modèles, les résultats peuvent être irréguliers, mais demander « retourne x en JSON » avait tendance à améliorer la précision
La PR liée à cette fonctionnalité est restée ouverte pendant près d’un an
C’est un peu dommage que les mainteneurs soient restés si silencieux
J’espère que l’an prochain nous pourrons mieux suivre et fusionner les PR de la communauté
J’utilise toujours oobabooga. Grâce à la prise en charge de exlv2, l’inférence est bien plus efficace sur deux 3090.
Dans mon souvenir, exl2 était plus rapide, mais surtout aux faibles profondeurs de bits, la quantification gptq semblait meilleure en précision.
Je me demande quelle valeur ajoutée cela apporte par rapport à
outlines.https://www.souzatharsis.com/tamingLLMs/notebooks/structured...
À partir des travaux récents autour d’outlines/xgrammar, j’espère que nous pourrons mettre à jour l’échantillonnage afin de prendre en charge davantage de formats, d’améliorer la précision et d’optimiser les performances.
Je me demande quelle est la meilleure manière de fournir une entrée structurée à un LLM.
Par exemple, lorsqu’on lui donne 100 phrases et qu’on lui demande de classer chacune d’elles de plusieurs façons. Recevoir des données structurées est facile, mais ma méthode consistant à préfixer chaque ligne par son numéro me paraît rudimentaire.
Cela dit, dans ce cas, le mieux est de fournir les phrases une par une pour éviter de perturber le modèle.
En construisant le prompt sous la forme
"Classe la phrase suivante. Les règles sont ..."+ la phrase, on peut exploiter le cache de préfixe, ce qui peut même être plus performant que de tout interroger en une seule fois.Bien sûr, cela suppose qu’il y ait un cache de préfixe et que la facturation ne se fasse pas au token d’entrée. De nos jours, la plupart des fournisseurs permettent de bénéficier d’un coût plus faible si l’on indique qu’on souhaite utiliser le cache de préfixe.