Results
Accuracy in one forward pass on the full test sets, with the same prompt and readout for both
models.
Table with columns: benchmark, Qwen3.5-9B, Tacit-9B, difference| benchmark | Qwen3.5-9B | Tacit-9B | difference |
|---|
| JevBench (public set, 231) | 0.805 | 0.823 | +1.7 pts |
| bev-decision (test split, 46,320) | 0.680 | 0.725 | +4.5 pts |
Usage
from transformers.dynamic_module_utils import get_class_from_dynamic_module Tacit = get_class_from_dynamic_module("tacit_decision.Tacit", "morriszjm/Tacit-9B")# one forward per decisiontacit = Tacit.from_pretrained("morriszjm/Tacit-9B")# or: send low-confidence decisions to the model's own reasoningtacit = Tacit.from_pretrained("morriszjm/Tacit-9B", adaptive=True, tau=0.5, max_cot_share=0.2)# or: the same on vLLM (pip install vllm), for throughputtacit = Tacit.from_pretrained("morriszjm/Tacit-9B", engine="vllm", adaptive=True) d = tacit.decide(state="Customer: my package was due last Monday and it still has not arrived.", question="What does the customer want?", options=["track_order", "cancel_order", "refund", "change_address"])print(d["answer"], d["probs"], d["route"]) # route: "one_forward" or "cot"
kind is "choice" (default), "yes_no", or "score" (options are ordered levels, lowest
first); decide_batch([...]) takes a list of such dicts.
Table with columns: argument, default, meaning| argument | default | meaning |
|---|
adaptive | False | send low-confidence decisions to the model's own reasoning (thinking on) |
tau | 0.5 | a decision is low-confidence when the log-probability gap between its top two options is below tau |
max_cot_share | 0.2 | at most this share of the last decisions goes to reasoning; removes the cap |
An escalated decision's answer is read from the label distribution after the reasoning, so it also
comes with probabilities. Without adaptive, nothing is generated. On vLLM the labels are read from
the top 20 log-probabilities of the full vocabulary (a label outside them counts as 0). The checkpoint also loads as a
plain causal LM (AutoModelForCausalLM, vLLM); tacit_decision.py holds the prompt it was trained
with.
Serving with vLLM
vllm serve morriszjm/Tacit-9B --host 127.0.0.1 --port 8000
tacit = Tacit.from_pretrained("morriszjm/Tacit-9B", engine="server", base_url="http://127.0.0.1:8000/v1", adaptive=True)
Only the tokenizer loads on the client. For many clients sharing one cap, run the HTTP gateway that
ships in this repo in front of the server:
hf download morriszjm/Tacit-9B tacit_decision.py --local-dir .python tacit_decision.py serve --model morriszjm/Tacit-9B --upstream http://127.0.0.1:8000/v1 --adaptive --port 8100curl -s 127.0.0.1:8100/v1/decide -H 'Content-Type: application/json' -d '{"state": "Customer: my package has not arrived.", "question": "What does the customer want?", "options": ["track_order", "refund"]}'
On Hopper GPUs vLLM compiles this model's gated-delta-rule kernel with nvcc on first use, so the serving machine needs the CUDA toolkit (engine="vllm" in Python switches to a Triton kernel by itself).
Apache-2.0, as is the base model by the Qwen team.