← ALL POSTS · /BLOG

Cover art for XERJ Query-1: a question compiled into a short program that the engine runs over stored facts
NEW AI MODEL · XERJ QUERY-1 · A NEUROSYMBOLIC PROMPT COMPILER BUILT INTO THE XERJ SEARCH ENGINE · SHIPS IN RC.101

XERJ QUERY-1: A NEW NEUROSYMBOLIC MODEL.
IT COMPILES PROMPTS TO VERIFIABLE SEARCH QUERIES, 400-1,500× CHEAPER THAN RAG AND LLM.

A new way to answer questions over your data: the model writes a small program, the engine runs it over your own facts. No embeddings to build, no GPU to rent, no answer you cannot check. Four CPU cores, offline, and hundreds of times cheaper than RAG or an LLM reading the data.

Retrieval finds documents. XERJ Query-1 turns a prompt into an answer you can check.

On a node started with xerj --facts-model-dir <DIR>, it does six things:

120 msvs2,700–3,300 ms
prompt to answer, XERJ Query-1 on 4 CPU cores on a namespace of up to 8,000 facts vs RAG / Opus 5.5
Ask. It writes the program.
RAG and Opus pipelines took 2.7 to 3.3 s per question on the same set and returned prose you cannot check.
$42vs$13,400–$694,000
per million questions, CPU vs RAG / GraphRAG / Opus reading the data
Runs on your records.
No model is paid per question; the same million questions cost $13,400 to $21,000 on RAG, $65,400 on GraphRAG, up to $694,000 with Opus reading the data.
1vs9–15
wrong of 160: verified or refused vs RAG on Opus 5.5 (Opus reading everything: 5–6)
Verified or refused.
Can't back it: UNKNOWN. Data disagrees: CONFLICT. Never invented.
17 minvs62–97 hours
indexing 10 GB of Wikipedia (3.3 million articles) on CPU vs embedding it with bge-small
No embeddings.
17 minutes and 0.7 CPU-hours to index, searchable at 9.3 ms. Embedding the same text takes 62 to 97 hours of CPU and 8.5 GB of vectors; building a GraphRAG graph would take about 4.7 million model calls (~$251,000).
0 svs19.6 s
re-index after a correction vs re-embedding the 5,000-record test store
A store you can correct.
Fix a fact, the next ask follows; earlier answers replay by revision. An embedding index re-encodes.
$468/monthvs$65,747/month
per month, for a 10 GB example workload of text (about 6.6 million passages) and 1 million questions: one 16-core 32 GB CPU server vs the managed stack
One server instead of a stack.
Facts, memory and brains over 24 MCP tools in one binary, against a vector DB, a graph DB, a cache and an LLM API. The infrastructure alone is close; the gap is the LLM bill on every question.

TRY IT IN ONE PROMPT.

Paste this into your AI agent (Claude Code, Codex, Cursor or any agent with a shell):

Install XERJ with the XERJ Query-1 model from https://xerj.org/llms.txt, start it with --facts-model-dir, write three facts about my project with subject_id, and ask one question about them; show me the program and the evidence it returns.

Or run it yourself:

$ curl -fsSL https://xerj.org/get | sh -s -- --with-model

Check that the model is loaded: curl -s localhost:9200/_facts | jq .model.state should print "loaded". The model is off by default; the installer prints the xerj --facts-model-dir <DIR> line that turns it on.

01·WHY SEARCH NEEDS A NEW AI MODEL · THE PROBLEM IN PLAIN WORDS

WHY DOES SEARCH NEED A NEW AI MODEL?
WHAT IS WRONG WITH LLMS?

Most questions people ask of their own data have one right answer that is already sitting in it. What is this customer's start date. Which of these two rivers carries more water. How many of these works are over 19 centimetres tall. A search engine can find the documents that mention the words. It cannot give you the answer. For the last few years the fix has been to hand the documents to a large language model, an LLM, and let it write the answer.

It works often enough to be tempting. It also has two problems that a better model does not fix. The first is that the answer is generated text. The model writes what seems likely given what it read, and a wrong answer reads exactly like a right one. Nothing in the pipeline checks the sentence against the data. In the head-to-head we report in §07, every pipeline built on Opus 5.5, one of the strongest models available, answered between 5 and 15 of 160 questions wrong. XERJ Query-1 answered one: nine times fewer wrong answers than RAG on Opus 5.5 (1 vs 9 to 15 of 160), five times fewer than Opus reading everything (1 vs 5 to 6).

The second problem is cost. A language model is paid per piece of text it reads and writes, on every question, for every user. The cheapest Opus 5.5 pipeline we measured cost $13 per 1,000 questions at list price; the one that reads the whole data set for each question cost $694. XERJ Query-1 cost $0.042 for the same 1,000 questions on four CPU cores, with no GPU and no network: 300 to 16,000 times cheaper per question. The rest of this post covers how AI search works today and what it costs, what XERJ Query-1 does differently, how we built it, and what we measured.

WHAT WE ARE CLAIMING and what we are not

Today we are introducing XERJ Query-1, a new AI model built into the XERJ search engine. A model that turns a question into a short, checkable program over a search index, inside the engine, that refuses when the data cannot support an answer. The research ideas behind it are decades old and we cite them in §04. What we know of no other search engine shipping is the combination: the model lives in the engine, its programs run over facts written to its store, each with its source, it says UNKNOWN rather than guessing, and it serves offline on four CPU cores in under a gigabyte.

On our standard evaluation, 1,600 questions scored against Opus 5.5 reading the same records under the same frozen prompt, XERJ Query-1 scores 90.29 where the reference defines 100, with 9 wrong answers against the reference's 22. That evaluation is the one the model was developed against, not a fresh set; the head-to-head numbers above are a separate test, and every caveat is in §07.

02·HOW AI SEARCH WORKS TODAY · THE BASELINE, THEN THE FOUR AI METHODS

HOW AI SEARCH WORKS TODAY.
EMBEDDINGS, RAG, GRAPHRAG, AND A MODEL THAT READS EVERYTHING.

Start with the baseline. A classic search engine keeps an index: a table from every word to the documents that contain it, like the index at the back of a book but built for every word at once. When you type a query the engine looks up each word, collects the documents, and ranks them; the usual rule, BM25, scores a document higher when it contains your words often and when those words are rare elsewhere. It is fast and it never answers. Ask "which of these two rivers carries more water" and you get the two reports back, and you do the reading and the comparing yourself.

Embeddings. The first AI step was to turn text into numbers. An embedding model reads a passage and produces a list of a few hundred numbers meant to capture its meaning, so that two passages that say the same thing in different words land close together. Searching embeddings finds "similar meaning" instead of matching words. Think of it as giving every page a coordinate on a map, where nearby pages are about the same thing. The map has to be drawn for every page before you can use it, and redrawn when the model that draws it changes.

RAG. Retrieval-augmented generation is the standard recipe for answering questions with an LLM: search first, then let the model read only the top hits. The data is cut into chunks, each chunk is indexed by words or by embedding, the question pulls the five or ten best chunks, and the model reads those and writes an answer. The analogy is a research assistant who is handed the five most relevant pages and asked to answer from them. The assistant is only as good as the five pages, and still writes the answer from memory of what they read.

GraphRAG. A newer recipe has the model read the whole corpus first and build a knowledge graph: the people, places and things it finds, the relationships between them, and a written summary of each cluster. Questions are then answered from the graph and its summaries. It is like asking the assistant to write an encyclopedia of your data before you ask the first question. The encyclopedia is expensive to write and has to be rewritten when the data changes.

Long context: the model reads everything. The simplest recipe is to put all the data in the prompt and ask. The model reads every token, a token being roughly a word piece, on every question. While it reads it keeps a working memory called the KV cache: a record of every token seen so far, held in GPU memory, one copy per user. It is the model's short-term memory, and it grows with the length of what it reads. This is the brilliant reader with no memory of your files, so you hand over the whole filing cabinet with every question.

Search with an LLM today A question goes to retrieval by keywords or embeddings, the model reads the top hits, and a generated answer comes out. Nothing checks the answer against the data. QUESTIONin plain words RETRIEVALkeywords or embeddings THE MODEL READSthe top hits GENERATEDanswer, as text NOTHING CHECKS THE ANSWER AGAINST THE DATA a wrong answer reads exactly like a right one

