CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

chromadb

Provides Chroma Python client patterns — collection creation and configuration, add/upsert/query, metadata and document filtering syntax, and embedding function integration. Use when the user asks to "set up ChromaDB", "create a Chroma collection", "add embeddings to ChromaDB", "query ChromaDB", "search vectors", "semantic search with ChromaDB", "filter ChromaDB results", "ChromaDB metadata filtering", "configure Chroma", "use ChromaDB persistent client", "delete from ChromaDB", or when writing any code that interacts with the chromadb Python package. Does not cover the JavaScript client, Chroma Cloud, choosing an embedding model, or general RAG architecture.

72

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Chroma Python Skill

This skill covers the Chroma Python client API (the chromadb package) for creating collections, storing records with embeddings, and querying with semantic search and metadata filtering.

Client Initialization

In-memory (ephemeral)

import chromadb

client = chromadb.Client()

Data is lost when the process exits. Use for testing and prototyping.

Persistent (file-based)

client = chromadb.PersistentClient(path="./chroma_data")

Data is saved to disk at the specified path. Use for local applications and development.

HTTP client (remote server)

client = chromadb.HttpClient(host="localhost", port=8000)

Connects to a standalone Chroma server. Use for production deployments.

Collection Management

Create or get a collection

collection = client.get_or_create_collection(
    name="my_collection",
    embedding_function=embedding_fn,
    configuration={"hnsw": {"space": "cosine"}},
)
  • get_or_create_collection — idempotent; creates if absent, returns existing if present. configuration applies only on creation: against an existing collection it is ignored silently, so the returned collection keeps whatever space it was built with. Read collection.configuration["hnsw"] back if the metric matters, and recreate the collection to change it.
  • create_collection — raises if the collection already exists.
  • get_collection — raises if the collection does not exist.

List, delete, count

client.list_collections()                    # list all collections
client.delete_collection("my_collection")    # delete by name
collection.count()                           # number of records

Adding Data

Add documents (auto-embedded)

collection.add(
    ids=["id1", "id2", "id3"],
    documents=["First document", "Second document", "Third document"],
    metadatas=[
        {"source": "web", "year": 2024},
        {"source": "api", "year": 2024},
        {"source": "manual", "year": 2023},
    ],
)
  • ids — required, must be unique strings. An ID that already exists is skipped silently: no exception, no warning, and the stored record is left untouched. Use upsert whenever the data may already be indexed.
  • documents — raw text; Chroma uses the collection's embedding function to generate embeddings.
  • metadatas — optional dict per record for filtering.

Add pre-computed embeddings

collection.add(
    ids=["id1", "id2"],
    embeddings=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
    metadatas=[{"source": "precomputed"}, {"source": "precomputed"}],
    documents=["doc one", "doc two"],  # optional when embeddings provided
)

Upsert (insert or update)

collection.upsert(
    ids=["id1", "id2", "id3"],
    documents=["new content 1", "new content 2", "new content 3"],
    metadatas=[{"version": 2}, {"version": 2}, {"version": 2}],
)

Inserts records if the ID does not exist; updates if it does. Prefer upsert over add when re-indexing data that may already exist.

Querying

Semantic search with text

results = collection.query(
    query_texts=["search phrase"],
    n_results=5,
)

Returns a dict that always has the keys ids, embeddings, documents, uris, included, data, metadatas, distances. Fields not requested via include are present but set to None; results["included"] lists which fields were populated. Each populated value is a list of lists (one inner list per query). Lower distance = more similar.

Important: query() returns nested lists, while get() returns flat lists. This is a common source of bugs:

# query() — nested: results["ids"] == [["id1", "id2"]]
# get()   — flat:   results["ids"] == ["id1", "id2"]

Semantic search with embeddings

results = collection.query(
    query_embeddings=[[0.1, 0.2, 0.3]],
    n_results=10,
)

Control returned fields with include

results = collection.query(
    query_texts=["search phrase"],
    n_results=5,
    include=["documents", "metadatas", "distances"],
)

Valid include values: "documents", "metadatas", "distances", "embeddings". The Include type also accepts "uris" and "data", which only apply to multimodal collections (images, audio) and are out of this skill's text-only scope.

include does not remove keys from the result — every key is always present, and unrequested ones are None. Check results["included"] to learn what was populated; "embeddings" in results is always True and tells you nothing.

Get by ID (no search)

results = collection.get(
    ids=["id1", "id2"],
    include=["documents", "metadatas"],
)

Get with filter (no search)

results = collection.get(
    where={"source": "web"},
    include=["documents", "metadatas"],
)

Filtering

Filters are applied via where (metadata) and where_document (text content) parameters on both query and get.

Filter operators quick reference

OperatorTypeExample
$eqEquality{"status": {"$eq": "active"}}
$neInequality{"status": {"$ne": "archived"}}
$gtGreater than{"price": {"$gt": 100}}
$gteGreater or equal{"rating": {"$gte": 4.5}}
$ltLess than{"price": {"$lt": 50}}
$lteLess or equal{"count": {"$lte": 10}}
$inIn list{"category": {"$in": ["a", "b"]}}
$ninNot in list{"tag": {"$nin": ["spam"]}}
$containsArray contains{"tags": {"$contains": "python"}}
$not_containsArray excludes{"tags": {"$not_contains": "draft"}}

