@claude-flow/embeddings
Version:
V3 Embedding Service - OpenAI, Transformers.js, Agentic-Flow (ONNX), Mock providers with hyperbolic embeddings, normalization, and chunking
275 lines (257 loc) • 11.7 kB
text/typescript
/**
* ADR-121 Phase 16 — Adaptive primitive selection (BEYOND SOTA).
*
* Phase 14's topology benchmark proved each RAG primitive dominates
* exactly one corpus shape:
*
* easy → plain wins (no fusion needed)
* duplicate-heavy → MMR wins on subtopic-coverage
* multi-intent → RRF wins on intent-boundary preservation
* q-a-gap → HyDE wins on centroid-finding
*
* Production RAG systems don't route between primitives automatically
* — they pick one pipeline at design time or run all-of-them and
* vote/reduce. Both choices leave performance on the table.
*
* This module ships **adaptive primitive selection**: extract three
* cheap signals from the candidate set + query variants + hypothetical
* answers, then pick the right primitive (or the compound) based on
* which signals fire. Cost: one extra small-fan cosine pass over the
* candidate set and the variant/hypothetical embeddings.
*
* Signals:
*
* 1. duplicateDensity — mean pairwise cosine among the top-N
* candidates from a plain search. High = corpus has near-dup
* structure → MMR signal.
*
* 2. queryIntentCohesion — mean pairwise cosine among the query
* reformulation vectors. Low = reformulations cover distinct
* intents → RRF signal.
*
* 3. qaSpaceGap — 1 - cosine(question_vector, mean(hypothetical_vectors)).
* High = the question and the hypothetical answers live in
* different vector regions → HyDE signal.
*
* The router applies a small set of thresholds (defaults from the
* Phase 14 topology data) and returns a structured decision the
* caller can act on or override.
*/
export interface RetrievalFeatures {
/** Mean pairwise cosine among top-N candidates from a plain search. Range [-1, 1]. Higher = more duplicate-like. */
readonly duplicateDensity: number;
/** Mean pairwise cosine among query reformulation vectors. Range [-1, 1]. Higher = more cohesive (single intent). */
readonly queryIntentCohesion: number;
/** 1 - cosine(question_vec, mean(hypothetical_vecs)). Range [0, 2]. Higher = larger Q/A space gap. */
readonly qaSpaceGap: number;
/**
* Phase 17 — Mean IDF of unique query tokens (relative to a BM25
* index over the corpus). Higher = query has rare/technical
* tokens that dense embeddings tend to underweight → hybrid
* sparse+dense retrieval wins. 0 when no BM25 index was provided.
*/
readonly rareTokenDensity: number;
/** How many top candidates were used to compute duplicateDensity. */
readonly candidateCountUsed: number;
/** How many variants were used to compute intent cohesion. */
readonly variantCount: number;
/** How many hypotheticals were used to compute Q/A gap. */
readonly hypotheticalCount: number;
/** True if the caller provided a BM25 index for rareTokenDensity. */
readonly bm25IndexProvided: boolean;
}
export interface AdaptiveRouterOptions {
/**
* Threshold above which duplicateDensity triggers the MMR signal.
* Default 0.85 — duplicates are very close to each other in
* cosine space.
*/
readonly duplicateThreshold?: number;
/**
* Threshold below which queryIntentCohesion triggers the RRF
* signal. Default 0.55 — orthogonal-ish reformulations.
*/
readonly intentCohesionThreshold?: number;
/**
* Threshold above which qaSpaceGap triggers the HyDE signal.
* Default 0.35 — meaningful question/answer space drift.
*/
readonly qaGapThreshold?: number;
/**
* Phase 17 — Threshold above which rareTokenDensity (mean query IDF)
* triggers the HYBRID signal. Default 2.0 — IDF ≥ 2.0 means the
* query's tokens appear in <~13% of the corpus, where BM25's
* sparse lexical signal materially helps dense retrieval.
*/
readonly rareTokenThreshold?: number;
/**
* If 2+ signals fire, return `compound` instead of one of the
* individual primitives. Default true.
*/
readonly preferCompoundWhenMultipleSignals?: boolean;
}
export type AdaptivePrimitive = 'plain' | 'mmr' | 'rrf' | 'hyde' | 'hybrid' | 'compound';
export interface AdaptiveDecision {
/** The recommended primitive. */
readonly primitive: AdaptivePrimitive;
/** Human-readable explanation citing which signals fired. */
readonly reason: string;
/** Which signals were above their respective thresholds. */
readonly signals: Readonly<{ mmr: boolean; rrf: boolean; hyde: boolean; hybrid: boolean }>;
/** Echo of the input features for traceability. */
readonly features: RetrievalFeatures;
}
function cosineSim(a: Float32Array | number[], b: Float32Array | number[]): number {
let dot = 0, na = 0, nb = 0;
const len = Math.min(a.length, b.length);
for (let i = 0; i < len; i++) {
dot += a[i]! * b[i]!;
na += a[i]! * a[i]!;
nb += b[i]! * b[i]!;
}
if (na === 0 || nb === 0) return 0;
return dot / (Math.sqrt(na) * Math.sqrt(nb));
}
function meanPairwiseCosine(vectors: ReadonlyArray<Float32Array | number[]>): number {
if (vectors.length < 2) return 0;
let total = 0, pairs = 0;
for (let i = 0; i < vectors.length; i++) {
for (let j = i + 1; j < vectors.length; j++) {
total += cosineSim(vectors[i]!, vectors[j]!);
pairs++;
}
}
return total / pairs;
}
function meanVector(vectors: ReadonlyArray<Float32Array | number[]>): Float32Array {
const dim = vectors[0]!.length;
const out = new Float32Array(dim);
for (const v of vectors) {
for (let i = 0; i < dim; i++) out[i]! += v[i]!;
}
for (let i = 0; i < dim; i++) out[i]! /= vectors.length;
return out;
}
/**
* Extract the routing signals from the available inputs.
*
* @param topCandidates — top-N hits from a cheap plain search,
* with vectors. Used for duplicateDensity.
* Pass `null` to disable that signal.
* @param queryVector — the user-question vector. Required for
* qaSpaceGap.
* @param variantVectors — N query reformulation vectors. Used for
* queryIntentCohesion. At least 2 required
* to compute the signal.
* @param hypotheticalVectors — N hypothetical-answer vectors. Used
* for qaSpaceGap. At least 1 required.
* @param bm25Input — optional {queryText, bm25Index} pair for the
* Phase 17 hybrid signal. Pass `null` to
* disable.
*
* Cost: O(N²·dim) for the pairwise cosines + O(|q|) for the BM25
* mean-IDF lookup.
*/
export function extractRetrievalFeatures(
topCandidates: ReadonlyArray<{ vector: Float32Array | number[] }> | null,
queryVector: Float32Array | number[],
variantVectors: ReadonlyArray<Float32Array | number[]>,
hypotheticalVectors: ReadonlyArray<Float32Array | number[]>,
bm25Input?: { queryText: string; index: { df: ReadonlyMap<string, number>; N: number } } | null,
): RetrievalFeatures {
// Duplicate density: pairwise cosine of top candidates.
const candVecs = (topCandidates ?? []).map(c => c.vector);
const duplicateDensity = meanPairwiseCosine(candVecs);
// Intent cohesion: pairwise cosine of variants. Need >=2 to compute.
const queryIntentCohesion = meanPairwiseCosine(variantVectors);
// Q/A space gap: distance between question vector and mean of hypotheticals.
let qaSpaceGap = 0;
if (hypotheticalVectors.length > 0) {
const mean = meanVector(hypotheticalVectors);
qaSpaceGap = 1 - cosineSim(queryVector, mean);
}
// Phase 17 — rare-token density from BM25 mean IDF.
let rareTokenDensity = 0;
let bm25IndexProvided = false;
if (bm25Input && bm25Input.index && typeof bm25Input.queryText === 'string') {
bm25IndexProvided = true;
// Inline the IDF computation to avoid an import cycle.
const tokens = Array.from(new Set(
bm25Input.queryText.toLowerCase().split(/[^a-z0-9]+/i).filter(t => t.length >= 2),
));
const { df, N } = bm25Input.index;
let total = 0;
let counted = 0;
for (const t of tokens) {
const dfT = df.get(t) ?? 0;
if (dfT === 0) continue;
const idf = Math.log(((N - dfT + 0.5) / (dfT + 0.5)) + 1);
total += idf;
counted++;
}
rareTokenDensity = counted === 0 ? 0 : total / counted;
}
return {
duplicateDensity,
queryIntentCohesion,
qaSpaceGap,
rareTokenDensity,
candidateCountUsed: candVecs.length,
variantCount: variantVectors.length,
hypotheticalCount: hypotheticalVectors.length,
bm25IndexProvided,
};
}
/**
* Apply the routing heuristic to a set of features. Returns the
* recommended primitive plus the reasoning trace.
*/
export function adaptiveRoute(
features: RetrievalFeatures,
options: AdaptiveRouterOptions = {},
): AdaptiveDecision {
const dupT = options.duplicateThreshold ?? 0.85;
const intentT = options.intentCohesionThreshold ?? 0.55;
const gapT = options.qaGapThreshold ?? 0.35;
const rareT = options.rareTokenThreshold ?? 2.0;
const compound = options.preferCompoundWhenMultipleSignals ?? true;
// Only fire a signal when the underlying input was actually present.
const mmrSignal = features.candidateCountUsed >= 2 && features.duplicateDensity > dupT;
const rrfSignal = features.variantCount >= 2 && features.queryIntentCohesion < intentT;
const hydeSignal = features.hypotheticalCount >= 1 && features.qaSpaceGap > gapT;
const hybridSignal = features.bm25IndexProvided && features.rareTokenDensity > rareT;
const fireCount = (mmrSignal ? 1 : 0) + (rrfSignal ? 1 : 0) + (hydeSignal ? 1 : 0) + (hybridSignal ? 1 : 0);
let primitive: AdaptivePrimitive;
let reason: string;
if (fireCount === 0) {
primitive = 'plain';
reason = `No signals fired (dup=${features.duplicateDensity.toFixed(3)}<=${dupT}, intentCohesion=${features.queryIntentCohesion.toFixed(3)}>=${intentT}, qaGap=${features.qaSpaceGap.toFixed(3)}<=${gapT}${features.bm25IndexProvided ? `, rareToken=${features.rareTokenDensity.toFixed(3)}<=${rareT}` : ''}); plain top-k is sufficient.`;
} else if (fireCount >= 2 && compound) {
primitive = 'compound';
const fired = [
mmrSignal ? `MMR(dup=${features.duplicateDensity.toFixed(3)}>${dupT})` : null,
rrfSignal ? `RRF(intent=${features.queryIntentCohesion.toFixed(3)}<${intentT})` : null,
hydeSignal ? `HyDE(qaGap=${features.qaSpaceGap.toFixed(3)}>${gapT})` : null,
hybridSignal ? `Hybrid(rareToken=${features.rareTokenDensity.toFixed(3)}>${rareT})` : null,
].filter(Boolean).join(', ');
reason = `Multiple signals fired: ${fired}; compound primitive composes them all.`;
} else if (mmrSignal) {
primitive = 'mmr';
reason = `MMR signal fired: duplicateDensity=${features.duplicateDensity.toFixed(3)}>${dupT}; corpus has near-duplicate top candidates, diversity rerank wins.`;
} else if (rrfSignal) {
primitive = 'rrf';
reason = `RRF signal fired: queryIntentCohesion=${features.queryIntentCohesion.toFixed(3)}<${intentT}; reformulations cover distinct intents, rank-fusion preserves boundaries.`;
} else if (hydeSignal) {
primitive = 'hyde';
reason = `HyDE signal fired: qaSpaceGap=${features.qaSpaceGap.toFixed(3)}>${gapT}; question and hypothetical answers diverge in vector space, HyDE bridges the gap.`;
} else {
primitive = 'hybrid';
reason = `Hybrid signal fired: rareTokenDensity=${features.rareTokenDensity.toFixed(3)}>${rareT}; query has rare/technical tokens that dense retrieval underweights, BM25 fusion recovers them.`;
}
return {
primitive,
reason,
signals: { mmr: mmrSignal, rrf: rrfSignal, hyde: hydeSignal, hybrid: hybridSignal },
features,
};
}