Diagram 1 · Search with an LLM today. Retrieval narrows what the model reads; the model still writes the answer from what it read, and the pipeline has no step that checks the sentence against the records.

03·WHAT GENERIC LLM SEARCH COSTS · PER QUESTION, PER CORPUS, PER USER

WHAT GENERIC LLM SEARCH COSTS.
RAG, GRAPHRAG, LONG CONTEXT AND THE KV CACHE, IN DOLLARS.

Here is the bill, on one store of 5,000 facts and the same 160 questions, at list prices: Opus 5.5 at $4 per million tokens read and $20 per million written, CPU at $0.03625 per vCPU-hour. Everything in this chapter is either measured in the head-to-head of §07 or arithmetic from a measured rate, and we say which.

Per 1,000 questions. Classic RAG on Opus 5.5, with five or ten chunks shown per question, cost $13.41 to $20.95. GraphRAG cost $65.39, plus $14.27 to build the graph for those 5,000 facts: 267 model calls, 72 minutes end to end. Opus 5.5 reading the whole store cost $19.41 when 40 questions share one reading, and $694.30 if you ask one question per call, which is what an interactive user does. Opus 5.5 with perfect retrieval, shown exactly the right collection every time, cost $28.84. XERJ Query-1 cost $0.042 of CPU time, measured on a live run.

MethodCost / 1,000 questionsCost / 1,000,000 questions
XERJ Query-1, live CPU run$0.042$42
RAG, embeddings top-5 + Opus 5.5$13.41$13,400
RAG, BM25 top-5 + Opus 5.5$13.83$13,800
Opus 5.5, whole store, 40 q per call$19.41$19,400
RAG, embeddings top-10 + Opus 5.5$19.85$19,900
RAG, BM25 top-10 + Opus 5.5$20.95$21,000
Opus 5.5, perfect retrieval (oracle)$28.84$28,800
GraphRAG + Opus 5.5$65.39$65,400
Opus 5.5, whole store, 1 q per call$694.30$694,000

Sorted by cost per 1,000 questions, cheapest first. Accent marks the best cell in the key column, red the worst. List prices on the same 160 questions; per-million figures are the per-1,000 figures times 1,000, arithmetic. The chart below shows the same column on a log axis.

Cost per 1,000 questions by arm Log-axis bars: XERJ Query-1 0.0416 dollars (projection 0.0052), RAG arms 13 to 21 dollars, Opus whole store 19.41, Opus oracle 28.84, GraphRAG 65.39, Opus one question per call 694.30. US DOLLARS PER 1,000 QUESTIONS, LIST PRICES, LOG AXIS $0.01 $0.1 $1 $10 $100 $1000 XERJ Query-1, live run (4 CPU cores, aarch64) (measured) XERJ Query-1, live run (4 CPU cores, aarch64): $0.0416 per 1,000 questions $0.0416 XERJ Query-1, int8-guard serving config (projection) (projection from 518 CPU-ms/q) XERJ Query-1, int8-guard serving config (projection): $0.0052 per 1,000 questions $0.0052 RAG, embeddings top-5 + Opus 5.5 RAG, embeddings top-5 + Opus 5.5: $13.4100 per 1,000 questions $13.41 RAG, BM25 top-5 + Opus 5.5 RAG, BM25 top-5 + Opus 5.5: $13.8300 per 1,000 questions $13.83 Opus 5.5, whole store, 40 q per call Opus 5.5, whole store, 40 q per call: $19.4100 per 1,000 questions $19.41 RAG, embeddings top-10 + Opus 5.5 RAG, embeddings top-10 + Opus 5.5: $19.8500 per 1,000 questions $19.85 RAG, BM25 top-10 + Opus 5.5 RAG, BM25 top-10 + Opus 5.5: $20.9500 per 1,000 questions $20.95 Opus 5.5, oracle listing Opus 5.5, oracle listing: $28.8400 per 1,000 questions $28.84 GraphRAG local search + Opus 5.5 (plus a $14.27 index build) GraphRAG local search + Opus 5.5: $65.3900 per 1,000 questions $65.39 Opus 5.5, whole store, 1 q per call Opus 5.5, whole store, 1 q per call: $694.3000 per 1,000 questions $694.30 Opus 5.5 at $4 / $20 per MTok in / out, no caching; CPU at $0.03625 per vCPU-hour (c7g.xlarge on-demand); same 160 questions, same 5,000-record store

Chart 3 · Cost per 1,000 questions, list prices, log axis. Opus 5.5 at $4 per million input tokens and $20 per million output tokens, output including thinking, no caching; CPU at $0.03625 per vCPU-hour, the on-demand rate of an AWS c7g.xlarge (4 vCPU, $0.145 per hour) in US East, read from the Vantage mirror of the EC2 price list on 2026-11-05 because the AWS on-demand page did not render its table to our fetch. The dimmed XERJ Query-1 bar is a projection from the int8-guard serving configuration measured on another host (518 CPU-ms per question), not a run on this store. GraphRAG's bar excludes its $14.27 index build. Measured on the same 160 questions; list prices.

Before the first question: embedding the corpus. We measured it at scale. On 10 GB of English Wikipedia, 3.3 million articles from the Hugging Face wikimedia/wikipedia English dump of November 2023, XERJ built its full-text index in 17 minutes and 0.7 CPU-hours on a shared 16-core aarch64 box, and answered keyword queries at 9.3 ms at the median. Embedding the same text with bge-small, at the rates we measured on a 32-thread x86 laptop and on that 16-core box, takes 62 to 97 hours of CPU and leaves 8.5 GB of vectors. Building a GraphRAG graph over it, at our measured cost per text unit, would take about 4.7 million model calls and roughly $251,000 before the first question (arithmetic). The smaller rates behind the same story: We measured the encoder on real 378-token passages with 8 CPU threads: 10.4 passages per second with a small model (bge-small) and 4.5 per second with a base one (bge-base). From those rates, labelled as arithmetic: a million passages takes 27 hours or 62 hours on that box, $7.75 or $17.90 of CPU; a hundred million takes 111 or 257 days, $775 or $1,790. The vectors then have to live in memory: 1.54 or 3.07 GB per million passages, 154 or 307 GB per hundred million, plus about 13 GB of search links. All of it is paid again whenever the embedding model changes.

Per user, while the model reads: the KV cache. From the published configuration of Llama 3.1 70B, with keys and values held in 16-bit, the cache is about 320 KiB per token: around 43 GB of GPU memory for a 128k-token context and about 57 GB to hold this store's 172,870 tokens, per concurrent user. Arithmetic from the configuration, not a measurement; the internals of closed models are unknown. Hosted APIs sell the same thing as prompt caching: writing this store into the cache costs about $0.86 per five-minute window it is kept warm, and each question then pays about $0.035 in cache reads before the answer is written. Our single-question runs did not hit the cache, so the $694.30 above is the uncached case.

A month of running it. We priced a 10 GB example workload, not a size limit: 10 GB of raw text, about 6.6 million passages of 378 tokens, and 1 million questions a month, at list prices fetched on 2026-11-05. On the XERJ side that is one AWS c7g.4xlarge (16 vCPU, 32 GB), sized from our measured 10 GB run: the index was 11.35 GB on disk and indexing peaked at 18.1 GB of RAM. Add the model in RAM and a 30 GB disk: $468 a month. The managed stack holds 6.6 million 384-dimension vectors, about 10 GB plus search links, in Pinecone, a graph of about 2 million entities in Neo4j AuraDB, and a Redis cache, and pays Opus 5.5 for every answer: $65,747 a month with GraphRAG, $21,175 with vector RAG alone. That is 141 times and 45 times. We want to be plain about where the gap comes from. The infrastructure alone is close, and on this sizing the stack's is a little cheaper: $357 a month against $468. The difference is the LLM bill on every question.

Setup, 10 GB example workload, 1M questions a monthWhat it runsMonthly totalvs XERJ
XERJ, one CPU serverEC2 c7g.4xlarge $423.40 + measured compute for 1M questions $42.00 + 30 GB gp3 $2.40$467.801×
Vector-RAG stackPinecone Standard $220.30 + Redis Cloud $5.00 + Opus 5.5 answers $20,950.00$21,175.3045×
GraphRAG stackPinecone Standard $220.30 + Neo4j AuraDB 2 GB $131.40 + Redis Cloud $5.00 + Opus 5.5 answers $65,390.00$65,746.70141×

Sorted by monthly total, cheapest first. Accent marks the cheapest, red the most expensive. List prices fetched 2026-11-05 from each vendor's pricing page and the AWS Price List; 730 hours a month; arithmetic from the stated workload and our measured per-question ledger. One-time builds are not in the monthly totals: on the stack side, embedding the 6.6 million passages costs about $50 (managed API or our own CPU), the first Pinecone upsert $53.96, and building the GraphRAG graph with Opus 5.5 $18,836.40; XERJ needs no graph build and no upsert. Caveats: smallest paid tiers that fit, no replicas or backups on either side, no committed-use, batch or prompt-cache discounts; a different top-k or answer length moves the LLM line linearly.

The $42 is the measured compute for 1 million questions, and it does not grow with the size of the data. More data on XERJ mostly means more disk, at $0.08 per GB-month, because segments are read from disk. On the managed stack the vector and graph tiers grow with the data.

Put together: 300 to 16,000 times cheaper per question ($42 against $13,400 to $694,000 per million). Averaged over the methods you could deploy, XERJ Query-1 is about 410 times cheaper per question than classic RAG on Opus 5.5, 580 times cheaper than Opus 5.5 reading the data, and 1,570 times cheaper than GraphRAG. Hundreds of times, on the same questions, with no graph to build, no corpus to embed, and no cache to keep warm. The accuracy side of the comparison is in §07.

04·MEET THE NEUROSYMBOLIC APPROACH · THE SCIENCE, WHO ALREADY USES IT, AND WHAT IS NEW HERE

MEET THE NEUROSYMBOLIC APPROACH.
PROMPTS BECOME VERIFIABLE, EXECUTABLE PROGRAMS, FOR CHEAP.

Neural networks are very good at reading language and bad at being exact. Symbolic systems, meaning programs, logic and databases, are exact and cannot read language. Neurosymbolic AI puts them together: the network writes something a symbolic system can run and check. That is the one-line definition. The research family goes back decades, and the version XERJ Query-1 belongs to was set out by NS-VQA (Yi et al., 2018), where a network reads a question about a picture, writes a small program, and a symbolic executor runs it to get the answer.

The approach is already in production, in places where a wrong answer is expensive. Amazon's Automated Reasoning checks in Bedrock Guardrails turn a policy document into logic rules and use formal verification to test a model's answer against them, returning findings such as valid, invalid or impossible, rather than asking a second model whether the first one sounded right. Google DeepMind's AlphaProof and AlphaGeometry 2 reached silver-medal standard at the 2024 International Mathematical Olympiad by having a language model propose steps and a symbolic engine or the Lean proof system prove or reject them. Wolfram's tools for chat models have the model write a Wolfram Language query and let a symbolic computation engine produce the number, so the answer does not change when you ask again. These systems verify, prove, or compute. None of them compiles a user's search prompt into a program over a search index, which is what XERJ Query-1 does.

The lineage for that last step is also old, and we want the novelty claim to survive a hostile reader. Mapping language to executable meaning is called semantic parsing. Text-to-SQL does it for databases. Neural Symbolic Machines trained a model to write programs over a knowledge base; program-aided language models have a large model write Python and let an interpreter do the arithmetic; search products offer natural-language querying by sending your question to a hosted model that writes a query string back. What nobody had done, stated as narrowly as we can: put the learned compiler inside the search engine itself, so its programs are pointers into typed facts in the engine's store, each written with its source; make refusal part of the contract, so it says UNKNOWN rather than guessing when a pointer cannot be grounded; and serve it offline on four CPU cores in under a gigabyte. We know of no other search engine that ships that combination, and we looked. If one exists we will add it here and link it.

XERJ Query-1 A question and the typed records in the engine's store go to the model, which writes a program of lookups, comparisons and counts. The engine runs it over the records and produces a trace; a verifier reads the trace and returns ANSWER, UNKNOWN or CONFLICT. Every step points at a record. QUESTIONin plain words TYPED RECORDSwritten to the store, with source THE MODEL WRITESa program: LOOKUP · COMPARE · COUNT THE ENGINE RUNS ITover the records TRACEsteps logged VERIFIERreads it ANSWERwith its record ids UNKNOWNnothing to point at CONFLICTrecords disagree EVERY STEP POINTS AT A RECORD the answer is computed by the engine, never written by the model

Diagram 2 · XERJ Query-1. The model never writes the answer. It writes a program over the engine's typed records; the engine runs it, logs each step, and a verifier reads the trace. A pointer that finds nothing gives UNKNOWN; two records that disagree give CONFLICT. The mechanism in detail is in §06.

What is different is inside the network, and it is small. XERJ Query-1 is the same kind of reader network as a language model: it reads the question and a numbered listing of records, token by token. The difference is what it is allowed to say back. A language model's output vocabulary is words, so its output is the answer, and the answer is whatever it wrote. XERJ Query-1's output vocabulary is program steps and record numbers: look up record 10, look up record 7, compare, return. That makes the output checkable, because every token names an operation or a record you can open. It makes it cheap, because the program is a handful of tokens written in one pass, with no corpus to re-read and no paragraph to compose. And it makes refusal honest: when the data does not contain what the program points at, the program fails to ground and the engine says UNKNOWN, instead of the model improvising a sentence to fill the gap.

What is different inside the network Left: a language model reads the question and text and its output tokens are the answer sentence. Right: XERJ Query-1 uses the same kind of reader network, but its output tokens are program steps and record numbers, and the answer comes from running them. A LANGUAGE MODEL INPUT: THE QUESTION + THE TEXTtokens in THE READER NETWORKa transformer The Gacunas River carries more water. output tokens = the answer text, sampled word by word THE ANSWER IS WHAT THE MODEL WROTE XERJ QUERY-1 INPUT: QUESTION + NUMBERED LISTINGof the engine's typed records THE SAME KIND OF READER NETWORKa transformer LOOKUP #10 · LOOKUP #7 · LT · RETURN output tokens = steps + record numbers THE ANSWER COMES FROM RUNNING THEM

Diagram 3 · What is different inside the network. Same reader, different output vocabulary. On the left the tokens are the answer; on the right the tokens are steps and record numbers, and the answer is whatever running them produces.

Prior art checked 2026-11-04: Liang et al. 2017 (ACL P17-1097); Wang et al. 2018, execution-guided neural program decoding; Chen et al. 2020, Neural Symbolic Reader; Gao et al. 2022, PAL; Chen et al. 2022, Program of Thoughts; Cheng et al. 2022, Binder; Khattab et al. 2023, DSPy; the 2025 Text-to-ES benchmark; vendor documentation for engine-side natural-language querying, which routes through an external model connector. Production examples checked 2026-11-05 against the linked AWS, DeepMind and Wolfram pages.

05·HOW WE MADE AND TRAINED IT · A CPU TARGET, RENTED GPUS, DONATED MONEY, RULES WRITTEN FIRST

HOW WE MADE AND TRAINED IT.
FP16 ON RENTED GPUS, INT8 ON FOUR CPU CORES, PAID FOR BY DONATIONS.

The goal was fixed before the first training run: a model that answers on an ordinary CPU, with no GPU, and with nothing leaving the machine. That rules out most of what a model is usually allowed to be. It has to be small enough to sit beside the index in memory, fast enough to answer in a fraction of a second on four cores, and exact enough that its output can be run rather than read. Everything below follows from that target.

Trained in FP16 on rented GPUs. FP16 means half-precision numbers: each weight takes half the memory of the usual 32-bit kind, and the whole recipe, training through shipping, stays in half precision end to end. We never used FP32. The GPUs were rented by the hour on Clore.ai, chosen by measured throughput per dollar on our own job rather than by name, alongside a few machines of our own. It was paid for by donations to the project. The total is in the thousands of US dollars, not millions; we will not print the figure and we will not round it up. We are grateful to everyone who gave: this model exists because they did.

