← ALL POSTS · /BLOG

Three grids of 160 squares: XERJ Query-1 1 wrong (given each question’s own records), RAG with BM25 top-10 on Opus 5.5 9 wrong, Opus 5.5 reading the whole store 6 wrong; filled squares right, blue wrong, rings declined
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, 320-1,570× 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 facts you have stored. No embeddings to build, no GPU to rent. Lookups come back with the records they used; counts come back with a hash of their program. Four CPU cores, offline, and in our 160-question test hundreds of times cheaper per question than RAG or an LLM reading the data.

Correction, 2026-11-06. A practitioner pointed out that this post reported no retrieval metrics, compared a search time with an answer time, and did not name its data. He was right on all three, and a line-by-line review against our own runs found more. We have changed the post:

  • The 120 ms answer time was measured on an earlier model setting that we never shipped, on one 40,000-fact namespace with a single repeated question. The shipped model answers our 160 test questions in about 220 ms at the median on 4 laptop cores.
  • The 2.7–3.3 s we gave for RAG was the whole question-to-answer chain through our command-line client. Search took under 1 ms to 106 ms, and Opus 5.5 took 1.5 to 2.3 s (API time) to write the answer; the client's own start-up, about 1 s, is no longer counted anywhere. It was never search time.
  • In the head-to-head, XERJ Query-1 read each question's own records while the RAG pipelines had to search one merged store. Opus 5.5 given the same records made 5 wrong answers against XERJ Query-1's 1, a difference our sample cannot separate. All 160 questions come from the model's development set.
  • Every data set is now named: English Wikipedia (wikimedia/wikipedia 20231101.en on Hugging Face), the public sources of our test records, Wikipedia passages with HotpotQA questions. R@k and nDCG@k on BEIR, on the practitioner's SearchTome benchmark and on our own test, with the latency split, are in benchmarks/query-1-2026-11.
  • We withdrew "never invented", "without the flag a node behaves exactly like rc.100", "every number can be re-run", "0 s to re-index", "under a gigabyte" and the int8-guard "served default" labels; replaced the 95,000-fact timing with a new measurement; corrected the statement that we never used FP32 and the citation of Neural Symbolic Machines (ACL P17-1003); and fixed the monthly cost table, which counted the CPU cost twice.

Retrieval finds documents. XERJ Query-1 answers questions over facts you store, and shows the program, and for lookups the records, behind each answer.

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

