XERJ QUERY-1· NEUROSYMBOLIC MODEL· SHIPS IN RC.101 · OPT-IN

PROMPTS IN.
VERIFIABLE PROGRAMS OUT.

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.

It is off by default. A node becomes a XERJ Query-1 node when you start it with xerj --facts-model-dir <DIR>. Without the flag, rc.101 behaves exactly like rc.100.

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; 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 store
A store you can correct.
Fix a fact, the next ask follows; earlier answers replay by revision. An embedding index re-encodes.
1 binaryvs3 services
facts, memory and brains over 24 MCP tools vs vector DB + graph DB + cache
Memory and brains beside it.
Agent memory, linked knowledge and facts together; no separate vector DB, graph DB or cache service.
01 · CAPABILITY

Ask. It writes the program.

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

You write a plain question: "What is Bo Chen's height?", "Which river is longer, X or Y?", "How many people have office Berlin?". The model reads the question plus a numbered listing of the facts in the namespace and emits a few program steps: LOOKUP #12, CHAIN, LT / GT / EQ / NOT, PICK_LT, or for counts SET → FIND → FILTER → AGG COUNT. It never writes the answer itself.

Compared with an LLM, whose output is the answer as text. Here the output is a program a machine can run and a person can read, and the answer comes from running it.

Request · a plain question (an evaluation item; the names are synthetic)

POST /_facts/world-dev-90014/_ask
{"question": "Which river carries less water, Arhalga or Gacunas?"}

Response · the program the model wrote, then the trace and the answer

program   LOOKUP #10 ; LOOKUP #7 ; LT ; RETURN
step 0    LOOKUP #10  → 6956 m3/s   doc3@v1/discharge, Arhalga River
step 1    LOOKUP #7   → 7567 m3/s   doc6@v1/discharge, Gacunas River
step 2    LT          → TRUE        6956 < 7567
answer    Arhalga River

What #10 and #7 mean

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

02 · CAPABILITY

Runs on your records.

$42vs$13,400–$694,000
per million questions, CPU vs RAG / GraphRAG / Opus reading the data

Each step points at a stored fact: subject, relation, typed value, source document and the character span the value came from. The executor runs the program in microseconds and returns the answer plus the trace: which record, which version, which span. Nothing in the answer is generated; it is read off the store.

Compared with RAG, where the model reads retrieved chunks and writes an answer nothing checks. Here the evidence is a pointer you can follow to the exact characters.

Request

POST /_facts/hr/_ask
{"question": "How tall is Ana Bel?"}

Response · answer with the trace

{
  "final_status": "ANSWER",
  "final_answer": "170 cm",
  "program": [["LOOKUP", 0], ["RETURN"]],
  "evidence": [{
    "fact_id": "f1",
    "doc_id": "d1", "version": 1,
    "char_start": 11, "char_end": 14, "quote": "170",
    "content_sha256": "…"
  }],
  "published_rev": 9
}
03 · CAPABILITY

Verified or refused.

1vs9–15
wrong of 160: verified or refused vs RAG on Opus 5.5 (Opus reading everything: 5–6)

A verifier checks the program against the store before anything is served. A pointer that resolves to nothing returns UNKNOWN. Two stored facts that disagree return CONFLICT, and that propagates through any comparison built on them. A count is served only when the executor can close the set and a second, independently seeded model writes the same program.

Measured on the same 160 questions over the same store: XERJ Query-1 answered 1 wrong; every pipeline built on a language model answered between 5 and 15 wrong. It declined 35, more than any other arm. That is the price of not guessing.

A fact that was never written

{"question": "What is Ana Bel's salary?"}
→ {"final_status": "UNKNOWN"}

Two heights stored for the same person, neither supersedes the other

{"question": "How tall is Bo Chen?"}
→ {"final_status": "CONFLICT", "evidence": []}

A count, served only with partner agreement

{"question": "How many people have office Berlin?"}
→ {"final_status": "ANSWER", "final_answer": "3",
   "path": "counting", "partner_agreement": true}
04 · CAPABILITY

No embeddings.

17 minvs62–97 hours
indexing 10 GB of Wikipedia (3.3 million articles) on CPU vs embedding it with bge-small

There is no vector step on this path. The "index" the model uses is the facts store the engine already keeps: typed records, an inverted index and doc-values. Writing 9,496 facts took 8.8 seconds and 15.4 MB on disk; no encoder ran, no vector memory was allocated, and there is nothing to redo when a model changes. The same binary still offers BM25 full-text search, aggregations and kNN for documents.

