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 Java 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.
Decision API is available in Nutrient Java SDK 2.1.13 and later.
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
stateJson 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.
questionsJson 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
Declare the sample’s package:
package io.nutrient.Sample;Import the classes used in the sample:
import io.nutrient.sdk.Vision;import io.nutrient.sdk.exceptions.NutrientException;
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;Building the state and questions
Decide works on plain JSON you already have — here, a parsed invoice record instead of a loaded document. This sample builds the JSON by hand to keep the dependency list minimal; use your preferred JSON library in production code:
public class DecideDocument { public static void main(String[] args) { String stateJson = "{" + " \"invoice\": {" + " \"vendor\": \"Acme Consulting LLC\"," + " \"total\": 1204.50," + " \"currency\": \"USD\"," + " \"description\": \"Software and subscriptions renewal\"" + " }" + "}";
String questionsJson = "[" + " {" + " \"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(stateJson, questionsJson):
try { String resultJson = Vision.decide(stateJson, questionsJson);
Files.writeString(Path.of("output.json"), resultJson); } catch (NutrientException | IOException e) { System.err.println("Error: " + e.getMessage()); } }}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 throws VisionException (a NutrientException) when a decision call fails.
Common failure scenarios include:
stateJsonorquestionsJsonis 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 value — any object, array, or string you already have.
- Build the questions array, choosing
noul,choice, orscoreper judgment. - Call
Vision.decide()with both payloads as 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 Java SDK guides.
Download this ready-to-use sample package to explore the decision API.