{"id":20803,"date":"2026-10-26T20:07:13","date_gmt":"2026-10-26T20:07:13","guid":{"rendered":"https:\/\/www.comet.com\/site\/?p=20803"},"modified":"2026-10-26T20:17:25","modified_gmt":"2026-10-26T20:17:25","slug":"train-a-decision-model","status":"publish","type":"post","link":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/","title":{"rendered":"Train your own decision model"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png\" alt=\"Train Your Own Decision Model: a shared state feeding several questions, each producing a probability distribution over its answers\" class=\"wp-image-20841\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png 1280w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured-300x169.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured-1024x576.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured-768x432.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/github.com\/anmorgan24\/decision-model\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Repo<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.comet.com\/anmorgan24\/decision-model\" target=\"_blank\" rel=\"noreferrer noopener\">Comet Project<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.comet.com\/opik\/anmorgan24\/projects\/01a15f6c-3276-73fa-8d99-f5c58ddb1c30\/experiments?groups=%5B%5D\">Opik Project<\/a><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Decision models drew wide attention in September, when TypeSafe AI launched <a href=\"https:\/\/typesafe.ai\/blog\/introducing-system-one-models-and-jev\">Jev<\/a>, a model that returns probabilities over a fixed set of answers instead of generating text. Open-source alternatives followed almost immediately: Latent.Space counted six in two days.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most writing about decision models since then defines them by their interface, which uses some sort of schema or structured output. But an LLM with structured output produces something almost identical. What makes decision models unique is how they generate this output and what their probabilities are trained to mean.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This article explains that difference and what it changes in practice. It defines what a decision model is, builds a small one on a transformer backbone, tracks its training in Comet, and tests the trained model in Opik to check whether it has the properties that decision models promise.<\/p>\n\n\n\n<h2 id=\"h-what-is-a-decision-model\" class=\"wp-block-heading\">What is a decision model?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A decision model takes an input, along with one or more questions whose possible answers are declared in advance, and returns a probability for each answer. The clearest way to see what that changes is to compare it with how an LLM produces structured output.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A structured-output LLM generates an answer token by token and constrains those tokens to fit the provided schema. Each output token costs another forward pass, later answers in the same response are conditioned on earlier ones, and the only probabilities available describe the next token rather than whether the answer is right.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By contrast, a decision model reads a probability for every allowed answer from a single pass, and those probabilities are trained to match how often each answer turns out to be right. This means cost and latency depend on the input rather than the length of the answer, several questions about the same input can be answered without influencing each other, and a 0.9 is meant to be right about 90% of the time.<\/p>\n\n\n\n<h3 id=\"h-decision-models-vs-classifiers\" class=\"wp-block-heading\">Decision models vs. classifiers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If that sounds like a classifier, it\u2019s because a decision model is much closer to one than to a chatbot. Transformer models come in two broad families. BERT-style encoders read the whole input at once, with every token able to attend to every other token in both directions, and they\u2019re pretrained by predicting hidden words from the context on either side. With a small head on top, they\u2019ve been the standard tool for classifying sentiment, spam, or intent since BERT came out in 2018. GPT-style decoders read left to right and are trained to predict the next token. They\u2019re built to generate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A decision model does the encoder\u2019s job: read the input and return a distribution over answers. What\u2019s changed is where the task comes from. A classic BERT classifier fixes its labels at training time, so adding a label means retraining the head. A decision model receives its questions and options with each request. A common summary of Jev is that it\u2019s BERT-like, but with the data, compute, and training recipe of modern LLMs. TypeSafe hasn\u2019t published Jev\u2019s backbone, so that describes the approach rather than a confirmed architecture.<\/p>\n\n\n\n<h3 id=\"h-calibration\" class=\"wp-block-heading\">Calibration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The difference in what the probabilities mean comes from training. LLMs are post-trained toward answers people prefer (RLHF) or answers that pass an automatic check (RLVR), and neither directly rewards a model for saying how likely it is to be right. TypeSafe trains Jev, for example, with a method it calls Reinforcement Learning for Calibrated Decisions (RLCD). The goal is calibration, or probabilities that match how often each answer turns out to be right.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Calibration is important because not every wrong decision costs the same. Missing an urgent ticket might cost far more than escalating one that could have waited, so the right cutoff isn\u2019t always 0.5; it depends on what each mistake costs. Calibrated probabilities let you set that cutoff in code: escalate a ticket above 0.2, auto-approve a refund above 0.95, and send the uncertain cases to a person. The same model can serve all of those decisions, and when costs change, you move the threshold instead of retraining.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But \u2018calibrated\u2019 isn\u2019t the same as \u2018accurate.\u2019 A model that says 0.6 on every item and is right 60% of the time is perfectly calibrated, but it tells you nothing about which items to trust.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Accuracy measures how often the top answer is right. Calibration measures whether the probabilities match how often answers turn out right, so a model can be accurate and overconfident, or calibrated and barely better than guessing, and <strong>a useful decision model needs both<\/strong>.<\/p>\n\n\n\n<h3 id=\"h-when-to-use-a-decision-model\" class=\"wp-block-heading\">When to use a decision model<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Decision models don\u2019t replace LLMs. LLMs remain the right tool for open-ended work like chat, coding agents, and anything that produces text a person reads. Decision models fit tasks where the possible answers can be listed in advance: classifying, routing, scoring, gating, and judging other models\u2019 outputs, often at high volume or under tight latency limits.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><\/td><td><strong>Generative LLMs<\/strong><\/td><td><strong>Decision models<\/strong><\/td><\/tr><tr><td><strong>Returns<\/strong><\/td><td>Text<\/td><td>A probability for each allowed answer<\/td><\/tr><tr><td><strong>Answer space<\/strong><\/td><td>Open. Output must be parsed and validated unless a schema constrains it.<\/td><td>Declared in advance; output is always one of the declared answers<\/td><\/tr><tr><td><strong>How the output is produced<\/strong><\/td><td>One token at a time, each conditioned on the ones before<\/td><td>Read out in a single pass<\/td><\/tr><tr><td><strong>What the probabilities mean<\/strong><\/td><td>Likelihood of the next token<\/td><td>Trained to match how often each answer turns out right<\/td><\/tr><tr><td><strong>Cost and latency<\/strong><\/td><td>Grow with input and output length<\/td><td>Grow with input length only<\/td><\/tr><tr><td><strong>Several questions on one input<\/strong><\/td><td>Later answers are conditioned on earlier ones<\/td><td>Can share one pass over the input while keeping questions independent<\/td><\/tr><tr><td><strong>Explains its answer<\/strong><\/td><td>Yes, it can write out its reasoning<\/td><td>No, it returns only the probabilities<\/td><\/tr><tr><td><strong>Best fit<\/strong><\/td><td>Open-ended work: chat, writing, code, agents<\/td><td>Tasks with answers you can list in advance: classifying, routing, scoring, gating, judging<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 id=\"h-build-a-small-decision-model\" class=\"wp-block-heading\">Build a small decision model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The clearest way to understand what makes a decision model unique is to build one. In this section, we\u2019ll use Qwen3-0.6B-Base with LoRA adapters and a decision head trained from scratch on <a href=\"https:\/\/huggingface.co\/datasets\/LocalLLaMA\/typed-decisions\">typed-decisions<\/a>, a public benchmark on Hugging Face (Apache-2.0). Each case pairs one <a href=\"https:\/\/typesafe.ai\/blog\/introducing-system-one-models-and-jev\">input, which Jev calls the state<\/a> (for example, a summary of an agent\u2019s run), with five typed questions about it. Every gold answer is a probability distribution rather than a single label.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019ll break this down into four pieces: the data format, packing one state and several questions into a single sequence, the head that turns hidden states into probabilities, and the loss that trains those probabilities to mean something. The full code, including training, evaluation, and Comet logging, is in <a href=\"https:\/\/github.com\/anmorgan24\/decision-model\">the companion repo<\/a>. The excerpts below are trimmed, and some variable names are changed for readability.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The data<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The typed-decisions benchmark covers four workflows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>whether an agent run needs human review and how urgently<\/li>\n\n\n\n<li>the right response and action for a customer support thread<\/li>\n\n\n\n<li>whether to pay, hold, or reject a vendor invoice<\/li>\n\n\n\n<li>whether to close, investigate, or contain a security alert<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Each case gives the model one state and asks five questions about it, mixing all three question types. In the agent-trace workflow, for example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>choice:<\/strong> one of several named options (action, outcome)<\/li>\n\n\n\n<li><strong>score:<\/strong> a level on an ordered scale (risk, urgency)<\/li>\n\n\n\n<li><strong>noul:<\/strong> yes\/no (needs_review)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A teacher model labeled each case three times, and the gold answer is the average of the three, so it stays spread out where a decision is ambiguous. In the case below, the teacher mostly wants a human review but puts some weight on stopping the agent. Training on these distributions teaches the model that an ambiguous case deserves an ambiguous answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s one case from the agent-trace workflow, trimmed to three of its five questions, one of each type.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"state\": {\"agent\": {\"autonomy\": \"unsupervised\", \"model\": \"internal-agent-v1\"},\n            \"constraints\": &#91;\"Never modify production without an approved change ticket\"],\n            \"task\": \"Reconcile the billing ledger against the payment provider.\",\n            \"trace_summary\": {\"constraint_violations\": 1, \"irreversible_actions\": 1, \"steps\": 11, \"tool_errors\": 1}},\n  \"questions\": {\n    \"action\": {\"type\": \"choice\", \"instructions\": \"What should the observability system do with this trace?\",\n               \"criteria\": {\"continue\": \"Let the agent proceed without interruption.\",\n                            \"human_review\": \"Queue this trace for a human to review.\",\n                            \"observe\": \"Keep running, but flag the trace for later sampling.\",\n                            \"stop\": \"Halt the agent now.\"}},\n    \"needs_review\": {\"type\": \"noul\", \"instructions\": \"This trace requires human review.\",\n                     \"criteria\": {\"true\": \"A human should inspect this run.\",\n                                  \"false\": \"No human attention is warranted.\"}},\n    \"urgency\": {\"type\": \"score\", \"instructions\": \"How quickly does this trace need attention?\",\n                \"criteria\": &#91;\"No time pressure; can wait indefinitely.\",\n                             \"Routine; handle within the normal queue.\",\n                             \"Elevated; should be handled within the same week.\",\n                             \"Critical; requires action within the same day.\"]}\n  },\n  \"gold\": {\n    \"action\": {\"label\": \"human_review\", \"probabilities\": {\"continue\": 0.02, \"human_review\": 0.70, \"observe\": 0.10, \"stop\": 0.18}},\n    \"needs_review\": {\"label\": \"true\", \"probabilities\": {\"false\": 0.10, \"true\": 0.90}},\n    \"urgency\": {\"label\": 3, \"probabilities\": {\"0\": 0.00, \"1\": 0.10, \"2\": 0.32, \"3\": 0.58}}\n  }\n}<\/code><\/pre>\n\n\n\n<h3 id=\"h-packing-several-questions-in-one-pass\" class=\"wp-block-heading\">Packing several questions in one pass<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The two key advantages of decision models (that cost depends on the input rather than the answer, and that several questions about the same input can be answered without influencing each other) are both entirely dependent on how the input is laid out.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The straightforward way to ask five questions about one state is to run five separate sequences, each repeating the state. That works, but the model reads the same state five times. Instead, we put the state and all five questions into a single sequence:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;state]&#91;question 1 ... Answer:]&#91;question 2 ... Answer:] ... &#91;question 5 ... Answer:]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On a T4, packing five questions cut the tokens processed by 2.1 times and the time per case from 329 to 175 ms.<\/p>\n\n\n\n<h3 id=\"h-the-attention-mask-questions-see-the-state-not-each-other\" class=\"wp-block-heading\">The attention mask: questions see the state, not each other<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Packing creates a problem, though. A transformer normally lets every token look back at every token before it, so question 3 could read questions 1 and 2, and its answer could change depending on what else was asked. An attention mask controls which tokens each token is allowed to look at. In our case this means every question can see the state, and no question can see another question.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To build the attention mask, each token is tagged with the part of the sequence it belongs to: 0 for the state, 1 for the first question, and so on. A token can look back at an earlier token only if that token belongs to the state or to the same question:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def build_attention_mask(segment_ids: list&#91;int]) -> torch.Tensor:\n    seg = torch.tensor(segment_ids)\n    causal = torch.ones(len(seg), len(seg), dtype=torch.bool).tril()\n    same_question = seg&#91;:, None] == seg&#91;None, :]\n    is_state = (seg == 0)&#91;None, :]\n    return causal &amp; (same_question | is_state)<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"726\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1024x726.png\" alt=\"Attention mask grid. State tokens attend causally to earlier state tokens. Each question attends to the full state and causally to its own tokens, never to another question. Each question\u2019s answer is read at its last token.\" class=\"wp-image-20805\" style=\"aspect-ratio:1.4085778781038374;width:765px;height:auto\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1024x726.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-300x213.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-768x544.png 768w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image.png 1249w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Each row is a token, and the colored cells are the tokens it can attend to. Every question sees the full state, and no question sees another question. Each question\u2019s answer is read from its last token.<\/em> <a href=\"https:\/\/github.com\/anmorgan24\/decision-model\/blob\/main\/scripts\/plot_attention_mask.py\"><em>Source code here<\/em><\/a><em>.<\/em><\/figcaption><\/figure>\n\n\n\n<h3 id=\"h-positions-each-question-starts-where-the-state-ends\" class=\"wp-block-heading\">Positions: each question starts where the state ends<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">There\u2019s one more detail worth noting. A transformer also tracks each token\u2019s position, and a question placed fifth in the sequence would see different positions than it would if asked alone. So each question\u2019s positions restart right after the state:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for i, question in enumerate(example.questions, start=1):\n    tokens = tokenizer.encode(format_question(question), add_special_tokens=False)\n    input_ids += tokens\n    position_ids += range(state_len, state_len + len(tokens))  # restart after the state\n    segment_ids += &#91;i] * len(tokens)\n    decide_index.append(len(input_ids) - 1)  # where the answer is read<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With the mask and the restarted positions, the model treats each question as if it had been asked on its own: question 3 gets the same answer whether it\u2019s packed with the other four or it\u2019s run alone after the state. The repo\u2019s tests check this to within floating-point error. In this way, packing changes the cost without affecting the answers. The state is processed once, however many questions you ask.<\/p>\n\n\n\n<h3 id=\"h-the-decision-head-from-hidden-state-to-probabilities\" class=\"wp-block-heading\">The decision head: from hidden state to probabilities<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After packing, each question\u2019s last token has only seen the full state and its own question, which are summarized in its hidden state. A regular LLM would pass this vector to its language-model head, which gives a probability for every possible next token. We pass it to a decision head instead, which gives a probability for each of the question\u2019s allowed answers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For choice and score questions, a linear layer maps the hidden state to one score (a logit) per option, and softmax turns the scores into probabilities. Slot 1 is the first option listed, slot 2 the second, and so on. Score questions work the same way, with one slot per level, in order.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A linear layer has a fixed number of outputs, but questions have different numbers of options, so the head is sized for the largest question it supports (16 options in the repo). For a question with fewer options, the extra outputs are masked out before the softmax and get zero probability.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Yes\/no questions need only one number, z. The head returns the pair [0, z], and softmax over that pair gives P(yes) = sigmoid(z). Expressing yes\/no this way means all three question types produce the same kind of output, so one loss and one set of metrics cover all of them.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def forward(self, hidden, question_type, num_options):\n    z = self.noul(hidden)  # yes\/no: a single logit\n    noul = torch.cat(&#91;torch.zeros_like(z), z, torch.zeros(len(z), self.max_options - 2)], dim=1)\n\n    qtype = question_type&#91;:, None]\n    logits = torch.where(qtype == CHOICE, self.choice(hidden),\n                         torch.where(qtype == SCORE, self.score(hidden), noul))\n\n    slots = torch.arange(self.max_options)\n    return logits.masked_fill(slots&#91;None, :] >= num_options&#91;:, None], float(\"-inf\"))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because slots are defined by position, the options themselves only arrive as text in the input, so the same head can answer questions whose options it has never seen. But this also opens a shortcut. If \u201cbilling\u201d is always listed first, the head can learn that slot 1 means billing without reading the options at all. So training shuffles the order of choice options every epoch, and the only way to know which slot is which is to read the list. Score levels stay in order, since their order carries meaning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lastly, the head\u2019s weights start at zero, so before any training every question gets a uniform distribution.<\/p>\n\n\n\n<h3 id=\"h-the-loss-training-the-probabilities-to-mean-something\" class=\"wp-block-heading\">The loss: training the probabilities to mean something<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Training adjusts the model\u2019s weights to make the loss smaller, so its probabilities mean whatever the loss rewards. If the loss only rewarded ranking the right answer first, a 0.9 would just mean \u201cmy top pick.\u201d We use cross-entropy against the gold distribution instead, which penalizes the model for putting low probability on answers the gold considers likely:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def decision_loss(logits, targets, batch_size):\n    log_probs = logits.log_softmax(dim=-1)\n    log_probs = log_probs.masked_fill(targets == 0, 0.0)  # unused slots are -inf, and 0 * -inf is undefined\n    return -(targets * log_probs).sum() \/ batch_size<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cross-entropy is what statisticians refer to as a <a href=\"https:\/\/www.tandfonline.com\/doi\/abs\/10.1198\/016214506000001437\">proper scoring rule<\/a>, meaning its average loss is lowest when the prediction matches the true distribution. For needs_review, where the gold puts 0.9 on yes, predicting 0.9 gives a loss of 0.33, while an overconfident 0.99 gives 0.47 and a hedging 0.7 gives 0.44. The model does best by reporting exactly the confidence the data supports.<\/p>\n\n\n\n<h3 id=\"h-the-baseline-reading-answers-from-the-language-model-head\" class=\"wp-block-heading\">The baseline: reading answers from the language-model head<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To see what the head and the loss contribute, we compare against reading answers from the LLM\u2019s own next-token probabilities, as many open-source alternatives do. The baseline uses the same backbone, adapters, and packed input, but at each question\u2019s last token it applies softmax over the language-model head\u2019s scores for the answer tokens (\u201c A\u201d, \u201c B\u201d, and so on, or \u201c No\u201d and \u201c Yes\u201d). Each model below changes one thing from the row above it:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Model<\/strong><\/td><td><strong>Trained on<\/strong><\/td><td><strong>Accuracy<\/strong><\/td><td><strong>KL<\/strong><\/td><td><strong>Brier<\/strong><\/td><td><strong>ECE<\/strong><\/td><\/tr><tr><td>Prior (reads nothing)<\/td><td>n\/a<\/td><td>0.502<\/td><td>0.402<\/td><td>0.199<\/td><td>0.074<\/td><\/tr><tr><td>Zero-shot baseline<\/td><td>nothing<\/td><td>0.430<\/td><td>0.628<\/td><td>0.354<\/td><td>0.245<\/td><\/tr><tr><td>Fine-tuned baseline (the usual recipe)<\/td><td>the gold label, scored against the full vocabulary<\/td><td>0.714<\/td><td>0.290<\/td><td>0.119<\/td><td>0.134<\/td><\/tr><tr><td>Baseline with our loss<\/td><td>the gold distribution, scored over the answer tokens<\/td><td>0.768<\/td><td>0.110<\/td><td>0.059<\/td><td>0.011<\/td><\/tr><tr><td>Decision model (decision head)<\/td><td>the gold distribution<\/td><td>0.757<\/td><td>0.111<\/td><td>0.061<\/td><td>0.019<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">ECE compares each prediction\u2019s confidence with the chance that a sample from the teacher agrees with its top answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Untrained, the baseline is worse than predicting each question\u2019s label frequencies without reading the input. Fine-tuning fixes most of that. Changing the objective does the rest: scoring the gold distribution over the answer tokens, rather than the gold label against the full vocabulary, adds five points of accuracy, cuts KL by 62%, and halves Brier. It also makes the probabilities mean what they say. The models trained on our loss are calibrated to within one to two points straight out of training, while the fine-tuned baseline is overconfident by 13. Temperature scaling brings the baseline down to three, which is still worse than our models before any scaling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Swapping the language-model head for a decision head changes nothing we can measure: every difference between the last two rows falls inside its 95% interval. With the same objective and the same packed input, the readout isn\u2019t what separates them; the loss is.<\/p>\n\n\n\n<h2 id=\"h-tracking-in-comet\" class=\"wp-block-heading\">Tracking in Comet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We make every decision on a validation set, 15% of each workflow\u2019s training cases, and score the test set once at the end. Training updates the head and the LoRA adapters, and keeps the epoch with the lowest validation log loss. Each run is one command and one Comet experiment, which logs validation metrics by epoch, question type, and question, along with reliability diagrams, the data split, and the checkpoint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python -m decision_model.train configs\/decision_head.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All of the runs in this article are in a <a href=\"https:\/\/www.comet.com\/anmorgan24\/decision-model\/view\/new\/panels\">public Comet project<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"570\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1-1024x570.png\" alt=\"Line chart of validation log loss by optimizer step. The fine-tuned baseline stays near 1.05 and then rises to about 1.25 in the third epoch. The baseline with the new loss and the decision head both fall steadily to about 0.87.\" class=\"wp-image-20806\" style=\"aspect-ratio:1.793103448275862\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1-1024x570.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1-300x167.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1-767x427.png 767w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-1.png 1304w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Validation log loss after each epoch (every 64 steps).<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The two objectives diverge as training continues. The fine-tuned baseline\u2019s validation log loss barely improves after its first epoch and rises sharply in its third, consistent with its probabilities growing more confident than its answers justify. The results use each model\u2019s best epoch, so the baseline is judged at its second, before that rise, and it still trails. The two models trained on our loss improve through all three epochs and finish within 0.002 of each other.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The model is only as calibrated as its training labels, so the last step fits a temperature for each question type on validation data. It rescales confidence without changing the top answer, so calibration can improve while accuracy stays the same.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"565\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2-1024x565.png\" alt=\"Two reliability diagrams for the fine-tuned baseline. Before temperature scaling (ECE 0.134), accuracy falls well below the perfect-calibration diagonal. After temperature scaling (ECE 0.027), the bars sit close to it.\" class=\"wp-image-20807\" style=\"aspect-ratio:1.813953488372093;width:817px;height:auto\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2-1024x565.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2-300x165.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2-767x423.png 767w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2-1536x847.png 1536w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-2.png 1979w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Reliability diagrams for the fine-tuned baseline on validation. Each bar shows how often answers at that confidence agree with a teacher sample; the dashed line is perfect calibration.<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Before scaling, the fine-tuned baseline is overconfident at every confidence above 0.4: answers it gives more than 90% confidence agree with a teacher sample only about 81% of the time. Dividing its logits by about two moves the bars onto the diagonal and cuts ECE from 0.134 to 0.027. The models trained on our loss need almost no correction; their fitted temperatures are within 8% of 1.<\/p>\n\n\n\n<h2 id=\"h-evaluating-in-opik\" class=\"wp-block-heading\">Evaluating in Opik<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We made every modeling decision on validation, so the test set is scored only once, at the end, in Opik. Each of the 400 test cases becomes an Opik dataset item holding the request (the state and its questions) and the gold answers. Each model then runs as one experiment over all 400 cases.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every model is called through the same function, predict(state, questions), which takes and returns the same request format as Jev. That means the same experiments could <a href=\"https:\/\/www.comet.com\/site\/blog\/jev-model-vs-llm-as-a-judge\/\">run against Jev<\/a>, or any model that accepts that format.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python -m decision_model.opik_eval --runs outputs\/logit_readout_sft outputs\/logit_readout outputs\/decision_head \\\n    --zero-shot configs\/logit_readout.yaml --split test<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Model<\/strong><\/td><td><strong>Accuracy<\/strong><\/td><td><strong>KL<\/strong><\/td><td><strong>Brier<\/strong><\/td><td><strong>ECE<\/strong><\/td><\/tr><tr><td>Zero-shot baseline<\/td><td>0.43<\/td><td>0.63<\/td><td>0.35<\/td><td>0.25<\/td><\/tr><tr><td>Fine-tuned baseline<\/td><td>0.76<\/td><td>0.27<\/td><td>0.11<\/td><td>0.12<\/td><\/tr><tr><td>Baseline with our loss<\/td><td>0.78<\/td><td>0.10<\/td><td>0.05<\/td><td>0.02<\/td><\/tr><tr><td>Decision model<\/td><td>0.78<\/td><td>0.09<\/td><td>0.05<\/td><td>0.02<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The test results match validation. The fine-tuned baseline\u2019s accuracy gap is smaller on test, two points instead of five, but its calibration gap is the same: KL 0.27 against 0.10, and ECE 0.12 against 0.02. The decision head and the language-model head still score the same.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Accuracy and calibration don\u2019t measure everything a decision model should do, so we ran three more experiments. Each one changes the request in a way that shouldn\u2019t change the answer, then measures how much the answers move:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Order:<\/strong> list each choice question\u2019s options in reverse.<\/li>\n\n\n\n<li><strong>Irrelevant option:<\/strong> add an option that has nothing to do with the decision.<\/li>\n\n\n\n<li><strong>Isolation:<\/strong> ask each question on its own instead of all five together.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">We measure each shift as the total variation distance between the two answer distributions, from 0 (identical) to 1 (no overlap).<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Model<\/strong><\/td><td><strong>Order: top answer changed<\/strong><\/td><td><strong>Order: shift<\/strong><\/td><td><strong>Irrelevant: probability taken<\/strong><\/td><td><strong>Irrelevant: shift<\/strong><\/td><td><strong>Isolation: shift<\/strong><\/td><\/tr><tr><td>Zero-shot baseline<\/td><td>30%<\/td><td>0.19<\/td><td>3%<\/td><td>0.04<\/td><td>0<\/td><\/tr><tr><td>Fine-tuned baseline<\/td><td>5%<\/td><td>0.08<\/td><td>1%<\/td><td>0.02<\/td><td>0<\/td><\/tr><tr><td>Baseline with our loss<\/td><td>9%<\/td><td>0.06<\/td><td>4%<\/td><td>0.03<\/td><td>0<\/td><\/tr><tr><td>Decision model<\/td><td>8%<\/td><td>0.07<\/td><td>5%<\/td><td>0.03<\/td><td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Option order has the largest effect. When the options are reversed, the untrained baseline changes its top answer on 30% of choice questions. In one customer-service case, the model is asked what the conversation is about. With the options listed alphabetically, it puts 0.96 on \u201ctechnical.\u201d With the options reversed, \u201ctechnical\u201d drops to 0.25 and \u201caccount\u201d rises to 0.65. The trained models, which saw the options in shuffled order during training, change their answer on 5 to 9% of questions: far less, but not zero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An irrelevant option has little effect on any model. It takes 1 to 5% of the probability and moves the other options by 2 to 4 points.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Isolation is 0 for every model, including the untrained one, because the attention mask makes it impossible for one question to affect another. For our models, the test confirms that the design works. It\u2019s more useful for models whose internals you can\u2019t inspect.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"228\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3-1024x228.png\" alt=\"Opik experiment comparison of order_flip and order_shift. Zero-shot baseline: 0.3 and 0.19. Decision head: 0.08 and 0.07. Baseline with the new loss: 0.09 and 0.06. Fine-tuned baseline: 0.05 and 0.08.\" class=\"wp-image-20808\" style=\"aspect-ratio:4.489208633093525\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3-1024x228.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3-300x67.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3-765x170.png 765w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3-1536x341.png 1536w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-3.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>The order test in Opik: how often each model\u2019s top answer changed (order_flip) and how far its probabilities moved (order_shift) when choice options were listed in reverse.<\/em><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"569\" src=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4-1024x569.png\" alt=\"Opik trace of one isolation-test case. One evaluation task contains six predict spans, one answering all five questions together and five answering one question each, followed by a metrics step. The input panel shows the gold probability distributions.\" class=\"wp-image-20809\" style=\"aspect-ratio:1.798270893371758\" srcset=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4-1024x569.png 1024w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4-767x426.png 767w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4-300x167.png 300w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4-1536x853.png 1536w, https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/image-4.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>A single case from the isolation test, traced in Opik. The first<\/em> <em>predict<\/em> <em>call answers all five questions together; the next five each answer one question on its own. The metric step then compares the two sets of answers.<\/em><\/figcaption><\/figure>\n\n\n\n<h2 id=\"h-what-this-shows\" class=\"wp-block-heading\">What this shows<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A decision model is only useful if its probabilities mean what they say. In these experiments, that depended on how the model was trained, not on its architecture. Fine-tuned the standard way, on a single correct label per question, the model was overconfident (ECE 0.12 on the test set). Trained on the full gold distribution instead, its ECE was 0.02, with or without a dedicated decision head. Packing all five questions into one pass roughly halved the cost without changing any answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Training didn\u2019t fix everything, though. When we listed the answer options in reverse order, the trained models still changed their top answer on 5 to 9% of questions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The code, configs, and every run are public: <a href=\"https:\/\/github.com\/anmorgan24\/decision-model\">the repo<\/a>, the <a href=\"https:\/\/www.comet.com\/anmorgan24\/decision-model\">Comet project<\/a>, and the <a href=\"https:\/\/www.comet.com\/opik\/anmorgan24\/projects\/01a15f6c-3276-73fa-8d99-f5c58ddb1c30\/experiments?groups=%5B%5D\">Opik project<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Decision models drew wide attention in September, when TypeSafe AI launched Jev, a model that returns probabilities over a fixed set of answers instead of generating text. Open-source alternatives followed almost immediately: Latent.Space counted six in two days. Most writing about decision models since then defines them by their interface, which uses some sort of [&hellip;]<\/p>\n","protected":false},"author":22,"featured_media":20841,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"customer_name":"","customer_description":"","customer_industry":"","customer_technologies":"","customer_logo":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6,7],"tags":[40,71,52],"coauthors":[133],"class_list":["post-20803","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-machine-learning","category-tutorials","tag-comet","tag-language-models","tag-llm"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Train your own decision model - Comet<\/title>\n<meta name=\"description\" content=\"What makes a decision model different from an LLM with structured output? Build one, track it in Comet, and test whether its probabilities hold up in Opik\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Train your own decision model\" \/>\n<meta property=\"og:description\" content=\"What makes a decision model different from an LLM with structured output? Build one, track it in Comet, and test whether its probabilities hold up in Opik\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/\" \/>\n<meta property=\"og:site_name\" content=\"Comet\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/cometdotml\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-26T20:07:13+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-26T20:17:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Abby Morgan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@anmorgan2414\" \/>\n<meta name=\"twitter:site\" content=\"@Cometml\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Abby Morgan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Train your own decision model - Comet","description":"What makes a decision model different from an LLM with structured output? Build one, track it in Comet, and test whether its probabilities hold up in Opik","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/","og_locale":"en_US","og_type":"article","og_title":"Train your own decision model","og_description":"What makes a decision model different from an LLM with structured output? Build one, track it in Comet, and test whether its probabilities hold up in Opik","og_url":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/","og_site_name":"Comet","article_publisher":"https:\/\/www.facebook.com\/cometdotml","article_published_time":"2026-10-26T20:07:13+00:00","article_modified_time":"2026-10-26T20:17:25+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","type":"image\/png"}],"author":"Abby Morgan","twitter_card":"summary_large_image","twitter_creator":"@anmorgan2414","twitter_site":"@Cometml","twitter_misc":{"Written by":"Abby Morgan","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#article","isPartOf":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/"},"author":{"name":"Abby Morgan","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/826ee39a2e30cf9d8d73155de09bb7b2"},"headline":"Train your own decision model","datePublished":"2026-10-26T20:07:13+00:00","dateModified":"2026-10-26T20:17:25+00:00","mainEntityOfPage":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/"},"wordCount":3374,"commentCount":0,"publisher":{"@id":"https:\/\/www.comet.com\/site\/#organization"},"image":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#primaryimage"},"thumbnailUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","keywords":["Comet","Language Models","LLM"],"articleSection":["Machine Learning","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/","url":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/","name":"Train your own decision model - Comet","isPartOf":{"@id":"https:\/\/www.comet.com\/site\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#primaryimage"},"image":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#primaryimage"},"thumbnailUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","datePublished":"2026-10-26T20:07:13+00:00","dateModified":"2026-10-26T20:17:25+00:00","description":"What makes a decision model different from an LLM with structured output? Build one, track it in Comet, and test whether its probabilities hold up in Opik","breadcrumb":{"@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#primaryimage","url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","contentUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","width":1280,"height":720,"caption":"Train Your Own Decision Model: a shared state feeding several questions, each producing a probability distribution over its answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.comet.com\/site\/blog\/train-a-decision-model\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.comet.com\/site\/"},{"@type":"ListItem","position":2,"name":"Train your own decision model"}]},{"@type":"WebSite","@id":"https:\/\/www.comet.com\/site\/#website","url":"https:\/\/www.comet.com\/site\/","name":"Comet","description":"Build Better Models Faster","publisher":{"@id":"https:\/\/www.comet.com\/site\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.comet.com\/site\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.comet.com\/site\/#organization","name":"Comet ML, Inc.","alternateName":"Comet","url":"https:\/\/www.comet.com\/site\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/#\/schema\/logo\/image\/","url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2025\/01\/logo_comet_square.png","contentUrl":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2025\/01\/logo_comet_square.png","width":310,"height":310,"caption":"Comet ML, Inc."},"image":{"@id":"https:\/\/www.comet.com\/site\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/cometdotml","https:\/\/x.com\/Cometml","https:\/\/www.youtube.com\/channel\/UCmN63HKvfXSCS-UwVwmK8Hw"]},{"@type":"Person","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/826ee39a2e30cf9d8d73155de09bb7b2","name":"Abby Morgan","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.comet.com\/site\/#\/schema\/person\/image\/dbbf1ae921ee179c768f508340415946","url":"https:\/\/secure.gravatar.com\/avatar\/28d4934d14261b4afe12e226f0eaa57c4fb0c2761ad4586eb9a5bec3b8160bc9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/28d4934d14261b4afe12e226f0eaa57c4fb0c2761ad4586eb9a5bec3b8160bc9?s=96&d=mm&r=g","caption":"Abby Morgan"},"description":"AI\/ML Growth Engineer @ Comet","sameAs":["https:\/\/www.comet.com\/","https:\/\/www.linkedin.com\/in\/anmorgan24\/","https:\/\/x.com\/anmorgan2414"],"url":"https:\/\/www.comet.com\/site\/blog\/author\/abigailmcomet-com\/"}]}},"jetpack_featured_media_url":"https:\/\/www.comet.com\/site\/wp-content\/uploads\/2026\/10\/decision-models-featured.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/20803","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/users\/22"}],"replies":[{"embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/comments?post=20803"}],"version-history":[{"count":3,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/20803\/revisions"}],"predecessor-version":[{"id":20844,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/posts\/20803\/revisions\/20844"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/media\/20841"}],"wp:attachment":[{"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/media?parent=20803"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/categories?post=20803"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/tags?post=20803"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.comet.com\/site\/wp-json\/wp\/v2\/coauthors?post=20803"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}