Saídas estruturadas AI_COMPLETE¶
As saídas estruturadas AI_COMPLETE permitem que você forneça um esquema JSON que as respostas de conclusão devem seguir. Isso reduz a necessidade de pós-processamento em seus pipelines de dados de AI e permite uma integração contínua com sistemas que exigem respostas determinísticas. AI_COMPLETE verifica cada token gerado em relação ao seu esquema JSON para garantir que a resposta esteja em conformidade com o esquema fornecido.
Todos os modelos compatíveis com o AI_COMPLETE oferecem suporte à saída estruturada, mas os modelos mais avançados geralmente geram respostas de maior qualidade.
Usando as saídas estruturadas AI_COMPLETE¶
Para obter uma resposta em um formato estruturado, especifique um objeto que represente um esquema JSON como o argumento response_format
. O objeto de esquema JSON fornecido define a estrutura, os tipos de dados e as restrições com as quais o texto gerado deve estar em conformidade, inclusive os campos obrigatórios. Para tarefas simples, você não precisa especificar nenhum detalhe do formato de saída, nem mesmo instruir o modelo a “responder em JSON”. Para tarefas mais complexas, solicitar ao modelo que responda em JSON pode aumentar a precisão; consulte Otimização da precisão da adesão ao JSON.
ai_complete(
...
response_format => {
'type': 'json',
'schema': {
'type': 'object',
'properties': {
'property_name': {
'type': 'string'
},
...
},
'required': ['property_name', ...]
}
}
Exemplo de SQL¶
O exemplo a seguir demonstra como usar o argumento response_format
para especificar um esquema JSON para a resposta.
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']}}}}
}
);
Resposta:
{
"sentiment_categories": [
{
"food_cost": "negative",
"food_quality": "positive",
"food_taste": "positive",
"wait_time": "neutral"
}
]
}
O exemplo a seguir demonstra como usar o argumento response_format
para especificar um esquema JSON para a resposta e como usar o argumento show_details
para retornar metadados de inferência.
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
);
Resposta:
{
"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
}
}
Exemplo em Python¶
Nota
A saída estruturada é compatível com snowflake-ml-python
versão 1.8.0 e posteriores.
O exemplo a seguir demonstra como usar o argumento response_format
para especificar um esquema JSON para a resposta.
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)
Resposta:
{"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}]}
Exemplo com Pydantic¶
Pydantic é uma biblioteca de validação de dados e gerenciamento de configurações para Python. Este exemplo usa o Pydantic para definir um esquema para o formato de resposta. O código executa as seguintes etapas:
Usa o Pydantic para definir um esquema
Converte o modelo Pydantic em um esquema JSON usando o método
model_json_schema
Passa o esquema JSON para a função
complete
como o argumentoresponse_format
Nota
Esse exemplo deve ser executado em uma planilha Python do Snowsight, que já tem uma conexão com o Snowflake. Para executá-lo em um ambiente diferente, talvez seja necessário estabelecer uma conexão com o Snowflake usando o Snowflake Connector para 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)
Resposta:
{"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}]}
Exemplo de REST API¶
Você pode usar a Snowflake Cortex LLM REST API para invocar COMPLETE com o LLM de sua escolha. Abaixo está um exemplo de fornecimento de um esquema usando Cortex LLM REST API:
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"
]
}
}
}
}'
Resposta:
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":{}}
Criação de uma definição do esquema JSON¶
Para obter uma melhor precisão das saídas estruturadas COMPLETE, siga estas diretrizes:
Use o campo “obrigatório” no esquema para especificar os campos obrigatórios. COMPLETE gera um erro se um campo obrigatório não puder ser extraído.
No exemplo a seguir, o esquema direciona COMPLETE para localizar as pessoas mencionadas no documento. O campo
people
está marcado como obrigatório para garantir que as pessoas sejam identificadas.{ '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' ] }
Resposta:
{ "dataset_name": "name", "created_at": "date", "people": [ { "name": "Andrew", "isAdult": true } ] }
Forneça descrições detalhadas dos campos a serem extraídos para que o modelo possa identificá-los com mais precisão. Por exemplo, o esquema a seguir inclui uma descrição de cada um dos campos de
people
:name
,age
eisAdult
.{ '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' } } } } } }
Uso da referência JSON¶
As referências de esquema resolvem problemas práticos ao usar as saídas estruturadas do Cortex COMPLETE. Com as referências, representadas por $ref
, você pode definir objetos comuns, como endereços ou preços, uma única vez e depois referenciá-los em todo o esquema. Dessa forma, quando você precisar atualizar a lógica de validação ou adicionar um campo, poderá alterá-lo em um único local, em vez de em vários locais, reduzindo o potencial de erros.
O uso de referências reduz os erros de implementações inconsistentes e simplifica as revisões de código. Os componentes referenciados criam hierarquias mais limpas que representam melhor os relacionamentos entre entidades em seu modelo de dados. À medida que os projetos se tornam mais complexos, essa abordagem modular ajuda você a gerenciar o débito técnico e a manter a integridade do esquema.
Bibliotecas de terceiros, como a Pydantic, oferecem suporte ao mecanismo de referência nativamente em Python, simplificando o uso do esquema em seu código.
As diretrizes a seguir se aplicam ao uso de referências no esquema JSON:
Limitação de escopo: o mecanismo
$ref
limita-se apenas ao esquema do usuário; não há suporte para referências de esquemas externos (como URLs HTTP).Posicionamento de definições: as definições de objetos devem ser colocadas no nível superior do esquema, especificamente na chave definitions ou
$defs
.Aplicação: embora a especificação do esquema JSON recomende o uso da chave
$defs
para definições, o mecanismo de validação do Snowflake impõe essa estrutura de forma rigorosa. Este é um exemplo de um objeto$defs
válido:
{
'$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'}}}
}
Exemplo usando a referência JSON¶
Este exemplo SQL demonstra o uso de referências em um esquema 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']
}
}
}
);
Resposta:
{
"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
}
}
Otimização da precisão da adesão ao JSON¶
Normalmente, as saídas estruturadas COMPLETE não requerem um prompt; já se entende que sua resposta deve estar em conformidade com o esquema especificado por você. Entretanto, a complexidade da tarefa pode influenciar significativamente a capacidade dos LLMs de seguir um formato de resposta JSON. Quanto mais complexa for a tarefa, mais você poderá melhorar a precisão dos resultados especificando um prompt.
Tarefas simples como classificação de texto, extração de entidades, paráfrases e tarefas de resumo que não exigem raciocínio complexo geralmente não requerem prompts adicionais. Para modelos menores e de menor inteligência, o simples uso de saídas estruturadas melhora significativamente a precisão da adesão ao JSON, pois ignora qualquer texto que o modelo forneça sem relação com o esquema fornecido.
Tarefas de média complexidade incluem qualquer tarefa simples em que o modelo é solicitado a realizar raciocínios adicionais, como fornecer a justificativa para uma decisão de classificação. Para esses casos de uso, recomendamos adicionar “Responda em JSON” no prompt para otimizar o desempenho.
Tarefas complexas de raciocínio levam os modelos a realizar tarefas ambíguas mais abertas, como avaliar e pontuar a qualidade de uma chamada com base na relevância, no profissionalismo e na fidelidade das respostas. Para esses casos de uso, recomendamos usar os modelos mais avançados, como o
claude-3-5-sonnet
da Anthropic ou omistral-large2
da Mistral AI, e adicionar “Responda em JSON” e detalhes sobre o esquema que você deseja gerar no prompt.
Para obter resultados mais consistentes, defina a opção temperature
como 0 ao chamar COMPLETE, independentemente da tarefa ou do modelo.
Dica
Para lidar com possíveis erros gerados por um modelo, use TRY_COMPLETE em vez de COMPLETE.
Considerações sobre custo¶
As saídas estruturadas Cortex COMPLETE incorrem em custo de computação com base no número de tokens processados, mas não incorrem em custo de computação adicional para a sobrecarga de verificação de cada token em relação ao esquema JSON fornecido. No entanto, o número de tokens processados (e faturados) aumenta com a complexidade do esquema. Em geral, quanto maior e mais complexo for o esquema fornecido, mais tokens de entrada e saída serão consumidos. Respostas altamente estruturadas com aninhamento profundo (por exemplo, dados hierárquicos) consomem um número maior de tokens do que esquemas mais simples.
Limitações¶
Você não pode usar espaços nas chaves do esquema.
Os caracteres permitidos para os nomes de propriedades são letras, dígitos, hífen e sublinhado. Os nomes podem ter no máximo 64 caracteres.
Você não pode se dirigir a esquemas externos usando
$ref
ou$dynamicRef
.
As seguintes palavras-chave de restrição não são compatíveis. O uso de uma palavra-chave de restrição não compatível resulta em um erro.
Tipo |
Palavras-chave |
---|---|
inteiro |
|
number |
|
string |
|
matriz |
|
objeto |
|
Essas limitações podem ser abordadas em lançamentos futuros.
Condições de erro¶
Situação |
Exemplo de mensagem |
Código de status HTTP |
---|---|---|
Falha na validação da solicitação. A consulta foi cancelada porque o modelo não seria capaz de gerar uma resposta válida. Isso pode ser causado por uma solicitação malformada. |
|
400 |
Falha na validação do esquema de entrada. A consulta foi cancelada porque o modelo não seria capaz de gerar uma resposta válida. Isso pode ser causado pela falta de propriedades necessárias na carga útil da solicitação ou pelo uso de recursos de esquema json não compatíveis, como restrições, ou pelo uso inadequado do mecanismo $ref (por exemplo, sair do esquema). |
|
400 |
Falha na validação da saída do modelo. O modelo não conseguiu gerar uma resposta que correspondesse ao esquema. |
|
422 |