Knowledge lives in documents.
- Build: write docs per domain, label a small slice, refit — no GPU.
- Serve: µs answers, or abstain when the question is off-corpus.
- Update: edit a doc, rebuild — bit-identical, reviewable in a PR.
Modelless decision engine · runs on localhost
Ask choice, score or yes/no — get an answer with calibrated confidence, or an abstain instead of a guess. No neural network to download — the only fitted numbers are small thresholds and heads rebuilt from your own documents and labels. Nothing leaves your machine.
loading the averaged numbers from data/bench.json…
127.0.0.1:7331 only,
accepts the one browser origin you allow, and sends no telemetry — no usage reports, no crash reports.Embed → route to your corpus → score options → calibrate → answer or abstain. Thresholds are fitted offline from your labeled data.
127.0.0.1:7331 only. Numbered steps in path order;
lanes group the stages. Write-up:
decision_flow.md.1 · You ask
Your app sends a state plus typed questions over loopback — choice, score or yes-no. Nothing leaves your machine.
IN · POST /decide
{
"state": "The piece leaves no holes under it on the left edge, sits flat on the surface, and the stack stays low.",
"questions": [
{
"id": "clean",
"kind": "noul",
"prompt": "Does the stack look clean?"
}
]
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /decide (answered request)
2 · Embed and route
The state is hashed into features and the domain argmax picks your corpus — the corpus IS the model here; no neural weights, no AI service.
3 · Score every option
Each option is scored against the corpus: the drafter's compressed-length delta blended with the query's cosine to the option's corpus centroid — the cosine is the signal that ranks.
4 · Calibrate
A sigmoid gate turns the signal into confidence. The calibrator starts bit-identical to the raw readout and refits only from outcome evidence you supply — offline, one-off, yours.
5a · Signal — the answer
With signal, one pick per question goes back with calibrated confidence and the full distribution riding beside it. This game-head question is one the engine is sure on.
IN · POST /decide
{
"state": "The piece leaves no holes under it on the left edge, sits flat on the surface, and the stack stays low.",
"questions": [
{
"id": "clean",
"kind": "noul",
"prompt": "Does the stack look clean?"
}
]
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /decide (answered request)
OUT · sure: outcome yes
{
"answers": [
{
"question_id": "clean",
"outcome": {
"noul": {
"yes": true
}
},
"probabilities": [0.51943666],
"confidence": 0.51943666
}
],
"routing": {
"lane": "modelless",
"reason": "game-head/tetris (corpus-fitted, decoded arm)"
},
"calibration": {
"method": "none",
"temperature": 1.0
}
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /decide (answered response)
5b · No signal — the honest abstain
Off-corpus or low confidence yields outcome null: the engine declines instead of guessing, and the full distribution still rides for risk-coverage analysis. This kitchen question is nowhere near the corpus — the distance gate catches it.
IN · POST /decide · off-corpus
{
"state": "My sourdough starter stopped rising after I moved it to a colder kitchen.",
"questions": [
{
"id": "route",
"kind": "choice",
"prompt": "Route this ticket to the team that owns it.",
"options": [
"deploy-ops",
"billing-support"
]
}
]
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /decide (off_corpus request)
OUT · not sure: outcome null
{
"answers": [
{
"question_id": "route",
"outcome": null,
"probabilities": [0.52577484, 0.47422513],
"confidence": 0.0019177794
}
],
"routing": {
"lane": "modelless",
"reason": "modelless corpus routing: ops=0 support=1; fused abstain (score+distance) armed"
},
"calibration": {
"method": "none",
"temperature": 1.0
}
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /decide (off_corpus response)
6 · Refit from your data
Thresholds are fitted offline from labeled outcomes — never telemetry. The feedback wire is where outcome labels come back: report whether a past answer was right, and the engine recalibrates once it has enough reports.
IN · POST /feedback · an outcome label
{
"p": 0.51943666,
"outcome": true
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /feedback (feedback request)
OUT · not enough reports yet — no refit
{
"refit": false
}captured from the reflex 0.2.4 release binary, 2026-10-03 · POST /feedback (feedback response)
Coming from LLMs? There is no training run. You write documents; a build step fits the rest.
| axis | Reflex · modelless | laya · open weights |
|---|---|---|
| Where knowledge lives | documents you can read, diff, review (any fitted heads are rebuilt from them) | 322M–421M weights nobody reads |
| New domain | author docs + refit from a labeled slice (a build step) | zero-shot from the base — or train a new specialist checkpoint |
| Updating | edit a document and rebuild — deterministic | gather data, retrain — or prompt around it |
| Abstain | a designed output: distance gate + calibrated confidence | not a primitive — it always answers; thresholding is the caller's job |
| Calibration | Platt-style 2-parameter refit, audited (G1) | raw head probabilities |
| Determinism | gated — cross-host bit-identity enforced at publish | repeat runs have matched — reproduced, not claimed |
| Breadth | bounded by the corpus | pretraining carries zero-shot breadth |
| Off-corpus | abstains, distribution still attached | answers anyway |
| Deploy footprint | KB–MB of corpus text | GB-scale weights, downloaded at runtime (measured below) |
In small, deterministic, replayable fits — not a training run. Confidence calibration is a
2-parameter Platt-style solve; the two abstain thresholds are percentiles of your calibration
slice; optional per-label linear heads are fitted at build time by a short fixed-order SGD. All
replay bit-identically. A /feedback endpoint can refit calibration from outcomes.
Closest cousins: nearest-centroid routing and retrieval-flavored scoring. The twist is the scorer — an LZ4 drafter treats the corpus as a compression dictionary, so "how many bytes does this option add" is a predictability signal with no learned parameters. The cosine term does most of the ranking.
Modelless fits a typed answer space in a domain you can describe — routing, triage, workflow decisions. Trained models win on broad knowledge you can't author. They compose: an abstain is a signal to escalate to a model only when it's worth it.
The arena's game heads write state and options as closed-grammar sentences so every answer decodes back to exact features. That is a convention of the game lanes, not the engine — text domains just use their documents.
The buyer's question, answered axis by axis: a self-hosted modelless engine, a general LLM API call, or a hosted decision model (the Jev class — how that works). Cells we can measure link the bench; what we cannot measure, we say so. Losses stay visible.
| axis | Reflex · self-hosted, modelless | LLM API · general chat model | Hosted decision model · Jev-style |
|---|---|---|---|
| Price unit | free after install — you author documents, we publish the engine | per token, at the vendor's rate card | per decision or subscription, at the host's pricing |
| Latency | in-process, no network — measured p50s on the bench | a network round-trip plus generation time | a model forward on the host plus network |
| Your data | stays on your machine — loopback bind, no telemetry | leaves with every prompt | leaves with every question |
| Knows when not to answer | yes — abstain is a designed, calibrated output | no — it always answers; thresholds are yours to build | varies by host — not a primitive you control |
| New domain | author documents, label a small slice, refit — deterministic, no GPU | wins: prompt it, works immediately, nothing to integrate | send the host your labeled decisions |
| Breadth | bounded by your corpus — off-corpus abstains | wins: pretraining covers what you never wrote down | bounded by what it was trained to decide |
| Updating | edit a document, rebuild — reviewable in a PR | vendor retrains; you re-prompt | host retrains on your outcomes |
| Licence | MIT engine — you can read, audit and fork every line | vendor terms of service | closed weights, host terms |
| Measured here | the whole board, losses included | not on this board — bring your own vendor bill | the Jev-class reference lanes |
They compose instead of competing: Reflex answers what your corpus covers, abstains on the rest, and the abstain is a clean hand-off — to a human, or to a lane you pay for only when it runs. The worked example below shows the flow end to end.
Measured bytes — rust env in ember, python env in blue, model weights in green; where a model stack is published (encoder checkpoint · trained specialists · trained heads) the model part splits into its own colors. Smallest first; click a bar for the full breakdown. Generated into data/sizes.json.
loading the measured footprints from data/sizes.json…
Where Reflex abstains, Rethink answers — a hosted trained head, paid only for those questions.
Meet RethinkOne file: the wire, questions that work, thresholds from your data, abstain as a fallback path.
mkdir -p .claude/skills/reflex-integration && curl -fsSL https://reflex.riir.ai/skills/reflex-integration/SKILL.md -o .claude/skills/reflex-integration/SKILL.mdmkdir -p .agents/skills/reflex-integration && curl -fsSL https://reflex.riir.ai/skills/reflex-integration/SKILL.md -o .agents/skills/reflex-integration/SKILL.mdClaude Code reads .claude/skills/; Zed reads .agents/skills/. Any agent
that takes a markdown instruction file works — it is plain instructions plus exact HTTP calls.
Without it, an agent asked to classify or route reaches for an LLM call. With it, the agent starts a local engine, asks typed questions, fits thresholds from your labeled data, handles abstain as a fallback — and reports measured accuracy instead of a guess.
One static binary, checksum-verified. reflex --version prints its feature set.
curl -fsSL https://raw.githubusercontent.com/gist-rs/reflex/main/install.sh | shiwr -useb https://raw.githubusercontent.com/gist-rs/reflex/main/install.ps1 | iexbrew tap gist-rs/tap && brew trust gist-rs/tap && brew install riir-reflex — macOS + Linux; installs the reflex command (Homebrew 6+ loads third-party tap formulas only after brew trust)scoop bucket add gist-rs https://github.com/gist-rs/scoop-bucket && scoop install riir-reflex — WindowsRun reflex, then send it JSON over HTTP from any language. No SDK needed.
These are real bytes from the release binary, recorded by a script and never typed by hand.
The full reference is on the API page.
The Tetris head answers a yes/no question (noul on the wire) about a board spot.
The heads ship as signed vessels you mint once (reflex mint-heads — one command, deterministic);
the engine loads them at boot from the directory you point it at.
curl -s -X POST http://127.0.0.1:7331/decide \
-H 'Content-Type: application/json' \
-d '{
"state": "The piece leaves no holes under it on the left edge, sits flat on the surface, and the stack stays low.",
"questions": [
{"id": "clean", "kind": "noul", "prompt": "Does the stack look clean?"}
]
}'{
"answers": [
{
"question_id": "clean",
"outcome": {"noul": {"yes": true}},
"probabilities": [0.51943666],
"confidence": 0.51943666
}
],
"routing": {
"lane": "modelless",
"reason": "game-head/tetris (corpus-fitted, decoded arm)"
},
"calibration": {"method": "none", "temperature": 1.0}
}The built-in demo corpus is deliberately tiny, so a mixed real-world ticket abstains
("outcome": null) rather than guess. Abstaining is an answer, not an error. The probabilities still come back,
so your code can apply its own threshold or hand the case to a fallback.
curl -s -X POST http://127.0.0.1:7331/decide \
-H 'Content-Type: application/json' \
-d '{
"state": "The staging deploy of release candidate 4.2 finished but the error budget is down to 12 percent and two health endpoints are flapping after the rollout. Rollback is one command. Decide what happens next.",
"questions": [
{
"id": "route",
"kind": "choice",
"prompt": "Route this ticket to the team that owns it.",
"options": ["deploy-ops", "billing-support"]
},
{
"id": "promote",
"kind": "noul",
"prompt": "Should the rollout be promoted to production right now?"
},
{
"id": "severity",
"kind": "score",
"prompt": "How severe is this situation?",
"options": ["routine", "needs-attention", "critical"]
}
]
}'{
"answers": [
{
"question_id": "route",
"outcome": null,
"probabilities": [0.52583224, 0.47416782],
"confidence": 0.0019263625
},
{
"question_id": "promote",
"outcome": null,
"probabilities": [0.49452087],
"confidence": 0.00008660555
},
{
"question_id": "severity",
"outcome": null,
"probabilities": [0.3601853, 0.2878931, 0.35192165],
"confidence": 0.004379213
}
],
"routing": {
"lane": "modelless",
"reason": "modelless corpus routing: ops=3 support=0; fused abstain (score+distance) armed; drafter-only=1"
},
"calibration": {"method": "none", "temperature": 1.0}
}Point RIIR_REFLEX_CORPUS at a directory — one <domain>.md file (or a folder of them)
per domain — and the engine boots on it: in-corpus questions answer, off-corpus questions abstain.
These bytes are from the sample corpus you can download below.
curl -s -X POST http://127.0.0.1:7331/decide \
-H 'Content-Type: application/json' \
-d '{
"state": "our deploy regressed the error budget after the rollout — run the rollback and verify the health endpoints",
"questions": [
{
"id": "route",
"kind": "choice",
"prompt": "Which runbook applies?",
"options": ["billing", "deploy", "onboarding"]
}
]
}'{
"answers": [
{
"question_id": "route",
"outcome": {"choice": {"index": 1}},
"probabilities": [0.3341486, 0.3407357, 0.32511574],
"confidence": 0.00016820431
}
],
"routing": {
"lane": "modelless",
"reason": "modelless corpus routing: billing=0 deploy=1 onboarding=0; fused abstain (score+distance) armed"
},
"calibration": {"method": "none", "temperature": 1.0}
}Download the sample corpus (3 domains, 8 documents), then:
tar xzf first-corpus.tar.gz
RIIR_REFLEX_CORPUS=first-corpus reflex
curl -s -X POST http://127.0.0.1:7331/decide -d '{"state":"our deploy regressed the error budget — run the rollback","questions":[{"id":"route","kind":"choice","prompt":"Which runbook applies?","options":["billing","deploy","onboarding"]}]}'.md file per domain, plain text. A corpus with no
domain loading cleanly refuses to boot with the reason, never a silent fallback.
The same engine, the same wire, your own corpus — this is the whole product in one scroll. Two support tickets go in: one the corpus covers, one no runbook owns. Real bytes from the release binary on the sample corpus — nothing hosted, nothing typed by hand.
A duplicate charge on the annual plan. The billing runbook covers exactly this — check the payment history, refund the second charge — so the engine routes it, and your policy can apply the runbook and close the ticket without a human. The decision is the routing; the automation is yours.
curl -s -X POST http://127.0.0.1:7331/decide \
-H 'Content-Type: application/json' \
-d '{
"state": "A customer was charged twice for the annual plan this morning and wants the duplicate charge refunded. The invoice shows two identical line items.",
"questions": [
{
"id": "route",
"kind": "choice",
"prompt": "Which runbook applies?",
"options": ["billing", "deploy", "onboarding"]
}
]
}'{
"answers": [
{
"question_id": "route",
"outcome": {"choice": {"index": 0}},
"probabilities": [0.3503429, 0.33383024, 0.3158269],
"confidence": 0.0008147955
}
],
"routing": {
"lane": "modelless",
"reason": "modelless corpus routing: billing=1 deploy=0 onboarding=0; fused abstain (score+distance) armed"
},
"calibration": {"method": "none", "temperature": 1.0}
}A legal threat mixed with a login defect. No runbook owns that combination, and the
probabilities come back near-tied with "outcome": null — the engine refuses to guess.
That refusal is the product: this is the ticket a pure classifier would have silently mis-routed.
curl -s -X POST http://127.0.0.1:7331/decide \
-H 'Content-Type: application/json' \
-d '{
"state": "A customer says they will contact a lawyer over a double charge unless someone calls them back today, and also mentions their login stopped working after the update.",
"questions": [
{
"id": "route",
"kind": "choice",
"prompt": "Which runbook applies?",
"options": ["billing", "deploy", "onboarding"]
}
]
}'{
"answers": [
{
"question_id": "route",
"outcome": null,
"probabilities": [0.30211508, 0.3375039, 0.36038098],
"confidence": 0.0023708344
}
],
"routing": {
"lane": "modelless",
"reason": "modelless corpus routing: billing=0 deploy=0 onboarding=1; fused abstain (score+distance) armed"
},
"calibration": {"method": "none", "temperature": 1.0}
}An abstain is a branch, not an error. Route the outcomes the engine gives you; escalate the rest to whoever owns judgement — a human queue today, a hosted lane when the volume justifies it.
ans = POST /decide (ticket text, questions)
for q in ans.answers:
if q.outcome is not null: # the corpus covered it
apply(q.outcome) # billing → verify the double charge, refund it, close
else: # off-corpus or mixed — the engine declined
escalate(ticket) # human queue; or a paid lane, per your policy
POST /feedback (q.confidence, human's final outcome) # refits calibration
# from real resolutions
The last line is the loop: when a human resolves an escalated ticket, the outcome refits the calibration slice — the thresholds tighten against reality, and more tickets take the decided path next week. No training run, no GPU; a build step.
127.0.0.1:7331, accepts one allowed browser origin, and sends no telemetry.