Served in int8 on a CPU, with a guard. For serving, the weights are stored as 8-bit integers, a quarter of the 32-bit size, which is what makes four cores and under a gigabyte possible. Smaller numbers can change answers, so the int8 path has a calibrated guard: when a computation drifts past a threshold it falls back to the exact path. The int8 configuration was accepted only when its answers agreed with the FP16 model's own answers on 400 of 400 checks, not on a benchmark score. In that configuration a question takes about 120 ms at the median on four cores, on a namespace of up to 8,000 facts.

The rules were written first. The scoring rules, the Net formula (right answers minus twice the wrong ones), the family weights, the bootstrap seed and what counts as a decline, were written down on 31 October, before a single answer under them was scored. The pre-registration also records that its author had seen the earlier results, because that is true and it matters. Every candidate recipe trains on three seeds (11, 29, 47), and the bar for promoting one was fixed before the screens ran: a mean gain of at least 3 points, an interval above zero, no family losing more than 1 point, no rise in confident-wrong answers. Several candidates scored 91 to 94 on a looser rubric variant and were never published, because that variant was known to be inflated.

Score trajectory with intervals Four points: the withdrawn v1 ruler at 100, then honest v2 points at 49.9, 87.48 with interval 84.02 to 90.97, and 90.29 with interval 86.93 to 93.89. XERJ SCORE v2, REFERENCE = 100 0 25 50 75 100 reference reported 100 capped / 130.7 unclipped; a measurement artefact, withdrawn 1 Nov 100 31 Oct v1 ruler, withdrawn first honest v2 point, no interval recorded 49.9 1 Nov v2, model C, counting off candidate 87.48, 95% CI [84.02, 90.97] 87.48 1 Nov v2, C2 facts + RC4 counting promoted bundle 90.29, 95% CI [86.93, 93.89] 90.29 4 Nov v2, A_s47 bundle, served bars are 95% intervals from a paired world-cluster bootstrap; the first two points carry no interval in the record

Chart 8 · The score trajectory, with the point we withdrew. The first XERJ Score, reported on 31 October as "100 capped, 130.7 unclipped", rewarded the model for declining count questions the reference was penalised for answering. It was an artefact of the rubric, and the report said so in its own text. We withdrew it on 1 November, pre-registered v2, and started again from 49.9. Intervals are 95% paired world-cluster bootstraps; the two early points carry no interval in the record. The scoring rules were written down before the second version was scored.

Calibration is tested on held-out wording, not tuned on the development set. A diagnostic on 4 November sorted every non-right item on the standard evaluation: 96 recoverable by recalibrating the decline policy alone, 126 by training, 169 beyond the executor's current op-set. A recalibration that uses no gold answers raised the development mean from 90.3 to 93.6 and passed every safety gate. We did not ship it, because on a separate 270-item set of fresh wordings it moved Net only from 0.333 to 0.348 while a looser variant made things worse. A gain that does not transfer is not a gain.

Bit-exact parity between the engine and the research code. The executor inside the engine is a port of the research implementation. Before landing, the engine served all 2,387 evaluation items from the bundle and the rows were compared with the research rows: program, raw output and evidence identical on every item, score reproduced to the hundredth. The one divergence found on the way, eight items that answered differently, was traced to the test driver asking historical questions at the wrong revision; the engine was right. The ES-YAML conformance suite, the gate that outranks everything else in this repository, read 1,457 passed and 0 failed on the landed branch. Every number in this post traces to a run; our provenance record maps each figure, example and chart to the file or commit it came from.

If you have read our other posts, this is the same method applied to a model instead of a file format: gates written before the work, losing rows kept, numbers published with the run that produced them, a decisions study that found our own defects. The model is the newest piece and it will get the same treatment as it changes.

06·HOW XERJ QUERY-1 WORKS · RECORDS, LISTING, PROGRAM, EXECUTOR, VERIFIER, AND WHAT A QUESTION COSTS

HOW XERJ QUERY-1 WORKS.
IN DETAIL: RECORDS, PROGRAM, TRACE, VERIFIER, AND THE COST OF A QUESTION.

Five parts, in the order a question meets them. Records. You, or your agent, write typed records into the engine's store: a subject, a relation, a value with its type and unit, and the exact span of text the value came from. Every record can be traced back to its sentence and its document version. The engine does not yet turn arbitrary documents into records on its own; in the evaluation worlds below, the records were extracted by the evaluation pipeline. The listing. When a question arrives, the engine gathers the candidate records and numbers them, and hands the question and the numbered listing to the model. The program. The model writes a few steps that refer to the listing by number: look up record 10, look up record 7, test whether the first is less than the second, return. The executor. Engine code runs the steps over the records, checking types and units as it goes, and logs each step with the record it touched. The verifier. A frozen rule set reads the trace and its decline codes, never the gold answer, and decides whether to return the value. A count is returned only when a second, independently trained model agrees on the program. The outcome is one of three statuses: ANSWER with its evidence, UNKNOWN when a pointer finds nothing or a set cannot be closed, CONFLICT when two records disagree.

Here is the whole mechanism on one real evaluation item. The evaluation world holds seven short reports about invented rivers; the evaluation pipeline extracted 20 records from them and wrote them to the store; four are shown. The question asks whether one river's discharge, the water it carries per second, is exceeded by another's.

# world-dev-90014, as stored (names are synthetic; the numbers are the data)
doc1 v1 | Dunzelfen River flows for 3636 km. It carries 2446 m3/s of water. It runs through Hallend.
doc2 v1 | Liosolzel River flows for 3079 km. It carries 1439 m3/s of water. It runs through Fallow.
doc3 v1 | Arhalga River flows for 499 km. It carries 6956 m3/s of water. It runs through Dunmere.
doc4 v1 | Iswenquel River flows for 3284 km. It carries 8692 m3/s of water. It runs through Esterra.
doc5 v1 | Tersolsol River flows for 1132 km. It carries 4884 m3/s of water.
doc6 v1 | Gacunas River flows for 202 km. It carries 7567 m3/s of water. It runs through Gorvland.
doc7 v1 | Pratav River flows for 478 km. It carries 3775 m3/s of water. It runs through Fallow.   (added by an edit)
# extracted records, 20 in all; four of them:
doc3@v1/length     | Arhalga River | length    | number 499  km
doc3@v1/discharge  | Arhalga River | discharge | number 6956 m3/s
doc3@v1/country    | Arhalga River | country   | text   Dunmere
doc6@v1/discharge  | Gacunas River | discharge | number 7567 m3/s
question · item dev-pf-0001/t0002/c1/rw2Is the Arhalga River's discharge exceeded by that of the Gacunas River?

XERJ Query-1 does not say "yes". It writes four steps. The engine runs them and logs this:

program  LOOKUP #10 ; LOOKUP #7 ; LT ; RETURN
trace    step 0  LOOKUP #10  VALUE number 6956 m3/s   doc3@v1/discharge, Arhalga River
         step 1  LOOKUP #7   VALUE number 7567 m3/s   doc6@v1/discharge, Gacunas River
         step 2  LT          TRUE                     6956 < 7567, units checked by the executor
         step 3  RETURN
answer   yes      gold: yes · Opus 5.5 on the same listing: yes

#10 and #7 are not words from your prompt. For each question the engine lists the facts in the namespace as a short numbered listing (#0, #1, #2 …). LOOKUP #10 means: read record 10 of that listing. Here record 10 is the Arhalga River's discharge, 6,956 m3/s, and record 7 is the Gacunas River's, 7,567 m3/s. Every step of the trace names the record it used, with its source document and span.

# the numbered listing the model reads, two of its 20 lines
#7   Gacunas River | discharge | number 7567 m3/s   doc6@v1
#10  Arhalga River | discharge | number 6956 m3/s   doc3@v1

The numbers never touch the model's output. The model chose two pointers and an operator. The engine did the arithmetic.

The same shape covers the questions that trip up a reader. A count is a loop the engine runs: find the subject's records, keep the ones that pass each filter, with an explicit rule for records that lack the field, and count what is left. A comparison is two values and an operator. A question that needs two hops follows a link from one record to the next. Every answer carries the ids of the records it was computed from, because the program is made of them. Run the same program over the same data tomorrow and you get the same trace. And when the data cannot support an answer, the program fails to ground instead of the model improvising: a pointer that resolves to nothing returns UNKNOWN, two records that disagree return CONFLICT, and that carries through any comparison built on them. Everything the verifier does not accept is returned as a decline, which our scoring counts as neither right nor wrong. Nine wrong answers in 1,600 is what that policy buys, and §07 says what it costs.

