Files
chatbot_v3/docs/adr/0005-reranking-model-and-sparse-analyzer-selection.md
2026-08-02 15:52:46 +03:30

167 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 0005. Reranking model and sparse (BM25) analyzer selection for the Farsi corpus
## Status
Proposed — the fusion/rerank *shape* and reranker model are decided; the
final BM25 analyzer and the commercial license status of the reranker are
still open per the follow-up items below.
## Context
ADR-0003 fixed the retrieval *shape* (dense_nomic + dense_openai + sparse
prefetch → RRF fusion → late-interaction rerank) but deliberately left two
things open: which model powers `late_interaction`, and the sparse-side
analyzer detail behind `bm25-fa-norm-stop` (ADR-0001). This ADR resolves
those, driven by the corpus being **Farsi (Persian)** — a morphologically
rich, low-resource language for most public embedding/rerank benchmarks and
for Qdrant's own hosted tooling.
Two things specific to Farsi drove this investigation rather than picking a
generic default:
- Qdrant's hosted `Qdrant/bm25` FastEmbed model's documented supported-
language list does not include Farsi (`fa`) stemming — using it as-is
would silently apply no stemming rule, or the wrong one, for this corpus.
- Farsi's morphology (verb conjugation, ezafe constructions, high-frequency
function words) makes pure BM25/keyword signals noisier than in English,
which raised the bar on how load-bearing the rerank stage needs to be —
treated here as close to mandatory for quality, not optional.
## Decision
### 1. Fusion: RRF by default, weighted RRF as a fallback
Confirms ADR-0003's fusion stage combines all three prefetch results
(`dense_nomic`, `dense_openai`, `sparse`) via **RRF** by default. If one
signal (typically sparse, on Farsi text) is observed to dominate the fused
ranking unexpectedly, **weighted RRF** is the fallback — not a switch to a
different fusion algorithm. This refines, not replaces, ADR-0003's fusion
decision.
### 2. Late-interaction reranker: jina-colbert-v2
Two multilingual late-interaction (ColBERT-style) options were evaluated
against the requirement of confirmed Farsi support:
| Model | Farsi support | License | Local hosting |
|---|---|---|---|
| **jina-colbert-v2** | Confirmed — `fa` explicitly listed among 89 supported languages | Conflicting: HF repo metadata says `cc-by-4.0`; Jina's own announcement states CC BY-NC-4.0 (non-commercial), commercial use via paid API/AWS/Azure only | Possible via PyLate/RAGatouille; requires `flash_attn` (CUDA GPU effectively required); reported loading issues via generic `transformers.AutoModel` |
| **LFM2-ColBERT-350M** | Not supported (8 languages: en, ar, zh, fr, de, ja, ko, es) | — | — |
| BGE-M3 (alternative, not adopted) | Strong Farsi performance in the FaMTEB benchmark | Apache-2.0 — unambiguous, commercial-friendly | CPU-runnable, no `flash_attn` dependency |
**jina-colbert-v2 is adopted** as the `late_interaction` reranker: it has
explicitly confirmed Farsi support among its 89 languages, which is the
primary requirement for this corpus. This means:
- A **CUDA GPU is now a required dependency** for the rerank stage
(`flash_attn`), not optional infrastructure — this changes the self-hosted
Docker deployment assumption from ADR-0001/0003, which had not committed
to GPU hosting.
- The **commercial license is unresolved** — HF repo metadata states
`cc-by-4.0` while Jina's own announcement states CC BY-NC-4.0
(non-commercial, with commercial use only via Jina's paid API/AWS/Azure
offering). This must be confirmed directly with Jina **before** this
project ships commercially on a self-hosted jina-colbert-v2 model; if
Jina confirms the non-commercial reading, self-hosting it commercially is
not an option and the fallback is Jina's paid hosted API or BGE-M3 as a
substitute reranker (same `late_interaction` vector shape, no schema
change needed either way).
- Loading it outside PyLate/RAGatouille (e.g. generic
`transformers.AutoModel`) has reported issues — plan to load it through
one of those two libraries, not a raw `transformers` call.
`late_interaction` continues to use `hnsw_config: m=0` (per ADR-0001,
now made explicit: HNSW indexing is disabled for this vector because it is
used only for reranking already-fetched candidates via MAX_SIM, never for
independent ANN search — the recommended pattern for rerank-only
multivectors) plus on-disk storage.
### 3. Sparse retrieval: custom BM25 pipeline, not Qdrant's hosted model
Confirms ADR-0001's choice: the project's own BM25 pipeline (Farsi
normalization/stopword/stemming computed outside Qdrant Cloud Inference) is
used instead of Qdrant's hosted `Qdrant/bm25` FastEmbed model, specifically
because that hosted model's documented language list omits Farsi. The
sparse vector is uploaded with `modifier="idf"`, the standard BM25 sparse
vector configuration.
Four analyzer variants were benchmarked, all sharing identical BM25 scoring
parameters (`k=1.2`, `b=0.75`) so any accuracy difference is attributable
entirely to the analyzer stage, not the ranking formula:
| Analyzer | Description |
|---|---|
| `bm25-raw` | no normalization |
| `bm25-fa-norm` | Farsi normalization only |
| `bm25-fa-norm-stop` | normalization + stopword removal — **current best performer** |
| `bm25-fa-norm-stem` | normalization + stemming |
`bm25-fa-norm-stop` is confirmed as the sparse analyzer named in ADR-0001,
consistent with Farsi's high density of function words (ezafe particles,
prepositions, common verbs) adding TF/IDF noise if left in.
### 4. BM25 parameters: keep `k=1.2`, `b=0.75`; tune analyzer, not formula
These are standard, well-validated defaults (Trotman, Puurula & Burgess,
2014) and are not the source of the observed analyzer-variant accuracy
differences — so formula tuning is deprioritized in favor of the analyzer
comparison and `b` sweep in the follow-ups below.
## Consequences
### Positive
- Resolves ADR-0003's two deferred decisions (reranker model, sparse
analyzer detail) with a corpus-specific rationale instead of a generic
default.
- jina-colbert-v2 is the only evaluated option with explicitly confirmed
Farsi support, directly matching this project's primary requirement.
- Isolating BM25 formula parameters from analyzer choice gives a clean,
defensible experimental basis for the `bm25-fa-norm-stop` selection.
### Negative
- Introduces a hard GPU dependency (`flash_attn`/CUDA) for the rerank stage
that ADR-0001/0003 hadn't assumed — self-hosted deployment now needs GPU
capacity, not just Docker on commodity hardware.
- Commercial license status is unresolved; shipping this commercially on a
self-hosted jina-colbert-v2 model without confirming licensing with Jina
is a legal risk, not just a technical one.
- Loading path is constrained to PyLate/RAGatouille due to reported
`transformers.AutoModel` issues — an extra library dependency and less
flexibility than a standard `transformers` load would give.
- `bm25-fa-norm-stop` is provisional until compared directly against
`bm25-fa-norm-stem` — Persian stemming can over-collapse distinct words
(irregular verb conjugation, Arabic-loanword plurals), so the current
"best performer" result could shift.
## Alternatives Considered
- **BGE-M3 as the reranker**: not adopted — Apache-2.0 license and
CPU-runnability are attractive and it remains the fallback if
jina-colbert-v2's license is confirmed non-commercial, but jina-colbert-v2
was prioritized for its explicit Farsi support.
- **LFM2-ColBERT-350M**: rejected outright — no Farsi support among its 8
supported languages.
- **Qdrant's hosted `Qdrant/bm25` FastEmbed model**: rejected — its
documented language support does not include Farsi stemming; using it
would risk silently wrong or absent stemming for this corpus.
- **Treating "RRF vs. rerank" as either/or**: rejected — RRF alone can't
resolve disagreement between two distinct dense embedding spaces plus a
noisy Farsi BM25 signal, so the two-stage prefetch-fusion-then-rerank
pipeline from ADR-0003 is kept, not replaced with either component alone.
## Follow-up / Open Items
1. Confirm jina-colbert-v2's commercial license status directly with Jina
before commercial deployment; fall back to Jina's paid hosted API or
BGE-M3 if the non-commercial reading is confirmed.
2. Provision GPU capacity for self-hosted jina-colbert-v2 (`flash_attn`
requires CUDA) as part of the deployment plan, not an afterthought.
3. Run an ablation: single dense model + sparse + rerank vs. the current
dual-dense-model + sparse + rerank setup, on real Farsi queries, to
justify (or drop) the second dense vector (`dense_openai`).
4. Compare `bm25-fa-norm-stop` vs. `bm25-fa-norm-stem` in isolation to
determine whether gains come from stopword removal, stemming, or both.
5. Sweep BM25 `b` (e.g. 0.5–0.9) for the winning analyzer, since document
length varies significantly across the corpus (short chat messages vs.
long articles) and `0.75` is a generic default, not corpus-tuned.