ADR-075: Neutral cross-encoder reranker protocol
- Status
-
Accepted
- Date
-
2026-07-17
- Amends
-
ADR-050 (cross-encoder reranking placement)
- Authors
-
Netresearch DTT GmbH
Context
nr_ measured (NRFE-3960, its ADR-029) that a cross-encoder
reranker over the bi-encoder candidate pool lifts top-1 retrieval
accuracy where the bi-encoder alone and a naive LLM reranker do not —
on the real BMDV corpus from 6/9 to 8/9. It shipped the capability as a
consumer-private client (Reranker over its own
Retrieved DTO) plus an HTTP sidecar in its
Build/ serving the one heavy dependency
(sentence-transformers + torch) outside PHP.
Its ADR-029 kept that placement deliberately consumer-side because no second consumer existed. The revisit trigger has fired: cross-encoder scoring is wanted for nr_llm-side ranking too (upgrading embed-and-rank from bi-encoder to cross-encoder quality, e.g. link-target ranking), which makes it a shared capability — exactly the category ADR-050 assigns to nr_llm, as long as it stays stateless.
Decision
1. Neutral protocol in nr_llm.
Netresearch\:
rerank(string $query, array $candidates): array
// $candidates: list<array{id: string, text: string}>
// return: list<array{id: string, score: float}>
No consumer DTO crosses the boundary. Implementations:
Http— speaks the sidecar contract (Reranker POST→{endpoint}/ rerank {"query", "documents": [ {"id", "text"}]} {"scores":, input order). Pools above the sidecar's batch cap ([ {"id", "score"}]} RERANKER_, default 128) are split into sequential requests; per-pair scoring makes the split score-neutral. A transport failure, non-200 status or off-protocol body throws the typedMAX_ DOCUMENTS Reranker(Exception Service\) — nr_llm never decides degradation.Rerank\ Exception Null— one entry per candidate in input order with a uniform score ofReranker 0.. The uniform value carries no ranking signal (a stable sort preserves the caller's ordering) and keeps the result shape-identical to0 Httpso consumer merge code needs no null branch.Reranker
2. Selection rule. Reranker builds the
Reranker container service from the extension
configuration: an empty reranker selects Null,
a configured endpoint selects Http with the configurable
reranker (default 30 s — a CPU cross-encoder can be slow for
a wide pool). Unreadable configuration fails open to Null.
This mirrors the selection nr_ai_search's Reranker
made consumer-side.
3. Sidecar moves alongside the client. Build/
(app., Dockerfile, requirements., README.) now
lives in nr_llm so client and server version together. The HTTP
contract is unchanged.
4. What stays consumer-side. DTO mapping (id/text extraction,
score attachment), the ordering merge (including how an unscored
candidate ranks), the degradation policy on Reranker
(e.g. fall back to the pre-rerank cosine ordering), and any
score-threshold gate (the score scale is model-specific).
Boundary with ADR-050. This supersedes the nr_llm-side reading of ADR-050 / nr_ai_search ADR-029 that cross-encoder reranking lives only in nr_ai_search. ADR-050's guardrail is untouched: the reranker is a stateless capability (candidates in, scores out) — no persistent index, no chunking, no reindex pipeline. nr_ai_search amends its ADR-028/ADR-029 in its own adoption change.
Public-service count: 29 → 30 (after ADR-076's 28 → 29). The Reranker
factory-built entry joins Category A (documented downstream contract).
This ADR is the new count authority, superseding ADR-076's count. The
breakdown is now 17 + 5 + 1 + 5 + 2 = 30
(Public).
Consequences
- A consumer depends on
Rerankerand drops its own client, factory and sidecar copy; only its DTO mapping, merge, degradation and gate code remain.Interface - Two new extension-configuration keys:
reranker,Endpoint reranker. Both default to off/30 s — nothing changes until an operator opts in.Timeout - The sidecar container is an optional runtime dependency of nr_llm deployments that enable reranking; nr_llm itself never requires it.
RerankerimplementsException Nr(ADR-053), so blanket consumer catch blocks keep working.Llm Exception Interface