Skip to content

[FEATURE] Add toolkit-native reranker chains with configurable fallback behavior #403

Description

@marora90

Package

lexical-graph

Problem statement

I’d like to make the reranking behavior a little easier to use in production.

Right now traversal search lets you pick one reranker, like tfidf, bedrock etc.

That works, but in a real deployment it would be preferable to try a better reranker first, like Bedrock, and fall back to the built-in TF-IDF reranker if Bedrock is throttled, unavailable, misconfigured, or returns no usable scores.

Proposed solution

Smallest possible implementation would be to keep all of the new behavior inside RerankStatements.

New shape can be something like:

ProcessorArgs(
    reranker="bedrock,tfidf",
    reranker_fallback_policy=my_policy, #optional
)

RerankStatements would add a private parser/helper that turns the configured value into an ordered list

Instead of the existing dispatch block, it would loop over the reranker chain

reranker = self.args.reranker.lower()
if reranker == 'model':
scored_values = self._score_values(values_to_score, query, search_results.entity_contexts)
elif reranker == 'tfidf':
scored_values = self._score_values_with_tfidf(values_to_score, query, search_results.entity_contexts)
elif reranker == 'bedrock':
scored_values = self._score_values_with_bedrock(values_to_score, query, search_results.entity_contexts)
else:
return search_results

something like:

for reranker in chain:
    try:
        scored_values = scorer(...)
        if scored_values:
            return scored_values

        if not self._should_fallback(reranker, scored_values=scored_values):
            return scored_values

    except Exception as e:
        if not has_next_reranker or not self._should_fallback(reranker, error=e):
            raise
        logger.warning("Reranker failed; trying fallback", exc_info=True)

where user code can decide whether to fall back on throttling, timeouts, empty scores, etc.

The current defaults will be maintained including support for a single reranker along with a basic default policy that mimics the behavior today.

Alternatives considered

A more involved implementation would be to introduce a small toolkit-owned reranker abstraction inside retrieval, then adapt statement and topic reranking to use it.

Rough shape:

retrieval/reranking/
  base.py        # shared dataclasses/protocols
  chain.py       # ordered fallback runner
  factory.py     # turns "bedrock,tfidf" into reranker objects
  providers/
    tfidf.py
    bedrock.py
    sentence_transformer.py
    noop.py

The useful contract could be like:
score(query, texts) -> scores

The existing rerankers can then become providers:

TfidfReranker
BedrockReranker
SentenceTransformerReranker
NoopReranker

A reranker abstraction is a little more upfront work, but will give one place to handle provider selection, fallback policy, lazy optional imports, timing, and future reranker implementations.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions