AI_COMPLETE 構造化出力

AI_COMPLETE 構造化出力では、完了応答が従わなければならない JSON スキーマを指定できます。これにより、 AI データパイプラインでの後処理の必要性が減り、決定論的な応答を必要とするシステムとのシームレスな統合が可能になります。AI_COMPLETE は生成されたトークンを JSON スキーマで検証し、レスポンスが提供されたスキーマに適合していることを確認します。

AI_COMPLETE でサポートされているモデルはすべて構造化出力をサポートしていますが、最も強力なモデルは通常、より質の高い応答を生成します。

AI_COMPLETE 構造化出力の使用

構造化形式で応答を得るには、 JSON スキーマ を表すオブジェクトを response_format 引数として指定します。与えられた JSON スキーマオブジェクトは、生成されるテキストが準拠しなければならない構造、データ型、制約 (必須フィールドなど) を定義します。簡単なタスクであれば、出力形式の詳細を指定する必要はありませんし、モデルに「respond in JSON」と指示する必要もありません。より複雑なタスクの場合、 JSON でモデルに応答を促すと、精度が向上します。 JSON アドヒアランス精度の最適化 をご参照ください。

ai_complete(
    ...
    response_format => {
        'type': 'json',
        'schema': {
            'type': 'object',
            'properties': {
                'property_name': {
                    'type': 'string'
                },
                ...
            },
            'required': ['property_name', ...]
        }
    }
Copy

SQL の例

次の例は、 response_format 引数を使用して、レスポンスに JSON スキーマを指定する方法を示しています。

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']}}}}

    }
);
Copy

応答:

{
    "sentiment_categories": [
    {
        "food_cost": "negative",
        "food_quality": "positive",
        "food_taste": "positive",
        "wait_time": "neutral"
    }
    ]
}

次の例は、 response_format 引数を使用してレスポンスに JSON スキーマを指定し、 show_details 引数を使用して推論メタデータを返す方法を示しています。

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
);
Copy

応答:

{
    "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
    }
}

Pythonの例

注釈

構造化出力は、 snowflake-ml-python バージョン 1.8.0 以降でサポートされています。

次の例は、 response_format 引数を使用して、レスポンスに JSON スキーマを指定する方法を示しています。

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)
Copy

応答:

{"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}]}

Pydanticの例

PydanticはPython用のデータ検証およびセット管理ライブラリです。この例ではPydanticを使って応答形式のスキーマを定義しています。コードは以下のステップを実行します:

  1. Pydanticを使ってスキーマを定義します。

  2. model_json_schema メソッドを使って Pydantic モデルを JSON スキーマに変換します。

  3. response_format 引数として complete 関数に JSON スキーマを渡します。

注釈

この例はSnowsight Pythonワークシートで実行することを想定しており、すでにSnowflakeに接続しています。別の環境で実行するには、 Snowflake Connector for Python を使用して Snowflake への接続を確立する必要があるかもしれません。

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)
Copy

応答:

{"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}]}

REST API の例

Snowflake Cortex LLM REST API を使用して、 COMPLETE を LLM で呼び出すことができます。以下は、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"
                ]
            }
        }
    }
}'
Copy

応答:

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":{}}

JSON スキーマ定義の作成

COMPLETE 構造化出力から最高の精度を得るには、以下のガイドラインに従ってください:

  • スキーマの "required "フィールド の必須フィールドを指定してください。COMPLETE は、必須フィールドを抽出できない場合にエラーを発生させます。

    次の例では、スキーマは COMPLETE で、ドキュメントで言及されている人物を検索するよう指示しています。 people のフィールドは、人物の識別を確実にするために必須とマークされています。

    {
        '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'
        ]
    }
    
    Copy

    応答:

    {
        "dataset_name": "name",
        "created_at": "date",
        "people": [
            {
                "name": "Andrew",
                "isAdult": true
            }
        ]
    }
    
  • モデルがより正確にフィールドを識別できるように、抽出するフィールドの 詳細な説明を提供 します。例えば、以下のスキーマには、 people: nameage、および isAdult のフィールドごとに説明が含まれています。

    {
        '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'
                        }
                    }
                }
            }
        }
    }
    
    Copy

JSON リファレンスの使用

スキーマリファレンスは、Cortex COMPLETE 構造化出力を使用する際の実用的な問題を解決します。 $ref で表されるリファレンスを使えば、住所や価格といった共通のオブジェクトを一度定義しておけば、スキーマ全体でそれらを参照することができます。こうすることで、バリデーションロジックを更新したり、フィールドを追加したりする必要があるときに、複数の場所で変更するのではなく、1つの場所で変更することができ、エラーの可能性を減らすことができます。

リファレンスを使うことで、一貫性のない実装によるバグを減らし、コードレビューを簡単にします。リファレンス・コンポーネントは、データ・モデル内のエンティティの関係をよりよく表現する、よりすっきりとした階層を作成します。プロジェクトが複雑化するにつれて、このモジュラーアプローチはスキーマの整合性を維持しながら技術的負債を管理するのに役立ちます。

Pydantic のようなサードパーティライブラリは Python ネイティブでリファレンスの仕組みをサポートしており、コードでのスキーマの使用を簡素化します。

JSON スキーマにおけるリファレンスの使用には、以下のガイドラインが適用されます。

  • 範囲の制限: ユーザーのスキーマだけに $ref メカニズムは限定されています。 HTTP URLs のような外部スキーマ・リファレンスはサポートされていません。

  • 定義の配置: オブジェクト定義はスキーマのトップレベル、特に定義または $defs キーの下に配置する必要があります。

  • 強制: JSON スキーマ仕様では、定義に $defs キーを使用することを推奨していますが、Snowflake の検証メカニズムではこの構造を厳格に適用しています。これは有効な $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'}}}
}
Copy

JSON リファレンスを使用した例

この SQL の例は、 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']
}
        }
    }
);
Copy

応答:

{
  "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
  }
}

JSON アドヒアランス精度の最適化

COMPLETE 構造化出力は通常、プロンプトを必要としません。構造化出力は、その応答があなたが指定したスキーマに従うべきであることをすでに理解しています。しかし、タスクの複雑さは、 LLMs 、 JSON の回答形式に従う能力に大きく影響します。タスクが複雑であればあるほど、プロンプトを指定することで結果の精度を高めることができます。

  • テキスト分類、エンティティ抽出、言い換え、要約など、複雑な推論を必要としない 単純なタスク は、一般的に追加のプロンプトを必要としません。知能の低い小さなモデルでは、構造化出力を使用するだけで、 JSON の遵守精度が大幅に向上します。

  • 中複雑度タスク には、分類決定の根拠を示すなど、モデルに追加の推論を求める単純なタスクが含まれます。このような大文字と小文字を使用する場合は、パフォーマンスを最適化するために、プロンプトに「Respond in JSON」を追加することをお勧めします。

  • 複雑な推論タスク モデルには、回答の関連性、専門性、忠実性に基づいて電話の品質を評価し、採点するような、よりオープンエンドのあいまいなタスクの実行を促します。これらのユースケースでは、Anthropic の claude-3-5-sonnet や Mistral AI の mistral-large2 のような最も強力なモデルを使用し、プロンプトに "Respond in JSON" と生成したいスキーマの詳細を追加することをお勧めします。

最も安定した結果を得るには、タスクやモデルに関係なく、 COMPLETE を呼び出すときに temperature オプションを 0 にセットします。

Tip

モデルによって発生する可能性のあるエラーを処理するには、 COMPLETE ではなく TRY_COMPLETE を使用します。

コストの考慮事項

Cortex COMPLETE 構造化出力では、処理されるトークンの数に応じた計算コストが発生しますが、提供された JSON スキーマに対して各トークンを検証するオーバーヘッドに対する追加の計算コストは発生しません。しかし、トークンの処理数(および請求数)はスキーマの複雑さとともに増加します。一般的に、提供されるスキーマが大きく複雑であればあるほど、より多くの入力および出力トークンが消費されます。深い入れ子を持つ高度に構造化されたレスポンス (階層データなど) は、単純なスキーマよりも多くのトークンを消費します。

制限事項

  • スキーマのキーにスペースを使用することはできません。

  • プロパティ名に使用できる文字は、文字、数字、ハイフン、アンダースコアです。名前の長さは最大64文字です。

  • $ref または $dynamicRef を使用して外部スキーマをアドレス指定することはできません。

以下の制約キーワードはサポートされていません。サポートされていない制約キーワードを使用するとエラーになります。

キーワード

integer

multipleOf

number

multipleOfminimummaximumexclusiveMinimumexclusiveMaximum

string

minLengthmaxLengthformat

array

uniqueItemscontainsminContainsmaxContainsminItemsmaxItems

object

patternPropertiesminPropertiesmaxPropertiespropertyNames

これらの制限は、将来のリリースで対処される可能性があります。

エラー条件

状況

メッセージ例

HTTP ステータスコード

リクエストの検証に失敗しました。モデルが有効なレスポンスを生成できないため、クエリはキャンセルされました。これは不正なリクエストによって引き起こされる可能性があります。

please provide a type for the response format objectplease provide a schema for the response format object

400

入力スキーマの検証に失敗しました。モデルが有効なレスポンスを生成できないため、クエリはキャンセルされました。これは、リクエストのペイロードに必要なプロパティが含まれていなかったり、 制約のようなサポートされていない JSON スキーマの機能を使用していたり、 $ref メカニズムを不適切に使用していたり (例えば、スキーマの外側に達している) することで発生します。

以下のいずれかの input schema validation error: <理由>:

  • /properties/city additional properties are not allowed

  • /properties/arrondissement regexp pattern ^[a-zA-Z0-9_-]{1,64}$ mismatch on string

  • /properties/province/type sting should be one of [\"object\", \"array\", \"string\", \"number\", \"integer\", \"boolean\", \"null\"]

  • 無効なref #/http://example.com/custom-email-validator.json#.有効なオブジェクトを#/$defs/セクションで定義してください。

400

モデル出力の検証に失敗しました。モデルはスキーマに一致する応答を生成できませんでした。

以下のいずれかの json mode output validation error: <理由>:

  • モデル出力のアンマーシャリング中にエラーが発生しました。Model が返した無効な JSON は、次の理由で解析できません: JSON 入力の予期せぬ終了。

422