Poznámky pro SQL vývojáře, kteří mají hello-world demo za sebou
Většina tutoriálů k AI_COMPLETE končí přesně tam, kde začíná to zajímavé. Naučí, jak funkci zavolat, co vrátí, a pak… co dál? Jak se udrží konzistentní tvar výstupu napříč miliony řádků? Na kterých parametrech skutečně záleží a které jsou jen šum? Co se má stát, když řádek spadne na chybu ve 3 ráno na naplánovaném tasku?
Toto je článek, který přichází na řadu po tom předchozím. Základy se přeskakují a jde se rovnou k částem, které rozhodují, jestli pipeline přežije kontakt s produkcí
AI_COMPLETE se hodí, když:
Přeskočit se dá, když:
Forma pouze s promptem ve formě stringu pokrývá 90 % práce:
SELECT AI_COMPLETE(
model => 'claude-haiku-4-5',
prompt => 'Summarize this review: ' || review_text
) FROM reviews;
Existují ještě dvě další formy: prompt + file (pro vstupy typu obrázek a dokument) a prompt object (pro víc obrázků nebo smíšený obsah). Stojí za zmínku, že response_format, funkce umožňující strukturovaný výstup, funguje jen s první zmíněnou string formou. Pokud je potřeba obrázkový vstup i strukturovaný výstup zároveň, jsou nutná dvě volání.
Rychlý přehled, než přijdou na řadu větší funkce.
model_parameters
Nastavení modelu žije v jednom objektu:
response_format
Strukturovaný výstup omezený schématem. Toto je hlavní téma; viz další sekce.
show_details
Nastavením show_details => TRUE se vrátí celý inferenční payload — choices, tokeny, časování a informace o tom, který model skutečně běžel — místo obyčejného řetězce. Vytažení zprávy zpátky ven:
AI_COMPLETE(..., show_details => TRUE):choices[0]:messages::STRING
Důvod, proč to používat, není samotná odpověď — je to všechno kolem odpovědi. Vrácený objekt odhaluje spotřebu tokenů na úrovni řádku i název modelu, což otevírá několik governance vzorů:
Vyplatí se to spárovat s QUERY_TAG a přiřazovat útratu podle use case napříč warehousem, takže náklady lze agregovat podle týmu nebo pipeline bez nutnosti parsovat historii dotazů.
return_error_details
Ve výchozím nastavení, když u řádku dojde k chybě, AI_COMPLETE pro daný řádek vrátí NULL a dotaz běží dál — chyba na více řádcích nesloží celý příkaz. To je správné výchozí chování, ale tichá hodnota NULL se snadno přehlédne. Nastavením return_error_details => TRUE se vrátí {value: …, error_message: …}, kde právě jedno z obou je non-null. Nulová režie navíc. Vyplatí se to použít v každé pipeline, která se dotýká víc než tisíce řádků — mění to otázku „proč je 3 % mých výstupů NULL?“ z forenzního problému na jednořádkový filtr.
Pár slov ke guardrails
Nastavením model_parameters => {‚guardrails‘: TRUE} se výstup modelu směruje přes Meta Llama Guard 3. Pokud je odpověď označená jako nebezpečný obsah, nahradí se přesným řetězcem „Response filtered by Cortex Guard“ — pokaždé, byte po bytu identicky.
Moderní LLM sice už samy odmítají většinu nebezpečných promptů. Tak proč platit za filtr navíc? Důvod je deterministická detekce odmítnutí. Bez guardrails každý model formuluje odmítnutí jinak a formulace se navíc mezi verzemi mění — detekce „odmítl model, nebo ne?“ dál v pipeline by vyžadovala NLP. S guardrails zapnutými stačí porovnání jednoho řetězce:
WHERE response = 'Response filtered by Cortex Guard'
Vyplatí se zapnout pro aplikace s koncovými uživateli a pro moderaci obsahu. Vyplatí se vynechat u interního dávkového obohacování dat, nad kterými je plná kontrola.
SELECT
email_id,
AI_COMPLETE(
model => 'claude-haiku-4-5',
prompt => 'Extract the issue: ' || body,
model_parameters => {'temperature': 0, 'max_tokens': 500},
return_error_details => TRUE
) AS result
FROM emails;
Toto je produkční tvar volání. Všechno, co následuje, na něm staví. Jen pozor na změnu formátu, ve kterém to vrací podle použitých parametrů!
Jaký problém to řeší
Výchozí výstup LLM je blob textu. Aby se dal použít jako data, dřív se do promptu musela nacpat věta typu „RETURN ONLY VALID JSON, NO MARKDOWN, NO PREAMBLE, START WITH {„, doufat, a pak defenzivně použít TRY_PARSE_JSON. S response_format engine validuje každý výstupní token proti schématu. Výstup se vrátí jako skutečný OBJECT/VARIANT. Žádné parsování řetězců.
Dvě syntaxe pro totéž
TYPE literal — nativní pro SQL, stručný:
response_format => TYPE OBJECT(
sentiment STRING,
score FLOAT,
topics ARRAY(STRING)
)
JSON schema — expresivnější:
response_format => {
'type': 'json',
'schema': {
'type': 'object',
'properties': {
'sentiment': {'type': 'string', 'enum': ['positive','negative','neutral']},
'score': {'type': 'number'},
'topics': {'type': 'array', 'items': {'type': 'string'}}
},
'required': ['sentiment', 'score']
}
}
Obě varianty produkují stejně řízený výstup. TYPE je kratší a čte se jako SQL CREATE TABLE. JSON nabízí to, co TYPE neumí — enum, description, $ref a rozlišení optional vs. required. V TYPE literálu se očekává, že se ve výstupu objeví každé definované pole — neexistuje syntaxe pro označení pole jako volitelné. V JSON schématu jsou povinná jen pole uvedená v required; zbytek je volitelný a model je může vynechat, pokud se na vstup nevztahují. Pokud je tahle flexibilita potřeba (například pole affected_skus, které má chybět, když nejsou zmíněné žádné SKU), je potřeba JSON forma.
Engine garantuje tvar výstupu. V rámci tohoto tvaru jsou užitečné tyto vlastnosti:
Mental model: Snowflake garantuje tvar. Hodnoty uvnitř tvaru jsou už mimo jeho kontrolu. Cokoliv jako rozsahy čísel, délky řetězců nebo regex formáty se validují v SQL až po volání.
SELECT
email_id,
AI_COMPLETE(
model => 'claude-haiku-4-5',
prompt => 'Extract a triage record from this support email:\n\n' || email_body,
model_parameters => {'temperature': 0, 'max_tokens': 1000},
response_format => {
'type': 'json',
'schema': {
'type': 'object',
'properties': {
'priority': {
'type': 'string',
'enum': ['low', 'medium', 'high', 'critical'],
'description': 'critical = production down; high = blocked workflow; medium = degraded; low = question'
},
'category': {
'type': 'string',
'enum': ['billing', 'auth', 'data_quality', 'performance', 'feature_request', 'other']
},
'summary': {
'type': 'string',
'description': 'One neutral sentence, no opinions.'
},
'is_escalation': {
'type': 'boolean',
'description': 'True if customer asks for a manager, refund, or threatens to leave.'
}
},
'required': ['priority', 'category', 'summary', 'is_escalation']
}
}
) AS triage
FROM support_emails;
Rozbalení výsledku je obyčejné SQL — žádné PARSE_JSON, žádné defenzivní TRY_:
SELECT
email_id,
triage:priority::STRING AS priority,
triage:category::STRING AS category,
triage:summary::STRING AS summary,
triage:is_escalation::BOOLEAN AS is_escalation
FROM extracted_tickets;
Cena AI_COMPLETE se odvíjí od několika rozhodnutí: který model, jak velké jsou vstupy a kolik volání připadá na řádek. Nic z nich není nezávislé — vzájemně se vyvažují způsobem, který začne hrát roli, jakmile se škáluje přes pár tisíc řádků.
Volba modelu má zhruba 60násobný rozptyl mezi nejlevnějšími open-source modely a frontier modely. Výchozí pravidlo pro rozhodování: začít s menšími modely typu llama nebo claude-haiku a eskalovat jen tehdy, když kvalita selže na reálném vzorku.
Nejdřív ale stojí za kontrolu context window — často je to limitující faktor, ne „chytrost“. Každý model má pevný strop tokenů, který musí pokrýt prompt, vstupní text, schéma i výstup dohromady. Orientační hodnoty: llama-3.1-8b má strop 8K tokenů, llama-3.3-70b 128K, claude-haiku-4-5 200K, claude-sonnet-4-8 1M. Pokud se vstup nevejde, volání skončí chybou — neořízne se potichu, a řádky, na kterých to selže, bývají zrovna ty nejdelší a nejzajímavější. Pomocí AI_COUNT_TOKENS(‚model‘, text) se dá před výběrem zkontrolovat 99. percentil délek vstupů. Model s větším kontextem, který zvládne data, je lepší volba než menší chytřejší model, na kterém potichu selže 2 % řádků.
Reálná otázka, ke které se člověk může dostat: má jedno volání AI_COMPLETE se schématem odvést práci pěti specializovaných AISQL volání (AI_SENTIMENT, AI_CLASSIFY, AI_EXTRACT a podobné)? Záleží na okolnostech a vyplatí se spočítat na vlastních datech.
Nejdřív stojí za zvážení spolehlivost, protože obvykle rozhoduje, kterým směrem se jde. Bundling koncentruje selhání. Jedno selhání validace schématu na řádku znamená ztrátu všech pěti polí najednou. Pět samostatných specializovaných volání může selhat u jednoho a přesto vrátit čtyři zbylá. Pokud tato pole plní finální tabulku, kde záleží na částečné úplnosti — nebo kde je opakované spuštění drahé — jde o reálnou cenu, kterou je potřeba zvážit.
Teď výpočet nákladů. Specializované funkce pořád pod kapotou posílají vstupní text přes model. Volání pěti z nich na stejném řádku znamená, že se vstup tokenizuje a zpracovává pětkrát. Hrubý nákladový model na řádek:
specialized: 5 × (input_tokens × price + small_output × price + overhead)
bundled: (input_tokens × price + larger_output × price + overhead)
U 1 500tokenového vstupu specializovaná cesta zaplatí ~7 500 vstupních tokenů jen na duplicitě, ještě před jakýmkoli výstupním nákladem. Bundlovaná cesta zaplatí 1 500. Ten rozdíl bývá obvykle dost velký na to, aby ušetřený rozpočet pokryl přechod na větší, chytřejší model u bundlovaného volání — a stejně to vyjde levněji.
Případy, kdy bundling nemusí dávat smysl:
Poctivé shrnutí: bundling je výhra na nákladech, ne na spolehlivosti. Velké vstupy se souvisejícími poli → bundling. Krátké vstupy nebo nezávislá pole → specializované funkce zůstávají konkurenceschopné a udržují menší prostor pro selhání.
Dokumentace ke strukturovaným výstupům (zde) popisuje tři režimy selhání, každý s jinou opravou.
Request validation failed (400) — argument response_format je špatně formulovaný. Časté příčiny: špatná syntaxe (response_format = místo response_format =>), předání řetězce tam, kde se očekával SQL OBJECT, chybějící obal ‚type‘: ‚json‘.
Oprava: přečíst si doslovnou chybovou hlášku. Snowflake parser slušně popisuje, který klíč chybí nebo je špatně formulovaný.
Input schema validation failed (400) — schéma je strukturálně v pořádku, ale jeho obsah je špatný. Nejčastější příčinou je použití keywordu v JSON schema, které Snowflake nepodporuje.
Oprava: nepodporovaná klíčová slova se odstraní. Kontroly na úrovni hodnot — rozsahy, délky, regex — se přesouvají mimo schéma do SQL validace až po volání.
Model output validation failed (422) — engine i schéma jsou v pořádku, ale model nedokázal vyprodukovat vyhovující výstup. Nejčastěji to znamená, že mu došel max_tokens uprostřed generování a JSON je useknutý.
Oprava, od nejlevnější po nejdražší: nejdřív se zvyšuje max_tokens. Pokud to nepomůže nebo nelze, může se zjednodušit schéma (odstranění volitelných polí, zploštění vnoření). Zřejmým fixem je přejít na větší model. Pokud selhání přetrvává i na frontier modelu s velkorysým počtem tokenů, prompt možná požaduje něco, co zdrojový text ve skutečnosti neobsahuje — potřeba je zapracovat na datech nebo na promptu, ne na volání.
Při zpracování milionů řádků vždy nějaké procento skončí chybou. Čisté řešení je třístupňová pipeline: první průchod levným modelem s return_error_details => TRUE, druhý krok rozdělení čistých řádků od chybových, a nakonec opakování chybových řádků na lepším modelu.

Malý model zvládne oněch 95 %, co funguje. Drahý model se dotkne jen zbylých 5 %, které selhaly. Frontier modely se nespouští na řádcích, které by zvládl i 8B model.
Dvě neintuitivní věci, které se ve velkém měřítku násobí:
AI_COMPLETE otevírá spoustu možností, které v SQL dřív nebyly možné — uvažování, extrakce, klasifikace, sumarizace, to vše v jednom sloupcovém výrazu. Zároveň otevírá spoustu nových způsobů, jak přijít o peníze: tiché selhání do NULL, desetimilionové inference, které utečou mimo kontrolu, schémata, která vypadají správně, ale potichu produkují poškozené řádky.
Nic z toho není důvod AI_COMPLETE nepoužívat — je to důvod, proč existuje tenhle článek.

Jan Šulc
Business analytik ve společnosti Revolt BI se zkušenostmi se Snowflake a moderními datovými platformami.
Navrhuje škálovatelné datové modely, automatizuje procesy zpracování dat a podílí se na implementaci AI řešení v oblasti enterprise analytiky. Jan pomáhá firmám propojit byznysové cíle s technickou architekturou a proměňovat data a AI v měřitelnou obchodní hodnotu.
Rezervujte si bezplatnou 30 minutovou konzultaci. Vybereme jeden use case z vašich dat a ukážeme vám, co to bude stát v kreditech.