Small, tested recipes that use Jev (TypeSafe's System One decision model, reached through OpenRouter) next to a Qdrant collection. Jev answers many yes/no and pick-one questions about a piece of text in one cheap request (about $0.00002 each, cached on disk), so each recipe is "ask Jev a batch of questions, then apply a plain function to the probabilities".
| Recipe | Use it to | Code |
|---|---|---|
| Query understanding | Turn a query into classes and facets, then filter or boost the search | jevqu/ |
| Rerank / prune | Re-order and de-duplicate the top results of any search | recipes/prune/ |
| Semantic chunker | Split documents at topic changes before embedding | recipes/chunker/ |
The recipes share the Jev client (jevqu/jev.py) and nothing else, so you can take one without the others.
uv sync --extra dev # core + tests
uv sync --extra chunk # chunker baselines and QASPER loader (chonkie, pyarrow)
uv sync --extra eval --extra notebook # STaRK evaluation, notebooks
export OPENROUTER_API_KEY=... # the code reads the environment, not .envInduce a taxonomy from your collection alone (no queries or relevance labels), label every point with it, and understand each query against it.
from qdrant_client import QdrantClient
from jevqu.jev import Jev
from jevqu.api import (create_query_understanding, set_query_understanding,
upload_points, understand_query, query_points)
client, jev = QdrantClient(...), Jev()
tax, report = create_query_understanding(client, "products", jev, sample=2000, embed=embed) # induce
set_query_understanding(client, "products", tax) # store + index
upload_points(client, "products", new_points, jev) # label on ingest
hits = query_points(client, "products", "waterproof hiking boots", jev,
dense=embed(["waterproof hiking boots"])[0], sparse=sparse_vec,
understanding="auto", limit=10) # searchunderstanding is auto, filter, boost or off. understand_query returns the matched classes and facets without searching. The taxonomy is stored in the collection (_query_understanding).
create_query_understanding(..., llm=LLM()) (from jevqu.llm) lets a chat model write the class names and Jev validate them. It is a baseline; the default path needs no chat model.
Evaluation: scripts/build_stark_collection.py and scripts/run_eval.py (STaRK-Amazon recall-loss curve). Demos: notebooks/.
One Jev request per query reads the top 20 results and answers, per result, "is it relevant?" and "is it a near-duplicate of a higher-ranked result?". Thresholds apply afterwards, so one set of requests serves any sweep.
from recipes.prune.prune import relevance, judge, prune
rel = relevance(jev, query, texts) # rerank: sort the top 20 by this
rel, dup = judge(jev, query, texts) # or also get duplicate probabilities
ids, backfilled = prune(ids, rel, dup, rel_min=0.5, dup_max=0.5, keep=10)Measured on WANDS (240 held-out queries): sorting the hybrid top 20 by Jev's relevance gives nDCG@10 +0.086 over hybrid and cuts irrelevant results in the top 10 from 102 to 38. It beats MiniLM-L-6 by +0.022 and bge-reranker-base by +0.039, but it is not faster than local cross-encoders. The notebook (recipes/prune/prune.ipynb) also covers variety (MMR with Jev relevance as the relevance term) and sweeps the thresholds.
Measured on NFCorpus (100 test queries, BGE-small top 50, nDCG@10 lift over BGE-small): Jev Score +0.074, Jev Choice +0.067, and Jev Iterative Choice +0.079, against +0.026 for ms-marco-MiniLM-L-6-v2 and -0.013 for bge-reranker-base. Iterative Choice sends one request per ranking position and took about five times as long as Score. See notebooks/jev_reranking_nfcorpus.ipynb, a Colab notebook; the cross-encoder rows come from a separate CPU run of the same code, as the notebook explains.
Jev reads windows of numbered sentences and judges, per gap, whether the next sentence starts a new topic. Cutting is a pure function of those probabilities, so one set of requests serves every threshold and size bound.
from recipes.chunker.chunker import sentences, gap_probs, cut, chunks
spans = sentences(text)
probs = gap_probs([text[a:b] for a, b in spans], jev)
starts = cut(probs, sizes=[b - a for a, b in spans], threshold=0.5, min_size=200, max_size=800)
pieces = chunks(text, spans, starts) # exact slices of `text`Measured on the QASPER test split (407 papers, 1,297 questions written by researchers, with the answer paragraphs marked by other researchers), retrieving 5 chunks per question from Qdrant at matched mean chunk size: against fixed-size, recursive and chonkie semantic chunking, recall +0.03 to +0.06, precision 8 to 14% higher and IoU 8 to 18% higher, every 95% interval above zero. The smallest gain is against a recursive splitter that breaks on paragraph boundaries. See recipes/chunker/chunker.ipynb.
Both results are one dataset family each, so treat them as evidence, not a guarantee.
| Path | What |
|---|---|
jevqu/jev.py, llm.py |
OpenRouter clients with on-disk cache and cost tracking |
jevqu/api.py |
the five public query-understanding calls |
jevqu/induce.py, classify.py, cascade.py |
taxonomy induction, point labeling, local classifier cascade |
jevqu/understand.py, qquery.py |
query to classes/facets; filter/boost search |
recipes/wands.py |
shared WANDS loader and hybrid index used by the recipe notebooks |
recipes/chunker/qasper.py |
QASPER loader and character-overlap scores for the chunker benchmark |
docs/ |
design notes and implementation plan |
uv run pytest # tests/ and recipes/; Jev is faked