Making decisions from JSON data
Not every judgment call fits a classification label or a structured extraction field. “Is this total plausible?”, “Which expense category applies?”, “How confident are we this clause is a liability waiver?” — these are calibrated yes/no, multiple-choice, or ordinal decisions over state you already have, not a document Nutrient needs to open and parse itself.
This sample shows how to get calibrated decisions from Nutrient’s decision model using Nutrient Python SDK. You supply the shared state and the questions to ask against it directly as JSON; the SDK returns a calibrated answer — with a confidence and the full probability distribution — for each question. It runs locally.
Download sampleHow Nutrient helps
The decision API is a generic pass-through onto Nutrient’s decision model — no document, schema, or template list is involved. The SDK handles:
- Rendering the shared state and each question into the model’s expected prompt format
- Embedding any inline images in the state at the position they appear
- Reading one label token’s logits per question — the model never generates text, so answers are deterministic reads, not samples
- Calibrating the raw result into a confidence and a full probability distribution per question
- Serializing every answer plus token usage to JSON
Questions and state
state_json is the shared context every question is evaluated against — any JSON object, array, or plain string. Embed images anywhere in the structure as {"type": "image", "source": "data:<mime>;base64,<bytes>"}; the SDK walks the whole state, substitutes each with the model’s media marker, and sends the image bytes alongside, in encounter order.
questions_json is an array of {id, type, instructions, options?}:
type: "noul"— a yes/no judgment.optionscan be omitted (default true/false wording) or given as exactly two entries for custom wording.type: "choice"— pick one of 2+options, each{key, description}.type: "score"— an ordinal judgment over 2+ unlabeledoptions, numbered low to high by position.
Every question is answered independently in the same call, against the same shared state.
The decision API requires the decision model feature in your license.
Complete implementation
Import the classes used in the sample:
import jsonfrom nutrient_sdk import Vision, NutrientExceptionBuilding the state and questions
Decide works on plain JSON you already have — here, a parsed invoice record instead of a loaded document:
def main(): state = { "invoice": { "vendor": "Acme Consulting LLC", "total": 1204.50, "currency": "USD", "description": "Software and subscriptions renewal", } }
questions = [ { "id": "amount_reasonable", "type": "noul", "instructions": "Is the total a plausible amount for this invoice?", }, { "id": "category", "type": "choice", "instructions": "Which expense category applies?", "options": [ {"key": "travel", "description": "Travel and lodging"}, {"key": "software", "description": "Software and subscriptions"}, {"key": "office_supplies", "description": "Office supplies and equipment"}, ], }, ]Getting the decisions
Call the static Vision.decide(state_json, questions_json) — both arguments are JSON strings, so serialize the Python values first:
try: result_json = Vision.decide(json.dumps(state), json.dumps(questions))
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
decide() returns JSON with top-level answers and usage objects:
answers— one entry per questionid. Each carries:type— the question’s type (noul,choice, orscore), echoed back.chosenIndex— the index of the selected option.chosenKey— the option’skey("true"/"false"fornoul; a lettered label like"A"forscore, since score options have nokey).confidence— the calibrated probability of the chosen answer.probabilities— the full calibrated probability distribution over every option, in option order.
usage—promptTokens(exact, from the model’s own decode) andoutputTokens(one per question answered —decide()never generates text, so this counts work done, not text length).
Choosing question types
Use noul for a single yes/no judgment, choice when the answer is one of several discrete categories, and score for an ordinal judgment (low to high) where the options don’t need individual names. Batching related judgments into fewer, larger choice/score questions is more efficient than asking many separate noul questions, since each question re-evaluates the full shared state.
Error handling
Vision API raises VisionException (a NutrientException) when a decision call fails.
Common failure scenarios include:
state_jsonorquestions_jsonis empty or missing- The decision model is missing or inaccessible, or the feature isn’t licensed
- A question’s
optionsare malformed for itstype(for example,choicewith fewer than two options)
In production code:
- Catch
NutrientException. - Return a clear error message.
- Log failure details for debugging.
Conclusion
The workflow for getting decisions is:
- Build the shared state as a JSON-serializable value — any object, array, or string you already have.
- Build the questions array, choosing
noul,choice, orscoreper judgment. - Call
Vision.decide()with both payloads serialized to JSON strings. - Write the JSON result to a file, then read each question’s
chosenKeyandconfidencefor downstream routing. - Handle
NutrientExceptionfor robust error recovery.
For related document workflows, refer to the Python SDK guides.
Download this ready-to-use sample package to explore the decision API.