urna retrieve
Query a .urna file with text and get JSON or JSONL of cited spans, each with its exact cosine score, stored text, citation and file hashes.
urna retrieve runs the same offline embedding, model gate and routing as urna ask, and prints the hits as JSON for a program or an agent: one object per hit with the exact cosine score, the stored canonical text, the citation_id and the hashes that identify the file.
Usage
urna retrieve [OPTIONS] <FILE> <QUERY>Arguments
| Argument | Description |
|---|---|
<FILE> | Path to the .urna file. |
<QUERY> | The query text, as one argument. Quote it in the shell. |
Options
| Option | Default | Description |
|---|---|---|
-k, --k <K> | 10 | Number of hits to return. |
--format <FORMAT> | jsonl | jsonl: one compact JSON object per line. json: one pretty-printed array. |
--embedder <EMBEDDER> | routed by the manifest model | Path to a query-embedder script to run instead of the routed one. It must speak the query embedder protocol. |
--candidates <CANDIDATES> | 4*k, at least 64 | The HNSW beam on an hnsw file, the candidates per path on a hybrid file. Ignored on an exact file. |
--model-path <MODEL_PATH> | none | Local model directory, passed to the embedder as --model-path: the potion table directory for a potion corpus, the model snapshot directory for a registry model. |
-h, --help | Print help. |
Behavior
retrieve and ask share one code path up to the output:
- Open and fully validate the file.
- Route the query embedder by the manifest
embedding_model: names starting withminishlab/potionrunembed_query_potion.py, every other name runsembed_query_model.py.--embedderreplaces the choice.--mrl-dimis passed when the manifest recordsfull_dim. - Apply the model gate: model name, dim and vector length, the placeholder
model_hash, thenmodel_hashequality.retrievehas no flag to skip it. - Route by the manifest
index_type(hnsw,hybrid, else exact) and rerank every candidate with exact cosine. - Attach the stored canonical text of each hit and print.
The interpreter and script lookups are in Query embedder protocol.
The installed binary answers potion corpora only
The installed embedder payload has no embed_query_model.py. On a corpus built with a registry model (wemm, jina, clip, siglip2), an installed binary exits 1 with embedder script not found: python/forge/embed_query_model.py (override with --embedder). Run retrieve from a checkout with the model's dependencies installed. See Open a corpus you downloaded and Known limits.
Preset-built files take the HNSW route
Files built with the hybrid preset declare index_type = "hnsw", so retrieve never runs their BM25 section, and a --candidates value below the file's ef_construction (400 by default) does not change the beam. See Known limits.
Output
stdout carries only JSON, so it pipes cleanly into jq or another program. stderr carries [urna] embedder interpreter: <path> and, on failure, Error: <message>.
With --format jsonl each hit is one line, highest score first. With no hits nothing is printed. With --format json the hits are one pretty-printed array, [] when empty.
Hit fields
Every hit carries these 11 fields, in this order:
| Field | Type | Meaning |
|---|---|---|
chunk_id | string | sha256: plus 64 hex digits. The content-derived identity of the chunk. |
score | number | The exact cosine similarity between the query and the chunk, recomputed by the rerank. Not a candidate-generator proxy and not a probability. |
score_type | string | Always "cosine". |
source_uri | string | The source the chunk came from, as the builder recorded it. |
offset_start | integer | Start of the stored span. Its unit depends on the builder: a UTF-8 byte offset for builder.chunk_text, the row ordinal for urna build corpora (the span is ordinal to ordinal + 1), a blob-relative byte range when the file has a blob span overlay. |
offset_end | integer | End of the stored span, in the same unit. |
citation_id | string | urna://<content_hash>/<chunk_id>. Resolves with urna cite. |
text | string | The stored canonical text of the chunk: the same bytes urna cite prints. Never a reopen of the original source. |
file_hash | string | sha256: over the whole file: the same digest sha256sum computes. |
content_hash | string | sha256: over the decoded canonical sections. Stable across text encodings; every citation carries it. |
rerank_source | string | "full_precision" when the rerank read float32 vectors, "stored_precision" when it read float16, int8 or int4. The same value on every hit of one call. |
The Python UrnaFile.retrieve returns the same 11 fields as RetrieveHit.
Real output on the quickstart corpus (-k 2 --format jsonl, stdout only):
{"chunk_id":"sha256:b5dfeb09a643f6f0afde3f361316dde3ea503ff86c525d297e6ba79b5781a4be","score":0.5004974007606506,"score_type":"cosine","source_uri":"demo/03-citations.md","offset_start":7,"offset_end":8,"citation_id":"urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:b5dfeb09a643f6f0afde3f361316dde3ea503ff86c525d297e6ba79b5781a4be","text":"because the citation points at content, two people who build the same logical corpus on two machines get the same citation, and a stored corpus and a compressed one cite identically. resolving a citation returns the exact canonical text and the original byte span it came from, which is what lets an agent quote a source it can prove.","file_hash":"sha256:e4d5f8907faad38c192dc6929e36dbf16db558410b2dae5abb4d65f10f508832","content_hash":"sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df","rerank_source":"full_precision"}
{"chunk_id":"sha256:2be5a0f62d1bb7a69556b1a59d15e3a7d7cf9991baa579e83c1ae67cdd657748","score":0.27243560552597046,"score_type":"cosine","source_uri":"demo/03-citations.md","offset_start":8,"offset_end":9,"citation_id":"urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:2be5a0f62d1bb7a69556b1a59d15e3a7d7cf9991baa579e83c1ae67cdd657748","text":"the returned similarity score is a real cosine value, recomputed by an exact rerank, never an approximate proxy. a result you can cite is a result you can trust.","file_hash":"sha256:e4d5f8907faad38c192dc6929e36dbf16db558410b2dae5abb4d65f10f508832","content_hash":"sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df","rerank_source":"full_precision"}The quickstart corpus was built by urna build, so offset_start and offset_end are row ordinals (7 to 8), not byte offsets.
Exit codes
| Code | Meaning |
|---|---|
0 | The search ran, including a run with no hits. |
1 | Any error: the file failed to open or validate, the embedder was not found or failed, the model gate refused, or the search rejected the query. The message is on stderr. |
2 | Usage error: a missing argument, an unknown flag, or a --format value other than jsonl or json. |
Examples
Print the score and citation of each hit:
urna retrieve corpus.urna "how do citations work" -k 3 2>/dev/null \
| jq -r '[.score, .citation_id] | @tsv'0.5004974007606506 urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:b5dfeb09a643f6f0afde3f361316dde3ea503ff86c525d297e6ba79b5781a4be
0.27243560552597046 urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:2be5a0f62d1bb7a69556b1a59d15e3a7d7cf9991baa579e83c1ae67cdd657748
0.25516077876091003 urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:19f36b3e072d553eb83626bf30db5e8f3b1f729a495f7826999ffeae53848e6eTake the top hit from the array form:
urna retrieve corpus.urna "can I use this offline" -k 1 --format json 2>/dev/null | jq '.[0].text'Resolve the first citation back to its stored text:
cid=$(urna retrieve corpus.urna "how do citations work" -k 1 2>/dev/null | jq -r .citation_id)
urna cite corpus.urna "$cid"For agent integration (a tool definition that calls retrieve, and cite as the verification step), see Give a corpus to an agent.
urna ask
Ask a .urna file a question in plain text and get the stored text of each hit with its urna:// citation, embedded offline and model-gated.
urna build
Reference for urna build, the launcher of the declarative corpus build: flags, how it finds the forge and Python, stages, output and exit codes.