Agent quickstart
Five steps, all runnable, all on port 9200: boot XERJ → check it is up → store a memory → recall it by meaning → run a semantic search. Every request and every response below is a real run against a freshly booted, empty XERJ — no seeding, no external embedding service, no API key. If you have curl, you have everything.
0 · Boot XERJ
One binary. --insecure disables TLS and API-key auth so you can talk to it over plain HTTP on the default ES-compat port 9200 — perfect for a local agent loop.
# from a release build (see /docs/install.html for downloads) $ ./engine/target/release/xerj --insecure --data-dir /tmp/xerj-agent &
New to XERJ? The install page has one-line downloads for every platform.
1 · Confirm it is up
The root path returns the ES-style info document. Any Elasticsearch client uses this as its handshake — which is exactly why an off-the-shelf ES client library points at XERJ unchanged.
$ curl -s localhost:9200/
{"name":"local","cluster_name":"xerj",
"cluster_uuid":"xerj-cluster-0000-0000-0000-000000000000",
"version":{"number":"8.13.0","build_flavor":"default","build_type":"tar",
"lucene_version":"9.10.0", ... },
"tagline":"You Know, for Search"}
Want the cluster's health instead? GET /_cluster/health returns {"status":"green", ...} on a healthy single node.
2 · Store a memory
An agent learns something worth keeping. Store it in a per-agent namespace — here agent-demo. You do not create the namespace first; it is created on the first store. You do not supply a vector; because the memory's text is a semantic_text field, XERJ auto-embeds it with its built-in embedder at store time.
$ 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"}
Store a second one so recall has something to rank against:
$ curl -sXPOST localhost:9200/_memory/agent-demo \
-H 'content-type: application/json' \
-d '{"text":"The production database is Postgres 16 in us-east-1.","metadata":{"kind":"fact"}}'
{"created":true,"id":"ddaecb5f-61ab-4b57-bc31-82a118e6131c","namespace":"agent-demo"}
3 · Recall by meaning
On a later turn the agent asks a question that shares no keywords with the stored memory. Set "semantic": true and pass the raw question as query: XERJ embeds it with the same built-in embedder used at store time and ranks memories by vector similarity.
$ 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": 2
}'
{"hits":[
{"id":"77eff57b-e432-431c-8b49-8a16b33ab551",
"metadata":{"kind":"preference"},
"score":0.655571460723877,
"text":"The user prefers metric units and a dark UI theme."},
{"id":"ddaecb5f-61ab-4b57-bc31-82a118e6131c",
"metadata":{"kind":"fact"},
"score":0.5951915979385376,
"text":"The production database is Postgres 16 in us-east-1."}],
"namespace":"agent-demo"}
The UI-preference memory ranks first for a question about "display settings" it shares no words with. Two other recall modes use the same endpoint:
- Keyword (BM25): drop
"semantic": trueand just send{"query":"database"}— exact-word relevance over the stored text. - Vector (kNN): send
{"vector":[…]}to recall against embeddings you supplied at store time. - Metadata filter: add
"filter":{"term":{"metadata.kind":"preference"}}to narrow any recall to a subset — nested metadata fields are queryable with no extra mapping.
4 · Semantic search over your own documents
Memory is one store; your own indices are another. Give a field the semantic_text type and XERJ auto-embeds it on ingest into a companion vector, so a semantic query retrieves by meaning. First create the index:
$ curl -sXPUT localhost:9200/notes \
-H 'content-type: application/json' \
-d '{"mappings":{"properties":{"body":{"type":"semantic_text"}}}}'
{"acknowledged":true,"shards_acknowledged":true,"index":"notes"}
Index a few documents. Note the verbs: an explicit id uses PUT /notes/_doc/{id}; an auto id uses POST /notes/_doc. (Sending POST to an explicit-id path is the one easy mistake — it will not index.)
$ curl -sXPUT localhost:9200/notes/_doc/1 -H 'content-type: application/json' \
-d '{"body":"How do I roll back a bad deployment?"}'
$ curl -sXPUT localhost:9200/notes/_doc/2 -H 'content-type: application/json' \
-d '{"body":"Reverting a broken release to the previous version."}'
$ curl -sXPUT localhost:9200/notes/_doc/3 -H 'content-type: application/json' \
-d '{"body":"Configuring TLS certificates for the ingress."}'
$ curl -sXPOST localhost:9200/notes/_refresh
Now search by meaning. The query text "undo a failed rollout" shares no words with any document, yet the rollback note wins:
$ curl -sXPOST localhost:9200/notes/_search \
-H 'content-type: application/json' \
-d '{
"query": {"semantic": {"field": "body", "query": "undo a failed rollout", "k": 2}},
"_source": ["body"]
}'
{"hits":{"total":{"value":2,"relation":"eq"},
"hits":[
{"_id":"2","_score":0.5531777143478394,
"_source":{"body":"Reverting a broken release to the previous version."}},
{"_id":"1","_score":0.53...,
"_source":{"body":"How do I roll back a bad deployment?"}}]}}
The "Configuring TLS certificates" note is correctly excluded. Filter _source to the fields you want — the auto-generated companion embedding lives in a body_vector field and is otherwise part of _source, so scoping it keeps responses small.
Going further — vector & hybrid
If you already have embeddings, add a dense_vector field and run exact knn; to combine keyword and vector recall in a single call, use a hybrid query with rrf fusion. Both are shown, with response shapes, on the endpoint contract. For worked, end-to-end walkthroughs see the recipes: agent memory, semantic search & RAG, hybrid search.
Clean up
Drop the memory namespace and the demo index when you are done:
$ curl -sXDELETE localhost:9200/_memory/agent-demo
{"dropped":true,"namespace":"agent-demo"}
$ curl -sXDELETE localhost:9200/notes
{"acknowledged":true}