An agent needs to find things, find them by meaning, remember what it learns, and recall it later. XERJ is one Elasticsearch-compatible binary that does all four — keyword, semantic, vector, and hybrid search plus a per-agent memory store — over plain HTTP on :9200. No SDK, no signup, no external embedding key. If your agent can make an HTTP request, it can use XERJ.
Store what an agent learned, then recall it with a question that shares no keywords — no vectors supplied, no index to create first. This is a real run against an empty XERJ.
$ curl -sXPOST localhost:9200/_memory/agent-demo \
-H 'content-type: application/json' \
-d '{"text":"The user prefers metric units and a dark UI theme.","metadata":{"kind":"preference"}}'
{"created":true,"id":"77eff57b-e432-431c-8b49-8a16b33ab551","namespace":"agent-demo"}
$ curl -sXPOST localhost:9200/_memory/agent-demo/_recall \
-H 'content-type: application/json' \
-d '{"query":"what display settings does the user like?","semantic":true,"k":1}'
{"hits":[{"id":"77eff57b-e432-431c-8b49-8a16b33ab551",
"metadata":{"kind":"preference"},
"score":0.655571460723877,
"text":"The user prefers metric units and a dark UI theme."}],
"namespace":"agent-demo"}
"semantic":true tells XERJ to embed the question
server-side and match by similarity — the recall text never contains the
words "display" or "settings".
Keyword & structured (match, term, bool), semantic retrieval by meaning, exact knn vector search, and hybrid fusion — all on the same ES-compatible _search endpoint.
Create indices with semantic_text and dense_vector mappings; write with _doc or NDJSON _bulk; read, filter, and aggregate. The wire an existing Elasticsearch client already speaks.
A dedicated /_memory/{ns} store — remember a fact, recall it by meaning / keyword / vector, filter by metadata, forget. Each namespace is a physically isolated index, so agents never read each other's memories.
dense_vector.rrf / linear.Exact request and response shapes for every operation are on the endpoint contract.
Point the agent at a messy folder of real documents — PDF, DOCX, HTML,
Markdown, plain text. One indexing pass walks the tree, extracts the text
from every format, chunks it, and bulk-indexes into XERJ with an
auto-embedded semantic_text body. After that the
agent just asks questions over :9200 and gets
back ranked, cited passages — no whole-file re-reads, no format blind spots.
# one-time: walk ./corpus, extract every format, chunk, bulk-index into `docfolder`
$ node xerj-index.mjs ./corpus # 29 files → 420 chunks · 1339 ms
# the agent asks a question — hybrid = BM25 on body_text, fused (rrf)
# with semantic recall on the auto-embedded body
$ curl -sXPOST localhost:9200/docfolder/_search \
-H 'content-type: application/json' \
-d '{"size":5,"query":{"hybrid":{"queries":[
{"query":{"match":{"body_text":"weeks of paid parental leave?"}}},
{"query":{"semantic":{"field":"body","query":"weeks of paid parental leave?","k":5}}}],
"fusion":"rrf"}}}'
On a 22-question set over that folder, this beat a fair shell-only agent that greps the raw files (searching the question's own terms, not a rigged keyword list): 21/22 (95.5%) answered versus 14/22 (63.6%). The decisive, capability-based gap is on answers that live only inside a PDF or DOCX — grep sees compressed bytes and matches nothing: 6/7 vs 0/7. On the plaintext buckets a fair grep keeps up: differently-phrased answers tie 5/5 vs 5/5, and the literal control ties 6/6 vs 6/6 — so XERJ is not winning the cases grep can actually read. What it adds there is single-query, ranked retrieval and, on large documents, far less context: over the large-document set it returns 17.24× fewer bytes than the whole answer files a fair grep must open — where the baseline is charged only the single answer-containing file it must open to read the line, nothing else. Query latency was p50 61.23 ms (max 116.09 ms).
Honest scope, the same standard as everywhere on this page: the built-in
semantic_text embedder is a
lexical feature-hashing model (384-dim cosine), not neural,
so its matching is word/sub-word overlap, not deep understanding — which is
exactly why the differently-phrased set is a tie, not a XERJ win;
knn is exact brute-force at this corpus size. And
the context win is scale-dependent: on the large-document set
it is 17.24×, but on tiny plaintext files it INVERTS to
0.23× — returning a few ranked passages can cost more
than reading the small file whole — so we don't claim it there. The full
corpus, query set, scorecard, and run steps are in the
document-folder-index recipe.
XERJ describes only what actually runs. Two things worth knowing:
semantic_text field at your own OpenAI-compatible embeddings endpoint, or pass your own vectors. The request shapes do not change.knn is exact at query time — every candidate vector is scored, so recall is exact; there is no approximate query-time traversal to tune.hybrid fusion "learned" returns a clear error instead of silently substituting another strategy.Five runnable curl steps against a fresh XERJ, real output at every step.
CONTRACT Endpoint referenceMethod, path, request body and response shape for all six operations.
OVERVIEW Build with XERJThe mental model and the map of the whole agent surface.
RECIPE Give an agent memoryA memory-backed agent — store, recall, filter, forget, per-agent isolation.
RECIPE Semantic search & RAGRetrieval by meaning with semantic_text — no separate vector DB.
Give an agent format-agnostic search over a folder of PDF, DOCX, HTML & Markdown — walk, extract, chunk, hybrid-query.
REFERENCE ES-compatible APIThe full Elasticsearch-wire surface your existing client already targets.
Point a crawler or an agent's tool-discovery step at these. Both are served from the site root and describe the same operations documented above.
/llms.txt — a plain-text manifest of the base URL, the six agent operations, and the honesty caveats, written for an LLM to read directly./openapi.json — an OpenAPI description of the endpoints, for tool/codegen pipelines.