Compared with an embedding index: the same records as 660 chunks took 19.6 seconds and 1.28 GB of memory to encode with a small embedding model on 8 threads, and must be re-encoded whenever that model changes.

Index build, measured, per arm (same 5,000 records)

XERJ Query-1   8.8 s wall · 5.5 CPU-s · 15.4 MB · no LLM call
BM25 RAG       0.02 s · no LLM call
Embedding RAG  19.6 s · 1.28 GB peak RSS (bge-small, 8 threads)
GraphRAG       267 LLM calls · $14.27 · 72 min serial

Still a full search engine on the same port

POST /hr/_search  {"query": {"match": {"bio": "designer Berlin"}}}
POST /hr/_search  {"aggs": {"by_office": {"terms": {"field": "office.keyword"}}}}
POST /vecs/_search {"knn": {"field": "v", "query_vector": [...], "k": 2}}
05 · CAPABILITY

A store you can correct.

0 svs19.6 s
re-index after a correction vs re-embedding the store

Facts go in through /_facts/{ns}/_bulk: text, number, date or boolean values, a stable subject_id per entity, and the source document with the quote span. A correction supersedes the old fact and the answer changes, while the old answer stays readable at its revision. A second, disagreeing fact turns the answer into CONFLICT until one is withdrawn. Every ask can be pinned to a revision. About 44 ms per ask on four x86 cores; about 120 ms at the median in the int8 serving configuration.

Compared with a prompt cache or a vector index, where a changed fact means re-embedding or re-reading. Here a change is one edit, and history is kept.

Write facts with their source

POST /_facts/hr/_bulk
{
  "documents": [{"doc_id": "d1", "version": 1,
                 "text": "Ana Bel is 170 cm tall and works in Berlin."}],
  "facts": [{"fact_id": "f1", "subject": "Ana Bel", "subject_id": "emp:1",
             "relation": "height", "kind": "number", "value": "170 cm",
             "doc_id": "d1", "version": 1, "char_start": 11, "char_end": 14}]
}
→ {"committed": {"from_rev": 1, "to_rev": 9}}

Correct it, then ask again, then ask as of before

POST /_facts/hr/_bulk
{"edits": [{"op": "correct",
            "doc":  {"doc_id": "d1", "version": 2, "text": "Ana Bel is 172 cm tall…"},
            "fact": {"fact_id": "f7", "subject_id": "emp:1", "relation": "height",
                     "kind": "number", "value": "172 cm", "doc_id": "d1", "version": 2},
            "supersedes": "f1"}]}
→ {"committed": {"from_rev": 13, "to_rev": 14}}

{"question": "How tall is Ana Bel?"}                 → "172 cm"  (evidence f7)
{"question": "How tall is Ana Bel?", "revision": 9}  → "170 cm"
DELETE /_facts/hr/f9                                   → a withdrawn fact; CONFLICT clears
06 · CAPABILITY

Memory and brains beside it.

1 binaryvs3 services
facts, memory and brains over 24 MCP tools vs vector DB + graph DB + cache

Agent memory (store, recall, forget; lexical ranking today) and brains (link, unlink, ego with evidence, overview) are separate surfaces, available over HTTP and as MCP tools. Brain names ending in -facts are reserved for facts namespaces. The model reads facts namespaces only; memory is for notes an agent wants back later, brains are for the links between them.

Compared with a chat model's context window, which forgets at the end of the session and is paid for on every turn. Memory here is a store on disk, recalled by query, with a graph on top.

Store a note, recall it, link it

POST /_memory/agent1  {"text": "Ana Bel is the Falcon tech lead", "tags": ["falcon"]}
POST /_memory/agent1/_recall  {"query": "who leads Falcon", "k": 2}
→ two notes with scores (lexical ranking)

POST /_graph/agent1/link
{"src": "mem:falcon", "dst": "person:ana-bel", "type": "led_by",
 "evidence": {"quote": "…", "source": "…", "offset": 0}}
→ 201 {"edge_id": "…"}
GET /_graph/agent1/ego?node=person:ana-bel&hops=1  → the edges, with evidence

The same surfaces as MCP tools

xerj_memory_store · xerj_memory_recall
xerj_brain_link · xerj_brain_unlink · xerj_brain_ego · xerj_brain_overview
xerj_facts_ask          (listed only when the node runs the model: 24 tools on, 23 off)
COMPARED · SAME 5,000 RECORDS · SAME 160 QUESTIONS · LIST PRICES

Five ways to answer a question

