The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →A fused ranking can look correct even after QueryFusionRetriever has changed scores held in a retriever’s cache. The underlying bug class is mutation: fusion writes to mutable NodeWithScore wrappers it may not own. GitHub issue #23351 reports remaining mutation paths in reciprocal_rerank and simple; it also describes an earlier fix scope for relative_score and dist_based_score. The exact behavior depends on the branch and package version, so those reports should not be treated as proof of release status.
Why a correct result can hide a corrupted cache
QueryFusionRetriever combines result lists keyed by query and retriever. Each result is a NodeWithScore: a wrapper around a node with a mutable score. Fusion can identify duplicate nodes by node hash, but that hash does not tell you whether two results use the same wrapper object.
As an Amazon Associate I earn from qualifying purchases.
That distinction creates two failure shapes. A cache may retain the exact wrapper passed into fusion, so an in-place score write immediately changes cached state. Or separate wrappers may represent the same node hash for different queries; if fusion writes a combined score into the first wrapper it retained, it can alter one query’s cache even though the wrappers were never aliases.
In either case, checking only the output ranking misses the side effect. A routine can calculate and return the intended fused scores while leaving a retriever’s cached results with those scores instead of their original query-specific values.
#1 Best Overall
How the four fusion modes handle scores
The fusion_mode choices in the examined fusion_retriever.py source snapshot are reciprocal_rerank, relative_score, dist_based_score, and simple. The implementation deduplicates by node hash, but the score calculations and reported mutation paths differ.
| Mode | What it combines | Mutation behavior in the examined source and issue reports |
|---|---|---|
reciprocal_rerank |
Rank contributions for each node hash; the source uses k = 60.0. |
The source snapshot assigns fused scores to retained input wrappers. Issue #23351 reports that a shared wrapper can therefore carry the fused score back into a cache. |
relative_score |
Per-result-set min/max normalization, retriever weighting, division by query count, and summing for duplicate hashes. | The source snapshot showed in-place score assignments. The issue author says PR #23333 addressed this mode, but the snapshot’s assignments and the report do not establish which branches or releases contain a fix. |
dist_based_score |
Relative-score fusion with distance-based bounds derived from the mean and standard deviation. | The source snapshot routes through relative-score processing. Issue #23351 describes this as part of the earlier fix scope associated with PR #23333; verify the target branch rather than assuming release status. |
simple |
The maximum score for each node hash. | The source snapshot writes the maximum into the first-seen wrapper. Issue #23351 reports cache corruption when distinct wrappers for the same hash have different scores. |
The issue says synchronous and asynchronous retrieval both reach the same fusion functions, so testing only one entry point can leave the other path unexamined.
Rank #2
Two cache shapes expose different failures
Shared wrapper: reciprocal-rank fusion
Issue #23351 reports a reproduction where the same wrapper appears in multiple query result lists and remains in the retriever’s cache. After rank contributions are calculated, the implementation assigns the fused value to the retained wrapper. The issue reports the current fused ranking at approximately 0.0333 and 0.0164, while the original cached scores change from 0.9 and 0.1 to those fused values. These are issue-reported example values, not an independent test result.
The important assertion is not only that the returned order and fused scores are correct. Inspect the original cache after fusion: it should still contain the scores that belonged to its query.
Distinct wrappers: simple fusion
Issue #23351 also describes two different wrapper objects with the same node hash and query-dependent scores. In its example, one query’s cached node has score 0.4, while the alternate query’s wrapper for that node has score 0.9. Simple fusion selects the maximum and writes 0.9 into the first-seen wrapper, changing the first query’s cache. The expected cache state in the issue is the original 0.4 and 0.1 values.
A shared-wrapper simple-fusion case can look harmless if the maximum already equals the score in that wrapper: the write changes no value. That does not rule out the distinct-wrapper case, where the same hash has different scores.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Build regression tests around ownership and post-state
Issue #23351 reports tests against llama-index-core 0.14.25, Python 3.12, on Linux x86_64. Its examples are useful test shapes, not independently verified results. A regression suite should vary the inputs and check both the output and what remains in every source cache.
| Dimension | Cases to cover | What to assert |
|---|---|---|
| Wrapper identity | The same wrapper reused across result lists; distinct wrappers with the same node hash. | Fusion does not alter retriever-owned scores, regardless of whether wrappers alias. |
| Scores | Equal scores; different query-dependent scores. | The returned fused score and ordering are correct, while each source list retains its original score. |
| Entry point | Synchronous and asynchronous retrieval. | Both paths preserve source state and produce the expected fused result. |
| Output ownership | Mutate a returned wrapper after fusion. | If the API is intended to return fresh wrappers, changing one must not change a retriever-owned wrapper. |
For the reciprocal-rank example, check that cache values remain 0.9 and 0.1 after the output is produced. For the distinct-wrapper simple example, check that the first query’s cache remains 0.4 and 0.1, despite the other query’s score of 0.9.
Best Value
What the proposed simple-mode fix changes
PR #23352 proposes retaining node-and-maximum-score data during simple fusion, then constructing fresh NodeWithScore wrappers for the output. That separates the fused result’s score from the wrappers owned by input results. The PR description lists regression coverage for distinct wrappers, shared wrappers, output non-aliasing, and asynchronous behavior; it says three of four new tests fail on main before the proposed change. Those are claims in the PR description, not independently run tests.
The PR says reciprocal-rank work is tracked separately and points to PR #21445, which it describes as rebuilding fresh wrappers as a side effect of adding retriever weights. The issue and PR statuses described in the available GitHub snapshot do not establish that all changes merged or shipped. Check the target branch and installed package version before relying on a fix; the examined source snapshot still showed in-place writes in simple, reciprocal-rank, and relative-score processing.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




