Struktureeritud väljundid ja funktsioonikutsumine: tootmismustrid
Struktureeritud väljundid ja funktsioonikutsumine on sild 'LLM-ist, mis genereerib teksti' kuni 'süsteemini, mis teeb tööd'. Tootmises on mustrid, mis loevad, skeemide, veakäsitluse, idempotentsuse ja graatsilise degradeerumise kohta — mitte ainult JSON-režiim.
Üleminek "LLM-jututoast" "LLM-jõul töötavale süsteemile, mis teeb tööd" toimub struktureeritud väljundi ja funktsioonikutsumise kihil. See on koht, kus LLM lõpetab lihtsalt proosa tootmise ja hakkab andmeid tootma, toiminguid otsustama ja sinu ülejäänud infrastruktuuriga integreeruma.
Tootmises ei tähenda "struktureeritud väljund" "JSON-režiim töötas minu testis ühe korra". See tähendab tugevat torustikku, mis käsitleb mudeli varieeruvust, vigastatud väljundeid, osalisi ebaõnnestumisi, skeemi arengut ja sasipuntra reaalsust, kus LLM-id ei järgi alati juhiseid.
See artikkel käsitleb mustreid, mis tegelikult tootmises püsima jäävad. Eeldame, et tunned põhitõdesid (oled kasutanud OpenAI tool_choice-i, Anthropici tööriistakasutust, JSON-skeemi piiranguid). Läheme sügavamale selles, mis muudab need süsteemid usaldusväärseks.
Kaks režiimi
Kaks seotud, kuid erinevat võimekust:
Struktureeritud väljund: LLM toodab skeemile vastavat väljundit (tüüpiliselt JSON). Kasutatakse siis, kui vajad LLM-i vastust programmilises formaadis.
Funktsiooni-/tööriistakutsumine: LLM-ile antakse hulk funktsioone, mida ta võib kutsuda, ta otsustab, milliseid kutsuda (kui üldse), produtseerib kõne parameetrid. Hostsüsteem käivitab funktsiooni ja tagastab tulemused. LLM saab kutsuda edasisi funktsioone või toota lõpliku vastuse.
Mudeli API-d avaldavad neid tüüpiliselt järgmiselt:
response_format(või samaväärne) parameeter, mis võtab JSON-skeemi, lihtsate struktureeritud väljundite jaoks — OpenAI "Structured Outputs", Google GeminiresponseSchemajms.toolsmassiiv, mis kirjeldab saadaolevaid funktsioone, pluss tööriistakutse vastus, kui see on välja kutsutud. Anthropici tööriistakasutuse API kahekordistub viisina, kuidas piirad väljundi skeemile — sa määratled ühe tööriista soovitud skeemiga ja sunnid mudeli seda kutsuma.
Mõlemad töötavad; need on omavahel seotud. "Funktsioonikutse" on sisuliselt struktureeritud väljund, kus skeem on funktsiooni signatuur.
Muster 1: Tihedad, selgesõnalised skeemid
Üksik suurim usaldusväärsuse võit on sinu skeemides.
Lõtv skeem:
{
"type": "object",
"properties": {
"category": { "type": "string" },
"priority": { "type": "string" }
}
}Tihe skeem:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account", "feature_request", "complaint"],
"description": "The ticket category. Use 'technical' for product bugs and 'account' for login/password issues."
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"],
"description": "Use 'urgent' only for outages or business-critical impact. 'High' for blocking issues on important customers. 'Medium' for standard impact. 'Low' for nice-to-haves."
}
},
"required": ["category", "priority"],
"additionalProperties": false
}Tihe versioon:
- Piirab väärtused teadaolevatele enumitele (vabavormilist triivi pole).
- Sisaldab kirjeldusi, mis toimivad sisemiste promptidena (mudel kasutab neid).
- Nõuab välju (nii et ei saa osalisi vastuseid).
- Keelab täiendavad väljad (juhuslikke hallutsineeritud võtmeid pole).
Tootmises peaks igal skeemi väljal olema kirjeldus. Iga enum peaks olema selgesõnaline. Iga nõutud väli peaks olema märgistatud. See on "skeem promptina" — sinu skeem teeb promptiinseneri tööd.
Muster 2: Piiratud genereerimine
Suuremad pakkujad toetavad nüüd piiratud genereerimist — mudel on dekodeerimistasemel piiratud tootma ainult kehtivaid väljundeid.
- OpenAI:
response_format: { type: "json_schema", json_schema: { ..., strict: true } } - Anthropic: Tööriistad rangete skeemidega.
- Avatud lähtekood:
outlines,lm-format-enforcer,jsonformer, vLLM-i grammatipõhine dekodeerimine.
Kasuta neid. Alati. Need elimineerivad terve klassi rikkumisi (vigastatud JSON, hallutsineeritud väljad, puuduvad nõutud väljad). Jõudluskulu on tühine.
Kui piiratud genereerimine pole saadaval (mõned avatud mudelid, mõned konfiguratsioonid), on valideerimine + uuestiproovimine varuvariant (vt Muster 4).
Muster 3: Skeemi versioneerimine
Skeemid arenevad. Sa lisad välju. Sa amortiseerid välju. Sa muudad enume.
Skeemi muudatus on koodimuudatus. See peaks olema:
- Versioneeritud. Märgi iga skeem versiooninumbriga.
- Testitud. Hindamiskomplekt käib uue skeemi vastu enne juurutust.
- Edastatud. Väljundi alamtarbijad teavad muudatusest.
- Tagasiühilduv, kui võimalik. Lisa uusi valikulisi välju; ära eemalda nõutud.
Muster, mida oleme näinud töötavat: säilita skeeme TypeScripti tüüpidena või Pydanticu mudelitena, versioneeri neid lähtekoodi juhtimises, genereeri neist JSON-skeemi. Tüübid teenivad nii mudeli API-d kui ka sinu rakenduse koodi.
class TicketClassificationV2(BaseModel):
category: Literal["billing", "technical", "account", "feature_request", "complaint"]
priority: Literal["low", "medium", "high", "urgent"]
confidence: float = Field(ge=0, le=1, description="Confidence in this classification, 0-1")
needs_human_review: bool = Field(description="True if any field has low confidence or unusual signal")
reasoning: str = Field(description="Brief reasoning for the classification, especially for non-obvious cases")Pydanticu mudel määratleb skeemi, valideerib väljundi ja teenib tüübina sinu Python-koodis. Üks tõeallikas.
Muster 4: Valideerimine ja uuestiproovimine
Isegi piiratud genereerimisega valideeri väljund enne kasutamist:
from pydantic import ValidationError
def call_with_validation(prompt, schema, max_retries=2):
for attempt in range(max_retries + 1):
response = llm_call(prompt, response_format=schema)
try:
parsed = schema.model_validate_json(response.content)
return parsed
except ValidationError as e:
if attempt < max_retries:
prompt = build_retry_prompt(prompt, response.content, e)
continue
raiseUuestiproovimise prompt peaks sisaldama algseid juhiseid, mudeli eelmist väljundit ja konkreetset kirjeldust selle kohta, mis valesti läks:
Your previous response had a validation error:
{error message}
Your previous output:
{previous output}
Please correct the issue and produce a valid response.Uuestiproovimised töötavad üllatavalt hästi — tavaliselt taastab üks uuestiproovimine mudeli veast.
Piirid: ära proovi igavesti uuesti (max 2–3). Ära proovi uuesti mittevalideerimisvigade puhul (kiirusepiirangud, sisufilter jne). Logi uuestiproovimised, et saaksid määra jälgida (tõusev uuestiproovimise määr signaleerib mudeli triivi või promptiprobleeme).
Muster 5: Tulemuste peegeldus
Kõrge panusega funktsioonikutsete jaoks lase mudelil tööriistatulemuste üle reflekteerida enne nende kasutamist.
Paljas tsükkel:
1. Call LLM with tools available.
2. Model decides to call tool X.
3. Execute X.
4. Pass result back to model.
5. Model produces final response.Peegeldustsükkel:
1. Call LLM with tools available.
2. Model decides to call tool X.
3. Execute X.
4. Pass result back to model.
5. Model evaluates: does this result match what I expected? Should I act on it?
6. If yes, model produces final response. If no, model calls another tool or asks for clarification.See püüab kinni juhtumeid nagu:
- Tööriist tagastas 0 tulemust, kui see oleks pidanud tagastama andmeid → mudel tunneb tühja juhtumi ära.
- Tööriist tagastas vea → mudel käsitleb seda selgesõnaliselt, mitte ei ignoreeri.
- Tööriist tagastas ootamatuid andmeid → mudel märkab ja kohaneb.
Rakendus: prompti mudelit, et see hindaks tööriistatulemusi selgesõnaliselt, võimalik, et struktureeritud "hinda siis tegutse" mustri kaudu.
See lisab latentsust ja tokeneid. Kõrge panusega toimingute (e-kirja saatmine, makse töötlemine, kirjete muutmine) jaoks on see seda väärt. Madala panusega teabe otsimise puhul jäta vahele.
Muster 6: Idempotentsus
LLM-id kutsuvad mõnikord sama tööriista kaks korda või proovivad uuesti kõnesid, mis on juba edukad olnud. Ilma idempotentsuseta saad duplikaate: kaks tagasimakset, kaks saadetud e-kirja, kaks loodud kirjet.
Mustrid idempotentsuse jaoks:
Idempotentsusvõtmed. Iga tööriistakutse saab unikaalse võtme (genereeritud kliendi pool, kaasatud kõnes). Alamtarbija API või sinu tööriista mähis kasutab võtit duplikaatide tuvastamiseks ja olemasoleva tulemuse tagastamiseks.
Get-or-create semantika. Tööriistad, mis loovad kirjeid, teevad esmalt kontrolli. "Create customer with email X" kontrollib esmalt, kas e-postiga X klient on olemas; kui jah, tagastab olemasoleva, mitte ei loo duplikaati.
Operatsioonilogid. Tööriistad logivad iga operatsiooni. Mähis kontrollib enne käivitamist logi; kui see on juba tehtud, tagastab vahemällu salvestatud tulemuse.
Konservatiivne tööriistadisain. Tööriistad, mis sooritavad tagajärgi tekitavaid toiminguid, on kavandatud nõudma selgesõnalist kinnitust või inimkinnitust. LLM ei saa neid kogemata tihedalt tsüklis vallandada.
Iga tööriista jaoks, millel on kõrvalmõjusid, kavanda idempotentsus. Selle vahelejätmine on suur tootmisvigade allikas.
Muster 7: Tööriistakutse jälgitavus
Sa pead teadma, mis tööriistakutsetega toimub. Iga kõne kohta logi:
- Ajatempel.
- Tööriista nimi ja argumendid.
- Tulemus (või viga).
- Kestus.
- Kasutaja/sessioon, kuhu see kuulub.
- Kõneahel selles voorus (kas see tööriistakutse oli pikema ahela osa?).
Ehita selle andmetele juhtpaneele. Levinud vaated:
- Tööriistakutse maht tööriista kaupa.
- Vea määr tööriista kaupa.
- Keskmine kestus tööriista kaupa.
- Tööriistajärjestuste mustrid ("milliseid tööriistu kutsutakse koos?").
- Hallutsineeritud tööriistakutsed (LLM proovis kutsuda tööriista, mida pole olemas).
Need näitavad, kus sinu süsteem ebaõnnestub ja kus see on kallis.
Muster 8: Hallutsineeritud argumendid
LLM-id leiutavad mõnikord väärtusi tööriistaparameetritele. Nad kutsuvad search_customers(email="...") e-postiga, mis ei vasta kasutaja tegelikule küsimusele. Või kutsuvad book_meeting(date="...") kuupäevaga, mida polnud mainitud.
Leevendamine:
Range skeem kirjeldustega. "The user_id must be one mentioned earlier in the conversation. Do not invent IDs."
Valideerimine tööriistamähises. Kui väärtus ei ole usutav (nt user_id-d pole olemas, kuupäev on minevikus), tagastab tööriist struktureeritud vea ja mudel kaalub uuesti.
Peegeldus. "Before calling this tool, confirm the values you're using are grounded in the conversation."
Piiratud tööriistakirjeldused. Tööriistad, mis tegutsevad konkreetsetel entiteetidel, avaldavad ainult entiteedi-ID-sid, mis on vestluses varem saadud. Ära avalda toorest otsingut.
Auditilogid. Püüa hallutsineeritud argumentide mustreid kinni ja kohanda promptisid/skeeme.
Muster 9: Graatsiline degradeerumine
Tööriistad ebaõnnestuvad. API-d lähevad maha. Tabad kiirusepiiranguid. Õige vastus on harva "ütle kasutajale, et miski ei tööta".
Mustrid:
Vahemällu salvestatud või vananenud andmed. Kui reaalajas andmeallikas pole saadaval, tagasta vahemällu salvestatud andmed märkega, et need on vananenud.
Osaline lõpetamine. Kui 5-st alamülesandest 3 õnnestuvad, teata, mis on tehtud ja mis mitte.
Varuvariandi teed. Kui esmane tööriist ebaõnnestub, teab mudel varuvariandist. Nt kui "search_documents" ebaõnnestub, langeb tagasi "search_web"-ile asjakohaste hoiatustega.
Kasutajale nähtavad veaolekud. Kui tööriist tõesti ei saa lõpetada, toodab mudel kasutajale selge veateate — mitte hallutsineeritud edu.
Mudel peab nendest mustritest teadma. Dokumenteeri need süsteemipromptis:
If a tool returns an error:
- Try the alternate tool if one exists.
- Report partial results clearly if the user has already provided information.
- Never claim success when a tool returned an error.Muster 10: Struktureeritud väljundite voogedastamine
Kasutuskogemuse jaoks on osaliste struktureeritud väljundite voogedastamine suurepärane — kasutaja näeb tulemusi reaalajas tekkimas, mitte ei oota.
Rakendus:
- Enamik kaasaegseid mudeli API-sid voogedastavad JSON-väljundit token-token.
- Sõelu osaline JSON inkrementaalselt (teegid nagu
partial-json-parservõi kirjuta väike voogedastatud sõelu). - Uuenda kasutajaliidest, kui väljad saabuvad.
See töötab eriti hästi väljundite puhul, millel on mitu sektsiooni. Pikk tootekirjeldus, analüüs mitme tähelepanekuga, koodirevisjon mitme leiuga — need tunduvad palju reageerivamad, kui need voogedastatakse.
Hoiatus: ära tee otsuseid osalise väljundi põhjal. Voogedasta kuvamiseks; oota lõpetamiseni, enne kui tegutsed struktureeritud tulemusega.
Muster 11: Funktsioonikutsumine vs selgesõnalised "decide"-kutsed
Natiivne funktsioonikutsumine on mugav — mudel "otsustab", millal tööriista kutsuda. Aga mõnede töövoogude jaoks on selgesõnaline "decide"-kutse usaldusväärsem.
Näide: klienditoe töövoog, kus mudel peab valima mitme toimingu vahel.
Natiivse funktsioonikutsumise lähenemine: Anna mudelile 5 tööriista (refund, send_article, escalate_to_human, ask_clarifying_question, close_ticket). Lase tal otsustada.
Selgesõnalise decide-kutse lähenemine: Esmalt kutsu mudelit ühe tööriistaga decide_action, mis võtab ühe parameetri: milline toiming. Seejärel, otsuse põhjal, kutsu mudelit uuesti ainult asjakohase tööriistaga.
Selgesõnaline lähenemine on aeglasem ja sõnaohtum, aga usaldusväärsem. Mudel on igal sammul rohkem keskendunud. Hostsüsteemil on töövoo üle rohkem kontrolli.
Kõrge panusega töövoogude puhul võidab selgesõnaline lähenemine sageli. Avastuslike või lihtsate töövoogude puhul on natiivne funktsioonikutsumine sobiv.
Muster 12: Tööriistatulemuse vormindamine
Kuidas sa tööriistatulemusi tagastad, loeb. Mudel loeb tulemust; vorming loeb.
Halb:
{"id": "cus_123", "n": "John", "p": "12345"}Parem:
{
"customer_id": "cus_123",
"name": "John Doe",
"phone": "+1-555-0123",
"tier": "premium",
"open_tickets": 0
}Parim (mõnel juhul):
Customer found:
- ID: cus_123
- Name: John Doe
- Tier: Premium
- Phone: +1-555-0123
- Open tickets: 0
This customer is in the premium tier and has no open tickets."Parim" vorm on inimloetav, sisaldab konteksti ja on mudelile järgnevas genereerimises lihtsam kasutada. "Parem" vorm on rohkem struktureeritud ja masinloetav. Kasuta seda vormingut, millega mudel käsitleb sinu alamtarbija ülesannete jaoks kõige paremini (testi seda).
Mõne tööriista puhul töötab nii struktureeritud kui ka narratiivi tagastamine ("Here's the result: [narrative]. Raw data: [JSON]") hästi.
Muster 13: Skeemiteadlikud uuestiproovimised
Mõned valideerimisvead on parandamatud (mudel sai ülesandest põhimõtteliselt valesti aru). Teised on kerged parandused.
Kasulik muster: klassifitseeri viga ja reageeri vastavalt.
def handle_validation_error(error):
if "missing required field" in str(error):
return retry_with_message("You omitted required field X. Please include it.")
elif "value not in enum" in str(error):
return retry_with_message("Value X is not in the allowed set. Choose from: ...")
elif "type mismatch" in str(error):
return retry_with_message("Field X must be a number, not a string.")
else:
# Unknown error — single generic retry
return retry_with_message("There was an error in your response. Please try again.")Kohandatud uuestiproovimised õnnestuvad sagedamini kui üldised uuestiproovimised.
Muster 14: Kompositsionaalsus
Tööriistad peaksid komponeeruma. Väikesed, fokusseeritud tööriistad, mis teevad ühte asja, saab mudel keerukatesse töövoogudesse kombineerida.
Monoliitne process_customer_request(query) tööriist, mis teeb kõike, on must kast. Mudel ei saa sisemist loogikat jälgida ega juhtida.
Hulga fokusseeritud tööriistu — search_customer(email), get_recent_orders(customer_id), check_subscription_status(customer_id), escalate_to_human(reason) — saab mudel kombineerida iga olukorra õigeks vooks.
Kujunda tööriistad õige granulaarsusega. Iga tööriist teeb ühte asja. Tööriistad komponeeruvad töövoogudesse.
Muster 15: Skeem "ma ei tea" jaoks
Peen muster: modelleeri ebakindlust skeemis selgesõnaliselt.
class CustomerInfo(BaseModel):
name: str
name_confidence: Literal["high", "medium", "low"]
needs_clarification: bool
clarification_question: Optional[str] = NoneMudel saab tagastada "madal kindlus" koos täpsustava küsimusega, mitte ei pea konfabuleerima.
See on palju parem kui mudel, mis täidab välju alati kindlalt, mõnikord hallutsineeritud andmetega.
Töötatud näide: arvete töötlemine
Et seda kokku tõmmata, siin on tootmiskvaliteediga arvete töötlemise süsteem.
Sisendid: PDF-arve e-kirja manusena. Eesmärk: Eralda struktureeritud andmed, marsruudi raamatupidamissüsteemi.
Skeem:
class LineItem(BaseModel):
description: str
quantity: float
unit_price: float
total: float
confidence: Literal["high", "medium", "low"]
class Invoice(BaseModel):
vendor_name: str
vendor_id: Optional[str] = None # null if not found in our records
invoice_number: str
invoice_date: date
due_date: Optional[date] = None
line_items: List[LineItem]
subtotal: float
tax: float
total: float
currency: str # ISO 4217
confidence: Literal["high", "medium", "low"]
needs_review: bool
review_reasons: List[str] # Specific reasons review is neededTöövoog:
- OCR-samm: Nägemismudel eraldab PDF-ist teksti.
- Ekstraktimissamm: LLM-kõne ülaltoodud skeemiga, piiratud genereerimine lubatud.
- Valideerimissamm: Pydantic valideerib. Valideerimisvead käivitavad ühe uuestiproovimise koos veatagasisidega.
- Ristkontrolli samm: Tööriistakutse, et otsida tarnijat meie kirjetest. Kui
vendor_namevastab teadaolevale tarnijale, lisavendor_id. Kui mitte, määraneeds_review=true. - Matemaatikakontrolli samm: Veendu, et
sum(line_items.total) ≈ subtotaljasubtotal + tax ≈ total. Kui mitte, määraneeds_review=true. - Kindluse kontrolli samm: Kui
confidenceon madal või mõnel reaartiklil on madal kindlus, määraneeds_review=true. - Marsruutimine: Kui
needs_review=true, saada inimsekti järjekorda. Muidu marsruudi raamatupidamissüsteemi. - Logimine: Iga samm logitud sisendi, väljundi, kestuse, vigadega.
Käsitletud rikkemoodid:
- Vigastatud JSON: piiratud genereerimine ennetab; uuestiproovimine käsitleb servapuhke.
- Hallutsineeritud väljad: skeem on range.
- Matemaatikavead: valideeritud.
- Tundmatud tarnijad: märgistatud.
- Madal kindlus: märgistatud.
- Tööriistavead: selgesõnaline käsitlus.
Tootmise jõudlus: ~95% arvetest läheb otse läbi; 5% märgistatud ülevaatamiseks. Automaatselt töödeldud arvete vea määr on <0,5% (täiesti aktsepteeritavates piires). Ülevaatamise järjekorrast ~80% kinnitatakse õigeks, 20% vajab parandust.
Selline näeb välja tootmiskvaliteediga struktureeritud väljund. Mitte ainult "JSON-režiim töötas ühe korra". Torustik, mis käsitleb reaalse maailma rikkeid.
Levinud vead
Paar mustrit, mida näeme korduvalt:
Viga 1: Pole valideerimist. Pydantic või zod või mistahes — lihtsalt valideeri. Ära usalda mudelit.
Viga 2: Ebamäärased kirjeldused. "category: string" ei aita mudelit. "category: one of billing, technical, account_access, where billing covers..." aitab.
Viga 3: Liiga palju tööriistu. 30 tööriista saadaval, mudel valib valed. Kureeri <10 asjakohase tööriista alla per kõne.
Viga 4: Pole uuestiproovimist valideerimise rikke korral. Üks vigastatud väljund tapab kogu voo. Proovi üks kord tagasisidega uuesti.
Viga 5: Pole jälgitavust. Kui tööriistakutsed tootmises ebaõnnestuvad, ei saa diagnoosida ilma jälgedeta.
Viga 6: Pole idempotentsust kõrvalmõjuga tööriistadel. Duplikaattagasimaksed, duplikaat-e-kirjad. Etteennustatav viga.
Viga 7: LLM-otsustatud argumentide usaldamine ilma valideerimiseta. Hallutsineeritud kasutaja-ID-d, hallutsineeritud kuupäevad. Valideeri tööriista argumendid enne käivitamist.
Viga 8: Skeemi versioneerimise vahelejätmine. Skeemi muudatused rikuvad alamtarbijad. Versioneeri seda.
Põhipoint
Struktureeritud väljundid ja funktsioonikutsumine on sild "LLM-ist, mis räägib" "LLM-ini, mis teeb tööd". Hästi tehtuna avavad need tootmise AI. Halvasti tehtuna purunevad huvitavates ja kallites kohtades.
Mustrid, mis loevad: tihedad skeemid, piiratud genereerimine, valideerimine uuestiproovimisega, peegeldus tööriistatulemustel, idempotentsus, graatsiline degradeerumine, skeemiteadlik veakäsitlus ja otsast-otsani jälgitavus.
Need pole valikuline lihv. Need on erinevus demo ja tootmissüsteemi vahel. Ehita need algusest peale sisse.