@claude-flow/embeddings
Version:
V3 Embedding Service - OpenAI, Transformers.js, Agentic-Flow (ONNX), Mock providers with hyperbolic embeddings, normalization, and chunking
87 lines • 3.89 kB
TypeScript
/**
* ADR-121 Phase 12 — Embedding-level fusion (HyDE).
*
* HyDE (Hypothetical Document Embeddings — Gao, Ma, Lin, Callan 2022,
* "Precise Zero-Shot Dense Retrieval without Relevance Labels") fixes
* a structural problem with question-based dense retrieval: question
* embeddings live in "question space" while documents embed into
* "answer space", so cosine similarity systematically underweights
* relevant docs whose surface form differs from the question.
*
* The HyDE recipe:
* 1. Have an LLM generate N hypothetical answers to the question.
* 2. Embed each hypothetical answer.
* 3. Average those embeddings into a single query vector.
* 4. Search with the averaged vector.
*
* Empirically: this single transform produces SOTA zero-shot
* retrieval on BEIR — beating supervised in-domain dense retrievers
* on many tasks. The trick works because hypothetical answers live
* in the same answer-space as the corpus, so the averaged vector
* lands near the true relevant docs.
*
* This module ships the **averaging** step as a pure function — the
* LLM-generation step is the caller's job (the orchestrator agent
* already has an LLM). Composes with every existing retrieval shape:
*
* HyDE + plain search = embeddings_search_text_hyde
* HyDE + MMR rerank = caller-composable (search_text_hyde
* results piped through mmrRerank)
* HyDE + RRF ensemble = caller-composable (search_text_hyde
* across multiple LLM samples, fused)
*
* Relationship to RRF (Phase 11):
* - RRF fuses at the **rank** level: N searches, merge ranks. More
* expensive but preserves intent boundaries between variants.
* - HyDE/avg fuses at the **embedding** level: 1 search after vector
* average. Cheaper but interpolates between variants — the
* averaged vector represents the "centroid" intent.
*
* The two are complementary, not redundant. Production systems
* often use HyDE inside one ranked list, and RRF across multiple
* ranked lists (one HyDE-search, one BM25-search, etc.).
*/
export interface AverageEmbeddingsOptions {
/**
* Per-vector weights. Length must equal vectors.length.
* Defaults to uniform = 1 each.
*
* Use weights when the caller has confidence signals — e.g. weight
* the user's original question 0.5× and the LLM's hypothetical
* answers 1.0× each, so the answers dominate (the HyDE paper's
* default recipe).
*/
readonly weights?: ReadonlyArray<number>;
/**
* L2-normalize each input vector BEFORE averaging. Default true.
*
* This is the standard HyDE recipe — without it, vectors with
* larger norms dominate the average regardless of their semantic
* weight. Set to false only if you already pre-normalized
* (cheaper) or if you specifically want norm-weighted averaging.
*/
readonly normalizeInputs?: boolean;
/**
* L2-normalize the OUTPUT averaged vector. Default true.
*
* Required for cosine-similarity search backings (HNSW with
* cosine, RaBitQ, DiskANN). The math: avg of unit vectors is not
* itself a unit vector.
*/
readonly normalizeOutput?: boolean;
}
/**
* Average N embedding vectors into a single embedding vector.
*
* Time complexity: O(N · dim). Memory: O(dim).
*
* Returns a brand-new Float32Array — does not mutate inputs.
*/
export declare function averageEmbeddings(vectors: ReadonlyArray<Float32Array | number[]>, options?: AverageEmbeddingsOptions): Float32Array;
/**
* Convenience: confirm a Float32Array is L2-unit-normalized within
* `tolerance`. Useful for tests + the smoke driver to verify the
* normalizeOutput contract.
*/
export declare function isUnitNorm(v: Float32Array, tolerance?: number): boolean;
//# sourceMappingURL=embedding-fusion.d.ts.map