Verified against Gemini API · 2026-08-07
Force reliable structured JSON out of messy unstructured text, every time
A controlled-generation prompt for the Gemini API's structured output mode that defines an explicit response schema so every call returns the same JSON shape regardless of how messy or inconsistently formatted the input text is, instead of an LLM-shaped JSON-ish string that needs a try/catch around every parse.
The prompt
Ready to copy — highlighted parts are example details you can swap.
TASK extract structured order details from free-text customer support emails INPUT TEXT a support inbox export of 40 emails, each requesting a return, exchange, or refund in the customer's own words SCHEMA CONTRACT Extract exactly these fields for every record, no more and no fewer: order_id (string or null), request_type (enum: return, exchange, refund, other), reason (short string), sentiment (enum: neutral, frustrated, angry), requires_human_review (boolean) FIELD RULES - Every field must be present in every record, even when there is nothing to extract for it — use the specified null/default rather than omitting the field. - Set the field to null rather than guessing a plausible value, and only set requires_human_review to true if the email is genuinely ambiguous about request_type. - If a value doesn't cleanly map to one of the listed enum options, use "other" and add a one-line note explaining what it actually was — never invent a new enum value on the fly. - Do not add commentary, an introductory sentence, or a trailing explanation outside the structured output — the schema contract above is the entire contents of a valid response, nothing else. WHAT TO DO WITH AMBIGUOUS INPUT If a single input record genuinely contains information for what should be two separate records (for example, one email requesting both a return and a separate unrelated complaint), split it into two records rather than merging mismatched information into one, and note the split has happened via whatever notes/flag field the schema provides for it. CONSISTENCY ACROSS RECORDS Apply the exact same interpretation of each field and each enum value to every record in this batch — if you'd classify a given phrase as "frustrated" sentiment in record 3, classify the same phrase the same way if it appears again in record 27. Don't let earlier or later records in the batch shift your calibration. HANDLING RECORDS THAT DON'T FIT ANY SCHEMA FIELD WELL If a record is genuinely too garbled, too short, or too off-topic to extract meaningful values for most fields, don't force plausible-looking values into every field anyway — populate what's genuinely extractable, set the rest to their null/default per the missing-field policy, and if the schema includes a review flag, set it so a human knows this record needs a manual look rather than trusting the automated extraction for it. MULTI-LANGUAGE INPUT If the batch includes records in more than one language, extract and classify consistently regardless of the source language — don't let sentiment or category classification become less reliable for non-English records than for English ones just because the underlying text differs; if you genuinely can't classify a non-English record with the same confidence, flag it via the review field rather than silently guessing. VALIDATION BEFORE RETURNING Before returning the final output, check your own result against the schema contract one more time: every record has every declared field, every enum value is one of the declared options, and no field contains a type mismatch (a string where a boolean was declared, for instance). This is a self-check, not optional — treat a self-caught schema violation the same as one a downstream validator would catch, and fix it before returning rather than after. OUTPUT Return only the structured data conforming to the schema contract above — one entry per record, in the order the input records appeared.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
The Gemini API's controlled generation (a declared response schema plus a JSON-only response mode) constrains the token-generation process itself to match the declared shape, which is mechanically different from asking a model in plain prose to 'return JSON' and hoping the output happens to parse — schema-constrained generation cannot emit a field name it wasn't told about or a value outside a declared enum, whereas prose-requested JSON routinely drifts (a trailing comma, an extra explanatory sentence before the object, a field renamed slightly between calls) in exactly the way that breaks a downstream JSON.parse call once it's actually running against real, messy production input instead of a clean test example. Declaring an explicit null-vs-guess policy for missing fields matters because an unconstrained extraction task will often fill a genuinely absent field — an order ID never mentioned in the email — with a plausible-looking placeholder rather than admit the information isn't there, and a downstream system consuming that field has no way to distinguish a real ID from a fabricated one unless the model is explicitly told that null is an acceptable, preferred answer over a guess. Routing unmappable values to an 'other' enum plus a free-text note, instead of letting the model invent a new enum value on the fly, keeps every downstream consumer's switch statement exhaustive and safe — a silently invented enum value is the kind of bug that doesn't surface in testing, only later, in production, against a real customer email nobody wrote a test case for. The explicit consistency-across-records instruction targets a specific batch-processing failure: without it, a model's threshold for what counts as 'frustrated' versus 'angry' sentiment can drift subtly across a long batch as it processes more examples, producing a dataset where the same underlying tone gets labeled two different ways depending on where in the batch it happened to appear.
What you get back
[ {"order_id": "ORD-88213", "request_type": "refund", "reason": "item arrived damaged", "sentiment": "frustrated", "requires_human_review": false}, {"order_id": null, "request_type": "other", "reason": "asking about a delivery delay, not a return or refund", "sentiment": "neutral", "requires_human_review": true} ]
Verified against
Gemini API Gemini 3 Pro · 2026-08-07
Changelog
- 2026-08-07 — Initial publish, verified against the Gemini API structured output mode on Gemini 3 Pro over a 40-email batch.
Need this built into your business?
If a prompt isn't enough — what Scult builds, built and maintained for you — that's Scult's day job.
EXPLORE WHAT SCULT BUILDS