A store you can change. The model answers from a store you control, every change to it is versioned, and a question can be asked as of an earlier revision. We tried the whole cycle on a fresh node. We wrote three short HR notes and six facts about them. "How tall is Ana Bel?" answered 170 cm and pointed at characters 11 to 14 of document d1. We corrected the record to 172 cm with one edit that names the fact it replaces; the same question now answers 172 cm, and asked at revision 9 it still answers 170 cm. A second, disagreeing height for Bo Chen made the answer CONFLICT; withdrawing it brought back 182 cm. Nothing was guessed in between, and each answer took about 44 ms on four x86 cores, on this small namespace.

ask    How tall is Ana Bel?            ANSWER 170 cm   evidence: d1 v1, characters 11-14, "170"
edit   correct d1 → v2, new fact supersedes the old one   committed, revision 13 → 14
ask    How tall is Ana Bel?            ANSWER 172 cm
ask    same question, revision: 9      ANSWER 170 cm   the old answer is still readable
write  a second height for Bo Chen, 175, no supersede
ask    How tall is Bo Chen?            CONFLICT        175 and 182 both stand
delete the 175 record                                     revision 12
ask    How tall is Bo Chen?            ANSWER 182 cm

Every symbol the model can write. Two models write programs, and both run through the same executor. The facts model writes pointer programs, two to six steps. The counting model writes set programs, or refuses. The counts below come from the served rows of the 2,387-item standard evaluation: the lookups, chains and comparisons are from the facts model's 2,387 programs, and the counting model writes a second, separate program set over the same items, 836 set programs of which 529 were served after verification.

LOOKUP #n
2,972
Fetch one fact by its number in the listing: the answer to "what is X's Y".
LOOKUP #10 → 6956 m3/s
CHAIN #n
378
Hop from the record just fetched to a related record, for two-step questions.
LOOKUP #2; CHAIN #4; RETURN
LT
192
Is the first value smaller? Order, dates, sizes.
LT → TRUE → "yes"
NOT
190
Flip a yes/no; UNKNOWN and CONFLICT pass through.
EQ; NOT → FALSE
GT
181
Is the first value greater?
GT → FALSE → "no"
EQ
151
Are the two values the same?
LOOKUP #2; LOOKUP #2; EQ
PICK_GT
31
Of two subjects, return the one with the larger value.
PICK_GT → Jorholen Nasfalpen
PICK_LT
28
Of two subjects, return the one with the smaller value.
LOOKUP #6; LOOKUP #15; PICK_LT
RETURN
2,378
Serve the top of the stack: a value, yes/no, CONFLICT or UNKNOWN.
every program ends here
SET
836
Open a set program: this is a how-many question.
SET; FIND …; AGG COUNT
FIND
739
Name the anchor and the relation that defines the set.
FIND credited artist = Henry Darger
FILTER
807
Narrow the set by a typed field, with a rule for members that lack it. Comparators eq, lt, gt, contains, ge.
FILTER height | number cm > 200 missing: exclude
AGG COUNT
739
Close the set and count members, or distinct values of a field.
each member 582 · distinct 157
REFUSE
998
Decline to write a set program: not a count the op-set can express.
first decision on 901 questions

Fourteen symbols, usage counts on the standard evaluation. The big number on each card is how often the served model wrote it. One more symbol, ABSTAIN, ends a program with UNKNOWN on purpose; it is trained and legal, and the served model used it 0 times, declining through UNKNOWN pointers instead.

Status the user seesMeaningFacts rows of 2,387
UNKNOWNa pointer resolved to nothing, or a set could not be closed1,484
ANSWERthe program grounded and returned a value, yes/no, a subject or a count805
CONFLICTtwo stored facts disagree and the program touched both98

Sorted by rows, most first. Facts path only; counting answers are served separately when the counting model's program verifies.

What the op-set cannot express. No sums, totals, averages, minimum or maximum, lists or grouped counts: the engine compiles only COUNT. No differences or ratios, which are routed away. No arithmetic on values, only comparisons. No joins beyond one hop. No programs longer than six steps or deeper than four stack slots; those stop and answer UNKNOWN. No free-text answers: every ANSWER is a stored value, a subject name, yes/no or an integer. And a count does not yet bind the noun of the question, the defect disclosed in §07.

For engineers. Pointer programs: LOOKUP #n, CHAIN, EQ / LT / GT / NOT, PICK_LT / PICK_GT, RETURN; across the four evaluation families the served model wrote 2,972 lookups, 378 chains, 583 comparisons or picks and 190 negations in 2,387 programs. Set programs: SET, FIND, FILTER with an explicit rule for missing values, AGG COUNT over members or distinct values, RETURN. Three statuses, shared with the reference reader: ANSWER, CONFLICT, UNKNOWN. It is still a neural network: it reads the question and the listing and emits program tokens. What is different is the type of its output, and where it runs.

What a question costs. XERJ Query-1 needs no GPU. The engine, the index and one model bundle sit in ordinary memory: 674 MiB resident with the model loaded, 159.6 ms to load it with its checksums verified. Answering is one pass of the model plus an executor that runs in microseconds. Two latency numbers appear in this post and they are not the same measurement. The 120 ms median is the int8-guard serving mode on a quiet x86 box with four cores, charted below. The 1,039 ms median in §07 is the live head-to-head: this bundle's own serving path on four cores of a shared aarch64 box that was also running training, 4.1 CPU-seconds per question. The $0.042 per 1,000 questions is priced from that slower run; the int8-guard mode would price at $0.0052, and that figure stays a projection until it is run on the same store.

Serving latency and footprint on CPU Grouped bars: p50 120 ms int8-guard versus 163 ms w8; p95 176 versus 174 ms. Beside them: 674 MiB resident, 159.6 ms load, 16.8 questions per second at concurrency 8. MILLISECONDS PER QUESTION, 4 CPU CORES, 8,000-FACT WORLD p50 int8-guard, tau x0.125 (served default): 120 ms 120 p50 w8, int8 weights dequantised per call (previous default): 163 ms 163 p50 p95 int8-guard, tau x0.125 (served default): 176 ms 176 p95 w8, int8 weights dequantised per call (previous default): 174 ms 174 p95 674 MiB RESIDENT MEMORY, MODEL LOADED 159.6 ms LOAD TIME, HASHES VERIFIED 16.8 q/s AT CONCURRENCY 8, INT8-GUARD int8-guard, served default w8, previous default

Chart 4 · Serving latency and footprint. Left: median and 95th-percentile latency per question for the two serving modes, 4 cores, 8,000 records, sequential; int8-guard also measured 7.4 questions per second sequential and 16.8 at concurrency 8. The tail barely moves because it is the guard's fallbacks, by design. Right: resident memory and load time of the released build serving the FP16 bundle on aarch64, model state loaded, checkpoint hashes verified. Measured on two machines, a quiet x86 box and an aarch64 build host, shown side by side, not combined.

What the index costs. Because the model's world is the engine's own index, there is no encoder pass over the corpus, no vector field and no context to cache. On 10 GB of English Wikipedia, 3.3 million articles, XERJ built its index in 17 minutes and 0.7 CPU-hours, 11.35 GB on disk, searchable at 9.3 ms at the median; embedding the same text takes 62 to 97 hours of CPU. Facts are fast to write too: 95,000 facts went into one namespace in 23 s, about 4,100 a second. On the smaller 1.1-million-document corpus below, the engine's whole index, which answers word queries, aggregations and XERJ Query-1 programs from the same files, is 712.9 MiB in the default layout and 303.7 MiB with both opt-in layouts on:

Index size by on-disk layout Four bars: base 712.9 MiB, freqs 548.8, ref 467.8, reff 303.7 MiB for the whole index of a 1 GB JSONL slice; and two bars for the stored-text family: 340.6 MiB base versus 34.9 MiB with source_ref. base: 712.9 MiB. default layout: positions kept, text stored in .seg 712.9 base freqs: 548.8 MiB. opt-in: index_options freqs, no positions, phrases refused 548.8 freqs ref: 467.8 MiB. opt-in: source_ref, long text fields read from the source file 467.8 ref reff: 303.7 MiB. opt-in: freqs + source_ref 303.7 reff WHOLE INDEX, MiB 1 GB JSONL slice, 1,117,217 docs, one force-merged segment .seg base: 340.6 MiB 340.6 base .seg source_ref: 34.9 MiB 34.9 source_ref .SEG STORED TEXT, MiB same slice, minus 89.8% default on-disk layout opt-in layouts, recall overlap@16 = 1.0000

Chart 1 · Index size by on-disk layout, 1 GB JSONL slice, 1,117,217 docs. Default layout and three opt-in layouts, plus the stored-text family alone before and after source_ref. All four return the identical top-16 result set on 200/200 recorded queries (overlap@16 = 1.0000). Opt-in because each gives something up: source_ref needs the source files present and unchanged (an edited file yields an explicit error, never stale text, at about +7 ms p50), and no-positions refuses phrase queries with a 400. Measured on the same 1 GB slice.

Common questions

Do I have to train the model on my data?
No. XERJ Query-1 is trained once, by us, on synthetic practice data. On your machine it is never retrained or fine-tuned. It reads your facts when you ask, so new data works immediately.
What do #10 and #7 mean?
They are record numbers. For each question the engine lists the facts in the namespace as a numbered listing, and LOOKUP #10 reads record 10 of it. The trace names every record it used.
Does it read my documents?
It reads the facts you, or your agent, write into a facts namespace through /_facts/{ns}/_bulk or the MCP tool. It does not yet extract facts from arbitrary documents on its own. Full-text search over your documents is the ordinary search, and it works alongside.
What happens when it does not know?
It says so. If no stored fact backs the answer it returns UNKNOWN; if two facts disagree it returns CONFLICT. It never guesses.
Does anything leave my machine?
No. The model and the engine run on your CPU. No question, fact or answer is sent anywhere.

How to run it today. Install the engine with the model, start it with the flag, write a few records, ask a question with a definite answer, and read the trace:

$ curl -fsSL https://xerj.org/get | sh -s -- --with-model
$ xerj --facts-model-dir ~/.local/share/xerj/model --insecure --data-dir ~/xerj-data

You write the facts yourself, through _bulk or the MCP tool, with a stable subject_id on each subject; the engine does not yet turn arbitrary documents into facts automatically. The same binary is the whole engine. xerj autoindex <folder> turns a folder into searchable data with no configuration: five files became two datasets, seven records and a seven-edge graph in 0.7 s on our test. Agents get memory and brains over MCP: store a note, recall by meaning, link records into a graph, read a node's neighbourhood with its evidence; 24 tools with the model on, 23 without it. And it is an Elasticsearch-compatible search engine: bulk indexing, full-text search, aggregations and kNN over your own vectors, on the same port.

Off by default. XERJ Query-1 ships in rc.101 compiled into the engine and stays off until you start the server with xerj --facts-model-dir <DIR>; without the flag the engine behaves exactly as rc.100 did. The model bundle is a separate release asset. Counting works on records that carry a stable subject id (subject_id), and in this release covers "how many" set counts, not sums. The step-by-step guide is the quickstart; the engine is one binary.
Also in rc.101: the engine. XERJ Query-1 ships inside the engine binary, and rc.101 is an engine release too. Five changes, each measured on a 1M-document corpus, each with an opt-out:
  • Smaller postings by default: the word index shrinks from 325.1 to 288.3 MB and reader memory after load from 573 to 480 MB, for about 2% at the 95th percentile of query latency.
  • Stored rows in compressed blocks: reading a page of 25 rows drops from 4,968 ms to 50 ms at the median; the cost is merge-time encode CPU, 48.6 s against 18.5 s.
  • The read fast paths are on by default, each proven identical to the old path on pages, scores, sources, totals and facets before it shipped.
  • Ingest keeps several bulks in flight and honours the node's Retry-After hint.
  • Background merges stay inside the memory budget: a 1M-passage index that reached 36.9 GB peak resident under a 6 GiB budget no longer does.
One-way upgrade: the first segment written in the new codecs moves the data directory to format 4, which older releases refuse to open. Take a backup before the first start if you may need to roll back. Details and opt-outs are in the CHANGELOG.
rc.101 engine changes on a 1M-document corpus Postings 325.1 to 288.3 MB; stored rows after merge 339.6 to 320.6 MB; reader RSS 573 to 480 MB; a page of 25 rows 4,968 to 50 ms p50; merge encode CPU 18.5 to 48.6 s, a cost. ON-DISK SIZE, MB .post, ZPS4 before: 325.1 MB 325.1 .post, ZPS4 rc.101: 288.3 MB 288.3 .post, ZPS4 rows, after merge before: 339.6 MB 339.6 rows, after merge rc.101: 320.6 MB 320.6 rows, after merge READER RSS, MB reader RSS after load before: 573 MB 573 reader RSS after load rc.101: 480 MB 480 after load, 1M TIME, LOG AXIS page of 25 rows, p50 ms page of 25 rows, p50 ms before: 4968 ms 4968 page of 25 rows, p50 ms rc.101: 50 ms 50 encode CPU at merge, s encode CPU at merge, s before: 18.5 s 18.5 encode CPU at merge, s rc.101: 48.6 s 48.6 10 100 1,000 10,000 before: rc.100 / previous default rc.101 default rc.101, the cost

Chart 6 · rc.101 engine changes, wins and the cost, 1M-document corpus. Left: on-disk size before and after for the postings codec (.post) and the stored-row section (after merge). Middle: reader memory after load. Right, on a log axis: reading a page of 25 rows, and the merge-time encode CPU, which went the wrong way and is drawn in the loss colour. Measured on a 1 million document corpus.

07·TESTS AND BENCHMARKS · THE HEAD-TO-HEAD, THREE WORKED QUESTIONS, THE STANDARD EVALUATION, EVERY CAVEAT

TESTS AND BENCHMARKS.
NINE TIMES FEWER WRONG ANSWERS THAN RAG ON OPUS 5.5. HUNDREDS OF TIMES CHEAPER.

The head-to-head. We took 5,000 records (subject, relation, typed value, source sentence) from 95 collections of our standard evaluation and merged them into one store: 357,085 characters, 172,870 Opus tokens. We asked 160 questions, forty from each of four families, drawn by a deterministic hash. Every method got the same rules as the reference prompt: answer, say UNKNOWN, or say CONFLICT, and a wrong answer costs two right ones. Every output was scored by the same frozen scorer against the same gold. The sample score is 100 times a method's family-weighted Net over the Net of Opus 5.5 given perfect retrieval, so that reader defines 100. Intervals are paired bootstraps over collections, 4,000 draws.

MethodRightWrongDeclinedCost / 1,000 questions
XERJ Query-1, live124135$0.042
Opus 5.5, whole store, 1 q per call (20-q probe)15 of 201 of 204$694.30
Opus 5.5, perfect retrieval (oracle)137518$28.84
Opus 5.5, whole store, 40 q per call130624$19.41
GraphRAG + Opus 5.5127924$65.39
RAG, BM25 top-10 + Opus 5.5124927$20.95
RAG, BM25 top-5 + Opus 5.51171231$13.83
RAG, embeddings top-10 + Opus 5.51091437$19.85
RAG, embeddings top-5 + Opus 5.51061539$13.41

Sorted by wrong answers, fewest first; out of 160 questions, list prices. Accent marks the best cell in the wrong and cost columns, red the worst; the 20-question probe is listed by its own count. Building the index, per method: XERJ Query-1 8.8 s wall, 5.5 CPU-s, 15.4 MB, no LLM calls. Opus 5.5 oracle and whole store: none. GraphRAG: 267 LLM calls, $14.27, 72 min serial (12 min at 6 in parallel). RAG, BM25: 0.02 s. RAG, embeddings: 19.6 s encode at 8 threads, 1.28 GB peak memory. The cost chart is in §03.

