decision models! new category, showed up on the hub about three weeks ago, and the name is doing them no favours because everyone reads it as "model that makes decisions" and imagines an agent booking flights.
it's not quite that. please do read on to learn more if this is new to you!
⏳ quick version if you're skimming:
chatbot → you want words
decision model → you want a number you can put in an if statement
key words to know
- noul: the yes/no one. is this true? → one number: how likely "yes" is
- choice: which of these 2–16 options? → a probability each
- score: where on this ordered scale? → a probability per level, plus an average
what a decision model actually is
you send a state (here, the photo plus one line: "a photo submitted to a dog adoption listing.") and a schema. this is the exact one used in my workflow:
{
"breed": {
"criteria": {
"bernese": "Large, tricolour: black, rust and white",
"golden_retriever": "Medium-large, golden feathered coat",
"newfoundland": "Giant, heavy-boned, thick flat coat, broad head",
"saint_bernard": "Giant, red-and-white patched coat"
},
"instructions": "Which breed is this dog?",
"type": "choice"
},
"dog_present": {
"instructions": "Is there a dog in this photo?",
"type": "noul"
},
"flat_friendly": {
"instructions": "Would this dog be comfortable living in a small city flat?",
"type": "noul"
},
"quality": {
"criteria": [
"Unusable",
"Poor",
"Acceptable",
"Excellent"
],
"instructions": "How sharp and well-exposed is this photo?",
"type": "score"
}
}
you get back a probability for every allowed answer:
{
"breed": {
"choice": "newfoundland",
"confidence": 0.97,
"probabilities": {
"bernese": 0.01,
"golden_retriever": 0.01,
"newfoundland": 0.97,
"saint_bernard": 0.01
},
"type": "choice"
},
"dog_present": {
"noul": 0.99,
"type": "noul"
},
"flat_friendly": {
"noul": 0.06,
"type": "noul"
},
"quality": {
"confidence": 0.67,
"probabilities": {
"Acceptable": 0.28,
"Excellent": 0.67,
"Poor": 0.04,
"Unusable": 0.02
},
"score": 2.59,
"type": "score"
}
}
it runs all of that through the model once.
three question types cover almost everything: noul, choice and score. you write the questions yourself, in plain english, at the moment you call it. no training step. no fixed category list. change the wording and the answers change.
same photo, two models
i gave both of these the same photo of a newfoundland and the four three questions: is there a dog, which breed (newfoundland, saint bernard, bernese or golden retriever), would it suit a small flat, and how good is the photo. one of them is a chatbot, one is a decision model. they're both 27b. look at what comes back.
the input. a newfoundland, in the park.
here's the chatbot, Qwen3.8-27B:
Yes, there's definitely a dog in the photo — a big one, standing in profile with its head turned toward the camera and its tongue lolling out.
the chatbot is correct. it's a dog, and it is a newfoundland. but nothing it said is a value. 1,702 characters to land on "newfoundland" and "excellent", and nowhere in there does it say how sure it is. it sounds exactly as certain about the breed as it does about the dog being a dog. you can't branch on that and neither can your code.
what the decision model said
this is clef's complete output.
the cool bit is the question no classifier could answer
look at flat_friendly. nobody trained clef on "dogs that suit flats". there's no dataset of it, no label list. i wrote the question in plain english about thirty seconds before i ran it, and it came back 94% no. which is right! newfoundlands are pretty big.
that's the bit that makes these different from the classifiers you already know. the questions are yours, they're typed, and you can change them on every call.
and it knows when a question is hard. in an earlier run i put a tibetan mastiff in the breed options - a breed that sort of looks like a brown newfoundland. same photo, and clef went 39.5% newfoundland, 36.6% mastiff. still right, but now telling you it's close. drop the lookalike and it's back to 97%. the doubt shows up when the question gets hard.
the chatbot, on that same run, also said newfoundland, and in another run with the same options it said tibetan mastiff. both times with a list of reasons, both times sounding exactly as sure as it does above.
...and this explains why decision models exist!
if answers["dog_present"]["noul"] < 0.5:
reject(photo) # 0.98 — skipped
elif answers["breed"]["confidence"] > 0.9:
auto_tag(photo, answers["breed"]["choice"]) # 0.40 with the mastiff in the options, nowhere near
else:
send_to_human(photo) # this one fires
the listing would go to a human to confirm the breed, which is the right call when two giant breeds look alike.
how i made the workflow
the whole comparison is one gradio gr.Workflow graph. you can find this exact workflow here.
nodes come in three kinds. inputs going in on the left - the photo, and three text boxes holding the question schema, the state, and the human written english version of the same questions for the chatbot. the two models in the middle. and the outputs out on the right.
the two models in the middle come from completely different places. clef is a gradio space i dropped in. qwen is a hosted model i'm calling through hf inference. normally that means two different libraries, two different ways of sending a photo, and two different shapes of answer to deal with. on the canvas they're just two nodes - drag a wire from the photo into each one and that's it. no glue code for either.
and the photo node feeds both, so neither model can be fed anything the other didn't get.
there's one more node in there, the small one: render_decisions. that's a plain python function of mine that turns clef's raw answer into a visual. you pass functions in with bind= and they show up on the canvas as nodes, so the space, the hosted model and my own code all sit in the same graph.
import gradio as gr
def render_decisions(response) -> str:
... # clef's answers in, html bars out
gr.Workflow(graph="workflow.json", bind={"render_decisions": render_decisions}).launch()
that's the app. the graph is a json file you edit on the canvas. and because the three outputs all hang off that one photo, gradio treats them as one connected workflow and gives you a single api endpoint that returns all three together. the canvas and the app are the same object:
from gradio_client import Client, handle_file
app = Client("hmb/what-a-decision-model-does")
bars, paragraph, answers = app.predict(handle_file("dog.jpg"), api_name="/decisions")
same graph, no browser.
run it yourself! drop in your own photo, or rewrite the questions and ask it something else entirely.
some more handy things to know
is this just a classifier? sort of, yeah. calibrated classifiers have been good for a while. the new part is that you write the categories in english at call time instead of training them in, and one model handles yes/no, pick-one and scored questions in the same pass.
what's a noul? it's just the yes/no question. you get back one number between 0 and 1. how likely "yes" is. 0.97 is almost certainly yes, 0.03 is almost certainly no, 0.5 means it genuinely doesn't know. fun fact! 💡 the name comes from "bernoulli", the maths word for a coin-flip. that's also why it's the only type with no separate confidence number: the probability already is the confidence.
is it smarter than a chatbot? no. both models above got the newfoundland right. the difference is the shape of the answer, and that one of them tells you how close it was.
can i just prompt an llm for json instead? you can, and people do. you get a number that looks like a probability but was generated as text, so it lands on round values, moves between runs, and nothing measured it. fine for a one-off, shaky as a threshold.
how many options can a choice take? 2 to 16. score is a fixed ordered scale you define. noul is just true/false.
does it do images? clef does. that's what's in this workflow, the state was a photo. some are text only.
how fast? it goes through the model once and reads the answer off. there's no text to write, which is where a chatbot spends nearly all its time. the small ones are tens of milliseconds on a gpu. a call through the space takes around ten seconds, but that's a free shared gpu with a queue in front of it, not the model being slow.
can i swap the questions without redeploying? yes. the schema is an input, not config. on the space above it's a text node you can edit on the canvas.
built with
- hmb/what-a-decision-model-does: the workflow in this article, running
- Cloudflare/clef: 27b decision model, apache-2.0, post-trained from qwen3.8-27b
- hugging-apps/clef-decision-model: the space it calls
- huggingface.co/models?other=decision-model: more decision models to discover on huggingface
Top comments (0)