≈220 msvs1.5–2.3 s
question to answer on the same 160 questions: XERJ Query-1 on 4 laptop CPU cores vs Opus 5.5 writing a RAG answer (search itself took under 1 ms to 106 ms)
Ask. It writes the program.
On the same 160 questions, Opus 5.5 took 1.5 to 2.3 s to write each RAG answer (API time per call; our command-line client's start-up is not counted), after a search that took under 1 ms to 106 ms. Writing the answer is where RAG's answer quality comes from; XERJ Query-1 writes a program instead and runs it on the CPU.
$42vs$13,400–$694,000
per million questions on our 160-question test, at list prices: XERJ Query-1's CPU time vs RAG, GraphRAG and Opus 5.5 reading the data
Runs on your records.
No API model is billed per question: XERJ Query-1 runs a small model on your CPU, and that CPU time is the $42. The same million questions cost $13,400 to $21,000 on RAG with Opus 5.5, $65,400 on GraphRAG and up to $694,000 with Opus re-reading the whole store for every question (ARITHMETIC from measured tokens).
1vs9–15
wrong of 160 questions from our development set, vs RAG on Opus 5.5. XERJ Query-1 was given each question's own records and declined 35; Opus 5.5 given the same records: 5 wrong
Checked or refused.
A missing fact gives UNKNOWN; facts that disagree give CONFLICT. Most RAG errors came from search missing the evidence. Answers are stored values or computed counts, never free text, but they can still be wrong: 1 of 160 here, and some counts in known cases.
17 minvs62–97 h
a keyword (BM25) index of 10 GB of English Wikipedia, 3.3 million articles, on CPU (measured) vs embedding it with bge-small (estimated)
No embeddings.
17 minutes and 0.7 CPU-hours; 100 article-title queries then took 9.3 ms at the median. Embedding the same text would take an estimated 62 to 97 hours and 8.5 GB of vectors. The two do different jobs: on BEIR, vector search beat BM25 by 0.04 to 0.17 nDCG@10. XERJ Query-1 answers over facts you write, not over this index.
≈2 msvs≈30 ms
to apply one correction: a XERJ facts write (measured) vs re-embedding the one changed chunk (ARITHMETIC)
A store you can correct.
Fix a fact and the next ask uses it (10 of 10 in our test); earlier answers replay by revision. An embedding index re-encodes the changed text, and the whole store (19.6 s for our 5,000-record test store) when its model changes.
1 binaryvs3 services
facts, memory and brains over 24 MCP tools vs vector DB + graph DB + cache
One server instead of a stack.
Agent memory, linked knowledge and facts together, over 24 MCP tools in one binary; no separate vector DB, graph DB or cache service. A monthly cost example, priced and not run, is in §03.

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.

Before we fixed the docs on 2026-11-05, 1 of 8 fresh agent runs got the model to answer; the fixed flow has not been re-tested yet.

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 prints "not_loaded" for about 3 seconds after start, then "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?

Many 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, and nothing in plain RAG checks it against the data. In the head-to-head we report in §07, most RAG wrong answers came when retrieval missed evidence: with all of it in context, Opus 5.5 got 76 of 79 right and 2 wrong. Every pipeline built on Opus 5.5 answered 5 to 15 of 160 questions wrong. XERJ Query-1 answered one wrong and declined 35, but it was given each question's own collection, so compare with care: 1 wrong against 9 to 15 for RAG on Opus 5.5, which had to retrieve, 6 for Opus reading the whole store, and 5 for Opus given the right collection, a difference our test is too small to call.

The second problem is cost. A language model is paid per piece of text it reads and writes, on every question, for every user. On our 160-question test the cheapest Opus 5.5 pipeline cost $13 per 1,000 questions at list price; reading the whole store for each question cost $694 (20-question probe, no caching). XERJ Query-1 used $0.042 of CPU time per 1,000 of those questions on four CPU cores, with no GPU and no network: 320 to 16,700 times cheaper per question on that test (ARITHMETIC). 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. It turns a question into a short, checkable program over facts stored in the engine, and declines when they cannot support an answer (with one known gap for sums). The research ideas behind it go back years, and we cite the work we build on 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: 674 MiB right after loading, about 1.5 GB after our 160-question test.

On our development evaluation, 1,600 questions over MoMA, Tate, PyPI, USGS, OurAirports, NASA and UCI retail data and invented worlds, scored against Opus 5.5 reading each question's whole world, XERJ Query-1 scores 90.29 where the reference is 100, with 9 wrong answers against the reference's 22. That evaluation is the one the model was developed against, not a fresh set, and the head-to-head's 160 questions are drawn from it, so the two are not independent. 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 answer is only as good as the chunks retrieved: with all evidence in them Opus 5.5 got 76 of 79 right in our test; when some was missing, 3 of 11.

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 must be updated when the data changes (the package supports incremental indexing; we did not measure it).

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, held in accelerator memory for every request in flight; a shared prefix can be cached and reused, which hosted APIs sell as prompt caching. 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 for our 160 test questions at list prices: the LLM methods read one merged store of 5,000 records (4,305 distinct); XERJ Query-1 read each question's own collection. Opus 5.5 at $4/$20 per million tokens, CPU at $0.03625 per vCPU-hour.: 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 measured, arithmetic from a measured rate or list price, or an assumption we state; we label 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: 267 model calls, 72 minutes one at a time (12 minutes 6 in parallel). 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 given each question's own world cost $28.84, averaged from its 1,600-question reference run (not re-run for this test). 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; cost averaged from its 1,600-question reference run)$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 our 160 test questions (the 1-per-call row on a 20-question probe, the oracle averaged from its 1,600-question run); per-million figures are per-1,000 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, earlier int8-guard bundle, not shipped (518 CPU-ms/q) XERJ Query-1, earlier int8-guard bundle, not shipped (projection from 518 CPU-ms per question): $0.0052 per 1,000 questions; the shipped bundle used 0.89 CPU-s per question on an x86 laptop, $0.009 per 1,000 (ARITHMETIC) $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 (reference-run cost) Opus 5.5, oracle listing (cost averaged from its 1,600-question reference run): $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, no caching; CPU at $0.03625 per vCPU-hour (c7g.xlarge on-demand); same 160 questions; LLM arms read the merged store, XERJ Query-1 each question's collection

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 int8-guard figure came from an earlier, unshipped bundle on a different workload (one 40,000-fact namespace, one templated question), so we replace it with the shipped bundle's laptop run. 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,299,578 articles: the first 22 of 41 files of the Hugging Face wikimedia/wikipedia 20231101.en dump), XERJ built its full-text index in 17 minutes and 0.7 CPU-hours with 16 workers on a shared 20-core aarch64 box, and answered 100 article-title keyword queries in 9.3 ms at the median (13.5 ms p95). We measured embedding speed on samples and extrapolated: embedding the same text with bge-small would take 62 to 97 hours of wall time and leave 8.5 GB of vectors (arithmetic). Building a GraphRAG graph over it would cost roughly $170,000 to $250,000 of Opus 5.5 calls before the first question (arithmetic; it depends on the text-unit size). The smaller rates behind the same story: we measured the encoder on 2,335 passages of about 378 tokens cut from XERJ's own documentation, 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. In an in-memory float32 HNSW index the vectors take 1.54 or 3.07 GB per million passages, 154 or 307 GB per hundred million, plus about 13 GB of search links, less if quantized or served from disk. 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 once; each question within five minutes then pays about $0.035 in cache reads, which also keeps the cache alive. 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 hypothetical workload: 10 GB of raw text, taken as 6.6 million passages of 378 tokens (at 4 characters per token; our Wikipedia text measured 4.64, about 5.5 million), and 1 million questions a month, at list prices fetched on 2026-11-05., 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 10 GB Wikipedia indexing run: 11.86 GB on disk after the final flush, and indexing peaked at 17.3 GiB of RAM. Add a 30 GB disk: $426 a month. A million questions at our measured 4.1 CPU-seconds each use about 10% of that server's CPU, so they add nothing to the bill. 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 154 times and 50 times the XERJ server line (arithmetic), which assumes the facts are already written and the CPU cost we measured on small namespaces. 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 $426. 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 + 30 GB gp3 $2.40 (1M questions at 4.1 CPU-seconds each use about 10% of its CPU, already paid for)$425.801×
Vector-RAG stackPinecone Standard $220.30 + Redis Cloud $5.00 + Opus 5.5 answers $20,950.00$21,175.3050×
GraphRAG stackPinecone Standard $220.30 + Neo4j AuraDB 2 GB $131.40 + Redis Cloud $5.00 + Opus 5.5 answers $65,390.00$65,746.70154×

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 roughly $170,000 to $250,000 (arithmetic). XERJ needs no graph build and no upsert, but XERJ Query-1 needs facts extracted and written, which we have not priced. 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 CPU time measured on namespaces of 6 to 148 facts. On a 95,000-fact namespace asks took about 0.8 s instead of 0.22 s and every test question was declined, so we have no useful cost figure at that size. More data on XERJ means more disk ($0.08 per GB-month) and, while indexing, more RAM (17.3 GiB peak for 10 GB). On the managed stack the vector and graph tiers grow with the data.

