Free lesson · GenAI Data Engineering
Evaluate Voyage 4's shared embedding space across model tiers
Test Voyage 4's cross-tier compatibility: index with voyage-4-large, query with voyage-4-lite or voyage-4-nano. Measure quality vs cost tradeoffs.
Course: GenAI Data Pipelines · Chapter 6 · Embedding Model Selection & Benchmarking
Free to read — no subscription required.
Introduction
When you've already indexed millions of documents with voyage-4, re-embedding them with a cheaper tier just to lower query costs is a non-starter — the rebuild cost dwarfs the savings, and the wrong call here means either a multi-day reindex or a silent retrieval-quality cliff in production. Voyage 4's model family (voyage-4, voyage-4-lite, voyage-4-nano) shares a common embedding space, so vectors produced by one tier are directly comparable to vectors produced by another, which makes asymmetric indexing possible — but the quality cost of that mismatch must be measured per corpus before it ships. By the end you'll be able to generate cross-tier embeddings, benchmark retrieval quality (MRR, recall@10) against same-tier baselines, and quantify the cost savings of indexing with the large model while querying with a cheaper tier.
Key Terminology
- Shared embedding space: a property of the Voyage 4 family where embeddings from voyage-4, voyage-4-lite, and voyage-4-nano live in the same vector geometry and can be compared with dot product or cosine similarity without recalibration.
- Cross-tier embedding: indexing documents with one Voyage 4 tier (typically voyage-4) and querying with a different, cheaper tier (voyage-4-lite or voyage-4-nano) to reduce query-time cost while keeping a single vector index.
- Same-tier baseline: a retrieval configuration where the same Voyage 4 model is used for both indexing and querying; it is the reference point against which cross-tier MRR and recall@10 are measured to quantify quality loss.
Concepts
Evaluating the shared embedding space rests on three ideas. First, the compatibility claim — that voyage-4, voyage-4-lite, and voyage-4-nano embeddings are interchangeable in a single index — is an empirical question, not a guarantee, and must be measured per corpus with MRR and recall@10. Second, the input_type parameter ("document" vs "query") is what lets cross-tier pairs cooperate inside the shared space, because it conditions each embedding for its retrieval role. Third, the cost-quality tradeoff is only meaningful when same-tier baselines (large/large, lite/lite) are evaluated alongside cross-tier pairs (large/lite, large/nano), since the delta between them is what justifies — or rules out — deploying the cheaper query model.
Code Walkthrough
Generating Cross-Tier Embeddings
The shared embedding space across Voyage 4's model tiers enables an asymmetric indexing pattern: index documents with the expensive voyage-4 model for maximum quality, then query with the cheaper voyage-4-lite or voyage-4-nano for cost efficiency. The embed_voyage and embed_voyage_query functions below use separate input_type parameters ("document" for indexing, "query" for search) to optimize embeddings for their retrieval role. The generate_cross_tier function combines these to produce document and query embeddings from different model tiers in a single call.
Code snippet python
1import voyageai 2 3vo = voyageai.Client() 4 5def embed_voyage(texts: list[str], model: str = "voyage-4") -> list[list[float]]: 6 result = vo.embed(texts, model=model, input_type="document") 7 return result.embeddings 8 9def embed_voyage_query(texts: list[str], model: str = "voyage-4-lite") -> list[list[float]]: 10 result = vo.embed(texts, model=model, input_type="query") 11 return result.embeddings 12 13def generate_cross_tier( 14 documents: list[str], 15 queries: list[str], 16 index_model: str = "voyage-4", 17 query_model: str = "voyage-4-lite", 18) -> tuple[list[list[float]], list[list[float]]]: 19 doc_embeddings = embed_voyage(documents, model=index_model) 20 query_embeddings = embed_voyage_query(queries, model=query_model) 21 return doc_embeddings, query_embeddings
- Lines 5-7: Document embeddings use
input_type="document"with the large model for maximum index quality. - Lines 9-11: Query embeddings use
input_type="query"with the lite model for cost-efficient search. The shared embedding space ensures these vectors are compatible.
Benchmarking Cross-Tier Quality and Cost Together
Retrieval quality and cost only mean something side by side: a quality drop without a dollar number can't be triaged, and savings without a same-tier baseline can't be interpreted. The benchmark_cross_tier function below evaluates multiple tier configurations — including same-tier baselines (large/large, lite/lite) alongside cross-tier combinations (large/lite, large/nano) — computing MRR and recall@10, then attaching monthly query cost and savings (relative to an all-large baseline) on the same result row. The is_cross_tier flag enables direct quality comparison between matched and mismatched configurations.
Code snippet python
1import numpy as np 2 3VOYAGE_PRICING = { # USD per million tokens — 7.5x spread between large and nano 4 "voyage-4": 0.06, 5 "voyage-4-lite": 0.02, 6 "voyage-4-nano": 0.008, 7} 8 9def benchmark_cross_tier( 10 benchmark: "EmbeddingBenchmark", 11 tier_configs: list[dict], 12 monthly_queries: int = 1_000_000, 13 avg_query_tokens: int = 50, 14) -> list[dict]: 15 results = [] 16 baseline_price = VOYAGE_PRICING["voyage-4"] 17 for config in tier_configs: 18 index_model = config["index_model"] 19 query_model = config["query_model"] 20 21 doc_embs = embed_voyage(benchmark.documents, model=index_model) 22 query_embs = embed_voyage_query(benchmark.queries, model=query_model) 23 24 rankings = [] 25 for q_idx in range(len(benchmark.queries)): 26 q_emb = np.array(query_embs[q_idx]) 27 scores = [float(np.dot(q_emb, np.array(d))) for d in doc_embs] 28 rankings.append(np.argsort(scores)[::-1].tolist()) 29 30 mrr = benchmark._compute_mrr(rankings) 31 recall_10 = benchmark._compute_recall(rankings, k=10) 32 33 query_cost = monthly_queries * avg_query_tokens * VOYAGE_PRICING[query_model] / 1_000_000 34 baseline_cost = monthly_queries * avg_query_tokens * baseline_price / 1_000_000 35 36 results.append({ 37 "index_model": index_model, 38 "query_model": query_model, 39 "mrr": round(mrr, 4), 40 "recall_at_10": round(recall_10, 4), 41 "is_cross_tier": index_model != query_model, 42 "monthly_query_cost": round(query_cost, 2), 43 "monthly_savings": round(baseline_cost - query_cost, 2), 44 "savings_pct": round((1 - query_cost / max(baseline_cost, 0.01)) * 100, 1), 45 }) 46 return results 47 48# Example configurations — same-tier baselines alongside cross-tier pairs 49configs = [ 50 {"index_model": "voyage-4", "query_model": "voyage-4"}, # baseline 51 {"index_model": "voyage-4", "query_model": "voyage-4-lite"}, # cross-tier 52 {"index_model": "voyage-4", "query_model": "voyage-4-nano"}, # cross-tier 53 {"index_model": "voyage-4-lite", "query_model": "voyage-4-lite"}, # cheaper baseline 54]
- Lines 17-22: For each configuration, regenerate embeddings with the chosen index and query models so each comparison is apples-to-apples.
- Lines 24-31: Compute MRR and recall@10 from dot-product rankings — the dot product is only meaningful because the Voyage 4 tiers share an embedding space.
- Lines 33-44: Attach monthly cost projections to the same result row so the quality delta and the dollar delta land together — no separate merge step downstream.
- Lines 48-53: The config list includes the voyage-4/voyage-4 same-tier baseline so the cross-tier MRR loss is explicit, not implied. Verify by checking that the same-tier baseline shows
is_cross_tier: Falsewith 0 savings, and the cross-tier rows show a quantified MRR delta against it.
In practice, cross-tier embedding with voyage-4 indexing and voyage-4-lite querying typically retains 95-98% of MRR while reducing query-time embedding costs by 67%. The nano tier reduces costs by 87% but may drop MRR below acceptable thresholds on complex domains — the benchmark above is what tells you which side of that line your corpus falls on.
Do's and Don'ts
Now that you have worked through the implementation, the practices below separate a durable approach from a fragile one.
Do's
- ✓Always benchmark cross-tier pairs against a same-tier baseline (e.g. voyage-4/voyage-4) so the MRR and recall@10 delta is interpretable as a quality cost, not an absolute number.
- ✓Pass
input_type="document"at index time andinput_type="query"at query time — the shared embedding space only holds when each side is conditioned for its role. - ✓Re-evaluate the cross-tier configuration on your own corpus before deploying; published numbers do not transfer cleanly across domains, especially for nano-tier queries.
Don'ts
- ✗Don't assume the 95-98% MRR retention figure applies to your corpus without running
benchmark_cross_tieron representative documents and queries. - ✗Don't drop to voyage-4-nano for queries on complex or specialist domains without checking recall@10 — the 87% cost reduction can come with a quality drop that breaks downstream retrieval.
- ✗Don't compare embeddings across model families (e.g. voyage-4 vs voyage-3) inside one index; the shared embedding space property is specific to the Voyage 4 family.
This lesson is free to read. Its 3 hands-on labs — real code, in a cloud IDE — are part of the GenAI Data Engineering subscription.
From · cancel anytime · Already a subscriber? Sign in →
More free lessons in GenAI Data Pipelines
- Ch 5Design multi-format storage strategies on GCS and PostgreSQL
- Ch 6Build an embedding benchmarking framework
- Ch 6Evaluate Voyage 4's shared embedding space across model tiersYou are here
- Ch 6Implement an embedding abstraction layer with provider switching
- Ch 7Build embedding pipelines with LiteLLM gateway routing
- Ch 7Track costs in real-time with Langfuse and enforce budgets
- Ch 7Orchestrate pipelines as Argo Workflows with Kafka triggers