{"article":{"slug":"bag-of-decisions-reranker","title":"Bag of Decisions Reranker","subtitle":null,"summary":"Doug Turnbull experiments with treating search queries as a \"bag of decisions\": an LLM generates query-specific yes/no relevance questions, Jev answers them per document, and the results are combined into a reranker, with code and evaluation results.","content_type":"blog_post","language":"en","canonical_url":"https://softwaredoug.com/blog/2026/10/08/bag-of-decisions.html","author":{"name":"Doug Turnbull","url":"https://softwaredoug.com/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Doug Turnbull's Blog","url":"https://softwaredoug.com/","listing_slug":null,"listing":null},"topics":[{"name":"Search","slug":"search","url":"https://listedarticles.com/topics/search"},{"name":"AI","slug":"ai","url":"https://listedarticles.com/topics/ai"},{"name":"LLMs","slug":"llms","url":"https://listedarticles.com/topics/llms"},{"name":"Information Retrieval","slug":"information-retrieval","url":"https://listedarticles.com/topics/information-retrieval"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":561,"reading_minutes":2,"published_at":"2026-10-08T00:00:00.000Z","added_at":"2026-10-12T14:33:53.076Z","updated_at":"2026-10-12T14:33:53.076Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":false},"profile_url":"https://listedarticles.com/articles/bag-of-decisions-reranker","markdown_url":"https://listedarticles.com/articles/bag-of-decisions-reranker.md","example":false,"citation":"Doug Turnbull, Doug Turnbull's Blog. \"Bag of Decisions Reranker.\" 8 Oct 2026. https://softwaredoug.com/blog/2026/10/08/bag-of-decisions.html (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://softwaredoug.com/blog/2026/10/08/bag-of-decisions.html"},"body_markdown":"What if instead of search queries being interpreted as a “bag of terms” we could see them as a “bag of decisions”?\n\nWe know Jev acts as [frontier quality reranker](https://hevmind.com/writing/jev-as-a-reranker/). As a fun experiment, I wanted to try another technique: Don’t just ask Jev “Is this relevant for {query}?”. Instead, let an LLM generate the query’s criteria: a rubric to assess relevance for just this query.\n\nWe prompt an LLM with “generate yes / no question where the affirmative indicates relevance”. For the query `desk for kids` it might produce:\n\n```\n1 -  Is the document about desks designed for children?\n2 -  Does the document offer products or information on kids’ desks?\n3 -  Does it mention child-sized or age-appropriate desk dimensions or ergonomics?\n4 -  Does it reference desks for homework, study, or activities for kids?\n5 -  Are features specific to children’s desks (e.g., height-adjustable for kids, safety/rounded edges, storage fo\nr school supplies) discussed?\n6 -  Does it include buying guides, reviews, or comparisons of kids’ desks?\n7 -  Does it provide pricing or availability for children’s desks?\n8 -  Does it mention desks for specific child age ranges (e.g., toddlers, elementary, teens) as kids’ desks?\n9 -  Are brands or models of children’s desks listed?\n10 -  Does it cover where to buy or how to choose a desk for kids?\n```\nEach of these can be turned into a `noul` question. `P(yes)` of each can be summed to score overall relevance:\n\n```\ndocument = \"\"\"\nThis adjustable study desk is perfect for kids ages 5–12.\nIts height grows with your child.\n\"\"\"\nresponse = client.decide(\n    state=document,\n    questions={\n        \"question_1\": Noul(\n            instructions=\"Is this document about desks designed for children?\"\n        ),\n        \"question_2\": Noul(\n            instructions=\"Does the document offer products or information on kids’ desks?\"\n        )\n        ...\n    },\n)\n```\nInstead of a query being represented as a bag of terms, we have a **bag of questions**. Since Jev gives the probability of ‘yes’, we can simply sum the probability of each question being true as the rerank score:\n\n```\ntotal_probability = 0.0\nfor question_id, answer in response.answers.items():\n    probability = answer.noul\n    print(f\"{question_id}: {probability:.2f}\")\n    total_probability += probability\n```\nNow `total_probability` can be our reranker’s final score. We can add that to BM25, or just take it directly. In other words:\n\n## How does it stack up?\n\nI ran an experiment comparing BM25 vs two variants ([gory details here](https://github.com/softwaredoug/search-experiments/blob/main/research/decisions/ecom.md)):\n\n1. Jev reranking: i.e. “Is this document relevant for this query?”\n2. Bag of question reranking - multi-question flow described above.\n\nI then evaluated on two e-commerce datasets Wayfair WANDS and Amazon ESCI. How did it fair?\n\nDoes this work? It seems to. Here’s some initial results:\n\n| Dataset | Strategy name | Mean NDCG | Median NDCG | \n|---|---|---|---|\n| WANDS | `bm25` | 0.5407 | 0.475 | \n| WANDS | `jev_reranker` | 0.574 | 0.561 | \n| WANDS | `bag_of_decisions` | 0.610 | 0.561 | \n| ESCI | `bm25` | 0.289 | 0.171 | \n| ESCI | `jev_reranker` | 0.314 | 0.205 | \n| ESCI | `bag_of_decisions` | 0.360 | 0.341 | \n\nOr graphically\n\nWe’re essentially doing per-query feature creation. Use an LLM as inspiration of what’s important to consider for *this query*. Then use Jev to measure whether we’re covering that criteria. We might wonder how we might modify the LLM call to provide more incisive + diverse questions then the ones above (IMO some are repetitive)\n\nIt’s a fun approach. We seem to be able to train more and more decision models, possible even [ones focused on specific domains](https://x.com/MaziyarPanahi/status/2108180624372605186). At least in this one toy experiment, it seems to be a promising approach worth deeper study. Enjoy!\n","body_html":"<p>What if instead of search queries being interpreted as a “bag of terms” we could see them as a “bag of decisions”?</p>\n<p>We know Jev acts as <a href=\"https://hevmind.com/writing/jev-as-a-reranker/\" rel=\"nofollow ugc noopener\">frontier quality reranker</a>. As a fun experiment, I wanted to try another technique: Don’t just ask Jev “Is this relevant for {query}?”. Instead, let an LLM generate the query’s criteria: a rubric to assess relevance for just this query.</p>\n<p>We prompt an LLM with “generate yes / no question where the affirmative indicates relevance”. For the query <code>desk for kids</code> it might produce:</p>\n<pre><code>1 -  Is the document about desks designed for children?\n2 -  Does the document offer products or information on kids’ desks?\n3 -  Does it mention child-sized or age-appropriate desk dimensions or ergonomics?\n4 -  Does it reference desks for homework, study, or activities for kids?\n5 -  Are features specific to children’s desks (e.g., height-adjustable for kids, safety/rounded edges, storage fo\nr school supplies) discussed?\n6 -  Does it include buying guides, reviews, or comparisons of kids’ desks?\n7 -  Does it provide pricing or availability for children’s desks?\n8 -  Does it mention desks for specific child age ranges (e.g., toddlers, elementary, teens) as kids’ desks?\n9 -  Are brands or models of children’s desks listed?\n10 -  Does it cover where to buy or how to choose a desk for kids?</code></pre>\n<p>Each of these can be turned into a <code>noul</code> question. <code>P(yes)</code> of each can be summed to score overall relevance:</p>\n<pre><code>document = &quot;&quot;&quot;\nThis adjustable study desk is perfect for kids ages 5–12.\nIts height grows with your child.\n&quot;&quot;&quot;\nresponse = client.decide(\n    state=document,\n    questions={\n        &quot;question_1&quot;: Noul(\n            instructions=&quot;Is this document about desks designed for children?&quot;\n        ),\n        &quot;question_2&quot;: Noul(\n            instructions=&quot;Does the document offer products or information on kids’ desks?&quot;\n        )\n        ...\n    },\n)</code></pre>\n<p>Instead of a query being represented as a bag of terms, we have a <strong>bag of questions</strong>. Since Jev gives the probability of ‘yes’, we can simply sum the probability of each question being true as the rerank score:</p>\n<pre><code>total_probability = 0.0\nfor question_id, answer in response.answers.items():\n    probability = answer.noul\n    print(f&quot;{question_id}: {probability:.2f}&quot;)\n    total_probability += probability</code></pre>\n<p>Now <code>total_probability</code> can be our reranker’s final score. We can add that to BM25, or just take it directly. In other words:</p>\n<h2 id=\"how-does-it-stack-up\">How does it stack up?</h2>\n<p>I ran an experiment comparing BM25 vs two variants (<a href=\"https://github.com/softwaredoug/search-experiments/blob/main/research/decisions/ecom.md\" rel=\"nofollow ugc noopener\">gory details here</a>):</p>\n<ol><li>Jev reranking: i.e. “Is this document relevant for this query?”</li><li>Bag of question reranking - multi-question flow described above.</li></ol>\n<p>I then evaluated on two e-commerce datasets Wayfair WANDS and Amazon ESCI. How did it fair?</p>\n<p>Does this work? It seems to. Here’s some initial results:</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Dataset</th><th>Strategy name</th><th>Mean NDCG</th><th>Median NDCG</th></tr></thead><tbody><tr><td>WANDS</td><td><code>bm25</code></td><td>0.5407</td><td>0.475</td></tr><tr><td>WANDS</td><td><code>jev_reranker</code></td><td>0.574</td><td>0.561</td></tr><tr><td>WANDS</td><td><code>bag_of_decisions</code></td><td>0.610</td><td>0.561</td></tr><tr><td>ESCI</td><td><code>bm25</code></td><td>0.289</td><td>0.171</td></tr><tr><td>ESCI</td><td><code>jev_reranker</code></td><td>0.314</td><td>0.205</td></tr><tr><td>ESCI</td><td><code>bag_of_decisions</code></td><td>0.360</td><td>0.341</td></tr></tbody></table></div>\n<p>Or graphically</p>\n<p>We’re essentially doing per-query feature creation. Use an LLM as inspiration of what’s important to consider for <em>this query</em>. Then use Jev to measure whether we’re covering that criteria. We might wonder how we might modify the LLM call to provide more incisive + diverse questions then the ones above (IMO some are repetitive)</p>\n<p>It’s a fun approach. We seem to be able to train more and more decision models, possible even <a href=\"https://x.com/MaziyarPanahi/status/2108180624372605186\" rel=\"nofollow ugc noopener\">ones focused on specific domains</a>. At least in this one toy experiment, it seems to be a promising approach worth deeper study. Enjoy!</p>","headings":[{"level":2,"text":"How does it stack up?","id":"how-does-it-stack-up"}]}}