Put together, on our 160-question test: 320 to 16,700 times cheaper per question ($42 against $13,400 to $694,000 per million, arithmetic). Per question on that test, XERJ Query-1 is 320 to 500 times cheaper than classic RAG on Opus 5.5, 470 times cheaper than Opus reading the whole store 40 questions at a time, and 1,570 times cheaper than GraphRAG (arithmetic). Hundreds of times, on the same questions, with XERJ Query-1 given each question's collection, no graph to build and no corpus to embed; the facts still have to be written. 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 turns a user's question into a program over facts stored in a search engine, 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 we have not found elsewhere, stated as narrowly as we can: 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; refusal as part of the contract, so it says UNKNOWN rather than guessing when a pointer cannot be grounded; and serving it offline on four CPU cores (674 MiB right after loading, about 1.5 GB after our test). We know of no other search engine that ships that combination. 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 LOOKUP POINTS AT A RECORD the answer is computed by the engine, never written by the model; counts name their filters, not yet their records

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 is cheap because the program is a handful of tokens read from a short listing (about 1,500 tokens): about 0.9 CPU-seconds per question on an x86 laptop in our test., 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, Neural Symbolic Machines (ACL P17-1003); ... 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.

Our goal was a model that answers on an ordinary CPU, with no GPU, and with nothing leaving the machine; the shipped model answers in about 0.22 s on four laptop cores (about 1 s on a loaded aarch64 box). 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 training stayed in FP16. The shipped weights are FP16 plus a 32-bit embedding table, and on CPU the models compute in 32-bit. 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. By our accounting the total is in the thousands of US dollars, not millions (invoices not published); 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. The shipped bundle stores FP16 weights (408 MB download); the facts model is converted to 8-bit weights at load. The process used 674 MiB after loading and about 1.5 GB after our test. Smaller numbers can change answers, so A guarded int8 mode exists, but the shipped bundle has no calibration for it and serves the plain w8 mode. On an earlier bundle the guarded int8 mode agreed with FP16 on 400 of 400 checks and w8 on 390. The shipped bundle serves w8; its 90.29 was scored in that mode., not on a benchmark score. The shipped configuration takes 219 to 226 ms per question at the median on four laptop cores on our 160 test questions.

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) against a bar fixed before the screens: mean gain of 3 points or more, an interval above zero, no family down more than 1 point. The shipped bundle did not meet it (+2.81, best single seed) and was promoted by owner decision., 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 set of 270 freshly worded questions over small invented worlds without entity ids 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 (1,600 scored plus 787 refusal items) from the previous bundle, and the rows matched the research rows: program, raw output and evidence identical on every item, score reproduced to the hundredth. For the shipped bundle, an offline serving tool matched all 2,387 items and the engine matched our 160 test questions. 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. Most numbers trace to a run, and we mark the few that do not. Our provenance record maps each figure to its source; it is not public yet.

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, each number tied to the run that produced it (most of those runs are not public yet), 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 model, trained with the same recipe from a different seed, writes the same program. The outcome is ANSWER, UNKNOWN, CONFLICT or UNSUPPORTED (outside the op-set): ANSWER with its evidence for lookups and comparisons, or with its program for counts; 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 lookup or comparison answer carries the ids of the records it was computed from, because the program is made of them; counts do not yet list the counted records. 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: right when the data cannot answer, otherwise 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"
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
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

