Sorties structurées AI_COMPLETE¶
Les sorties structurées AI_COMPLETE vous permettent de fournir un schéma JSON que les réponses d’achèvement doivent respecter. Cela réduit le besoin de post-traitement dans vos pipelines de données AI et permet une intégration transparente avec les systèmes qui exigent des réponses déterministes. AI_COMPLETE vérifie chaque jeton généré par rapport à votre schéma JSON afin de s’assurer que la réponse est conforme au schéma fourni.
Tous les modèles pris en charge par AI_COMPLETE permettent une sortie structurée, mais les modèles les plus puissants génèrent généralement des réponses de meilleure qualité.
Utilisation des sorties structurées AI_COMPLETE¶
Pour obtenir une réponse dans un format structuré, spécifiez un objet représentant un schéma JSON comme argument response_format
. L’objet de schéma JSON fourni définit la structure, les types de données et les contraintes auxquels le texte généré doit se conformer, y compris les champs obligatoires. Pour les tâches simples, vous n’avez pas besoin de spécifier les détails du format de sortie, ni même d’instruire le modèle de « respond in JSON ». Pour les tâches plus complexes, le fait d’inviter le modèle à répondre en JSON peut améliorer la précision ; voir Optimisation de la précision de l’adhésion à JSON.
ai_complete(
...
response_format => {
'type': 'json',
'schema': {
'type': 'object',
'properties': {
'property_name': {
'type': 'string'
},
...
},
'required': ['property_name', ...]
}
}
Exemple SQL¶
L’exemple suivant montre comment utiliser l’argument response_format
pour spécifier un schéma JSON pour la réponse.
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Return the customer sentiment for the following review: New kid on the block, this pizza joint! The pie arrived neither in a flash nor a snail\'s pace, but the taste? Divine! Like a symphony of Italian flavors, it was a party in my mouth. But alas, the party was a tad pricey for my humble abode\'s standards. A mixed bag, I\'d say!',
response_format => {
'type':'json',
'schema':{'type' : 'object','properties' : {'sentiment_categories':{'type':'array','items':{'type':'object','properties':
{'food_quality' : {'type' : 'string'},'food_taste': {'type':'string'}, 'wait_time': {'type':'string'}, 'food_cost': {'type':'string'}},'required':['food_quality','food_taste' ,'wait_time','food_cost']}}}}
}
);
Réponse :
{
"sentiment_categories": [
{
"food_cost": "negative",
"food_quality": "positive",
"food_taste": "positive",
"wait_time": "neutral"
}
]
}
L’exemple suivant montre comment utiliser l’argument response_format
pour spécifier un schéma JSON pour la réponse et comment utiliser l’argument show_details
pour renvoyer des métadonnées d’inférence.
SELECT AI_COMPLETE(
model => 'mistral-large2',
prompt => 'Return the customer sentiment for the following review: New kid on the block, this pizza joint! The pie arrived neither in a flash nor a snail\'s pace, but the taste? Divine! Like a symphony of Italian flavors, it was a party in my mouth. But alas, the party was a tad pricey for my humble abode\'s standards. A mixed bag, I\'d say!',
response_format => {
'type':'json',
'schema':{'type' : 'object','properties' : {'sentiment_categories':{'type':'array','items':{'type':'object','properties':
{'food_quality' : {'type' : 'string'},'food_taste': {'type':'string'}, 'wait_time': {'type':'string'}, 'food_cost': {'type':'string'}},'required':['food_quality','food_taste' ,'wait_time','food_cost']}}}}
},
show_details => TRUE
);
Réponse :
{
"created": 1738683744,
"model": "mistral-large2",
"structured_output": [
{
"raw_message": {
"sentiment_categories": [
{
"food_cost": "negative",
"food_quality": "positive",
"food_taste": "positive",
"wait_time": "neutral"
}
]
},
"type": "json"
}
],
"usage": {
"completion_tokens": 60,
"prompt_tokens": 94,
"total_tokens": 154
}
}
Exemple Python¶
Note
Les sorties structurées sont prises en charge à partir de la version 1.8.0 de snowflake-ml-python
.
L’exemple suivant montre comment utiliser l’argument response_format
pour spécifier un schéma JSON pour la réponse.
from snowflake.cortex import complete, CompleteOptions
response_format = {
"type": "json",
"schema": {
"type": "object",
"properties": {
"people": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"},
},
"required": ["name", "age"],
},
}
},
"required": ["people"],
},
}
prompt = [{
"role": "user",
"content": "Please prepare me a data set of 5 ppl and their age",
}]
options = CompleteOptions(
max_tokens=4096,
temperature=0.7,
top_p=1,
guardrails=False,
response_format=response_format
)
result = complete(
model="claude-3-5-sonnet",
prompt=prompt,
session={session_object}, # session created via connector
stream=True,
options=options,
)
output = "".join(result)
print(output)
Réponse :
{"people": [{"name":"John Smith","age":32},{"name":"Sarah Johnson","age":28},
{"name":"Michael Chen","age":45},{"name":"Emily Davis","age":19},{"name":"Robert Wilson","age":56}]}
Exemple de Pydantic¶
Pydantic est une bibliothèque de validation de données et de gestion des paramètres pour Python. Cet exemple utilise Pydantic pour définir un schéma pour le format de réponse. Le code exécute les étapes suivantes :
Utilise Pydantic pour définir un schéma
Convertit le modèle Pydantic en un schéma JSON à l’aide de la méthode
model_json_schema
Transmet le schéma JSON à la fonction
complete
en tant qu’argumentresponse_format
Note
Cet exemple est destiné à être exécuté dans une feuille de calcul Snowsight Python, qui dispose déjà d’une connexion avec Snowflake. Pour l’exécuter dans un environnement différent, vous devrez peut-être établir une connexion vers Snowflake à l’aide du Snowflake Connector pour Python.
from pydantic import BaseModel, Field
import json
from snowflake.cortex import complete, CompleteOptions
from snowflake.snowpark.context import get_active_session
class Person(BaseModel):
age: int = Field(description="Person age")
name: str = Field(description="Person name")
people: list[Person] = Field(description="People list")
ppl = Person.model_json_schema()
'''
This is the ppl object, keep in mind there's a '$defs' key used
{'$defs': {'Person': {'properties': {'age': {'description': 'Person age', 'title': 'Age', 'type': 'integer'}, 'name': {'description': 'Person name', 'title': 'Name', 'type': 'string'}}, 'required': ['age', 'name'], 'title': 'Person', 'type': 'object'}}, 'properties': {'people': {'description': 'People list', 'items': {'$ref': '#/$defs/Person'}, 'title': 'People', 'type': 'array'}}, 'required': ['people'], 'title': 'People', 'type': 'object'}
'''
response_format_pydantic={
"type": "json",
"schema": ppl,
}
prompt=[{"role": "user", "content": "Please prepare me a data set of 5 ppl and their age"}]
options_pydantic = CompleteOptions( # random params
max_tokens=4096,
temperature=0.7,
top_p=1,
guardrails=False,
response_format=response_format_pydantic
)
model_name = "claude-3-5-sonnet"
session = get_active_session()
try:
result_pydantic = complete(
model=model_name,
prompt=prompt,
session=session,
stream=True,
options=options_pydantic,
)
except Exception as err:
result_pydantic = (chunk for chunk in err.response.text) # making sure it's generator, similar to the valid response
output_pydantic = "".join(result_pydantic)
print(output_pydantic)
Réponse :
{"people": [{"name":"John Smith","age":32},{"name":"Sarah Johnson","age":45},
{"name":"Mike Chen","age":28},{"name":"Emma Wilson","age":19},{"name":"Robert Brown","age":56}]}
Exemple REST API¶
Vous pouvez utiliser l’API REST du LLM de Snowflake Cortex pour invoquer COMPLETE avec le LLM de votre choix. Vous trouverez ci-dessous un exemple de fourniture d’un schéma utilisant l’API REST du LLM de Cortex :
curl --location --request POST 'https://<account_identifier>.snowflakecomputing.com/api/v2/cortex/inference:complete'
--header 'Authorization: Bearer <jwt>' \
--header 'Accept: application/json, text/event-stream' \
--header 'Content-Type: application/json' \
--data-raw '{
"model": "claude-3-5-sonnet",
"messages": [{
"role": "user",
"content": "Order a pizza for a hungry space traveler heading to the planet Zorgon. Make sure to include a special instruction to avoid any intergalactic allergens."
}],
"max_tokens": 1000,
"response_format": {
"type": "json",
"schema":
{
"type": "object",
"properties":
{
"crust":
{
"type": "string",
"enum":
[
"thin",
"thick",
"gluten-free",
"Rigellian fungus-based"
]
},
"toppings":
{
"type": "array",
"items":
{
"type": "string",
"enum":
[
"Gnorchian sausage",
"Andromedian mushrooms",
"Quasar cheese"
]
}
},
"delivery_planet":
{
"type": "string"
},
"special_instructions":
{
"type": "string"
}
},
"required":
[
"crust",
"toppings",
"delivery_planet"
]
}
}
}
}'
Réponse :
data: {"id":"4d62e41a-d2d7-4568-871a-48de1463ed2a","model":"claude-3-5-sonnet","choices":[{"delta":{"content":"{\"crust\":","content_list":[{"type":"text","text":"{\"crust\":"}]}}],"usage":{}}
data: {"id":"4d62e41a-d2d7-4568-871a-48de1463ed2a","model":"claude-3-5-sonnet","choices":[{"delta":{"content":" \"thin\"","content_list":[{"type":"text","text":" \"thin\""}]}}],"usage":{}}
data: {"id":"4d62e41a-d2d7-4568-871a-48de1463ed2a","model":"claude-3-5-sonnet","choices":[{"delta":{"content":", \"topping","content_list":[{"type":"text","text":", \"topping"}]}}],"usage":{}}
data: {"id":"4d62e41a-d2d7-4568-871a-48de1463ed2a","model":"claude-3-5-sonnet","choices":[{"delta":{"content":"s\": [\"Quasar","content_list":[{"type":"text","text":"s\": [\"Quasar"}]}}],"usage":{}}
Créer une définition du schéma JSON¶
Pour obtenir la meilleure précision possible à partir des sorties structurées COMPLETE, suivez les conseils suivants :
Utilisez le champ « required » dans le schéma pour spécifier les champs obligatoires. COMPLETE génère une erreur si un champ obligatoire ne peut être extrait.
Dans l’exemple suivant, le schéma indique à COMPLETE de trouver les personnes mentionnées dans le document. Le champ
people
est marqué comme obligatoire pour s’assurer que les personnes sont identifiées.{ 'type': 'object', 'properties': { 'dataset_name': { 'type': 'string' }, 'created_at': { 'type': 'string' }, 'people': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': { 'type': 'string' }, 'age': { 'type': 'number' }, 'isAdult': { 'type': 'boolean' } } } } }, 'required': [ 'dataset_name', 'created_at', 'people' ] }
Réponse :
{ "dataset_name": "name", "created_at": "date", "people": [ { "name": "Andrew", "isAdult": true } ] }
Fournissez des descriptions détaillées des champs à extraire afin que le modèle puisse les identifier plus précisément. Par exemple, le schéma suivant comprend une description de chacun des champs
people
:name
,age
etisAdult
.{ 'type': 'object', 'properties': { 'dataset_name': { 'type': 'string' }, 'created_at': { 'type': 'string' }, 'people': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': { 'type': 'string', 'description': 'name should be between 9 to 10 characters' }, 'age': { 'type': 'number', 'description': 'Should be a value between 0 and 200' }, 'isAdult': { 'type': 'boolean', 'description': 'Persons is older than 18' } } } } } }
Utilisation de la référence JSON¶
Les références de schémas permettent de résoudre des problèmes pratiques lors de l’utilisation des sorties structurées COMPLETE de Cortex. Grâce aux références, représentées par $ref
, vous pouvez définir une seule fois des objets communs tels que des adresses ou des prix, puis y faire référence dans l’ensemble du schéma. Ainsi, lorsque vous devez mettre à jour la logique de validation ou ajouter un champ, vous pouvez le modifier en un seul endroit et non à plusieurs emplacements, ce qui réduit le risque d’erreurs.
L’utilisation de références permet de réduire les bogues dus à des implémentations incohérentes et de simplifier les révisions de code. Les composants référencés créent des hiérarchies plus propres qui représentent mieux les relations entre les entités dans votre modèle de données. Au fur et à mesure que les projets se complexifient, cette approche modulaire vous aide à gérer la dette technique tout en préservant l’intégrité du schéma.
Des bibliothèques tierces telles que Pydantic prennent en charge le mécanisme de référence de manière native en Python, ce qui simplifie l’utilisation des schémas dans votre code.
Les lignes directrices suivantes s’appliquent à l’utilisation des références dans le schéma JSON :
Limitation du champ d’application : Le mécanisme
$ref
est limité au schéma de l’utilisateur ; les références de schémas externes (telles que les URLs HTTP) ne sont pas prises en charge.Placement des définitions : Les définitions d’objets doivent être placées au niveau supérieur du schéma, plus précisément sous la clé definitions ou
$defs
.Application : Bien que la spécification du schéma JSON recommande l’utilisation de la clé
$defs
pour les définitions, le mécanisme de validation de Snowflake applique strictement cette structure. Il s’agit d’un exemple d’objet valide$defs
:
{
'$defs': {
'person':{'type':'object','properties':{'name' : {'type' : 'string'},'age': {'type':'number'}}, 'required':['name','age']}},
'type': 'object',
'properties': {'title':{'type':'string'},'people':{'type':'array','items':{'$ref':'#/$defs/person'}}}
}
Exemple utilisant la référence JSON¶
Cet exemple SQL illustre l’utilisation des références dans un schéma JSON.
select ai_complete(
model => 'claude-3-5-sonnet',
prompt => 'Extract structured data from this customer interaction note: Customer Sarah Jones complained about the mobile app crashing during checkout. She tried to purchase 3 items: a red XL jacket ($89.99), blue running shoes ($129.50), and a fitness tracker ($199.00). The app crashed after she entered her shipping address at 123 Main St, Portland OR, 97201. She has been a premium member since January 2024.',
'response_format' => {
'type': 'json',
'schema': {
'type': 'object',
'$defs': {
'price': {
'type': 'object',
'properties': {
'amount': {'type': 'number'},
'currency': {'type': 'string'}
},
'required': ['amount']
},
'address': {
'type': 'object',
'properties': {
'street': {'type': 'string'},
'city': {'type': 'string'},
'state': {'type': 'string'},
'zip': {'type': 'string'},
'country': {'type': 'string'}
},
'required': ['street', 'city', 'state']
},
'product': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'category': {'type': 'string'},
'color': {'type': 'string'},
'size': {'type': 'string'},
'price': {'$ref': '#/$defs/price'}
},
'required': ['name', 'price']
}
},
'properties': {
'customer': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'membership': {
'type': 'object',
'properties': {
'type': {'type': 'string'},
'since': {'type': 'string'}
}
},
'shipping_address': {'$ref': '#/$defs/address'}
},
'required': ['name']
},
'issue': {
'type': 'object',
'properties': {
'type': {'type': 'string'},
'platform': {'type': 'string'},
'stage': {'type': 'string'},
'severity': {'type': 'string', 'enum': ['low', 'medium', 'high', 'critical']}
},
'required': ['type', 'platform']
},
'cart': {
'type': 'object',
'properties': {
'items': {
'type': 'array',
'items': {'$ref': '#/$defs/product'}
},
'total': {'$ref': '#/$defs/price'},
'item_count': {'type': 'integer'}
}
},
'recommended_actions': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'department': {'type': 'string'},
'action': {'type': 'string'},
'priority': {'type': 'string', 'enum': ['low', 'medium', 'high', 'urgent']}
}
}
}
},
'required': ['customer', 'issue','cart']
}
}
}
);
Réponse :
{
"created": 1747313083,
"model": "claude-3-5-sonnet",
"structured_output": [
{
"raw_message": {
"cart": {
"item_count": 3,
"items": [
{
"color": "red",
"name": "jacket",
"price": {
"amount": 89.99,
"currency": "USD"
},
"size": "XL"
},
{
"color": "blue",
"name": "running shoes",
"price": {
"amount": 129.5,
"currency": "USD"
}
},
{
"name": "fitness tracker",
"price": {
"amount": 199,
"currency": "USD"
}
}
],
"total": {
"amount": 418.49,
"currency": "USD"
}
},
"customer": {
"membership": {
"since": "2024-01",
"type": "premium"
},
"name": "Sarah Jones",
"shipping_address": {
"city": "Portland",
"state": "OR",
"street": "123 Main St",
"zip": "97201"
}
},
"issue": {
"platform": "mobile",
"severity": "high",
"stage": "checkout",
"type": "app_crash"
}
},
"type": "json"
}
],
"usage": {
"completion_tokens": 57,
"prompt_tokens": 945,
"total_tokens": 1002
}
}
Optimisation de la précision de l’adhésion à JSON¶
Les sorties structurées COMPLETE n’ont généralement pas besoin d’une exigence ; elles comprennent déjà que leur réponse doit être conforme au schéma que vous avez spécifié. Cependant, la complexité de la tâche peut influencer de manière significative la possibilité pour les LLMs de suivre un format de réponse JSON. Plus la tâche est complexe, plus vous pouvez améliorer la précision des résultats en spécifiant un prompt.
Les tâches simples telles que la classification de textes, l’extraction d’entités, les paraphrases et les tâches de résumé qui ne nécessitent pas de raisonnement complexe ne requièrent généralement pas de prompt supplémentaire. Pour les modèles plus petits et moins intelligents, l’utilisation de sorties structurées améliore considérablement la précision de l’adhésion à JSON, car elle ignore tout texte fourni par le modèle qui n’est pas lié au schéma fourni.
Les tâches de complexité moyenne comprennent toute tâche simple dans laquelle il est demandé au modèle d’effectuer un raisonnement supplémentaire, par exemple de justifier une décision de classification. Pour ces cas d’utilisation, nous vous recommandons d’ajouter « Respond in JSON » dans l’invite afin d’optimiser les performances.
Les tâches de raisonnement complexes incitent les modèles à effectuer des tâches ambiguës plus ouvertes, telles que l’évaluation et la notation de la qualité d’un appel en fonction de la pertinence, du professionnalisme et de la fidélité des réponses. Pour ces cas d’utilisation, nous vous recommandons d’utiliser les modèles les plus puissants tels que
claude-3-5-sonnet
d’Anthropic oumistral-large2
de Mistral AI et d’ajouter « Respond in JSON », ainsi que des détails sur le schéma que vous souhaitez générer dans le prompt.
Pour obtenir les résultats les plus cohérents, définissez l’option temperature
sur 0 lorsque vous appelez COMPLETE, quelle que soit la tâche ou le modèle.
Astuce
Pour traiter les erreurs éventuelles soulevées par un modèle, utilisez TRY_COMPLETE plutôt que COMPLETE.
Considérations relatives aux clients¶
Les sorties structurées COMPLETE de Cortex entraînent un coût de calcul basé sur le nombre de jetons traités, mais n’entraîne pas de coût de calcul supplémentaire pour la vérification de chaque jeton par rapport au schéma JSON fourni. Toutefois, le nombre de jetons traités (et facturés) augmente avec la complexité du schéma. En général, plus le schéma fourni est grand et complexe, plus il y a de jetons d’entrée et de sortie consommés. Les réponses hautement structurées avec une imbrication profonde (par exemple, des données hiérarchiques) consomment un plus grand nombre de jetons que les schémas plus simples.
Limitations¶
Vous ne pouvez pas utiliser d’espaces dans les clés du schéma.
Les caractères autorisés pour les noms de propriétés sont les lettres, les chiffres, le trait d’union et le trait de soulignement. Les noms peuvent comporter 64 caractères au maximum.
Vous ne pouvez pas résoudre des schémas externes en utilisant
$ref
ou$dynamicRef
.
Les mots-clés de contraintes suivants ne sont pas pris en charge. L’utilisation d’un mot-clé de contrainte non pris en charge entraîne une erreur.
Type |
Mots-clés |
---|---|
entier |
|
number |
|
string |
|
tableau |
|
objet |
|
Ces limites pourraient être corrigées dans les prochaines versions.
Conditions d’erreur¶
Situation |
Exemple de message |
Code de statut HTTP |
---|---|---|
La validation de la requête a échoué. La requête a été annulée car le modèle n’était pas en mesure de générer une réponse valide. Cela peut être dû à une requête mal formée. |
|
400 |
La validation du schéma d’entrée a échoué. La requête a été annulée car le modèle n’était pas en mesure de générer une réponse valide. Cela peut être dû à l’absence de propriétés requises dans les données utiles de la requête ou à l’utilisation de fonctions du schéma JSON non prises en charge, telles que les contraintes, ou à l’utilisation inappropriée du mécanisme $ref (par exemple, en allant au-delà du schéma) |
|
400 |
La validation de la sortie du modèle a échoué. Le modèle n’a pas pu générer une réponse correspondant au schéma. |
|
422 |