Sorties structurées AI_COMPLETE¶
AI_COMPLETE vous permet de fournir un schéma JSON ou un littéral de type que les réponses d’achèvement doivent suivre afin de produire une sortie structurée. La sortie structurée réduit le besoin de post-traitement dans vos pipelines de données d’AI et permet une intégration transparente avec les systèmes qui requièrent des réponses déterministes. AI_COMPLETE vérifie chaque jeton généré par rapport à la définition de votre sortie structurée, afin de s’assurer que la réponse est conforme à la structure de type JSON donnée.
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é.
Utiliser AI_COMPLETE avec des littéraux de type¶
Les littéraux de type vous permettent de définir une sortie structurée pour AI_COMPLETE avec des types SQL et de tirer parti des mappages des types SQL vers JSON de Snowflake. Commencez votre littéral de type par le mot-clé TYPE, et utilisez un OBJECT SQL comme type de niveau supérieur. Les propriétés de votre objet de niveau supérieur peuvent correspondre à n’importe quel type SQL avec mappage pris en charge vers JSON.
Note
Les littéraux de type ne sont pris en charge que pour la version à invite de texte à chaîne unique d’AI_COMPLETE. Pour plus d’informations, voir AI_COMPLETE (Chaîne unique).
L’exemple suivant utilise un littéral de type pour produire une sortie structurée pour une invite. L’invite contient à la fois les instructions au modèle et les données à traiter. Le littéral de type response_format produit la réponse du modèle sous la forme d’un objet JSON avec une note de niveau supérieur contenant une date, une address, un items_count et un tableau des price contenant les prix.
SELECT AI_COMPLETE(
model => 'llama3.3-70b',
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 OBJECT(note OBJECT(items_count NUMBER, price ARRAY(STRING), address STRING, member_date STRING)),
);
Ci-dessous figure une réponse complète à cette requête :
{
"note": {
"items_count": 3,
"price": [
"$89.99",
"$129.50",
"$199.00"
]
}
}
}
Limitations des littéraux de type¶
La spécification d’une sortie structurée en tant que littéral de type est soumise aux limitations suivantes :
Les types STRING et VARCHAR sont mappés avec des chaînes JSON.
Les types VARCHAR ne sont pas garantis pour produire une sortie d’une longueur spécifique.
Les types FIXED sans échelle sont mappés avec des entiers JSON. Tous les autres types numériques sont mappés avec des nombres JSON.
Les littéraux de type possèdent également des restrictions sur les types pris en charge :
L’objet vide OBJECT() n’est pas autorisé en tant que littéral de type.
Tous les types SQL ne possèdent pas de mappage pour la sortie structurée. Ces derniers comprennent entre autres :
VARIANT
MAP
L’utilisation d’un type de données non pris en charge renvoie une erreur.
Utiliser AI_COMPLETE avec des schémas JSON¶
Lorsque vous souhaitez avoir plus de contrôle sur la sortie structurée, utilisez un schéma JSON comme valeur pour response_format. L’objet de schéma JSON fourni définit la structure, les types de données et les contraintes auxquelles le texte généré doit se conformer, y compris les champs obligatoires. Pour les tâches simples, il n’est pas nécessaire de spécifier des détails sur le format de sortie, ni même de demander au modèle de « répondre en JSON ».
Pour les tâches plus complexes, inviter le modèle à répondre en JSON peut améliorer la précision. Consultez 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', ...]
}
}
Important
Pour OpenAI (GPT), les exigences suivantes s’appliquent :
Le champ additionalProperties doit être défini sur
falsedans chaque nœud du schéma.Le champ obligatoire doit être inclus et contenir les noms de chaque propriété du schéma.
Les autres modèles ne requièrent pas ces champs, mais vous pouvez les inclure quand même afin de ne pas avoir besoin d’un schéma différent pour des modèles OpenAI.
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': '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': '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_schemaTransmet le schéma JSON à la fonction
completeen 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")
class People(BaseModel):
people: list[Person] = Field(description="People list")
ppl = People.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
peopleest 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,ageetisAdult.{ '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
$refest 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é
$defspour 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-sonnetd’Anthropic oumistral-large2de 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
$refou$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 |