AI_COMPLETE v praxi

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í

Jan Šulc
31. července 2026
Kdy je AI_COMPLETE ten správný nástroj

AI_COMPLETE se hodí, když:

  • Je potřeba open-ended uvažování s vlastními pravidly zakódovanými v promptu
  • Jedno volání má zvládnout víc věcí najednou — klasifikaci, extrakci i uvažování zároveň
  • Na správnosti záleží natolik, že se vyplatí cena frontier modelu: vysoce rizikové záznamy, malé množství důležitých dat, cokoliv zapisující do finální tabulky, kde je opakované spuštění drahé.

Přeskočit se dá, když:

  • Specializovaná AISQL funkce (AI_SENTIMENT, AI_CLASSIFY, AI_FILTER, AI_EXTRACT a podobné) už úkol zvládá. Jsou levnější, rychlejší a konzistentnější.
  • Deterministická SQL transformace by fungovala stejně dobře.
Tvary volání

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í.

Parametry, se kterými se skutečně pracuje

Rychlý přehled, než přijdou na řadu větší funkce.

model_parameters

Nastavení modelu žije v jednom objektu:

  • temperature — 0 pro extrakci a klasifikaci, 0,7+ pro kreativní psaní. AI_COMPLETE má jako výchozí hodnotu 0, což je správná výchozí hodnota.
  • max_tokens — nastavuje se explicitně. Výchozí ořezání bývá nemilé překvapení.
  • top_p — nechává se na pokoji, pokud není jasný důvod ho měnit.
  • guardrails — viz níže.

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ů:

  • Přiřazení nákladů na řádek. Objekt usage se ukládá spolu s výsledkem. Pak lze zodpovědět otázku „kolik stálo obohacení dat tohoto zákazníka minulý měsíc?“ sečtením prompt_tokens + completion_tokens a spojením s cenovou tabulkou modelů. Bez tohohle zbývají jen agregáty na úrovni celého účtu.
  • Detekce odlehlých hodnot. Řádek, který spotřebuje 30K vstupních tokenů, když medián je 800, je signál — poškozený vstup, ujetý prompt, typ obsahu, na který pipeline nebyla navržená. Stojí za to na to upozornit dřív, než se to promítne do faktury.
  • Audit guardrails. Když je guardrails => TRUE, blok usage obsahuje guardrail_tokens. Každý řádek s guardrail_tokens > 0 měl výstup filtrovaný — užitečné jak pro compliance reporting, tak pro odhalování pokusů o prompt injection v aplikacích s koncovými uživateli.
  • Detekce driftu v čase. Rostoucí průměrný počet vstupních tokenů týden po týdnu obvykle znamená, že rostou zdrojová data (delší supportní e-maily, rozvláčnější dokumenty). Trend se dá zachytit dřív, než se objeví na faktuře.
  • Přiřazení modelu. Vrácené pole model přesně říká, který model daný řádek zpracoval. Užitečné při cross-region inferenci nebo podmíněném výběru modelu — model, který skutečně běžel, nemusí být ten, který byl nominálně požadovaný.

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.


Vše dohromady
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ů!

Strukturované výstupy — klíčová funkce

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.

Co schéma řídí (v JSON formě)

Engine garantuje tvar výstupu. V rámci tohoto tvaru jsou užitečné tyto vlastnosti:

  • type — string, number, integer, boolean, array, object
  • enum — výstup musí být jedna z uvedených hodnot, bez výjimek
  • required — uvedená pole se musí objevit, jinak volání skončí chybou
  • properties — pojmenovaná pole uvnitř objektu a jejich typy
  • items — typ prvků uvnitř pole
  • description — předává se modelu jako nápověda pro dané pole (nevaliduje se)
  • $ref / $defs — znovupoužití schématu, kompatibilní s Pydantic od května 2025

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í.

Ukázkový příklad: třídění support ticketů
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;
Tipy pro návrh schématu
  • Každé kategorické pole by mělo být enum. Volné řetězce se špatně spojují s referenčními tabulkami a bez enum bude model kolísat mezi „high“, „High“ a „HIGH!!“.
  • description je pro model, ne pro toho, kdo schéma píše. Píše se, jako by se pravidlo vysvětlovalo novému analytikovi. Snowflake to ve vlastní dokumentaci označuje za nejúčinnější způsob, jak zvýšit přesnost.
  • Descriptions jsou tokeny. Účtují se jako vstup na každém řádku. Jakmile je kvalita ověřená na vzorku, zkracují se, nebo se úplně odstraňují.
Tokenomics

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ů.

Výběr modelu

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ů.

Bundling vs. specializované funkce: kompromis, nad kterým se vyplatí přemýšlet

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:

  • Vstupy jsou velmi krátké. Název produktu má penalizaci za duplicitu téměř nulovou.
  • Úkoly spolu nesouvisí a přirozeně běží v různých fázích pipeline.
  • Jen část polí je potřeba jen někdy. Platit za výpočet všech pěti na každém řádku je plýtvání, pokud tři z nich jsou potřeba jen u 5 % řádků.

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í.

Zpracování chyb

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í.

Karanténní vzor

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.

Praktické tipy

Dvě neintuitivní věci, které se ve velkém měřítku násobí:

  • Prompty jsou tokeny a tokeny promptu se účtují na každém řádku. „Please carefully analyze the following text and provide me with the customer’s most significant pain point“ → „give customer’s key pain point.“ Stejný výsledek, zhruba o 60 % méně vstupních tokenů. Na 10 milionech řádků se to znásobí. O promptech je vhodné přemýšlet minimalisticky.
  • Velikost warehousu má pro volání Cortexu jen minimální význam. Inference běží na GPU infrastruktuře Snowflake, ne na warehousu. Škálování na XL nezrychlí AI_COMPLETE — jen se zaplatí víc za stejnou propustnost. Na orchestrační práci bohatě stačí XS nebo S. Větší warehousy se šetří pro SQL, které volání Cortexu obaluje.

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. 



VYZKOUŠEJTE AI FUNKCE SNOWFLAKE PŘÍMO NA VAŠICH DATECH

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.

Přečtěte si některý z našich dalších článků