Rerankers
Rerankers reorder an initial set of search results by scoring each document’s relevance to the query with a dedicated model. Vector search casts a wide, fast net; a reranker then refines the top candidates for higher-quality final results.
The Reranker interface
All rerankers implement rerank.Reranker:
import "github.com/redis/redis-vl-golang/extensions/rerank"
type Reranker interface {
// Rank returns documents sorted by descending relevance.
Rank(ctx context.Context, query string, docs []string) ([]rerank.Result, error)
}
Each Result carries the document’s position in the input slice, its text, and the model’s relevance score:
type Result struct {
Index int // position in the input slice
Document string // original document text
Score float64 // relevance score assigned by the reranker
}
The hosted providers (Cohere, VoyageAI) retry transient API errors (429, 5xx) up to MaxRetries times (default 3) with exponential backoff.
Cohere
// APIKey defaults to COHERE_API_KEY
r, err := rerank.NewCohereReranker(rerank.CohereConfig{
Model: "rerank-english-v3.0", // the default
Limit: 3, // top-n results (default 5)
})
results, err := r.Rank(ctx, "query text", []string{"doc one", "doc two", "doc three"})
CohereConfig fields: APIKey, Model (default rerank-english-v3.0), Limit (default 5), BaseURL (default https://api.cohere.com), MaxRetries, HTTPClient.
VoyageAI
// APIKey defaults to VOYAGE_API_KEY; Model is required
r, err := rerank.NewVoyageAIReranker(rerank.VoyageAIConfig{
Model: "rerank-2",
Limit: 3, // default 5
})
results, err := r.Rank(ctx, "query text", docs)
Unlike Cohere, VoyageAI has no default model — Model must be set explicitly (e.g. "rerank-2"), matching the Python library’s behavior.
|
Local cross-encoder (hf module)
hf.NewCrossEncoder reranks with a Hugging Face cross-encoder running in-process through ONNX Runtime — no API key, and documents never leave your machine. It is the Go port of Python’s HFCrossEncoderReranker and lives in the same separate hf module as the local vectorizer (see Local embeddings for installation and the ONNX Runtime requirement).
import "github.com/redis/redis-vl-golang/extensions/vectorize/hf"
// cross-encoder/ms-marco-MiniLM-L-6-v2 by default
ce, err := hf.NewCrossEncoder(ctx, hf.CrossEncoderConfig{Limit: 3})
if err != nil {
log.Fatal(err)
}
defer ce.Close()
results, err := ce.Rank(ctx, "query text", []string{"doc one", "doc two", "doc three"})
The model is downloaded from the Hugging Face Hub on first use and cached locally. CrossEncoder satisfies rerank.Reranker, so it is a drop-in replacement for the hosted providers.
Configuration
| Field | Description |
|---|---|
|
Hugging Face cross-encoder model id (default |
|
ONNX export path inside the repository (default |
|
Path to the onnxruntime shared library (default: |
|
Model cache location and Hugging Face Hub settings, as for the local vectorizer. |
|
Maximum number of results returned by |
|
Override the model’s pair truncation length. |
|
Number of (query, document) pairs scored per model run (default 32, matching sentence-transformers |
Score semantics
Scores follow sentence-transformers semantics: raw logits or sigmoid-activated values in [0, 1], as dictated by the model’s own configuration. For the default ms-marco model, higher is more relevant, but scores are raw logits and can be negative — compare scores relatively, not against a fixed threshold, unless you know the model applies a sigmoid.
Only single-label cross-encoders are supported; multi-label classification models are rejected at construction. Call Close() when finished to release the ONNX Runtime session.
Retrieve, then rerank
The standard pattern: over-fetch candidates with a fast vector query, then let the reranker pick the best few.
import (
"github.com/redis/redis-vl-golang/query"
"github.com/redis/redis-vl-golang/extensions/rerank"
)
// 1. Retrieve a generous candidate set with vector search
q := query.NewVectorQuery("embedding", queryVector).
NumResults(20).
ReturnFields("content")
candidates, err := index.Query(ctx, q)
if err != nil {
log.Fatal(err)
}
// 2. Extract the document texts
docs := make([]string, 0, len(candidates))
for _, doc := range candidates {
docs = append(docs, doc["content"].(string))
}
// 3. Rerank and keep the top 3
r, err := rerank.NewCohereReranker(rerank.CohereConfig{Limit: 3})
if err != nil {
log.Fatal(err)
}
results, err := r.Rank(ctx, "how do I reset my password?", docs)
for _, res := range results {
// res.Index maps back to the original candidate for its metadata
original := candidates[res.Index]
fmt.Printf("%.4f %s (distance %v)\n", res.Score, res.Document, original["vector_distance"])
}
Result.Index refers to the position in the docs slice you passed in, so you can recover each reranked document’s full metadata from the original candidate list.
| Rerankers score every (query, document) pair, so cost grows with candidate count. Fetching 3-10x your final result count is usually a good balance between recall and latency. |
Next steps
-
Vectorizers — generate the embeddings used for retrieval
-
Querying with RedisVL — build the candidate-retrieval query