ragbisect
Website: https://mi2arun.github.io/contextpull/ragbisect.html
Bisect a RAG pipeline to find the broken stage. Stage-by-stage diagnostics for RAG pipelines, and the benchmark harness for ContextPull.
ragbisect (formerly stagewise) and ContextPull are one project in two packages, kept apart on purpose. ContextPull is the retrieval server; ragbisect is the neutral instrument that measures it against bm25, dense and hybrid pipelines on the same self-built eval set. ragbisect has no dependency on ContextPull and works on any pipeline you already have.
Other tools score your pipeline end to end and tell you it is bad. ragbisect scores retrieval, ranking and generation separately, on an eval set it builds itself from your corpus, and tells you which stage is losing the most quality, and which built-in alternative would recover it.
It is a measuring instrument, not a RAG framework. Zero runtime dependencies.
Install
uv add --dev ragbisect # or: pip install ragbisect (PyPI: ragbisect 0.1.0)
export OPENAI_API_KEY=... # used for question generation, judging and the built-in dense config
Any OpenAI-compatible endpoint works (OPENAI_BASE_URL). Anthropic models work
for generation and judging (--model anthropic:claude-...); embeddings still
need an OpenAI-compatible endpoint.
Use
Wrap your existing pipeline in one method:
class MyRetriever:
def retrieve(self, query: str, k: int) -> list[str]:
"""Return chunk IDs, most relevant first."""
Optional extras on the adapter, all read if present: generate(query, chunk_ids) -> str to have faithfulness judged; stats() -> dict with tokens_in, tokens_out, tool_calls, queries (and usd) to fill the cost columns; a concurrency = N attribute to allow N parallel queries. Wall time per query is recorded for every config. --sample N evaluates a seeded subset, for expensive adapters.
Then point ragbisect at your corpus and your adapter:
ragbisect run --corpus ./docs --adapter ./my_pipeline.py:MyRetriever
config recall@5 mrr@5 ndcg@5|hit faith n
---------------------------------------------------------------------------------
your adapter 0.812 0.667 0.885 n/a 214
conceptual 0.842 0.702 0.899 n/a 171
exact_lookup 0.698 0.528 0.831 n/a 43
bm25 (built-in) 0.771 0.611 0.862 n/a 214
dense (built-in) 0.836 0.688 0.893 n/a 214
hybrid dense+bm25 rrf (built-in) 0.897 0.741 0.912 n/a 214
Bottleneck for 'your adapter': retrieval — recall@5 is 0.81; 19% of questions never see their gold chunk in the top 5. Weakest shape: exact_lookup (recall 0.70, n=43).
'hybrid dense+bm25 rrf (built-in)' would raise recall@5 from 0.81 to 0.90 (+0.09).
What it does
- Parses and chunks your corpus (
.md,.txt), or takes your own chunks as a JSONL of{"id", "text", "source"}so your IDs are the ground truth. - Generates an eval set in five shapes. Conceptual and exact-lookup
questions come from one chunk each via a model call. Comparison pairs are
found by lexical near-duplicate detection across documents and phrased by
the model; gold is both chunks. Aggregation questions count an identifier
family (TX-4401, TX-4419, …) spread over several chunks; table questions
target one cell of a pipe table. Those two are computed, not generated, and
cost nothing.
--shapesselects. Cached; re-runs are free until the corpus changes. Shapes a corpus cannot support are skipped and the report says why. - Scores each stage: recall@k (retrieval), NDCG@k conditioned on a hit
(ranking), and faithfulness via an LLM judge if your adapter also has
generate(query, chunk_ids) -> str(generation). - Localizes the fault: the stage furthest from its ceiling is the bottleneck. Built-in bm25 / dense / hybrid-RRF configs run on the same questions so you can see what a change would buy on your data.
- Prints what it spent in tokens.
Commands
ragbisect generate --corpus ./docs --n 200 # just build (and inspect) the eval set
ragbisect run --corpus ./docs # built-in configs only, no adapter
ragbisect run --corpus ./chunks.jsonl --adapter ./p.py:R --k 10 --dump-misses misses.jsonl
Everything lands in .ragbisect/: the cache, chunks.jsonl, and
questions.jsonl. Hand-check the questions. If they are bad, everything
downstream is worthless.
Status
Milestones from CLAUDE.md:
- M1 eval set generator (conceptual, exact lookup)
- M2 retrieval scoring; dense vs dense+BM25 RRF comparison
- M3 CLI with stage table and one-line verdict
- M4 comparison, aggregation and table query shapes
- M5 ablation across candidate configs with a recommendation
Known limits (read before trusting a number)
- Single gold chunk. Doc corpora explain the same thing in several places.
When the retriever returns a chunk that also answers the question but is not
the one it was generated from, that counts as a miss. On the
uvdocs about half of the exact-lookup misses looked like this, so recall@k here is a lower bound. Repeated identifiers are deduped and very common ones (present in >5% of chunks) are rejected, which reduces but does not remove the effect.--dump-misseslets you see for yourself. - Two of five query shapes. Comparison, aggregation and table questions are not generated yet, and those are the shapes naive RAG fails on hardest.
- No PDF parsing.
.mdand.txtonly. - Generation scoring is thin by design: one LLM judge on a sample.
- Baseline chunker is the baseline. If you already chunk, pass your own
.jsonlso ground truth uses your IDs.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ragbisect-0.2.0.tar.gz.
File metadata
- Download URL: ragbisect-0.2.0.tar.gz
- Upload date:
- Size: 39.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6ff704f51f398027efbdd9df495d3e5260fd0c5e21d726775d83022778edfa6
|
|
| MD5 |
b18260126f73dccd7cb6c706daf27499
|
|
| BLAKE2b-256 |
f5c34f38fa3966f5bfd54d90c3fcd6cbad607f10f9debe09552a494753f28564
|
Provenance
The following attestation bundles were made for ragbisect-0.2.0.tar.gz:
Publisher:
publish-pypi.yml on mi2arun/ragbisect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ragbisect-0.2.0.tar.gz -
Subject digest:
a6ff704f51f398027efbdd9df495d3e5260fd0c5e21d726775d83022778edfa6 - Sigstore transparency entry: 2832268855
- Sigstore integration time:
-
Permalink:
mi2arun/ragbisect@dea8197943626183d2399f9a8fa15126e37f61c5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mi2arun
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@dea8197943626183d2399f9a8fa15126e37f61c5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ragbisect-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ragbisect-0.2.0-py3-none-any.whl
- Upload date:
- Size: 31.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
370b9c6d88f954114eb12e1737488bf9437ca76beeefffc10b0e6bbd42cb439e
|
|
| MD5 |
3338065fd5f691c718a27e4708c0a89f
|
|
| BLAKE2b-256 |
e32f87a9d929e6e1d941023dba51893272f56a8dd0bef686cb3b04778613fa29
|
Provenance
The following attestation bundles were made for ragbisect-0.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on mi2arun/ragbisect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ragbisect-0.2.0-py3-none-any.whl -
Subject digest:
370b9c6d88f954114eb12e1737488bf9437ca76beeefffc10b0e6bbd42cb439e - Sigstore transparency entry: 2832268907
- Sigstore integration time:
-
Permalink:
mi2arun/ragbisect@dea8197943626183d2399f9a8fa15126e37f61c5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mi2arun
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@dea8197943626183d2399f9a8fa15126e37f61c5 -
Trigger Event:
push
-
Statement type: