🤖 AI Tools

Gemini API Structured Output Schema Builder: response_mime_type JSON, response_schema from Pydantic, Enums, propertyOrdering, Nullable Fields, and Validation Retries

Design a Gemini API structured output setup for an extraction or classification job: the schema in the subset Gemini accepts, the google-genai Python config, enum and nullable handling, field order that keeps reasoning before answers, and a validation and retry plan for bad responses.

0.0
0Reviews
P
October 9, 2026

Prompt

Act as an LLM application engineer who builds production extraction pipelines on the Gemini API, knows which JSON schema features Gemini structured output supports, and ships schemas that parse on the first try.

Inputs:
- The task: what goes in (document type, length, language) and what must come out: [ExtractionTask]
- Fields you need, with type, allowed values, required or optional, and an example value: [FieldList]
- Model and SDK: Gemini model name, google-genai Python or JS SDK version, Vertex AI or Gemini Developer API: [ModelAndSDK]
- Sample documents or snippets, including a messy or incomplete one: [SampleDocs]
- Downstream consumer: database table, Pydantic model, or API that reads the JSON: [Downstream]
- Output format: [Format]

Generate:
1. A schema design table from FieldList: field, Gemini schema type, enum values, nullable, required, and description text that tells the model how to fill it.
2. A Pydantic model (or TypeScript type if ModelAndSDK uses JS) that maps to Downstream, kept to schema features Gemini structured output accepts, with unsupported features (for example complex conditional keywords) replaced by simpler patterns and marked [confirm in current Gemini docs].
3. The google-genai request config: response_mime_type set to application/json and response_schema set to the model, plus the system instruction.
4. Field ordering: put evidence or source quote fields before the answer fields and set propertyOrdering where the SDK needs it, so the model writes support before conclusions.
5. Null and enum rules: when to return null instead of guessing, an "unknown" enum value where a category may not apply, and a single text/x.enum option if the job is pure classification.
6. A test run plan over SampleDocs: expected JSON for each, including the messy one.
7. Validation and retry: parse with the Pydantic model, what to do on a validation error (retry once with the error message, then route to human review), and what to log.
8. A short cost and size note: keep descriptions short, cap list lengths, and avoid deep nesting.

Constraints:
- Do not invent SDK parameters; when unsure of a name or limit in ModelAndSDK, write [confirm in current Gemini docs].
- Never make up field values in examples beyond SampleDocs.
- Code first, short explanations. No em dashes.

Instructions

Replace every [bracket] with your details before running. Works on ChatGPT, Claude, and Gemini.

Generated Output

This image was generated using the prompt above.

Gemini API Structured Output Schema Builder: response_mime_type JSON, response_schema from Pydantic, Enums, propertyOrdering, Nullable Fields, and Validation Retries - Result

Examples

Example Input

ExtractionTask: pull fields from US commercial insurance certificates of insurance (COI PDFs converted to text), one to two pages each, English
FieldList: insured_name (string, required); policy_type (enum: general_liability, auto, workers_comp, umbrella, other; required); policy_number (string, required); effective_date and expiration_date (ISO date, required); each_occurrence_limit (integer dollars, nullable); additional_insured (boolean, nullable); source_quote (string, the line the limit came from)
ModelAndSDK: gemini-2.5-flash, google-genai Python SDK, Gemini Developer API
SampleDocs: one clean COI with GL and auto; one scan with the umbrella limit cut off at the page edge
Downstream: Postgres table vendor_coi, one row per policy; Pydantic model feeds a SQLAlchemy insert
Format: schema table, Pydantic code, request config, ordering, null rules, test plan, retry plan

Example Output

1) SCHEMA TABLE
| Field | Type | Enum | Nullable | Required | Description |
| source_quote | string | | no | yes | Exact line from the certificate the limit came from |
| insured_name | string | | no | yes | Named insured as printed |
| policy_type | string enum | general_liability, auto, workers_comp, umbrella, other | no | yes | Coverage type of this row |
| policy_number | string | | no | yes | As printed, keep dashes |
| effective_date | string (date) | | no | yes | YYYY-MM-DD |
| expiration_date | string (date) | | no | yes | YYYY-MM-DD |
| each_occurrence_limit | integer | | yes | yes | Dollars, null if not readable |
| additional_insured | boolean | | yes | yes | Null if the box is unclear |

2) PYDANTIC
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field

class PolicyType(str, Enum):
    general_liability = "general_liability"
    auto = "auto"
    workers_comp = "workers_comp"
    umbrella = "umbrella"
    other = "other"

class Policy(BaseModel):
    source_quote: str = Field(description="Exact line the limit came from")
    insured_name: str
    policy_type: PolicyType
    policy_number: str
    effective_date: str = Field(description="YYYY-MM-DD")
    expiration_date: str = Field(description="YYYY-MM-DD")
    each_occurrence_limit: Optional[int] = Field(description="Dollars; null if unreadable")
    additional_insured: Optional[bool]

class Certificate(BaseModel):
    policies: list[Policy] = Field(max_length=8)

3) REQUEST CONFIG
from google import genai
from google.genai import types
client = genai.Client()
resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=coi_text,
    config=types.GenerateContentConfig(
        system_instruction="Extract one entry per policy on this certificate. Copy values exactly. Use null when a value is cut off or unreadable. Never guess limits.",
        response_mime_type="application/json",
        response_schema=Certificate,
    ),
)
cert = resp.parsed  # Certificate instance when parsing succeeds [confirm in current Gemini docs]

4) ORDERING
source_quote is declared first so the model copies the evidence line before writing the limit. The Python SDK keeps Pydantic field order; if you pass a raw dict schema, set propertyOrdering explicitly [confirm in current Gemini docs].

5) NULL AND ENUM RULES
- Umbrella limit cut off on the page edge: each_occurrence_limit = null, source_quote holds the partial line.
- Coverage that fits no enum value: policy_type = other.

6) TEST PLAN
- Clean COI: expect 2 policies (general_liability, auto), all dates ISO, limits as integers.
- Cut off scan: expect an umbrella row with a null limit and a partial source_quote; never a guessed number.

7) VALIDATION AND RETRY
try:
    cert = Certificate.model_validate_json(resp.text)
except ValidationError as e:
    retry once, adding to contents: "Your last JSON failed validation: " + str(e)[:500]
    on second failure: insert into coi_review_queue with the raw text and the error
Log: model, prompt version, document id, validation result, retry count.

8) COST AND SIZE
Short field descriptions, max_length on policies, no nested objects beyond one level.

Reviews (0)

Please login to leave a review.
Loading reviews...