Head to head: score with intervals and wrong answers XERJ Query-1 96.1 with 1 wrong; Opus oracle 100 with 5 wrong; whole-store Opus 92.9 with 6; GraphRAG 85.8 with 9; BM25 top-10 83.5 with 9; BM25 top-5 73.2 with 12; embeddings top-10 63.8 with 14; top-5 59.8 with 15. SAMPLE SCORE, OPUS 5.5 ORACLE = 100, WITH 95% INTERVAL WRONG OF 160 ARM MINUS XERJ QUERY-1 WRONG COUNT, 95% CI 40 60 80 100 XERJ Query-1 XERJ Query-1: score 96.1 [82.6, 114.9], wrong 1 96.1 1 reference arm Opus 5.5, oracle listing Opus 5.5, oracle listing: score 100.0 [100.0, 100.0], wrong 5 100 5 [-1, +10] Opus 5.5, whole store, 40 q per call Opus 5.5, whole store, 40 q per call: score 92.9 [81.0, 104.0], wrong 6 92.9 6 [+0, +11] GraphRAG local search + Opus 5.5 GraphRAG local search + Opus 5.5: score 85.8 [73.8, 98.5], wrong 9 85.8 9 [+1, +16] excludes 0 RAG, BM25 top-10 + Opus 5.5 RAG, BM25 top-10 + Opus 5.5: score 83.5 [70.7, 94.7], wrong 9 83.5 9 [+2, +15] excludes 0 RAG, BM25 top-5 + Opus 5.5 RAG, BM25 top-5 + Opus 5.5: score 73.2 [58.5, 86.3], wrong 12 73.2 12 [+4, +18] excludes 0 RAG, embeddings top-10 + Opus 5.5 RAG, embeddings top-10 + Opus 5.5: score 63.8 [46.5, 78.4], wrong 14 63.8 14 [+5, +21] excludes 0 RAG, embeddings top-5 + Opus 5.5 RAG, embeddings top-5 + Opus 5.5: score 59.8 [41.6, 77.3], wrong 15 59.8 15 [+6, +24] excludes 0 160 questions over 95 collections; score = 100 x family-weighted Net(arm) / Net(oracle); CIs: paired bootstrap over collections, 4,000 draws; XERJ Query-1's upper bound (114.9) is clipped at the axis

Chart 2 · Score with intervals, and wrong answers, per method. Dots are sample scores against the Opus 5.5 oracle at 100; bars are 95% paired bootstrap intervals over collections. The right-hand columns are the wrong-answer count out of 160 and the 95% interval of the method's wrong count minus XERJ Query-1's. Measured, same 160 questions.

What the numbers say, in plain words. The finding that holds up is the error rate. Of 160 questions XERJ Query-1 answered one wrong: nine times fewer wrong answers than RAG on Opus 5.5 (1 vs 9 to 15), five times fewer than Opus reading everything (1 vs 5 to 6). Every pipeline built on a language model answered between 5 and 15 wrong, and the paired intervals of the difference exclude zero against GraphRAG and every RAG variant; against whole-store Opus the interval touches zero, and against the oracle it does not separate. On overall score the picture is narrower. XERJ Query-1 at 96.1 is significantly ahead of embedding RAG and BM25 top-5, and within noise of GraphRAG, BM25 top-10, whole-store Opus and the oracle at this sample size. It declined more than any method, 35 of 160, and that is where its score goes: a decline earns nothing. That is the price of refusing to guess, and §08 is about lowering it.

Three worked questions, as "show your work". In the standard evaluation the reference reader is Opus 5.5, given the identical listing of records under a frozen prompt: "Answer each question from the listing given with it and from nothing else: no outside knowledge, no tools, no code, no calculators. Work it out yourself." Every item below is a real row; the program and trace are from the served rows, the reference answer from the reference run.

A count with two conditions. The reader has to keep two filters and a running tally in its head; the program makes the filters explicit and the engine does the tally.

question · s1b/s1b-d-00248/w1 · countingHeight above 19 cm and medium mentioning lithographs: how many Pierre-Auguste Renoir works?
XERJ QUERY-1

6

gold: 6 · right
SET
FIND    artist = Pierre-Auguste Renoir
FILTER  height | number cm  > 19 cm   missing: exclude
FILTER  medium | text  contains "lithographs"   missing: exclude
AGG     COUNT each member
RETURN  6   verifier rc4: accepted
OPUS 5.5, SAME LISTING

10

wrong

Two filters and a tally held in a reader's head at once, and the tally slipped. In the program the filters are explicit and the tally is a loop; there is nothing to hold.

A comparison where the data disagrees with itself. The store holds two different heights for one artwork.

question · dev-pf-0001/t0170/c1/ct0 · comparison over a conflicted recordWhich of "Head of Christ" and "The Crucifixion and Last Supper" exceeds the other in height?
XERJ QUERY-1

CONFLICT

gold: CONFLICT · right
LOOKUP #12   CONFLICT          two heights stored, neither supersedes
LOOKUP #0    VALUE number 210 mm
PICK_LT      CONFLICT
RETURN       CONFLICT
OPUS 5.5, SAME LISTING

Head of Christ

wrong

The store held two disagreeing heights for one work. The reference picked one and answered. The executor's lookup returned CONFLICT and the comparison could not proceed, which is correct under the rubric and, we would argue, in production.

And one of the nine wrong answers. We show it because the error is visible in the trace: the pointer and the record it resolved to are named, so the mistake can be found, counted and trained against.

question · s1b/s1b-d-00315/w1 · one of our nineMorfen Vodraga is employed in which occupation?
XERJ QUERY-1

Elquel Vobelri Works

gold: surveyor · wrong
LOOKUP #5    VALUE text Elquel Vobelri Works   the employer record, not the occupation
RETURN
OPUS 5.5, SAME LISTING

surveyor

right

The model pointed at the wrong record for the right subject. We show it because the failure is legible: the trace names the pointer and the record it resolved to, so the error can be found, counted and trained against. A wrong paragraph carries no such handle.

The standard evaluation. This is a different test from the head-to-head: 1,600 questions in their own collections, each scored against Opus 5.5 reading that collection's listing. There are four families of 400 questions: teacher_dev (small worlds with edits over time and planted conflicts: rivers, artworks, code), bench_select (a museum catalogue, a retail ledger and similar real-shaped data, including questions the records cannot answer), s1b_dev and d1_select (facts about code structure, employer rosters, and counting over an artist catalogue). Per family, Net is right answers minus twice the wrong ones, out of 400. The score is 100 times XERJ Query-1's weighted Net over the reference's, so the reference defines 100, under the rules pre-registered on 31 October and the withdrawn first score described in §05.

FamilyXERJ Query-1 right / wrong / declinedOpus 5.5 right / wrong / declined
d1_select319 / 0 / 81381 / 2 / 17
bench_select222 / 2 / 176245 / 3 / 152
s1b_dev337 / 3 / 60358 / 5 / 37
teacher_dev330 / 4 / 66378 / 12 / 10
Total, 1,6001,208 / 9 / 3831,362 / 22 / 216

Sorted by XERJ Query-1 wrong answers, fewest first. Accent marks the family with the fewest wrong answers, red the most, for each system. 400 questions per family.

Quality per family, XERJ Query-1 against the reference Grouped bars of Net per family. teacher_dev 0.805 vs 0.885, bench_select 0.545 vs 0.598, s1b_dev 0.828 vs 0.870, d1_select 0.798 vs 0.943. NET = (RIGHT - 2 x WRONG) / 400 ITEMS, PER FAMILY teacher_dev XERJ Query-1: Net 0.8050 (330 R / 4 W / 66 declined) 0.805 teacher_dev Opus 5.5 reference: Net 0.8850 (378 R / 12 W / 10 declined) 0.885 teacher_dev bench_select XERJ Query-1: Net 0.5450 (222 / 2 / 176) 0.545 bench_select Opus 5.5 reference: Net 0.5975 (245 / 3 / 152) 0.598 bench_select s1b_dev XERJ Query-1: Net 0.8275 (337 / 3 / 60) 0.828 s1b_dev Opus 5.5 reference: Net 0.8700 (358 / 5 / 37) 0.870 s1b_dev d1_select XERJ Query-1: Net 0.7975 (319 / 0 / 81) 0.797 d1_select Opus 5.5 reference: Net 0.9425 (381 / 2 / 17) 0.943 d1_select Score = 100 x weighted Net(model) / weighted Net(reference) = 100 x 2.975 / 3.295 = 90.29 XERJ Query-1, served bundle Opus 5.5 reference, same listing, same prompt

