# Gemini Embeddings Examples
This directory contains examples demonstrating the Gemini embedding API functionality.
## Quick Start
### Simple Embedding
The simplest way to generate an embedding (equivalent to the curl example):
```elixir
alias Gemini.APIs.Coordinator
alias Gemini.Types.Response.EmbedContentResponse
{:ok, response} = Coordinator.embed_content("What is the meaning of life?")
values = EmbedContentResponse.get_values(response)
# => [0.123, -0.456, 0.789, ...]
```
**Run:** `mix run examples/simple_embedding.exs`
### Full Demo
Comprehensive demonstration of all embedding features:
**Run:** `mix run examples/embedding_demo.exs`
This demo shows:
1. Simple text embedding
2. Semantic similarity comparison
3. Batch embedding (efficient)
4. Document retrieval embeddings
5. Task type optimization
## API Reference
### Basic Embedding
```elixir
# Simple embedding with default model (gemini-embedding-001)
{:ok, response} = Coordinator.embed_content("Your text here")
# With specific model
{:ok, response} = Coordinator.embed_content(
"Your text here",
model: "gemini-embedding-001"
)
# Get the embedding values
values = EmbedContentResponse.get_values(response)
# => [0.123, -0.456, ...]
```
### Batch Embedding
More efficient when embedding multiple texts:
```elixir
texts = [
"First text",
"Second text",
"Third text"
]
{:ok, response} = Coordinator.batch_embed_contents(texts)
all_values = BatchEmbedContentsResponse.get_all_values(response)
# => [[0.1, 0.2, ...], [0.3, 0.4, ...], [0.5, 0.6, ...]]
```
### Task Types
Optimize embeddings for specific use cases:
```elixir
# For search queries
{:ok, query_emb} = Coordinator.embed_content(
"How does AI work?",
task_type: :retrieval_query
)
# For documents being searched
{:ok, doc_emb} = Coordinator.embed_content(
"AI is the simulation of human intelligence...",
task_type: :retrieval_document,
title: "Introduction to AI"
)
# For semantic similarity
{:ok, emb} = Coordinator.embed_content(
"Text to compare",
task_type: :semantic_similarity
)
# For classification
{:ok, emb} = Coordinator.embed_content(
"Text to classify",
task_type: :classification
)
# For clustering
{:ok, emb} = Coordinator.embed_content(
"Text to cluster",
task_type: :clustering
)
```
### Available Task Types
- `:retrieval_query` - Text is a search query
- `:retrieval_document` - Text is a document being searched
- `:semantic_similarity` - For semantic similarity tasks
- `:classification` - For classification tasks
- `:clustering` - For clustering tasks
- `:question_answering` - For Q&A tasks
- `:fact_verification` - For fact verification
- `:code_retrieval_query` - For code retrieval
### Semantic Similarity
Compare embeddings to measure similarity:
```elixir
alias Gemini.Types.Response.ContentEmbedding
# Get two embeddings
{:ok, resp1} = Coordinator.embed_content("The cat sat on the mat")
{:ok, resp2} = Coordinator.embed_content("A feline rested on the rug")
# Calculate cosine similarity
similarity = ContentEmbedding.cosine_similarity(
resp1.embedding,
resp2.embedding
)
# => 0.85 (high similarity)
```
Cosine similarity ranges from -1 to 1:
- **1.0** = Identical meaning
- **0.5-0.9** = Very similar
- **0.0** = Unrelated
- **-1.0** = Opposite meaning
### Matryoshka Representation Learning (MRL) and Dimension Control
The `gemini-embedding-001` model uses **Matryoshka Representation Learning (MRL)**, a technique that creates high-dimensional embeddings where smaller prefixes are also useful. This allows flexible dimensionality with minimal quality loss.
#### Understanding MRL
MRL teaches the model to learn embeddings where the first N dimensions are independently useful. You can truncate embeddings to smaller sizes while maintaining most of the semantic information. This is different from traditional dimensionality reduction (like PCA) - MRL is built into the model's training.
#### Choosing the Right Dimension
```elixir
# 768 dimensions - RECOMMENDED for most applications
{:ok, response} = Coordinator.embed_content(
"Your text here",
model: "gemini-embedding-001",
output_dimensionality: 768
)
# • 25% storage of full embeddings
# • Only 0.26% quality loss vs 3072
# • Excellent balance of quality and efficiency
# 1536 dimensions - High quality
{:ok, response} = Coordinator.embed_content(
"Your text here",
model: "gemini-embedding-001",
output_dimensionality: 1536
)
# • 50% storage of full embeddings
# • Same MTEB score as 3072 (68.17)
# 3072 dimensions - Maximum quality (default)
{:ok, response} = Coordinator.embed_content(
"Your text here",
model: "gemini-embedding-001"
)
# • Full embeddings (largest storage)
# • Pre-normalized by API
# • MTEB: 68.17
```
#### MTEB Benchmark Scores
The following table shows MTEB (Massive Text Embedding Benchmark) scores for different dimensions:
| Dimension | MTEB Score | Storage vs 3072 | Quality Loss |
|-----------|------------|-----------------|--------------|
| 3072 | 68.17 | 100% | Baseline |
| 2048 | 68.16 | 67% | -0.01% |
| 1536 | 68.17 | 50% | Same |
| 768 | 67.99 | 25% | -0.26% |
| 512 | 67.55 | 17% | -0.91% |
| 256 | 66.19 | 8% | -2.90% |
| 128 | 63.31 | 4% | -7.12% |
**Key Insight:** Performance is not strictly tied to dimension size - 1536 dimensions achieve the same score as 3072!
#### Critical: Normalization Requirements
**IMPORTANT:** Only 3072-dimensional embeddings are pre-normalized by the API. All other dimensions MUST be normalized before computing similarity metrics.
```elixir
alias Gemini.Types.Response.ContentEmbedding
# Embed with 768 dimensions
{:ok, response} = Coordinator.embed_content(
"Your text",
output_dimensionality: 768
)
# MUST normalize for non-3072 dimensions
normalized = ContentEmbedding.normalize(response.embedding)
# Now safe to compute similarity
similarity = ContentEmbedding.cosine_similarity(normalized, other_normalized)
```
**Why normalize?** Cosine similarity focuses on vector direction (semantic meaning), not magnitude. Non-normalized embeddings have varying magnitudes that distort similarity calculations.
#### Normalization and Distance Metrics
```elixir
alias Gemini.Types.Response.ContentEmbedding
# Normalize embeddings (required for non-3072 dimensions)
normalized_emb1 = ContentEmbedding.normalize(embedding1)
normalized_emb2 = ContentEmbedding.normalize(embedding2)
# Cosine similarity (higher = more similar, range: -1 to 1)
similarity = ContentEmbedding.cosine_similarity(normalized_emb1, normalized_emb2)
# Euclidean distance (lower = more similar, range: 0 to ∞)
distance = ContentEmbedding.euclidean_distance(normalized_emb1, normalized_emb2)
# Dot product (higher = more similar, equals cosine for normalized)
dot = ContentEmbedding.dot_product(normalized_emb1, normalized_emb2)
# Check if normalized (L2 norm should be 1.0)
norm = ContentEmbedding.norm(normalized_emb1)
# => ~1.0
```
**Note:** For normalized embeddings, cosine similarity equals dot product!
## Available Models
### Recommended Model
- **`gemini-embedding-001`** (Stable, Recommended)
- Output dimensions: 3072 (default), flexible from 128-3072
- Supports MRL (Matryoshka Representation Learning) for dimension reduction
- Supports all task types
- MTEB Score: 67.99 (768d) to 68.17 (1536d/3072d)
- 3072-dimensional embeddings are pre-normalized; others require normalization
- Recommended dimensions: 768, 1536, or 3072
### Deprecated Models
- **`embedding-001`** - Deprecating October 2025
- **`embedding-gecko-001`** - Deprecating October 2025
- **`gemini-embedding-exp-03-07`** - Deprecating October 2025
**Note:** Migrate to `gemini-embedding-001` before October 2025.
## Use Cases
### 1. Semantic Search
```elixir
# Embed your document corpus
documents = ["Doc 1 text", "Doc 2 text", "Doc 3 text"]
{:ok, doc_response} = Coordinator.batch_embed_contents(
documents,
task_type: :retrieval_document
)
# Embed the search query
{:ok, query_response} = Coordinator.embed_content(
"search query",
task_type: :retrieval_query
)
# Compare query with each document
doc_response.embeddings
|> Enum.zip(documents)
|> Enum.map(fn {doc_emb, doc_text} ->
similarity = ContentEmbedding.cosine_similarity(
query_response.embedding,
doc_emb
)
{doc_text, similarity}
end)
|> Enum.sort_by(fn {_, sim} -> sim end, :desc)
# Returns documents sorted by relevance
```
### 2. Clustering
```elixir
# Embed texts for clustering
texts = ["Text 1", "Text 2", "Text 3", ...]
{:ok, response} = Coordinator.batch_embed_contents(
texts,
task_type: :clustering
)
# Use embeddings with a clustering algorithm
# (e.g., K-means, hierarchical clustering)
embeddings = BatchEmbedContentsResponse.get_all_values(response)
```
### 3. Classification
```elixir
# Embed training examples
{:ok, train_response} = Coordinator.batch_embed_contents(
training_texts,
task_type: :classification
)
# Embed new text to classify
{:ok, new_response} = Coordinator.embed_content(
new_text,
task_type: :classification
)
# Find nearest neighbors in training set
# Use their labels for classification
```
## Authentication
Embeddings support both Gemini API and Vertex AI authentication:
```elixir
# With Gemini API (default)
{:ok, response} = Coordinator.embed_content("Text", auth: :gemini)
# With Vertex AI
{:ok, response} = Coordinator.embed_content("Text", auth: :vertex_ai)
```
## Error Handling
```elixir
case Coordinator.embed_content(text) do
{:ok, response} ->
values = EmbedContentResponse.get_values(response)
# Process embedding values
{:error, reason} ->
# Handle error
Logger.error("Embedding failed: #{inspect(reason)}")
end
```
## Performance Tips
1. **Use batch embedding** when processing multiple texts - it's more efficient:
```elixir
# Good: Single API call
Coordinator.batch_embed_contents(["text1", "text2", "text3"])
# Less efficient: Multiple API calls
Enum.map(["text1", "text2", "text3"], &Coordinator.embed_content/1)
```
2. **Use appropriate task types** for better quality embeddings
3. **Consider dimension reduction** for storage/memory constraints:
```elixir
Coordinator.embed_content(text, output_dimensionality: 256)
```
## Related Documentation
- [Gemini API Embeddings Guide](https://ai.google.dev/docs/embeddings_guide)
- [Text Embedding Models](https://ai.google.dev/models/gemini#text-embedding)
- [Semantic Retrieval](https://ai.google.dev/docs/semantic_retrieval)
## Advanced Use Case Examples
The `use_cases/` directory contains complete, production-ready examples demonstrating real-world applications:
### MRL and Normalization Demo
**File:** `use_cases/mrl_normalization_demo.exs`
Comprehensive demonstration of Matryoshka Representation Learning:
- Quality vs storage tradeoffs across dimensions (128-3072)
- MTEB benchmark comparison
- Normalization requirements and effects
- Distance metrics comparison (cosine, euclidean, dot product)
- Best practices for dimension selection
**Run:** `mix run examples/use_cases/mrl_normalization_demo.exs`
### RAG (Retrieval-Augmented Generation) System
**File:** `use_cases/rag_demo.exs`
Complete RAG pipeline implementation:
- Build and index knowledge base with RETRIEVAL_DOCUMENT task type
- Embed queries with RETRIEVAL_QUERY task type
- Retrieve top-K relevant documents using semantic similarity
- Generate contextually-aware responses
- Compare RAG vs non-RAG generation quality
**Run:** `mix run examples/use_cases/rag_demo.exs`
**Key Features:**
- Document title optimization for better embeddings
- Semantic similarity ranking
- Context-aware generation
- Side-by-side comparison with baseline
### Search Reranking
**File:** `use_cases/search_reranking.exs`
Semantic reranking for improved search relevance:
- Start with keyword-based search results
- Rerank using semantic similarity
- Compare keyword vs semantic ranking
- Hybrid ranking strategy (keyword + semantic)
- Handle synonyms and conceptual relevance
**Run:** `mix run examples/use_cases/search_reranking.exs`
**Key Features:**
- E-commerce product search example
- Visual ranking comparison
- Hybrid scoring (0.3 × keyword + 0.7 × semantic)
- Intent understanding beyond keywords
### K-NN Classification
**File:** `use_cases/classification.exs`
Text classification using K-Nearest Neighbors with embeddings:
- Few-shot learning with minimal training examples
- Customer support ticket categorization
- K-NN classification algorithm
- Confidence scoring and accuracy evaluation
- Dynamically add new categories without retraining
**Run:** `mix run examples/use_cases/classification.exs`
**Key Features:**
- Multi-category classification (technical_support, billing, account, product_inquiry)
- Accuracy evaluation and confidence analysis
- Dynamic category addition demonstration
- No model training required
## Basic Examples
- `simple_embedding.exs` - Basic embedding (curl equivalent)
- `embedding_demo.exs` - Comprehensive feature demonstration
## Async Batch Embedding API
For production-scale embedding with 50% cost savings, use the async batch API:
```elixir
# Submit async batch job
{:ok, batch} = Gemini.async_batch_embed_contents(
["Text 1", "Text 2", "Text 3", ...],
display_name: "My Knowledge Base",
task_type: :retrieval_document,
output_dimensionality: 768
)
# Wait for completion with progress tracking
{:ok, completed} = Gemini.await_batch_completion(batch.name,
poll_interval: 5_000,
on_progress: fn batch -> IO.puts("Progress: #{batch.state}") end
)
# Retrieve embeddings
{:ok, embeddings} = Gemini.get_batch_embeddings(completed)
```
**Run:** `mix run examples/async_batch_embedding_demo.exs`
See `examples/ASYNC_BATCH_EMBEDDINGS.md` for complete documentation.