Reflex

Modelless decision engine · runs on localhost

Decisions in microseconds.
Honest when it doesn't know.

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.

TL;DRloading the measured numbers from data/bench.json…

loading the averaged numbers from data/bench.json…

What leaves your machine
  • ✓ Nothing, by default. The engine listens on 127.0.0.1:7331 only, accepts the one browser origin you allow, and sends no telemetry — no usage reports, no crash reports.
  • ↗ One optional upload: the rulebook demo's Ask Cloudflare button on the playground sends the painted board to a stateless Worker (no storage) — disclosed there; Ask this tab never leaves the tab.
  • ↙ One optional download: enabling the laya comparison lane fetches its Apache-2.0 weights from Hugging Face at first use, SHA-256-verified. A download — nothing of yours is sent.
  • ✓ Your documents, labels and question text stay in process memory on your machine; the corpus you build is a local file you can read, diff and delete.

How it works

Embed → route to your corpus → score options → calibrate → answer or abstain. Thresholds are fitted offline from your labeled data.

The Reflex decision flow, numbered along the path in four lanes. 1 your question — a state plus typed questions (choice, score or yes-no) on your machine. 2 embed and route: hashed features, the domain argmax picks your corpus. 3 score every option — corpus-is-the-model. 4 calibrate: a sigmoid gate turns the signal into confidence. If there is signal, 5a answers with one pick per question plus calibrated confidence; if not, 5b abstains — declines rather than guesses, the full distribution still riding. 6 loops back to the calibrator: thresholds refitted offline, one-off, from your own labeled data.
Runs on 127.0.0.1:7331 only. Numbered steps in path order; lanes group the stages. Write-up: decision_flow.md.
Step by step: what goes in and what comes out
  1. 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. 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. 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. 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.

  5. 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)

  6. 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)

  7. 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)

The corpus is the model.

Coming from LLMs? There is no training run. You write documents; a build step fits the rest.

REFLEX · MODELLESS — the product

Knowledge lives in documents.

  1. Build: write docs per domain, label a small slice, refit — no GPU.
  2. Serve: µs answers, or abstain when the question is off-corpus.
  3. Update: edit a doc, rebuild — bit-identical, reviewable in a PR.
LAYA · OPEN WEIGHTS — the comparison

Knowledge lives in weights.

  1. Build: someone else's training run (322M–421M parameters).
  2. Serve: one forward pass per question; always answers.
  3. Update: gather data and retrain — or prompt around it.
Compare side by side
axisReflex · modellesslaya · open weights
Where knowledge livesdocuments you can read, diff, review (any fitted heads are rebuilt from them)322M–421M weights nobody reads
New domainauthor docs + refit from a labeled slice (a build step)zero-shot from the base — or train a new specialist checkpoint
Updatingedit a document and rebuild — deterministicgather data, retrain — or prompt around it
Abstaina designed output: distance gate + calibrated confidencenot a primitive — it always answers; thresholding is the caller's job
CalibrationPlatt-style 2-parameter refit, audited (G1)raw head probabilities
Determinismgated — cross-host bit-identity enforced at publishrepeat runs have matched — reproduced, not claimed
Breadthbounded by the corpuspretraining carries zero-shot breadth
Off-corpusabstains, distribution still attachedanswers anyway
Deploy footprintKB–MB of corpus textGB-scale weights, downloaded at runtime (measured below)
Where does the learning happen?

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.

Is this RAG? k-NN?

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.

When should I use which?

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.

Where does the game "grammar" live?

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.

Three ways to get a decision

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.

axisReflex · self-hosted, modellessLLM API · general chat modelHosted decision model · Jev-style
Price unitfree after install — you author documents, we publish the engineper token, at the vendor's rate cardper decision or subscription, at the host's pricing
Latencyin-process, no network — measured p50s on the bencha network round-trip plus generation timea model forward on the host plus network
Your datastays on your machine — loopback bind, no telemetryleaves with every promptleaves with every question
Knows when not to answeryes — abstain is a designed, calibrated outputno — it always answers; thresholds are yours to buildvaries by host — not a primitive you control
New domainauthor documents, label a small slice, refit — deterministic, no GPUwins: prompt it, works immediately, nothing to integratesend the host your labeled decisions
Breadthbounded by your corpus — off-corpus abstainswins: pretraining covers what you never wrote downbounded by what it was trained to decide
Updatingedit a document, rebuild — reviewable in a PRvendor retrains; you re-prompthost retrains on your outcomes
LicenceMIT engine — you can read, audit and fork every linevendor terms of serviceclosed weights, host terms
Measured herethe whole board, losses includednot on this board — bring your own vendor billthe 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.

What each lane costs on disk

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…

Start here

Playground

Ask your own engine a question. Nothing is uploaded.

Open the playground

Arena

Tetris, Flappy and three-lanes — every lane on the same seeded game.

Watch the games

Benchmark

Every suite, every lane, losses published.

Audit the tables

Need more than the floor?

Where Reflex abstains, Rethink answers — a hosted trained head, paid only for those questions.

Meet Rethink

For agents

One skill file teaches your coding agent to use Reflex.

Install the skill

Give your coding agent a decision engine.

One file: the wire, questions that work, thresholds from your data, abstain as a fallback path.

Claude Codemkdir -p .claude/skills/reflex-integration && curl -fsSL https://reflex.riir.ai/skills/reflex-integration/SKILL.md -o .claude/skills/reflex-integration/SKILL.md
Zed / any agentmkdir -p .agents/skills/reflex-integration && curl -fsSL https://reflex.riir.ai/skills/reflex-integration/SKILL.md -o .agents/skills/reflex-integration/SKILL.md
Which agents does it work with?

Claude 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.

What does it change?

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.

Install

One static binary, checksum-verified. reflex --version prints its feature set.

macOS / Linuxcurl -fsSL https://raw.githubusercontent.com/gist-rs/reflex/main/install.sh | sh
Windowsiwr -useb https://raw.githubusercontent.com/gist-rs/reflex/main/install.ps1 | iex
Homebrewbrew 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)
Scoopscoop bucket add gist-rs https://github.com/gist-rs/scoop-bucket && scoop install riir-reflex — Windows

Ask it

Run 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.

It answers

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.

Request
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?"}
  ]
}'
Response · HTTP 200
{
  "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}
}

It abstains

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.

Request
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"]
    }
  ]
}'
Response · HTTP 200
{
  "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}
}

Serve your own corpus

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.

Request
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"]
    }
  ]
}'
Response · HTTP 200
{
  "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}
}

Your first corpus in 5 minutes

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"]}]}'
Write your own domains the same way — a .md file per domain, plain text. A corpus with no domain loading cleanly refuses to boot with the reason, never a silent fallback.

A worked example: ticket triage

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.

Ticket 1 — the engine decides

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.

Request
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"]
    }
  ]
}'
Response · HTTP 200
{
  "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}
}

Ticket 2 — the engine abstains, you escalate

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.

Request
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"]
    }
  ]
}'
Response · HTTP 200
{
  "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}
}

Your code, your policy

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.

Notes & sources

Gates, claims and where each is proven
  • Every number above the fold (TL;DR, chart, G1 badge) is computed in your browser from data/bench.json, which is generated by the open harness — never hand-typed. publish_bench.py
  • G1 calibration — the fitted confidence must beat both the raw readout and the conformal-naive floor. It is not an accuracy gate; suites whose calibration window is too thin make no claim. per-suite verdicts Angelopoulos & Bates, conformal prediction (2021)
  • G2 latency (p99 per decision set) and G4 zero allocations on the hot path are GOAT-gated in the engine repo. decision_set_goat.rs gate table
  • G5 laya parity — our Rust port must match the reference checkpoints' top-1 answers (≥ 99.9%) within 1e-3 probability drift. laya_riir_parity.rs
  • laya weights are never redistributed. The optional comparison lane loads Apache-2.0 checkpoints (ModernBERT, mmBERT, mask-scoring head) from Hugging Face at runtime, SHA-256-verified; their license ships in every release's THIRD_PARTY_LICENSES.md. brainfunctioncollapse laya
  • Privacy: the engine listens on 127.0.0.1:7331, accepts one allowed browser origin, and sends no telemetry.
  • The skill-section pattern is borrowed with thanks from the laya page; the file itself is written fresh against Reflex.
  • Not affiliated with, or endorsed by, TypeSafe AI; Jev is their product, named only to compare against.