One store merged from 95 collections of the standard evaluation, 160 questions with known answers, scored by the same frozen rubric. Costs are per million questions, ARITHMETIC: the measured per-1,000-question list price times 1,000. Opus 5.5 at $4 per million input tokens and $20 per million output tokens; CPU at the AWS c7g.xlarge on-demand rate of $0.145 per hour ($0.03625 per vCPU-hour) at 4.1 CPU-seconds per question, the live run.

ApproachReturnsCheckableIndex timeCost / million qWrong of 160Offline on CPU
Keyword search (BM25)documentsno answer to check0.02 sCPU onlyn/ayes
Embeddings + RAG on Opus 5.5 (top-10)generated textno19.6 s, 1.28 GB$19,85014no
BM25 + RAG on Opus 5.5 (top-10)generated textno0.02 s$20,9509no
GraphRAG on Opus 5.5generated textno72 min, 267 LLM calls, $14.27$65,3909no
Opus 5.5 reading the whole store (40 q per call; 1 q per call: $694,300)generated textnonone$19,4106no
Opus 5.5 with perfect retrievalgenerated textnoassumes perfect retrieval$28,8405no
XERJ Query-1program + traceyes, step by step8.8 s, 15.4 MB$421yes

Source: the head-to-head measured on 2026-11-05 for the XERJ Query-1 announcement (n = 160, 95% paired bootstrap intervals; the score gap is statistically clear against embedding RAG and BM25 top-5, the wrong-answer gap against every LLM pipeline). Records were pre-extracted for every arm; questions are from the model's own evaluation distribution. The XERJ cost is the live run on a shared aarch64 box; the int8 configuration on a quiet x86 box measures lower.

WHAT IT DOES NOT DO · VERIFIED ON RC.101
No fact extraction from arbitrary documents. Users or agents write facts (xerj autoindex yields records and a graph, not facts namespaces). The river example in the announcement uses records that came from the evaluation pipeline.
A count does not check the noun of the question. "How many devices in Berlin in total?" over a store with no devices relation returns the member count (ANSWER 3) as verified. Disclosed here and in the docs; the real fix is in the next model.
It over-declines. 383 of 1,600 standard-evaluation items and 35 of 160 in the head-to-head came back UNKNOWN where the reference reader answered. That is the price of not guessing, and the first thing the next training program targets.
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 until the next model.
Off by default. Without --facts-model-dir the node is rc.100: no /_facts route, no model load, the same MCP tool list.
COUNTING · IN DETAIL

Counting, in detail

set counts, partner-verified · needs subject_id · no sums in rc.101

Set counts over facts that carry a subject_id: "how many X have Y", "distinct values of a field". The model writes a set program, the executor runs it over the store, and the count is served only when a second, independently seeded model writes the same program. Not sums, totals or thresholds in this release.

Compared with asking a language model to count from retrieved chunks, where the number is generated and nothing checks it. Here the count is a loop the engine ran, and the program is named in the response.

Facts with subject ids, then a count

facts: [{"subject": "Ana Bel", "subject_id": "emp:1", "relation": "office", "value": "Berlin"},
        {"subject": "Bo Chen", "subject_id": "emp:2", "relation": "office", "value": "Berlin"},
        {"subject": "Cy Dao",  "subject_id": "emp:3", "relation": "office", "value": "Berlin"},
        {"subject": "Di Eck",  "subject_id": "emp:4", "relation": "office", "value": "Munich"}]

{"question": "How many people have office Berlin?"}
→ {"final_status": "ANSWER", "final_answer": "3",
   "path": "counting", "partner_agreement": true, "program_sha256": "…"}

{"question": "How many people have role engineer?"}   → "2"

Without subject ids the set cannot be closed

→ {"final_status": "UNKNOWN", "path": "facts", "counting": {"status": "INCOMPLETE"}}

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.
TRY IT · ONE BINARY · FOUR CPU CORES · UNDER A GIGABYTE

Run it today

Install the engine with the model bundle, start the node with the flag, write a few facts, ask a question. The trace is the product.

curl -fsSL https://xerj.org/get | sh -s -- --with-model
xerj --facts-model-dir ~/.local/share/xerj/model --insecure --data-dir ~/xerj-data
curl -s localhost:9200/_facts | jq '.model.state'      # "loaded"

The write contract, the two ask routes and the counting rules are in the model quickstart and the facts API reference. The announcement, with the measurements and how the model was trained, is at /blog/xerj-query-ai-model.

XERJ Query-1 was trained on donated GPU time. If it is useful to you, fund the next model.