Generating extraction schemas
Structured extraction starts with a JSON Schema: the schema tells the extraction model exactly which fields to fill, and an LLM’s structured-output mode guarantees the result matches it. Writing that schema by hand is the slow part — you study sample documents, name every field, decide what’s nullable, what repeats, what’s an enum. Schema generation drafts it for you: describe the document type, state your requirements in plain language, attach a few example documents, and get back a schema ready for structured-output extraction.
This sample shows how to generate an extraction schema from example documents using Nutrient Python SDK. By default, generation runs an in-process local vision model, so documents don’t have to leave your machine.
Download sampleHow Nutrient helps
Nutrient Python SDK runs the full schema generation workflow behind a single method call. The SDK handles:
- Rendering the example documents’ pages and sending them to the vision model in one request
- Drafting a schema grounded in the document type, your requirement, and the examples
- Validating and hard-clamping the draft — field budget, nesting depth, and the feature set of your target structured-output dialect
- Optionally drafting cross-field constraint rules (sums, orderings) and validating each one against the schema
- Serializing the result to JSON
The result always satisfies the limits you configured, regardless of what the model drafted — the same call with the same settings yields a schema your extraction target accepts.
How generation works
Three inputs shape the schema:
- Document type (required) — The class of documents the schema must represent, such as “invoice” or “application form”. It grounds the schema’s vocabulary.
- Requirement (optional) — A free-form description of what the schema must capture: fields of interest, granularity, naming preferences — anything you’d tell a colleague.
- Example documents (optional, up to five) — Opened documents whose pages are rendered and shown to the model as representative samples. The schema is designed so other documents of the same type also fit, not just the examples. At most 20 pages are sent in total across all examples; pages beyond that bound aren’t rendered.
Generation requires a vision model. By default, the SDK runs a local vision model in process and downloads the required model files on first use. To use an external OpenAI-compatible server instead, select the Custom provider and configure the connection as shown in the describe image with local AI guide. Schema generation requires the vision schema generation feature in your license.
Targeting a structured-output dialect
LLM providers each accept a different subset of JSON Schema in their structured-output modes — one rejects oneOf, another rejects maxItems, a third silently ignores keywords it doesn’t know. The compatibility setting picks the target dialect, and the SDK guarantees the generated schema stays inside it:
PORTABLE(default) — The intersection accepted by the major structured-output modes and local grammar-constrained inference engines. The safest choice when you don’t know where the schema will run.OPENAI,ANTHROPIC,GEMINI— Unlock the keywords that specific provider documents as supported, while enforcing its documented rejections.NONE— No dialect filter; any standard JSON Schema feature the other knobs enable.
Every generated schema also satisfies the strict-mode invariants shared by all providers: an object root, every property listed in required, additionalProperties: false on every object, and optionality expressed as a null type union rather than a missing requirement.
Bounding the schema
Generated schemas stay within the budgets you set — properties beyond max_fields are truncated and structures deeper than max_nesting_depth are flattened, so a verbose model can’t produce an unusable schema. Feature knobs (allow_enums, allow_nullable, allow_string_formats, and others) control which JSON Schema features the draft may use.
Cross-field constraints
A schema describes each field in isolation; it can’t say “the subtotal equals the sum of the line item amounts.” Setting include_constraints = True asks the model to also encode such relationships as JsonLogic(opens in a new tab) constraint rules — inert JSON expressions your validation layer can evaluate after extraction. Every rule is checked against the generated schema: a rule referencing a field that doesn’t exist is dropped, so the constraints always match the schema they ship with.
Complete implementation
Import the classes used in the sample:
from nutrient_sdk import Document, DocumentSettings, SchemaGenerationRequest, Vision, NutrientExceptionBuilding the request
Open the example documents in context managers(opens in a new tab) so resources are cleaned up after processing, then describe what the schema must represent:
def main(): try: with Document.open("input_forms.pdf") as first_example, \ Document.open("input_forms_detection.pdf") as second_example: request = SchemaGenerationRequest() request.document_type = "application form" request.requirement = ( "Capture the applicant's identity (full name, date of birth, contact details), " "every checkbox group as an enumerated field, and the signature and date block. " "Repeating rows belong in arrays of objects." ) request.add_example_document(first_example) request.add_example_document(second_example)Configuring the schema shape
Create standalone document settings carrying the schema knobs — bound the field budget and opt in to cross-field constraints:
settings = DocumentSettings() schema_generation = settings.schema_generation_settings schema_generation.max_fields = 40 schema_generation.include_constraints = TrueGenerating the schema
Call the static Vision.generate_schema() with the request and settings:
result_json = Vision.generate_schema(request, settings)
with open("output.json", "w") as f: f.write(result_json) except NutrientException as e: print(f"Error: {e}")
if __name__ == "__main__": main()Understanding the output
generate_schema() returns a JSON envelope with two members — the shape is the same whether or not constraints are enabled:
schema— The generated JSON Schema, ready to pass to an LLM structured-output mode. Every property carries adescriptiontelling the extractor what to put there.constraints— Cross-field rules (empty unlessinclude_constraintsis on). Each carries a shortid, a one-sentencedescription, and arule— a plain, standard JsonLogic expression that evaluates totruewhen the extracted document is internally consistent, for example,{"==": [{"var": "total"}, {"+": [{"var": "subtotal"}, {"var": "tax_amount"}]}]}.
Pass schema to your extraction call, and after extraction, evaluate each constraint rule against the extracted data with any JsonLogic implementation — a false result flags the document for review. Comparison policy (such as tolerating sub-cent rounding differences in numeric checks) is yours to apply in that evaluation step.
Error handling
Vision API raises VisionException (a NutrientException) when generation fails. Common failure scenarios include a missing document type, more than five example documents, an unreachable vision model endpoint, or a model response that isn’t valid JSON after a retry. In production code, catch NutrientException, return a clear error message, and log failure details for debugging.
Conclusion
The workflow for generating an extraction schema is:
- Open up to five representative example documents using context managers for automatic resource cleanup.
- Build a
SchemaGenerationRequestwith the document type (required), a natural-language requirement, and the examples. - Configure the schema shape — field budget, nesting, target dialect, constraints — through
schema_generation_settings. - Call
Vision.generate_schema()to draft, validate, and clamp the schema. - Write the envelope to a file; feed
schemato your structured-output extraction andconstraintsto your validation layer. - Handle
NutrientExceptionfor robust error recovery.
For configuring the vision model connection, refer to the describe image with local AI guide. For related extraction workflows, refer to the Python SDK guides.
Download this ready-to-use sample package to explore schema generation.