Other tools score a pipeline end to end and tell you it is bad. ragbisect builds an eval set from your own corpus, scores retrieval and ranking separately, compares your pipeline with bm25, dense and hybrid on the same questions, and names the stage that is losing the most. It works on any pipeline and has no dependency on ContextPull.
class MyRetriever:
def retrieve(self, query: str, k: int) -> list[str]:
"""Return chunk IDs, most relevant first."""uvx ragbisect run --corpus ./docs --adapter ./my_pipeline.py:MyRetrieverconceptual“What’s our approach to refunds?” The one shape dense retrieval handles well. Generated from a chunk by a model.
exact_lookup“What does error TX-4419 mean?” Embeddings blur identifiers. Generated only when a chunk explains one.
comparison“What changed between the 2024 and 2025 policy?” Cross-document near-duplicate pairs; gold is both chunks.
aggregation“How many TX-44xx codes are documented?” Identifier families counted programmatically. No model.
table“Storage temp for the R-40?” One cell of a pipe table, only where the row key is unambiguous. No model.
Shapes a corpus cannot support are skipped and the report says why, rather than generating junk questions.
config recall@5 mrr@5 ndcg@5|hit faith n ms/q tok/q calls/q
-----------------------------------------------------------------------------------------------------------
your adapter 0.918 0.768 0.878 n/a 207 4 — —
conceptual 0.943 0.806 0.891 n/a 123 4
exact_lookup 0.859 0.696 0.858 n/a 84 3
bm25 (built-in) 0.918 0.768 0.878 n/a 207 2 — —
dense (built-in) 0.903 0.719 0.847 n/a 207 340 — —
hybrid dense+bm25 rrf (built-in) 0.952 0.818 0.895 n/a 207 61 — —
Bottleneck for 'your adapter': ranking — when the gold chunk is retrieved it ranks at NDCG 0.88; MRR@5 is 0.77. Weakest shape: exact_lookup (recall 0.86, n=84).
'hybrid dense+bm25 rrf (built-in)' would raise recall@5 from 0.92 to 0.95 (+0.03).
spend: 0 API calls; 1431 cache hits; 0 completion tokens; ≈ $0.0000
Retrieval is recall@k: did the gold chunk appear at all. Ranking is NDCG conditioned on a hit: given it was retrieved, how near the top. The stage furthest from its ceiling is the bottleneck. Cost columns show wall time for every config and model tokens and tool calls when an adapter reports them, which is how agentic configurations get compared with push pipelines on cost as well as recall.
generate(query, chunk_ids) to have faithfulness judged by a model.stats() returning tokens, tool calls and dollars, for the cost columns.concurrency = N to allow parallel queries.--sample N for expensive adapters; --shapes to pick question shapes.