Documentation
¶
Overview ¶
Package reranker implements fine-ranking (reranking) of coarse retrieval results to improve top-k precision. All rerankers are pure Go with no CGO or external model downloads: the cross-encoder runs on the bundled ONNX runtime, the sparse reranker reuses the BM25 package, the LLM reranker consumes an injected llm.Backend, and the ensemble fuses several rerankers via the fuse package.
Index ¶
- Constants
- func DefaultFeatures(query string, res index.SearchResult) []float64
- type ABConfig
- type AdaptiveConfig
- type AdaptiveLTRanker
- func (a *AdaptiveLTRanker) ExamplesFitted() int
- func (a *AdaptiveLTRanker) FeedbackRecorded() int
- func (a *AdaptiveLTRanker) Fit(ctx context.Context, examples []LTRExample) error
- func (a *AdaptiveLTRanker) Fitted() bool
- func (a *AdaptiveLTRanker) Name() string
- func (a *AdaptiveLTRanker) RecordFeedback(ctx context.Context, examples []LTRExample) (int, error)
- func (a *AdaptiveLTRanker) RefitNow(ctx context.Context) (int, error)
- func (a *AdaptiveLTRanker) Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
- type CrossEncoderConfig
- type CrossEncoderReranker
- type CrossEncoderTokenizerFunc
- type EnsembleReranker
- type Experiment
- func (e *Experiment) AddVariant(name string, rr Reranker) error
- func (e *Experiment) Complete(a, b string) (*ExperimentResult, error)
- func (e *Experiment) RecordSample(variant string, s RelevanceSample) error
- func (e *Experiment) Rerank(ctx context.Context, variant, query string, results []index.SearchResult) ([]index.SearchResult, error)
- func (e *Experiment) SampleCount(variant string) (int, error)
- type ExperimentResult
- type FeatureFunc
- type LLMReranker
- type LLMRerankerConfig
- type LTRConfig
- type LTRExample
- type LTRanker
- type RankedRelevance
- type RelevanceSample
- type Reranker
- type SparseReranker
- type VariantMetrics
Constants ¶
const DefaultLLMRerankSystemPrompt = `` /* 303-byte string literal not displayed */
DefaultLLMRerankSystemPrompt is the system prompt used by LLMReranker when none is supplied.
const DefaultLLMRerankUserPrompt = "Query:\n%s\n\nPassage:\n%s\n\nRelevance score (0-10):"
DefaultLLMRerankUserPrompt is the user prompt template. Placeholders are substituted with the query and passage.
const DefaultNDCGCutoff = 3
DefaultNDCGCutoff is the cutoff K used for NDCG when the experiment config does not specify one.
Variables ¶
This section is empty.
Functions ¶
func DefaultFeatures ¶
func DefaultFeatures(query string, res index.SearchResult) []float64
DefaultFeatures returns the built-in feature vector: coarse retrieval score, query-term overlap fraction, and an inverse-length term.
Types ¶
type ABConfig ¶
type ABConfig struct {
// NDCGCutoff is the K used for the NDCG@K metric. Defaults to
// DefaultNDCGCutoff.
NDCGCutoff int
}
ABConfig configures an ABTest.
type AdaptiveConfig ¶
type AdaptiveConfig struct {
// LTR configures the underlying LTRanker (features, learning rate,
// epochs, L2). Zero values use the LTRanker defaults.
LTR LTRConfig
// RefitThreshold is the number of accumulated feedback examples that
// triggers an automatic refit. Must be >= 1. Defaults to 100.
RefitThreshold int
}
AdaptiveConfig configures an AdaptiveLTRanker.
type AdaptiveLTRanker ¶
type AdaptiveLTRanker struct {
// contains filtered or unexported fields
}
AdaptiveLTRanker is a feedback-driven reranker: it wraps an LTRanker and adapts its weights from live relevance feedback (e.g. clicks, explicit thumbs up/down, or human judgments) instead of a static training set.
Call Fit once with an initial labeled corpus, then feed fresh judgments through RecordFeedback. Each call appends the examples to an internal buffer; once the buffer reaches RefitThreshold the model is retrained on the full accumulated set. The reranker always returns a valid ranking, so it is safe to keep wired into a pipeline while it learns.
All methods are safe for concurrent use.
func NewAdaptiveLTRanker ¶
func NewAdaptiveLTRanker(cfg AdaptiveConfig) *AdaptiveLTRanker
NewAdaptiveLTRanker creates a feedback-driven LTRanker.
func (*AdaptiveLTRanker) ExamplesFitted ¶
func (a *AdaptiveLTRanker) ExamplesFitted() int
ExamplesFitted returns the number of examples the current model was trained on.
func (*AdaptiveLTRanker) FeedbackRecorded ¶
func (a *AdaptiveLTRanker) FeedbackRecorded() int
FeedbackRecorded returns the total number of feedback examples accepted through RecordFeedback since construction.
func (*AdaptiveLTRanker) Fit ¶
func (a *AdaptiveLTRanker) Fit(ctx context.Context, examples []LTRExample) error
Fit trains the initial model on the given labeled examples. It can also be called again later to reset the accumulated corpus.
func (*AdaptiveLTRanker) Fitted ¶
func (a *AdaptiveLTRanker) Fitted() bool
Fitted reports whether the underlying LTRanker has been trained.
func (*AdaptiveLTRanker) RecordFeedback ¶
func (a *AdaptiveLTRanker) RecordFeedback(ctx context.Context, examples []LTRExample) (int, error)
RecordFeedback accepts new labeled examples observed since the last call. The examples are appended to the internal buffer and, once the buffer reaches the configured threshold, the model is retrained on the full accumulated set (initial Fit examples plus all recorded feedback). It returns the total number of examples the current model was trained on.
func (*AdaptiveLTRanker) RefitNow ¶
func (a *AdaptiveLTRanker) RefitNow(ctx context.Context) (int, error)
RefitNow retrains the model immediately on the full accumulated example set (initial Fit examples plus all recorded feedback) and returns the total number of examples used.
func (*AdaptiveLTRanker) Rerank ¶
func (a *AdaptiveLTRanker) Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank implements Reranker, delegating to the current model.
type CrossEncoderConfig ¶
type CrossEncoderConfig struct {
// Model is a loaded ONNX cross-encoder (required).
Model *onnx.Model
// Tokenize converts a (query, passage) pair into the model's inputs
// (required).
Tokenize CrossEncoderTokenizerFunc
// Output names the model output to read as the relevance score. When
// empty, the model's last declared output is used.
Output string
// Sigmoid applies the logistic function to the raw model output. Cross
// -encoders trained for classification typically emit logits that are
// mapped through sigmoid to a [0,1] relevance probability.
Sigmoid bool
}
CrossEncoderConfig configures a CrossEncoderReranker.
type CrossEncoderReranker ¶
type CrossEncoderReranker struct {
// contains filtered or unexported fields
}
CrossEncoderReranker scores each (query, passage) pair with a lightweight cross-encoder running on the bundled pure-Go ONNX runtime. Unlike bi-encoder similarity, a cross-encoder attends jointly over the query and passage, which usually yields a sharper relevance signal for fine ranking.
func NewCrossEncoderFile ¶
func NewCrossEncoderFile(path string, cfg CrossEncoderConfig) (*CrossEncoderReranker, error)
NewCrossEncoderFile loads a cross-encoder ONNX model from disk.
func NewCrossEncoderReranker ¶
func NewCrossEncoderReranker(cfg CrossEncoderConfig) (*CrossEncoderReranker, error)
NewCrossEncoderReranker creates a cross-encoder reranker from a loaded model.
func (*CrossEncoderReranker) Name ¶
func (r *CrossEncoderReranker) Name() string
Name implements Reranker.
func (*CrossEncoderReranker) Rerank ¶
func (r *CrossEncoderReranker) Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank scores every result's chunk against the query with the cross-encoder and returns them ordered by the resulting relevance score.
type CrossEncoderTokenizerFunc ¶
CrossEncoderTokenizerFunc converts a (query, passage) pair into the named input tensors a cross-encoder ONNX model expects (e.g. "input_ids", "attention_mask", "token_type_ids"). Like the embedder's tokenizer it is dependency-injected so the reranker stays model-agnostic.
type EnsembleReranker ¶
type EnsembleReranker struct {
// Rerankers is the set of sub-rerankers to run (at least one required).
Rerankers []Reranker
// Fusion, when set, combines the per-reranker score maps via the given
// fusion strategy. When nil, Weights (if set) drive a weighted mean,
// otherwise Reciprocal Rank Fusion is used as a robust default.
Fusion fuse.Fusion
// Weights optionally supplies one weight per sub-reranker for a weighted
// mean of min-max-normalized scores. It is only used when Fusion is nil.
Weights []float64
// RRFK is the Reciprocal Rank Fusion constant used when no Fusion or
// Weights is supplied. Defaults to 60.
RRFK int
}
EnsembleReranker combines several rerankers into one by fusing their individual scores. This is useful when different rerankers are strong in different regimes (e.g. a cross-encoder for semantic fit plus a sparse reranker for exact-term matches).
func NewEnsembleReranker ¶
func NewEnsembleReranker(rerankers ...Reranker) (*EnsembleReranker, error)
NewEnsembleReranker creates an ensemble from the given rerankers.
func (*EnsembleReranker) Rerank ¶
func (r *EnsembleReranker) Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank runs every sub-reranker over the same candidate set, fuses their per-chunk scores, and returns the results ordered by the fused score.
type Experiment ¶
type Experiment struct {
// contains filtered or unexported fields
}
Experiment is an A/B test over rerankers: each arm is a named variant whose reranking quality is measured against labeled RelevanceSamples. Collect samples per arm (typically by running each variant's Rerank and labeling the results), then call Complete for a full comparison.
func (*Experiment) AddVariant ¶
func (e *Experiment) AddVariant(name string, rr Reranker) error
AddVariant registers a named reranker variant. Names must be unique.
func (*Experiment) Complete ¶
func (e *Experiment) Complete(a, b string) (*ExperimentResult, error)
Complete finalizes the experiment by comparing the two named arms. Each arm must have at least one sample; arm order is irrelevant.
func (*Experiment) RecordSample ¶
func (e *Experiment) RecordSample(variant string, s RelevanceSample) error
RecordSample appends one labeled sample to the named variant.
func (*Experiment) Rerank ¶
func (e *Experiment) Rerank(ctx context.Context, variant, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank runs the named variant's reranker over the given candidates, returning the ranking so it can be labeled and fed back via RecordSample.
func (*Experiment) SampleCount ¶
func (e *Experiment) SampleCount(variant string) (int, error)
SampleCount returns the number of labeled samples recorded for the named variant.
type ExperimentResult ¶
type ExperimentResult struct {
// A and B are the metrics for each arm.
A, B VariantMetrics
// WinRateA is the fraction of pairwise sample comparisons where A's
// NDCG strictly exceeds B's.
WinRateA float64
// TStat and PValue come from a Welch t-test on the per-sample NDCG
// values (A vs B). PValue is approximated with the normal CDF.
TStat float64
PValue float64
// Significant reports whether PValue < 0.05.
Significant bool
}
ExperimentResult is the completed comparison of two arms.
type FeatureFunc ¶
type FeatureFunc func(query string, res index.SearchResult) []float64
FeatureFunc computes the feature vector for a (query, result) pair. The vector length must be constant across all calls for a given reranker.
type LLMReranker ¶
type LLMReranker struct {
// contains filtered or unexported fields
}
LLMReranker uses an LLM as a judge to score each candidate passage's relevance to the query. It is the highest-quality (and highest-cost) reranker in the package; the LLM backend is dependency-injected so no specific provider is required.
func NewLLMReranker ¶
func NewLLMReranker(cfg LLMRerankerConfig) (*LLMReranker, error)
NewLLMReranker creates an LLMReranker. It returns an error if no backend is provided.
func (*LLMReranker) Rerank ¶
func (r *LLMReranker) Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank asks the LLM to score each candidate passage and returns the results ordered by the judge's score (0-1 normalized to 0-1). Candidates beyond MaxCandidates (when set) are appended at the tail in coarse order with the coarse score used as their RerankScore.
type LLMRerankerConfig ¶
type LLMRerankerConfig struct {
// Backend is the LLM used to judge relevance (required).
Backend llm.Backend
// SystemPrompt overrides the judge's system message. Defaults to
// DefaultLLMRerankSystemPrompt when empty.
SystemPrompt string
// UserPrompt overrides the per-passage prompt template. It must contain
// two verbs for the query and passage placeholders, in that order.
// Defaults to DefaultLLMRerankUserPrompt when empty.
UserPrompt string
// MaxCandidates limits how many top coarse results are judged. The rest
// keep their coarse ordering at the tail. Zero means judge all.
MaxCandidates int
// Temperature controls sampling (0 recommended for deterministic judging).
Temperature float64
}
LLMRerankerConfig configures an LLMReranker.
type LTRConfig ¶
type LTRConfig struct {
// Features overrides the default feature extraction. When nil, the
// built-in features (coarse score, keyword overlap, length) are used.
Features FeatureFunc
// LearningRate is the gradient-descent step size. Defaults to 0.5.
LearningRate float64
// Epochs is the number of full passes over the training set. Defaults to 200.
Epochs int
// L2 is the L2 weight-decay coefficient. Defaults to 0.001.
L2 float64
}
LTRConfig configures an LTRanker's training.
type LTRExample ¶
type LTRExample struct {
// Query is the retrieval query.
Query string
// Result is the candidate (its coarse Score and Chunk are used by the
// default feature function).
Result index.SearchResult
// Label is the ground-truth relevance in [0,1].
Label float64
}
LTRExample is a single labeled training example: a (query, result) pair and a relevance label in [0,1] (1 = relevant, 0 = not).
type LTRanker ¶
type LTRanker struct {
// contains filtered or unexported fields
}
LTRanker is a pointwise learning-to-rank model: a logistic-regression classifier over per-candidate features. After Fit, it scores candidates by the learned relevance probability. Before any training it acts as an identity reranker (coarse score preserved), so it is always safe to wire in.
func NewLTRanker ¶
NewLTRanker creates an LTRanker from a config.
func (*LTRanker) Fit ¶
func (r *LTRanker) Fit(_ context.Context, examples []LTRExample) error
Fit trains the logistic model on the given labeled examples using full-batch gradient descent with L2 decay.
type RankedRelevance ¶
type RankedRelevance struct {
// ChunkID identifies the chunk.
ChunkID string
// Relevant is 1 for relevant, 0 for not.
Relevant float64
}
RankedRelevance pairs a chunk with its ground-truth relevance.
type RelevanceSample ¶
type RelevanceSample struct {
// Query is the query this sample belongs to.
Query string
// Ranked is the candidate set in the order the variant ranked them,
// each with its binary relevance.
Ranked []RankedRelevance
}
RelevanceSample is one query's labeled evaluation: the candidate set and the ground-truth relevance of each candidate in the order the variant ranked them. Relevance uses the same binary convention as LTRExample (1 = relevant, 0 = not).
func MarkRelevantIDs ¶
func MarkRelevantIDs(query string, chunkIDs, relevantIDs []string) RelevanceSample
MarkRelevantIDs returns a sample for the given ordered ranking in which exactly the listed chunk IDs are marked relevant.
func MarkTopRelevant ¶
func MarkTopRelevant(query string, chunkIDs []string) RelevanceSample
MarkTopRelevant returns a sample that marks the first chunk of the ranking as relevant and every other chunk as not.
type Reranker ¶
type Reranker interface {
// Rerank reorders and scores the given results for the query.
Rerank(ctx context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
// Name returns a stable identifier for this reranker, recorded in
// SearchResult.Reranker for score attribution.
Name() string
}
Reranker is the interface satisfied by all fine-ranking strategies.
Rerank takes a query and an already-retrieved (coarse) list of results and returns a new, reordered list. Implementations MUST:
- preserve the same set of chunks (no additions, no drops),
- set each result's RerankScore, RerankRank, and Reranker fields,
- return results sorted by RerankScore descending (RerankRank 1 = best).
The original Score (coarse retrieval score) is left untouched so callers can compare coarse vs fine ranking.
type SparseReranker ¶
type SparseReranker struct {
// Config are the BM25 saturation / length parameters.
Config bm25.Config
// KeepUnscored leaves chunks that scored zero (no keyword overlap) at
// the end of the list, ranked by their coarse score. When false, only
// chunks with a positive BM25 score are kept.
KeepUnscored bool
}
SparseReranker is a BM25-based reranker that re-scores coarse results by keyword relevance. It builds a BM25 index over just the candidate chunks and uses BM25 scores as the fine-rank score, which tends to promote exact-term matches that dense vectors can under-rank.
func NewSparseReranker ¶
func NewSparseReranker() *SparseReranker
NewSparseReranker creates a SparseReranker with default BM25 parameters.
func (*SparseReranker) Rerank ¶
func (r *SparseReranker) Rerank(_ context.Context, query string, results []index.SearchResult) ([]index.SearchResult, error)
Rerank re-scores the given results with BM25 and returns them ordered by the resulting score. Results are never dropped when KeepUnscored is true; otherwise chunks with zero keyword overlap are filtered out.
type VariantMetrics ¶
type VariantMetrics struct {
// Name is the variant name.
Name string
// Samples is the number of labeled samples this arm was scored on.
Samples int
// NDCGAtK is the mean NDCG@K across samples.
NDCGAtK float64
// MRRAtK is the mean reciprocal rank of the first relevant result.
MRRAtK float64
// PrecisionAtK is the fraction of relevant results in the top K.
PrecisionAtK float64
}
VariantMetrics holds the retrieval-quality metrics for one arm.