Build with XERJ
XERJ is the search, data, and memory layer for AI agents. It speaks the Elasticsearch wire protocol on port 9200, so any HTTP client an agent can already make works out of the box — no SDK, no signup, no external embedding key. This page is the map: the six operations an agent needs, what each one really does, and where to go next.
Everything below is verified against a running XERJ (--insecure, single node, empty data dir). Copy any request, paste it into your agent's tool, and it runs.
The six canonical agent operations
An agent needs to find things, find things by meaning, remember things, and recall them later. XERJ exposes exactly those as six operations over two families of endpoints — normal ES search on your indices, and a dedicated namespaced /_memory API.
| Operation | Endpoint | What it does |
|---|---|---|
xerj_search | POST /{index}/_search · match/term/bool | Full-text and structured search (BM25 + filters). The exact-word path. |
xerj_semantic_search | POST /{index}/_search · semantic query | Search by meaning over a semantic_text field. The server embeds the query text for you. |
xerj_vector_search | POST /{index}/_search · knn | Nearest-neighbor over a dense_vector field, using vectors you supply. |
xerj_hybrid_search | POST /{index}/_search · hybrid query | Fuse keyword + vector results in one query with rrf or linear fusion. |
xerj_memory_store | POST /_memory/{namespace} | Remember a fact for later. Text is auto-embedded; metadata optional. |
xerj_memory_recall | POST /_memory/{namespace}/_recall | Recall top-k relevant memories by meaning, keyword, or vector. |
The full request/response shape for every one of these is on the endpoint contract page. If you just want to see it run end-to-end, start with the agent quickstart.
Three jobs, one binary
- Search layer. Keyword, structured, semantic, vector, and hybrid retrieval over your own indices — the same
_searchendpoint an ES client already targets. - Data layer. Index documents with
POST /{index}/_doc(auto-id),PUT /{index}/_doc/{id}(explicit id), or NDJSONPOST /{index}/_bulk. Read them back, delete them, aggregate over them. - Memory layer. A dedicated per-agent memory store at
/_memory/{namespace}— store, recall, list, forget. Each namespace is a physically isolated index, so agent A can never read agent B's memories.
A 30-second taste
Store something an agent learned, then recall it by meaning — no vectors supplied, no external service, no index to create first (the namespace is created on first store):
$ 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"}
The recall query never contains the words "metric" or "theme" — "semantic":true tells XERJ to embed the query text with the same built-in embedder it used at store time and match by vector similarity. That output is a real run, not an illustration.
What is honest to claim
XERJ is aggressive about not faking capabilities. Two things worth knowing before you build:
- The built-in embedder is deterministic and local, not a large neural model. It runs with zero configuration and no external key, which is why the examples here are fully reproducible offline — but for production-grade semantic quality you point XERJ at your own OpenAI-compatible embeddings endpoint, or pass your own vectors. The request shapes do not change.
knnis exact at query time. Every candidate vector is scored, so recall is exact — there is no approximate query-time graph traversal to tune. Vectors you pass are what get compared.
Where a feature is not implemented, XERJ fails loudly rather than pretending. For example, hybrid fusion only supports rrf and linear; asking for "fusion":"learned" returns a clear error (hybrid fusion learned is not yet supported; use rrf or linear) instead of silently substituting another strategy.