Every symbol the model can write. Three models ship, and all their programs run through the same executor. The facts model writes pointer programs, two to six steps. The counting model writes set programs, or refuses, and a partner counting model must write the same set program before a count is served. The counts below come from all 2,387 served items of the standard evaluation (its 1,600 scored questions plus 787 refusal items): 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 that finishes ends here (9 of 2,387 hit the step limit and return UNKNOWN)
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. Right after loading on an empty node the process used 674 MiB (811 MiB peak) and loading took 159.6 ms with checksums verified; after our 160-question test it held about 1.5 GB. Answering runs the counting stage and the facts model; the executor itself takes microseconds (43.5 µs median). Two latency numbers appear in this post and they are not the same measurement. The 120 ms median was an earlier, unshipped bundle in int8-guard mode on one 40,000-fact namespace with one templated question; the shipped bundle took 219 to 226 ms on our test on the same laptop. 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 shipped bundle on an x86 laptop used 0.89 CPU-s per question, $0.009 per 1,000 (ARITHMETIC)., 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, EARLIER BUNDLE, ONE 40,000-FACT NAMESPACE p50 int8-guard, tau x0.125 (earlier bundle, not shipped): 120 ms 120 p50 w8, int8 weights dequantised per call (the shipped mode; earlier bundle): 163 ms 163 p50 p95 int8-guard, tau x0.125 (earlier bundle, not shipped): 176 ms 176 p95 w8, int8 weights dequantised per call (the shipped mode; earlier bundle): 174 ms 174 p95 674 MiB AT LOAD; ABOUT 1.5 GB AFTER OUR TEST 159.6 ms LOAD TIME, HASHES VERIFIED 16.8 q/s CONCURRENCY 8, EARLIER INT8-GUARD BUNDLE int8-guard, not shipped w8, shipped mode

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. We think the tail barely moves because of the guard's fallbacks; we did not measure that per question. Right: memory and load time right after loading, pre-release build serving the FP16 bundle on aarch64, model state loaded, checkpoint hashes verified. Left: an earlier bundle in int8-guard mode on a quiet x86 laptop, one 40,000-fact namespace and one templated question. Two machines, shown side by side, not combined.

What the index costs. Because XERJ Query-1 reads facts stored in the engine, there is no encoder pass over the corpus and no vector field; the facts still have to be extracted and written. On 10 GB of English Wikipedia (3.3 million articles) XERJ built its keyword index in 17 minutes and 0.7 CPU-hours, 11.86 GB on disk, 9.3 ms at the median on 100 title queries; embedding the same text would take 62 to 97 hours of wall time (arithmetic). Facts are fast to write too: 95,000 facts (the first sentence of 95,000 English Wikipedia articles) went into one namespace in 23 s on an x86 laptop, about 4,100 a second. On a 1.08 GB slice of English Wikipedia passages (1,117,217 passages), XERJ's full-text index, which answers word queries and aggregations 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.08 GB of English Wikipedia passages, 1,117,217 passages, 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.08 GB of English Wikipedia passages (1,117,217 passages). Default layout and three opt-in layouts, plus the stored-text family alone before and after source_ref. All four layouts return the same top-16 results as rc.100 on 200 of 200 recorded queries over those Wikipedia passages (agreement, not a quality score). Opt-in because each gives something up: source_ref needs the source files present and unchanged (an edited file should give an explicit error; in rc.101 a same-size edit can still return the old text, a known bug), costs 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. We trained XERJ Query-1 once, on practice questions over invented worlds and public data (Tate, PyPI, MQuAKE, HotpotQA, 2WikiMultihopQA), some written by Claude Opus 5.5. 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 through /_facts/{ns}/_bulk (the MCP tool only asks). 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?
Usually it says so: UNKNOWN when no fact backs a lookup, CONFLICT when two disagree. It can still be wrong (1 of 160 in our test), and a 'total' question can come back as a member count.
Does anything leave my machine?
The model and engine send no question, fact or answer anywhere. The installer asks AI agents to send a short usage report; turn it off with XERJ_DISABLE_FEEDBACK=true.

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 /_facts/{ns}/_bulk, with a stable subject_id on each subject (needed for counting); the engine does not yet turn documents into facts.; 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 it by word overlap (the lexical embedder is the default), link records into a graph, read a node's neighbourhood with its evidence; in rc.101, 24 tools with the model on and 23 without it (main adds one more, xerj_sources). 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 every /_facts path returns 404 and the MCP tool list is unchanged; rc.101's engine changes still apply, including the one-way data-directory upgrade. 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. Two were measured on 1 million English Wikipedia passages with 1,337 HotpotQA queries; the merge fix came from a measured 36.9 GB peak; four have an opt-out.:
  • Smaller postings by default: on 1 million English Wikipedia passages the word index shrinks from 325.1 to 288.3 MB and reader memory from 573 to 480 MB, for about 2% at p95 query latency.
  • Stored rows in compressed blocks: on 1 million Wikipedia passages a page of 25 rows drops from 4,968 ms to 50 ms at the median; merge encode CPU rises from 18.5 s to 48.6 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 once reached 36.9 GB peak resident under a 6 GiB budget; merges are now bounded at budget/40 (after-fix peak not yet measured).
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 1 million English Wikipedia passages 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 1 million English Wikipedia passages (up to 1,200 characters each) with 1,337 HotpotQA questions.

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 merged 95 collections of our standard evaluation into one store of 5,000 records (subject, relation, typed value, source sentence): 82 distinct collections and 4,305 distinct records, because 13 collections were included twice by mistake. 87% of the records come from public data (MoMA, Tate, OurAirports, USGS, the NASA Exoplanet Archive, UCI Online Retail and PyPI metadata), the rest from invented worlds; 357,085 characters, 172,870 Opus tokens. We asked 160 questions, forty from each of four families, all from our 1,600-question development set; the sampling script was not saved. 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, live, each question in its own namespace124135$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, re-scored reference run)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 wrote 160 per-question namespaces (9,496 facts) in 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 (given each question's own collection): score 96.1 [82.6, 114.9], wrong 1 96.1 1 reference arm Opus 5.5, oracle listing (reference-run cost) 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 (reads the merged store): score 92.9 [81.0, 104.0], wrong 6; wrong minus XERJ [+0, +11] 92.9 6 [+0, +11] GraphRAG local search + Opus 5.5 GraphRAG + Opus 5.5 (retrieves from the merged store): score 85.8 [73.8, 98.5], wrong 9; wrong minus XERJ [+1, +16] 85.8 9 [+1, +16] excludes 0 RAG, BM25 top-10 + Opus 5.5 RAG, BM25 top-10 + Opus 5.5 (all evidence retrieved for 87.8% of scored questions): score 83.5 [70.7, 94.7], wrong 9; wrong minus XERJ [+2, +15] 83.5 9 [+2, +15] excludes 0 RAG, BM25 top-5 + Opus 5.5 RAG, BM25 top-5 + Opus 5.5 (all evidence retrieved 78.9%): score 73.2 [58.5, 86.3], wrong 12; wrong minus XERJ [+4, +18] 73.2 12 [+4, +18] excludes 0 RAG, embeddings top-10 + Opus 5.5 RAG, embeddings top-10 + Opus 5.5 (all evidence retrieved 77.8%): score 63.8 [46.5, 78.4], wrong 14; wrong minus XERJ [+5, +21] 63.8 14 [+5, +21] excludes 0 RAG, embeddings top-5 + Opus 5.5 RAG, embeddings top-5 + Opus 5.5 (all evidence retrieved 68.9%): score 59.8 [41.6, 77.3], wrong 15; wrong minus XERJ [+6, +24] 59.8 15 [+6, +24] excludes 0 160 questions over 82 distinct collections (95 entries, 13 duplicated); score = 100 x family-weighted Net(arm) / Net(oracle); 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, against 9 to 15 for RAG on Opus 5.5, 6 for Opus reading the whole store and 5 for Opus given the right collection, the like-for-like comparison. Language-model pipelines answered 5 to 15 wrong; the intervals exclude zero against GraphRAG and RAG, which also had to retrieve; against whole-store Opus the interval touches zero, against the oracle it does not separate.; 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, whose retrieval often missed the evidence, and within noise of GraphRAG, BM25 top-10, whole-store Opus and the oracle. It declined 35 of 160, more than BM25 RAG, GraphRAG and Opus (18 to 31) but fewer than embedding RAG (37, 39); that is where its score goes. 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 each question's whole world (a superset of the model's listing) 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

Opus 5.5 counted 10 where the answer is 6; we did not analyse why. In the program the filters are explicit and the count is a loop the engine runs.

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. The reference's answer carries no pointer, because the test did not ask for one.

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 s1b_dev (0.043) and bench_select (0.053), and by most on d1_select (0.145), where XERJ Query-1 declines 81 questions against the reference's 17. 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 (best of three seeds)
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 we knew on 5 November, and those found in review. (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. All 160 head-to-head questions are items of it, so XERJ Query-1 is on home ground and the language-model methods were not trained for it. Found in review: XERJ Query-1 was given each question's own collection while the RAG arms retrieved from the merged store; 13 collections were included twice; the 120 ms latency we first published came from an unshipped bundle; our provenance file and scripts are not public. (2) Robustness to new wordings and training overlap were not re-measured for this bundle. On a separate set of 270 freshly worded questions over small invented worlds, with 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 82 distinct collections in the head-to-head; the intervals are wide. The wrong-answer gap survives against the RAG arms, which also had to retrieve; against Opus given the right collection it does not separate. (4) All costs are list prices from the logged token counts; the oracle's cost is averaged from its 1,600-question run. Opus 5.5 ran at its default effort through a subscription, whose own billed figures, which include cache writes, are higher and sit in our private results file. Language-model latencies are the Messages API time recorded for each call: 1.5 to 2.3 s for Opus to write the answer, with our command-line client's start-up not counted; retrieval took 0.75 to 106 ms. XERJ Query-1 took 0.22 s at the median on a quiet laptop (about 1 s on a loaded, shared aarch64 box). (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 against 216 for the reference, and 35 of 160 against 18 to 39 for the other methods. 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 scores: 96.1 is 160 questions drawn from the 1,600, against the Opus oracle; 90.29 is all 1,600 against the reference. Do not add them up.

What it does not do yet. Counting covers set counts only, "how many X with Y" (filters such as height above 19 cm included), and only over facts that carry a subject_id; sums are outside the op-set and usually declined, but see the defect below. 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. Our review found a second case: an "or" question (people in Berlin or Munich) was served as a verified count of the wrong set. 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 namespace of 95,000 facts (the first sentences of 95,000 English Wikipedia articles) asks took 0.49 to 1.55 s (median 0.82 s) on a laptop with the shipped model, and all 20 test questions were declined. 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 with the same rules, scored against the same gold answers. The language-model methods read one merged store of 5,000 records (4,305 distinct); XERJ Query-1 read each question's own collection. The standard evaluation is 1,600 questions, scored against Opus 5.5 reading each question's whole world. 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 ran on 10 GB of English Wikipedia (Hugging Face wikimedia/wikipedia 20231101.en, the first 22 of 41 files); embedding speed was measured on samples and extrapolated. The engine is open at github.com/xerj-org/xerj. Retrieval metrics (R@k and nDCG on BEIR, on SearchTome and on this test), the latency split and every data set are in benchmarks and methodology (the scripts follow in a later change); the evaluation data, gold answers and scorer are not public yet, so others cannot re-run the head-to-head today.

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.

Before we fixed the docs on 2026-11-05, 1 of 8 fresh agent runs got the model to answer; the fixed flow has not been re-tested yet.

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 prints "not_loaded" for about 3 seconds after start, then "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. The method, rules and numbers are in this post; the runs, scripts and provenance record are not public yet., 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; about 96 non-right items would be right with a different decline policy, and 169 need operations it lacks. Counting works only on records with a stable subject id and covers set counts with filters, not sums. 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 namespace of 95,000 facts (the first sentences of 95,000 English Wikipedia articles) asks took 0.49 to 1.55 s (median 0.82 s) on a laptop with the shipped model, and all 20 test questions were declined. 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. By our own accounting, 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 rules before the runs, the losing rows kept, and the provenance record, which we have not yet published. 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.