Skip to content
Get Started

Embeddings

An embedding is a vector representation of data — usually text — where semantically similar items map to nearby points in the vector space. Rig’s embeddings system turns your data into these vectors so you can power semantic search, similarity comparison, and Retrieval-Augmented Generation.

In Rig, an Embedding carries the original document text alongside its vector representation (a Vec<f64>).

The smallest thing that works: create an embedding model from a provider client and embed a few strings with EmbeddingsBuilder. Each .document(...) call adds one piece of text to embed.

use rig::client::{EmbeddingsClient, ProviderClient};
use rig::embeddings::EmbeddingsBuilder;
use rig::providers::openai;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Requires the OPENAI_API_KEY environment variable to be set.
let openai_client = openai::Client::from_env()?;
let model = openai_client.embedding_model("text-embedding-3-small");
let embeddings = EmbeddingsBuilder::new(model)
.document("Some text")?
.document("More text")?
.build()
.await?;
println!("Generated {} embeddings", embeddings.len());
Ok(())
}
Generated 2 embeddings

To embed richer data than plain strings, a type must implement the Embed trait, which tells Rig which fields to turn into embeddings. You can implement it by hand, or derive it with the Embed derive macro (available with the derive feature of rig enabled).

The easiest approach is the derive macro. Mark the field(s) to embed with #[embed]:

use rig::Embed;
#[derive(Embed)]
struct Foo {
id: i32,
#[embed]
name: String,
}

For full control, implement Embed manually and push each text fragment through the TextEmbedder. Here only the definition field is embedded:

use rig::embeddings::{Embed, EmbedError, TextEmbedder};
struct WordDefinition {
id: i32,
word: String,
definition: String,
}
impl Embed for WordDefinition {
fn embed(&self, embedder: &mut TextEmbedder) -> Result<(), EmbedError> {
// Only the definition needs to be embedded.
embedder.embed(self.definition.to_owned());
Ok(())
}
}

To embed a batch of Embed values, pass them to EmbeddingsBuilder with .documents(...). This is the idiomatic way to process multiple documents at once — the builder respects the provider’s max batch size and handles concurrency for you.

use rig::client::EmbeddingsClient;
use rig::embeddings::EmbeddingsBuilder;
use rig::providers::openai;
let documents = vec![
Foo { id: 1, name: "Rig".to_string() },
Foo { id: 2, name: "Playgrounds".to_string() },
];
let model = openai::Client::from_env()?.embedding_model("text-embedding-3-small");
let embeddings = EmbeddingsBuilder::new(model)
.documents(documents)?
.build()
.await?;

The returned value is an iterator over (T, OneOrMany<Embedding>), where T is your Embed type. You can collect these for use elsewhere, or insert them straight into a vector store.

Embeddings become useful once they’re searchable. Insert them into any store implementing the InsertDocuments trait:

use rig::vector_store::InsertDocuments;
// `qdrant` here is a store integration; see the vector store docs for setup.
let qdrant = create_qdrant_vector_store();
qdrant.insert_documents(embeddings).await?;

See Vector Stores & RAG for how embeddings power retrieval, and Vector Stores for setup of each supported store.

  • Prepare documents. Clean and normalize text before embedding, and chunk large documents into focused pieces.
  • Match models. Query embeddings must use the exact same model as the stored documents — vectors from different models are not comparable.
  • Handle errors. Validate input isn’t empty and handle provider API errors gracefully.
  • Batch. Prefer .documents(...) for multiple items so Rig can batch requests efficiently.