Shorthand: {"key": "value"} is equivalent to {"key": {"$eq": "value"}}.

Logical operators

# AND
where={"$and": [{"category": "electronics"}, {"price": {"$gte": 500}}]}

# OR
where={"$or": [{"category": "electronics"}, {"category": "wearables"}]}

Document content filtering

results = collection.query(
    query_texts=["search"],
    where_document={"$contains": "specific phrase"},
)

For complete filter operator details and advanced patterns, consult references/api-reference.md.

Embedding Functions

Chroma supports pluggable embedding functions. Set the embedding function when creating a collection — it is used automatically for add, update, upsert, and query operations.

SentenceTransformers (local, no API key)

from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction

ef = SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")
collection = client.get_or_create_collection("my_col", embedding_function=ef)

OpenAI

from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction

ef = OpenAIEmbeddingFunction(model_name="text-embedding-3-small")
collection = client.create_collection("my_col", embedding_function=ef)

Requires the openai package and an API key. The key is read from CHROMA_OPENAI_API_KEY — note the CHROMA_ prefix, which is the default for every hosted provider wrapper (CHROMA_COHERE_API_KEY, CHROMA_HUGGINGFACE_API_KEY, …). The unprefixed OPENAI_API_KEY is still honoured and takes precedence when set, even over an explicit api_key_env_var. To read from somewhere else:

ef = OpenAIEmbeddingFunction(
    model_name="text-embedding-3-small",
    api_key_env_var="MY_OPENAI_KEY",   # persisted with the collection
)

Passing api_key="sk-…" inline also works but raises a DeprecationWarning and is deliberately not stored in the collection configuration, so the key is lost the next time the collection is opened. Prefer an environment variable.

Default embedding function

If no embedding_function is specified, Chroma uses its built-in default (all-MiniLM-L6-v2 via ONNX). This is suitable for prototyping but should be replaced with a purpose-chosen model for production.

Collection Configuration

Distance metrics

Set via the space key of the configuration argument at creation time:

MetricValueUse case
Cosine"cosine"Normalized embeddings (most common)
L2 (Euclidean)"l2"Default; raw distance
Inner product"ip"Pre-normalized, dot-product similarity
collection = client.create_collection(
    name="my_col",
    configuration={"hnsw": {"space": "cosine"}},
)

The pre-1.x form metadata={"hnsw:space": "cosine"} still works and produces the same configuration. The metric cannot be changed afterwards — modify rejects it, so switching metrics means recreating the collection.

For HNSW tuning parameters and advanced configuration, consult references/patterns.md.

Update and Delete

Update existing records

collection.update(
    ids=["id1"],
    documents=["updated content"],
    metadatas=[{"version": 3}],
)

Only provided fields are updated. Omitted fields remain unchanged.

Delete records

collection.delete(ids=["id1", "id2"])
collection.delete(where={"status": "archived"})
collection.delete(where_document={"$contains": "deprecated"})

Peek (sample data)

sample = collection.peek(limit=5)  # returns a sample of records (default 10)

Useful for inspecting collection contents during development.

Common Errors

SymptomCauseFix
add silently does nothingThe ID already exists — the write is dropped with no error and no warningUse upsert when re-indexing; count() will not move and the stored record keeps its old content
Dimension mismatchEmbedding size differs from collectionEnsure consistent embedding model
ValueError on deleteNo criteria givenProvide ids, where, or where_document
ValueError: Embedding function <name> not found on the first add, query or collection.configuration access after get_collection (which itself returns normally)A custom embedding function's class is not registered in this processImport the module that defines it and decorate the class with @register_embedding_function
Collection expecting embedding with dimension of N, got 384 after reopeningCollection was created with a legacy custom embedding function, so Chroma fell back to the default ONNX modelGive the function name() / get_config() / build_from_config(), or pass it to get_collection every time

Embedding function persistence

Chroma stores the configuration of the embedding function with the collection, so get_collection rebuilds it automatically and passing embedding_function again is optional. This works for the built-in wrappers (OpenAI, SentenceTransformer, Cohere, …) and for custom functions that implement name(), get_config() and build_from_config() — those are saved as {"type": "known", "name": …, "config": {…}}.

A custom function that implements only __call__ is saved as {"type": "legacy"} and cannot be rebuilt. Reopening that collection silently falls back to the default ONNX model, which is where the wrong-results and dimension-mismatch failures come from. Either upgrade the function (see references/api-reference.md) or pass it on every get_collection call.

Additional Resources

Reference Files

For detailed API documentation and advanced patterns, consult:

  • references/api-reference.md — Complete method signatures, all filter operators with examples, include options, result structure
  • references/patterns.md — Batch ingestion, metadata schema design, HNSW tuning, collection lifecycle, common query patterns, error handling
Repository

provectus/awos-recruitment

Path

.claude/skills/chromadb/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.