Chart 5 · Net per family, XERJ Query-1 and the reference. The reference leads on every family, by least on bench_select, where both decline most, and by most on d1_select, where XERJ Query-1 declines 81 counts the reference answered. The arithmetic line under the bars reproduces 90.29 exactly.

90.29
score, served bundle
absolute 95% interval [86.93, 93.89], world-cluster bootstrap
+2.81
over the 87.48 candidate
paired 95% interval [1.11, 4.52]; it excludes zero, so the gain is real and not a reseed
9
wrong answers
Wilson upper bound on the wrong rate 1.27%; the reference's 22 wrong are scored by the same rule
823 / 998
values given, when right
right answers that carried a value rather than a correct decline; the candidate gave 802
The caveats, all of them. (1) The standard evaluation is the one the model was developed against, not a fresh or sealed set; its bench_select and d1_select families were also the design data for the decline and routing rules, so in-distribution numbers flatter. In the head-to-head, too, XERJ Query-1 is on home ground and the language-model methods were not trained for it. (2) Robustness to new wordings and training overlap were not re-measured for this bundle. On a separate 270-item fresh-wording set, which has no reference run and no 0-to-100 score, the previous candidate answered 112 right, 11 wrong and declined 147. Counting on that set is unmeasurable there: its worlds carry no subject ids, so every count is declined (0 of 60 count items, 1 of 30 list items), a property of the test set, not evidence either way about counting. (3) n=160 over 95 collections in the head-to-head; the intervals are wide, and most score gaps against the strong baselines are not significant. The wrong-answer gap is the finding that survives. (4) All costs are list prices from the logged token counts; Opus 5.5 ran at its default effort through a subscription, whose own billed figures, which include cache writes, are higher and are in the results file. Language-model latencies include the network; XERJ Query-1's is local CPU on a shared box under load. (5) Every head-to-head method started from the same pre-extracted records. Extraction from raw documents is unmeasured for all of them; GraphRAG's index re-extracts entities from the records, and it is a faithful minimal implementation (Louvain communities, no second extraction pass, one community level), each shortcut making its index cheaper than the reference package. (6) The promoted bundle is the best of three seeds of its training family. The family's three-seed mean did not pass our retention rule (the bench family slipped 1.67 points on average); the single seed did, with bench at −0.25. We promoted it on that basis and record it here rather than quietly. (7) XERJ Query-1 over-declines: 383 of 1,600 items where the reference declines 216, and 35 of 160 where no other method declined more than 24. Of the teacher family's 66 declines, most suppressed an answer the executor had already computed correctly. (8) The 96.1 and the 90.29 are different evaluations: 160 questions over one merged store against an oracle reader, versus 1,600 questions in their own collections. Do not add them up.

What it does not do yet. Counting covers set counts only, "how many X with Y", and only over facts that carry a subject_id; sums and thresholds are outside the op-set and are declined. One defect we found in our own end-to-end test ships with this release: a count tallies the members of the matched set and does not check the noun in your question. Ask for the total of a quantity the store does not hold, such as devices across the Berlin offices when the store only knows who sits in Berlin, and you can get a member count back as a verified answer. The response names the program it ran; read it before trusting a count. A proper sum operation, and refusing when the noun is not in the store, is the next model. It over-declines, 383 of 1,600, and its robustness to fresh wordings is unmeasured for this bundle. Large fact sets are slow and decline more: on a 95,000-fact namespace an ask took 1.2 to 2.4 seconds and returned UNKNOWN in our test. Keep namespaces small (one per document set, project or customer) until the next model. We also ran an honest end-to-end test of the whole experience. Eight fresh agents, Haiku 4.5 and 5.5 and Opus 4.6 and 5.5, each got one prompt, a records file and twelve questions, and were told to install the product from its own instructions and decide which capability to use. Seven of eight runs answered all twelve questions right, by full-text search and their own arithmetic. Only Opus 5.5 got XERJ Query-1 to answer at all: the write contract for facts was not in the instructions, and the other agents never got a fact into the store. We fixed the docs.

How we measured. Every method answered the same 160 questions over the same 5,000 stored records, and every answer was scored with the same rules against the same gold answers. The standard evaluation is 1,600 questions in their own collections, scored against Opus 5.5 reading the same records. Costs use list prices we fetched on 2026-11-05: Anthropic's Opus 5.5 token prices for the language-model methods, and the on-demand rate of an AWS c7g-class CPU server for XERJ Query-1. Indexing and embedding at scale ran on 10 GB of English Wikipedia. The scripts and the engine are open at github.com/xerj-org/xerj, so every number here can be re-run.

TRY IT IN ONE PROMPT.

Paste this into your AI agent (Claude Code, Codex, Cursor or any agent with a shell):

Install XERJ with the XERJ Query-1 model from https://xerj.org/llms.txt, start it with --facts-model-dir, write three facts about my project with subject_id, and ask one question about them; show me the program and the evidence it returns.

Or run it yourself:

$ curl -fsSL https://xerj.org/get | sh -s -- --with-model

Check that the model is loaded: curl -s localhost:9200/_facts | jq .model.state should print "loaded". The model is off by default; the installer prints the xerj --facts-model-dir <DIR> line that turns it on.

08·DISCLOSURE, AND WHAT WE NEED NEXT · WHAT IS OPEN, WHAT IS NOT, THE GAPS, AND THE ASK

DISCLOSURE, AND WHAT WE NEED NEXT.
OPEN METHOD, OPEN GAPS, AND 100,000 H100 GPU-HOURS.

What is open, and what is not. The XERJ engine is open source under Apache-2.0. Everything that made this post is open too: the method, the scoring rules, the numbers, and every run behind them, mapped in our provenance record. The model itself is not open yet. XERJ Query-1 ships as a compiled part of the engine binary plus a separate model bundle, and the crates that train and serve it are proprietary for now. We would rather say that plainly than let the word "open" do work it has not earned.

The known gaps, each with its number. XERJ Query-1 over-declines: 383 of 1,600 items, against 216 for the reference, most of them answers the executor had already computed correctly. Counting works only on records that carry a stable subject id, and covers set counts, not sums or thresholds. Robustness to fresh wordings is unmeasured for this bundle, and the fresh set we built cannot judge counting at all. Large fact sets are slow and decline more: on a 95,000-fact namespace an ask took 1.2 to 2.4 seconds and returned UNKNOWN in our test. Keep namespaces small (one per document set, project or customer) until the next model. The op-set is limited: 168 catalogue items need joins, grouping and sums the programs cannot yet express, and no amount of training moves them.

The ask. XERJ Query-1 was trained on thousands of donated dollars of rented GPU time. To reach the next quality level we estimate we need about 100,000 H100 GPU-hours for the next training program. That is our estimate, not a measured number. It buys larger and more varied training worlds, robustness to fresh wordings measured on sealed sets with subject ids, sums and thresholds in the op-set with the verifier to match, and a re-scoring of everything in this post on data the model has never seen. If you can fund part of that, the project takes donations in USDT on the TRON network; a donation page at xerj.org/donate will carry the same address. We are grateful to the people who have already given: this model exists because they did.

# XERJ model donations wallet · USDT (TRC20)
TF4Nzy4KWum9gtxXQddfTop7B52nSJsr7S

How to help without money. Try it, with the two lines in §06, on records you know the answers to. File what breaks or surprises you as GitHub issues, including answers that should have been given and were declined. Send pull requests to the engine. And if you have evaluation data with entity ids, questions with definite answers over records whose subjects are identified, share it: that is the single thing that would most improve the next round of measurement.

Research in the open. XERJ Query-1 is the first model of a series, and the series is being built in public: the rules before the runs, the losing rows kept, the provenance beside every number. The next models are trained the same way, with a larger op-set that includes sums and thresholds, robustness to fresh wordings measured on sealed sets, and training worlds that carry entity ids so counting can be judged. Donations fund those next models, not only this one.

Ask it something with a definite answer, and read the trace. The trace is the product.