Skip to content

Embeddings

Generate vector embeddings from text to enable semantic search, clustering, and similarity comparison.

Quick Start

ruby
class MyAgent < ApplicationAgent
  embed_with :openai, model: "text-embedding-3-small"

  def vectorize
    embed(input: params[:text])
  end
end

response = MyAgent.with(text: "Hello world").vectorize.embed_now
vector = response.data.first[:embedding]  # => [0.123, -0.456, ...]

Basic Usage

Generate embeddings using embed with synchronous or asynchronous execution:

ruby
response = ApplicationAgent.embed(
  input: "The quick brown fox",
  model: "text-embedding-3-small"
).embed_now
vector = response.data.first[:embedding]
ruby
job = ApplicationAgent.embed(
  input: "Long document text",
  model: "text-embedding-3-small"
).embed_later(
  queue: :embeddings,
  priority: 10
)
ruby
response = ApplicationAgent.embed(
  input: [ "First text", "Second text", "Third text" ],
  model: "text-embedding-3-small"
).embed_now

vectors = response.data.pluck(:embedding)

Response Structure

Embedding responses contain the vector data:

ruby
response = ApplicationAgent.embed(
  input: "Sample text",
  model: "text-embedding-3-small"
).embed_now

# Access embedding vector
vector = response.data.first[:embedding]  # Array of floats

# Check dimensions
vector.size  # => 1536 (varies by model)

Configuration

Configure the embedding provider using embed_with:

ruby
class EmbeddingAgent < ApplicationAgent
  embed_with :openai, model: "text-embedding-3-small"
end

response = EmbeddingAgent.embed(input: "Your text").embed_now

Mixing Providers

Use different providers for prompting and embeddings:

ruby
class HybridAgent < ApplicationAgent
  generate_with :anthropic, model: "claude-3-5-sonnet-20241022"
  embed_with :openai, model: "text-embedding-3-small"
end

# Use Anthropic for chat
chat_response = HybridAgent.prompt(message: "Hello").generate_now

# Use OpenAI for embeddings
embed_response = HybridAgent.embed(input: "Hello").embed_now

This lets you choose the best provider for each task—for example, using Anthropic's Claude for reasoning while leveraging OpenAI's specialized embedding models.

Provider-Specific Options

OpenAI

ruby
class OpenAIAgent < ApplicationAgent
  embed_with :openai,
    model: "text-embedding-3-small",
    dimensions: 512  # Reduce from default 1536
end

Ollama

ruby
class OllamaAgent < ApplicationAgent
  embed_with :ollama,
    model: "nomic-embed-text",
    host: "http://localhost:11434"
end

See OpenAI Provider and Ollama Provider for more options.

Callbacks

Process embeddings with before and after callbacks:

ruby
class TrackedAgent < ApplicationAgent
  embed_with :openai, model: "text-embedding-3-small"

  before_embed :log_start
  after_embed :log_complete

  private

  def log_start
    Rails.logger.info "Starting embedding generation"
  end

  def log_complete
    Rails.logger.info "Embedding complete"
  end
end

response = TrackedAgent.embed(input: "Text").embed_now
# Logs before and after generation

See Callbacks for more on callback functionality.

Compare text similarity using cosine similarity:

ruby
def cosine_similarity(vec1, vec2)
  dot_product = vec1.zip(vec2).map { |a, b| a * b }.sum
  magnitude1 = Math.sqrt(vec1.map { |v| v**2 }.sum)
  magnitude2 = Math.sqrt(vec2.map { |v| v**2 }.sum)
  dot_product / (magnitude1 * magnitude2)
end

documents = [
  "The cat sat on the mat",
  "Dogs are loyal companions",
  "Machine learning is a subset of AI",
  "The feline rested on the rug"
]

# Generate embeddings
response   = ApplicationAgent.embed(
  input: documents,
  model: "text-embedding-3-small"
).embed_now
embeddings = response.data.map { |item| item[:embedding] }

# Query
query = "cat on mat"
query_embedding = ApplicationAgent.embed(
  input: query,
  model: "text-embedding-3-small"
).embed_now.data.first[:embedding]

# Calculate similarities
results = embeddings.map.with_index do |embedding, i|
  similarity = cosine_similarity(query_embedding, embedding)
  { document: documents[i], similarity: }
end.sort_by { |r| -r[:similarity] }

results.first[:document]  # => "The cat sat on the mat"

Model Dimensions

Different models produce different embedding dimensions:

ruby
# OpenAI text-embedding-3-small: 1536 dimensions (default)
# OpenAI text-embedding-3-large: 3072 dimensions
# OpenAI text-embedding-ada-002: 1536 dimensions
# Ollama nomic-embed-text: 768 dimensions

response = ApplicationAgent.embed(
  input: "Test",
  model: "text-embedding-3-small"
).embed_now
dimensions = response.data.first[:embedding].size
ruby
class CompactAgent < ApplicationAgent
  embed_with :openai,
    model: "text-embedding-3-small",
    dimensions: 512  # Smaller, faster
end