Bench & doctor¶
marq bench — search-quality benchmarks¶
Runs a set of test queries through four backends and reports precision@k, recall (overall and at 1/3/5), MRR, F1, and latency for each:
| Backend | What it runs |
|---|---|
bm25 |
search only |
vector |
vsearch only |
hybrid |
Full query pipeline, RRF-fused, no reranking |
full |
Full query pipeline, RRF-fused, with reranking |
$ uv run marq bench tests/fixtures/bench-sample-collection.json -c tasknote
...
Summary:
----------------------------------------------------------------------
bm25 P@k= 0.500 R@1= 0.333 R@3= 0.500 R@5= 0.667 MRR= 0.458 F1= 0.500 Avg=19ms
hybrid P@k= 0.917 R@1= 0.500 R@3= 0.917 R@5= 1.000 MRR= 0.750 F1= 0.944 Avg=2757ms
full P@k= 1.000 R@1= 0.667 R@3= 1.000 R@5= 1.000 MRR= 0.833 F1= 1.000 Avg=3706ms
vector P@k= 0.917 R@1= 0.667 R@3= 0.917 R@5= 1.000 MRR= 0.833 F1= 0.944 Avg=98ms
(tests/fixtures/bench-sample-collection.json is checked into the repo
and pairs with the tests/fixtures/sample-collection/ fixture used
throughout these docs — index it as tasknote per the
Quickstart and this command runs as-is.)
bm25 is fastest but weakest on anything conceptual; full (the
default query pipeline) is slowest but strongest — exactly the
tradeoff Search & query describes, quantified.
Fixture format¶
A fixture is a JSON file with a top-level description, version, an
optional default collection, and a queries array
(BenchmarkQuery in src/qmd_py/bench.py):
{
"description": "...",
"version": 1,
"collection": "sample",
"queries": [
{
"id": "exact-http-api",
"query": "HTTP API",
"type": "exact",
"description": "Direct keyword match - 'HTTP API' appears verbatim in api.js",
"expected_files": ["src/api.js"],
"expected_in_top_k": 1
}
]
}
expected_files— paths (relative to the collection) that should appear somewhere in the results.expected_in_top_k— how many ofexpected_filesshould appear in the firstkresults specifically (used for precision@k; recall is computed against the whole returned result set, not just the top-k).typeis free-form and only used for your own grouping/labeling — not interpreted by the scorer.
-c/--collection on the command line overrides the fixture's own
collection field. --format json gives the full per-query breakdown
(marq bench <fixture> --format json) instead of the summary table.
marq doctor — health check¶
$ uv run marq doctor
marq doctor
Postgres schema: qmd_py
LLM router: http://localhost:8099
✓ Postgres connectivity: PostgreSQL 16.14 (Debian 16.14-1.pgdg12+1) on x86_64-pc-linux-gnu, ...
✓ pgvector extension: 0.8.5
✓ Migrations: up to date (2c4074f0444c)
✓ Collections: 1 configured
✓ Vector index: bge-m3-q8_0 up to date
✓ LLM router: reachable, 8 model(s) available
✓ embed model: bge-m3-q8_0
✓ generate model: qwen2.5-3b-instruct-q4_k_m
✓ rerank model: qwen3-reranker-0.6b-q8_0
Effective configuration:
MARQ_POSTGRES_URL postgresql+psycopg://qmd:***@localhost:5433/qmd
MARQ_POSTGRES_SCHEMA qmd_py
MARQ_LLM_BASE_URL http://localhost:8099
MARQ_EMBED_MODEL bge-m3-q8_0
MARQ_GENERATE_MODEL qwen2.5-3b-instruct-q4_k_m
MARQ_RERANK_MODEL qwen3-reranker-0.6b-q8_0
MARQ_DEFAULT_USER_EMAIL local@marq.local
Each check, and what it means when it fails:
- Postgres connectivity — can't connect at all; check
MARQ_POSTGRES_URL. - pgvector extension — the
vectorextension isn't installed in the database; needsCREATE EXTENSION vectoras a superuser, once. - Migrations — compares the schema's current Alembic revision
against the latest one in
alembic/versions/; a mismatch meansalembic upgrade headhasn't been run. Only works from a source checkout (needsalembic.ini) — reports a soft warning, not a failure, when run from an installed package elsewhere. - Collections — zero configured isn't a failure, just a nudge to run
collection add. - Vector index — reports how many active documents still need
embedding for the configured
MARQ_EMBED_MODEL. - LLM router — unreachable, or reachable but missing one of the three configured models (embed/generate/rerank) by exact id.
The Effective configuration block always redacts the password portion
of MARQ_POSTGRES_URL (user:***@host) before printing it — safe to
paste into a bug report or a chat.