Retrieval¶
varagity.retrieval.base
¶
Retriever protocol and registry (the spec §5.1 registry pattern).
Each retrieval method module defines one implementation decorated with
@register("name"); callers resolve the configured method with
get_retriever(settings.RETRIEVAL_METHOD). Registered: semantic,
bm25, hybrid (v1), reranked (v2, spec_v2 §5), and hyde
(ADR-016); adding a method later means one new file plus its import line —
no caller edits, exactly as the reranked and hyde additions proved.
Retriever
¶
Bases: Protocol
Interface every retrieval method implements.
runtime_checkable because the protocol appears in Prefect flow
signatures (varagity.pipeline.query_flow): Prefect builds a pydantic
parameter schema from the annotations at decoration time, which requires
types usable with isinstance.
Source code in varagity/retrieval/base.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
encode_query(query, verbose=None)
¶
Encode a query into the vector its retrieve would use.
Split out of retrieve so callers that track pipeline stages
(varagity.pipeline.query_flow, spec §10.1 step 2) can run query
embedding as its own stage and pass the result back via
query_vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted (each method owns its own
query encoding — e.g. e5 query mode for |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
list[float] | None
|
The query embedding, or |
list[float] | None
|
queries ( |
Source code in varagity/retrieval/base.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve the top-k chunks for a query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted (each method owns its own
query encoding — e.g. e5 query mode for |
required |
k
|
int
|
Number of chunks to return. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Pre-computed :meth: |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The top-k chunks, best first, each carrying its |
list[RetrievedChunk]
|
full persisted metadata record. |
Source code in varagity/retrieval/base.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
get_retriever(name)
¶
Look up a registered retrieval method by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Registry key (e.g. |
required |
Returns:
| Type | Description |
|---|---|
Retriever
|
The registered retriever instance. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If no retriever is registered under |
Source code in varagity/retrieval/base.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
register(name)
¶
Class decorator registering a retriever instance under name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Registry key (the |
required |
Returns:
| Type | Description |
|---|---|
Callable[[T], T]
|
The decorator, which instantiates and registers the class unchanged. |
Source code in varagity/retrieval/base.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
varagity.retrieval.semantic
¶
Dense (semantic) retrieval over pgvector (spec §10.1 step 3, §11.2).
Embeds the query in e5 query mode — the asymmetric counterpart of the
passage-mode embedding used at ingest time — and runs the cosine top-k
search of :class:~varagity.stores.vector_store.ContextualVectorDB.
SemanticRetriever
¶
Retriever backed by the pgvector cosine index.
The registry instantiates it without arguments (no I/O at import time); dependencies then resolve from settings per call — a fresh store connection per query keeps a long-lived chat session robust. Tests and the eval harness inject their own store/client instead.
Source code in varagity/retrieval/semantic.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
__init__(*, store=None, embeddings=None)
¶
Create the retriever.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
ContextualVectorDB | None
|
Vector store to search; opened from settings per call when omitted. |
None
|
embeddings
|
EmbeddingsClient | None
|
Embeddings client for query encoding; resolved via the model registry when omitted. |
None
|
Source code in varagity/retrieval/semantic.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
encode_query(query, verbose=None)
¶
Embed a query in e5 query mode (spec §9.5).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
list[float]
|
The query embedding. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If query embedding still fails after retries. |
Source code in varagity/retrieval/semantic.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve the top-k chunks by cosine similarity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query; embedded in e5 query mode. |
required |
k
|
int
|
Number of chunks to return. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Pre-computed :meth: |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The top-k chunks, best first, with cosine-similarity scores. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If query embedding still fails after retries. |
OperationalError
|
If the vector store is unreachable. |
Source code in varagity/retrieval/semantic.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
varagity.retrieval.bm25
¶
Sparse (BM25) retrieval over Elasticsearch (spec §10.1 step 3, §11.3).
A thin wrapper over :class:~varagity.stores.bm25_store.ElasticsearchBM25:
search the contextual BM25 index, then hydrate the full chunk records from
pgvector by the (doc_id, original_index) identity — Elasticsearch only
stores the identity and text fields, and grounded answers need the full
metadata (source for citations, context for the prompt).
BM25Retriever
¶
Retriever backed by the contextual BM25 index.
The registry instantiates it without arguments (no I/O at import time); dependencies then resolve from settings per call — fresh store connections per query keep a long-lived chat session robust. Tests and the eval harness inject their own stores instead.
Source code in varagity/retrieval/bm25.py
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | |
__init__(*, bm25=None, store=None)
¶
Create the retriever.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bm25
|
ElasticsearchBM25 | None
|
BM25 store to search; opened from settings per call when omitted. |
None
|
store
|
ContextualVectorDB | None
|
Vector store for hydration; opened from settings per call when omitted. |
None
|
Source code in varagity/retrieval/bm25.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
encode_query(query, verbose=None)
¶
Return None: BM25 searches raw text and never encodes queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query (unused). |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
None
|
Always |
None
|
state stays empty, spec §10.1). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in varagity/retrieval/bm25.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve the top-k chunks by BM25 relevance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, passed to Elasticsearch unformatted. |
required |
k
|
int
|
Number of chunks to return. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Accepted for protocol compatibility; unused (BM25 is purely lexical). |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The top-k chunks, best first, with BM25 scores and full hydrated |
list[RetrievedChunk]
|
metadata. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
ConnectionError
|
If Elasticsearch is still unreachable after retries. |
OperationalError
|
If the vector store is unreachable. |
Source code in varagity/retrieval/bm25.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
hydrate(scored_keys, store, traces=None)
¶
Hydrate identity tuples into full chunk records (spec §11.4).
Shared by the bm25 and hybrid retrievers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scored_keys
|
list[tuple[tuple[str, int], float]]
|
|
required |
store
|
ContextualVectorDB
|
The vector store holding the full rows. |
required |
traces
|
dict[tuple[str, int], RetrievalTrace] | None
|
Per-identity rank provenance to attach (spec_v2 §9.2); when
omitted the chunks carry |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The hydrated chunks in the given order. A key missing from pgvector |
list[RetrievedChunk]
|
(the stores disagree — e.g. a partially failed ingest) is dropped |
list[RetrievedChunk]
|
with a warning rather than surfacing an uncitable result. |
Source code in varagity/retrieval/bm25.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
varagity.retrieval.hybrid
¶
Hybrid retrieval — weighted reciprocal-rank fusion (spec §10.1, §11.4).
The cookbook's fusion, verbatim in structure: over-retrieve k * 10 from
each store, combine by weighted reciprocal rank (weight * 1/(rank+1)
— only rank positions matter, raw scores from the two stores are never
compared), dedupe on the (doc_id, original_index) identity, take the
top-k, and hydrate full rows from pgvector. This is the default
RETRIEVAL_METHOD (≈49% tier of the Anthropic retrieval-quality ladder);
the reranked method (spec_v2 §5) composes this retriever and
cross-encodes its output for the ≈67% tier.
HybridRetriever
¶
Retriever fusing the pgvector and Elasticsearch stores.
The registry instantiates it without arguments (no I/O at import time); dependencies then resolve from settings per call — fresh store connections per query keep a long-lived chat session robust. Tests and the eval harness inject their own stores/client instead.
Source code in varagity/retrieval/hybrid.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
__init__(*, store=None, bm25=None, embeddings=None)
¶
Create the retriever.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store
|
ContextualVectorDB | None
|
Vector store (semantic arm + hydration); opened from settings per call when omitted. |
None
|
bm25
|
ElasticsearchBM25 | None
|
BM25 store (sparse arm); opened from settings per call when omitted. |
None
|
embeddings
|
EmbeddingsClient | None
|
Embeddings client for query encoding; resolved via the model registry when omitted. |
None
|
Source code in varagity/retrieval/hybrid.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
encode_query(query, verbose=None)
¶
Embed a query in e5 query mode for the semantic arm (spec §9.5).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
list[float]
|
The query embedding (the BM25 arm searches the raw text). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If query embedding still fails after retries. |
Source code in varagity/retrieval/hybrid.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve the top-k chunks by weighted rank fusion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query; the semantic arm embeds it in e5 query mode, the BM25 arm searches it unformatted. |
required |
k
|
int
|
Number of chunks to return. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Pre-computed :meth: |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The top-k chunks, best first, with fused scores (maximum 1.0 |
list[RetrievedChunk]
|
given weights summing to 1.0) and full hydrated metadata. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If query embedding still fails after retries. |
ConnectionError
|
If Elasticsearch is still unreachable after retries. |
OperationalError
|
If the vector store is unreachable. |
Source code in varagity/retrieval/hybrid.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
fuse(semantic_keys, bm25_keys, *, semantic_weight, bm25_weight, k)
¶
Weighted reciprocal-rank fusion of two ranked lists (spec §11.4).
Each occurrence of an identity contributes weight * 1/(rank+1) for
its rank in that list; an identity present in both lists accumulates both
contributions (which is also what dedupes it). Ties keep semantic-list
order (sorted is stable and the semantic list is scored first).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
semantic_keys
|
list[Identity]
|
|
required |
bm25_keys
|
list[Identity]
|
Identities from the BM25 arm, best first. |
required |
semantic_weight
|
float
|
Weight of the semantic arm. |
required |
bm25_weight
|
float
|
Weight of the BM25 arm. |
required |
k
|
int
|
Number of fused results to keep. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[Identity, float]]
|
The top-k |
list[tuple[Identity, float]]
|
possible score is |
list[tuple[Identity, float]]
|
lists). |
Source code in varagity/retrieval/hybrid.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
fuse_with_traces(semantic, bm25, *, semantic_weight, bm25_weight, k)
¶
Fuse two scored ranked lists, keeping the per-arm ranks (spec_v2 §9.2).
The trace-building sibling of :func:fuse: fusion math is identical
(delegated), but the per-arm rank/score maps :func:fuse builds and
discards are preserved into a :class:~varagity.stores.records
.RetrievalTrace per fused survivor — the "why it ranked here" data the
provenance panel renders. Raw arm scores still never enter the fusion
math; they ride along for display only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
semantic
|
list[tuple[Identity, float]]
|
|
required |
bm25
|
list[tuple[Identity, float]]
|
|
required |
semantic_weight
|
float
|
Weight of the semantic arm. |
required |
bm25_weight
|
float
|
Weight of the BM25 arm. |
required |
k
|
int
|
Number of fused results to keep. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
The |
list[tuple[Identity, float]]
|
func: |
dict[Identity, RetrievalTrace]
|
best first) plus a per-identity trace for each survivor, with |
|
tuple[list[tuple[Identity, float]], dict[Identity, RetrievalTrace]]
|
1-based per-arm/fused ranks and |
|
tuple[list[tuple[Identity, float]], dict[Identity, RetrievalTrace]]
|
rerank stage, when active, overwrites the final fields downstream). |
Source code in varagity/retrieval/hybrid.py
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
varagity.retrieval.reranked
¶
Cross-encoder re-ranking over a base retriever (spec_v2 §5 — the ≈67% tier).
Re-ranking composes an existing retrieval method rather than replacing
fusion: over-fetch a wide candidate pool (RERANK_CANDIDATES) from the
configured RERANK_BASE_METHOD, cross-encode every candidate's original
content against the query (the contextual blurb already did its job at
the embedding/BM25 stage), and keep the RERANK_TOP_N most relevant —
the Anthropic cookbook's 150→20 over-fetch, scaled to this corpus.
RERANK_ENABLED is a kill switch orthogonal to method selection: when
false the retriever degrades to its base method's ranking (logged), so
a GUI toggle and the eval baseline both work without renaming the method.
RerankedRetriever
¶
Base retriever → cross-encoder rerank → top-N (spec_v2 §5.2).
The registry instantiates it without arguments (no I/O at import time); the base retriever and rerank client then resolve from settings per call, so fusion improvements flow through and a config change needs no restart. Tests and the eval harness inject their own instead.
Source code in varagity/retrieval/reranked.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
__init__(*, base=None, rerank=None)
¶
Create the retriever.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Retriever | None
|
Retriever producing the candidate pool; resolved from
|
None
|
rerank
|
RerankClient | None
|
Cross-encoder client; resolved via the model registry
( |
None
|
Source code in varagity/retrieval/reranked.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
encode_query(query, verbose=None)
¶
Encode a query exactly as the base retriever would (spec_v2 §5.2).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
list[float] | None
|
The base retriever's query encoding ( |
list[float] | None
|
base — nothing to encode). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If query embedding still fails after retries. |
Source code in varagity/retrieval/reranked.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve a wide candidate pool, cross-encode it, keep the top-N.
The pool is max(RERANK_CANDIDATES, k) from the base retriever;
the cut is min(k, RERANK_TOP_N) (re-ranking narrows — it never
returns more than the caller asked for). With RERANK_ENABLED
off, the base ranking passes through the same cut, logged as a
degradation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query; the base retriever owns its encoding, the cross-encoder scores the raw pair. |
required |
k
|
int
|
Number of chunks the caller wants (bounds the |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Pre-computed :meth: |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The |
list[RetrievedChunk]
|
with cross-encoder scores and the full rank provenance trace |
list[RetrievedChunk]
|
(or the base ranking when the kill switch is off). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
HTTPError
|
If the rerank request still fails after retries. |
Source code in varagity/retrieval/reranked.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
apply_rerank(candidates, scored)
¶
Reorder candidates by cross-encoder relevance, filling the rerank trace.
Each result's index points into candidates (which arrive
best-first from the base retriever, so index + 1 is the pre-rerank
rank). The surviving chunk's score becomes the cross-encoder
relevance — the "final score" the provenance panel shows — while the
base retriever's fused score/ranks stay intact on the trace.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
candidates
|
list[RetrievedChunk]
|
The base retriever's candidate pool, best first. |
required |
scored
|
list[RerankResult]
|
Cross-encoder judgments for (a subset of) the candidates. |
required |
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The judged candidates, most relevant first, each with |
list[RetrievedChunk]
|
|
list[RetrievedChunk]
|
up, − moved down), and |
list[RetrievedChunk]
|
candidate without a base trace (an injected test fake) gets one |
list[RetrievedChunk]
|
built from its pre-rerank score and rank. |
Source code in varagity/retrieval/reranked.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | |
varagity.retrieval.hyde
¶
HyDE — Hypothetical Document Embeddings over a base retriever (ADR-016).
The Gao et al. (2022) pattern adapted to the registry convention: one
non-streaming LLM call writes a hypothetical answer passage for the
query, and the dense arm searches with that passage's embedding instead
of the query's. The passage is embedded in e5 passage mode
(:meth:~varagity.models.embeddings.EmbeddingsClient.embed_passages — the
paper's document-encoder choice), landing it in the same vector space as
the ingested corpus, where passage↔passage neighbors are exactly the
chunks that look like the answer.
Like reranked, HyDE composes a base retriever
(HYDE_BASE_METHOD: semantic | hybrid) rather than forking
fusion — and the substitution is dense-arm only: the base receives the
user's original query text (a hybrid base's BM25 arm keeps its exact
keyword recall) plus the hypothetical passage's vector via the
query_vector seam. Pairing with the cross-encoder stacks the other way
around: RETRIEVAL_METHOD=reranked + RERANK_BASE_METHOD=hyde, so the
reranker judges candidates against the user's real query, never the
hypothetical (hyde therefore refuses reranked as its own base —
config-validated).
Failure is a fallback, not an error (the condense-stage posture): a
transient LLM failure (after the client's own tenacity retries), an
empty cleaned passage, or an absurdly long one all degrade to the base
method's raw-query retrieval at WARNING. HYDE_ENABLED=false is the
kill switch, checked inside the retriever exactly as RERANK_ENABLED is
— deliberately orthogonal to method selection. The generated passage
must pass through :func:~varagity.models.llm.clean_response: llama.cpp
emits <think> blocks and the non-streaming
:meth:~varagity.models.llm.LLMClient.generate does not strip them — an
unstripped one embedded as the search probe silently destroys retrieval.
HydeRetriever
¶
LLM hypothetical passage → passage-mode embedding → base retriever.
The registry instantiates it without arguments (no I/O at import time); the base retriever and model clients then resolve from settings per call, so a config change needs no restart. Tests and the eval harness inject their own instead.
Source code in varagity/retrieval/hyde.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |
__init__(*, base=None, llm=None, embeddings=None)
¶
Create the retriever.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Retriever | None
|
Retriever the hypothetical-passage vector is handed to;
resolved from |
None
|
llm
|
LLMClient | None
|
Chat client generating the passage; resolved via the model
registry ( |
None
|
embeddings
|
EmbeddingsClient | None
|
Embeddings client for the passage-mode encoding;
resolved via the model registry ( |
None
|
Source code in varagity/retrieval/hyde.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
encode_query(query, verbose=None)
¶
Encode the hypothetical passage the dense arm searches with.
Generates the passage and embeds it in e5 passage mode — the
vector its :meth:retrieve uses, per the protocol contract. With
HYDE_ENABLED=false (logged), or whenever no usable passage was
generated (the fallback), the base method's own query encoding is
returned instead — retrieval then behaves exactly like the base.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query, unformatted. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
list[float] | None
|
The passage-mode embedding of the hypothetical passage, or the |
list[float] | None
|
base retriever's query encoding on the degraded paths. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If embedding still fails after retries. |
Source code in varagity/retrieval/hyde.py
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
retrieve(query, k, verbose=None, *, query_vector=None)
¶
Retrieve top-k via the base method, dense arm steered by HyDE.
The base retriever receives the user's original query text (a
hybrid base's BM25 arm keeps exact keyword recall; a composing
reranked cross-encodes the real query) together with the
hypothetical passage's vector — the dense arm is the only arm HyDE
redirects. Chunks and their traces pass through untouched: the
base's ranking is the ranking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The user's query; searched verbatim by every non-dense consumer. |
required |
k
|
int
|
Number of chunks to return. |
required |
verbose
|
int | None
|
Console verbosity (0–2); defaults to
|
None
|
query_vector
|
list[float] | None
|
Pre-computed :meth: |
None
|
Returns:
| Type | Description |
|---|---|
list[RetrievedChunk]
|
The base retriever's top-k chunks, best first. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
APIError
|
If embedding still fails after retries. |
Source code in varagity/retrieval/hyde.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |