Embeddings with Ollama

Ollama · Practical · 12 min

Local embeddings let you build private search and RAG without sending documents to anyone.

Embedding models turn text into vectors (lists of numbers) so that texts with similar meaning have similar vectors. Ollama runs them locally like any other model; the docs use embeddinggemma, and the library has others such as nomic-embed-text and mxbai-embed-large.

Create embeddings with the Python library:

import ollama
result = ollama.embed(model="embeddinggemma", input=["first text", "second text"])
vectors = result.embeddings      # a list of vectors, one per input

or with POST /api/embed and the same fields. Pass a list to embed many texts in one request, which is much faster than one call each.

Facts that matter:

  • Vector length depends on the model, typically 384 to 1,024 numbers.
  • /api/embed returns normalised vectors (length 1), so cosine similarity equals the plain dot product: multiply matching numbers and add them up.
  • Use the same embedding model for your documents and for search queries. Vectors from different models can't be compared.
  • Inputs longer than the model's context are truncated by default. Split long documents into chunks first.

A simple semantic search: embed all document chunks once and store the vectors (in a list, or a vector database for larger sets). For a question, embed it, compute similarity with each stored vector and take the best matches. That is the retrieval half of RAG; frameworks such as LangChain and LlamaIndex can do it with Ollama embeddings too.

Embedding models are small and fast, so they often run comfortably on a CPU.

Example

import ollama

docs = ["Refunds are processed within 5 business days.",
        "Our office is closed on public holidays.",
        "You can reset your password from the login page."]
doc_vecs = ollama.embed(model="embeddinggemma", input=docs).embeddings
print(len(doc_vecs), len(doc_vecs[0]))            # 3 768

def similarity(a, b):                             # vectors are normalised: dot = cosine
    return sum(x * y for x, y in zip(a, b))

question = "How long until I get my money back?"
q_vec = ollama.embed(model="embeddinggemma", input=question).embeddings[0]

scores = [similarity(q_vec, v) for v in doc_vecs]
best = max(range(len(docs)), key=lambda i: scores[i])
print(round(scores[best], 2), docs[best])         # 0.62 Refunds are processed within 5 business days.

Remember

  • ollama.embed(model, input=[...]) returns one vector per input
  • Vectors are normalised: dot product = cosine similarity
  • Use the same embedding model for documents and queries; chunk long texts

Watch out for

  • Embedding documents with one model and queries with another, which makes similarity scores meaningless.

Check yourself

  1. Why send a list of 100 chunks in one embed call instead of 100 separate calls?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.ollama.com/capabilities/embeddings