Chroma

Chroma is an embedded vector store: data lives in a local directory. You do not need Docker or Postgres first. It fits laptops, workshops, and small-to-medium corpora. Official docs: docs.trychroma.com.

If you already run PostgreSQL, use pgvector next. If you want a dedicated service with strong filters / hybrid search, use Qdrant.


Install

python -m venv .venv
source .venv/bin/activate
pip install chromadb
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install chromadb

Add openai or ollama when you bring your own vectors (Embeddings). Python 3.10+.


PersistentClient

PersistentClient(path="...") writes the database to disk so it survives restarts. Docs default the path to .chroma; this tutorial uses ./chroma so it is obvious in the repo root.

import chromadb

client = chromadb.PersistentClient(path="./chroma")
collection = client.get_or_create_collection(name="faq")

In-memory chromadb.Client() vanishes when the process exits—fine for probing the API. heartbeat() checks liveness. reset() wipes the whole database and cannot be undone.


add: ids, documents, optional embeddings

Every record needs a unique string id. You must pass documents, embeddings, or both. Documents alone: Chroma embeds them with the collection’s embedding function. If you pass embeddings, Chroma stores them as-is and does not re-embed.

collection.add(
    ids=["refund-0", "hours-0"],
    documents=[
        "Refunds must be requested within 7 days of delivery using order KH-8842.",
        "Support is online 09:00–18:00 on weekdays (Beijing time).",
    ],
    metadatas=[
        {"source": "refund.md", "lang": "en"},
        {"source": "hours.md", "lang": "en"},
    ],
)

Bring-your-own embeddings (better for Chinese, and explicit everywhere):

from embeddings_util import embed_openai  # see the Embeddings chapter

texts = ["Refunds must be requested within 7 days of delivery using order KH-8842."]
collection.add(
    ids=["refund-0"],
    documents=texts,
    embeddings=embed_openai(texts),
    metadatas=[{"source": "refund.md"}],
)

Adding an existing id is ignored. Overwrite with update / upsert (see current docs). Embedding dimensionality must match the collection.


query

results = collection.query(
    query_texts=["How do I refund order KH-8842?"],
    n_results=2,
    where={"lang": "en"},
)
print(results["ids"])
print(results["documents"])
print(results["distances"])
print(results["metadatas"])

If you already have a query vector, pass query_embeddings=[vec]. Do not mix embedding models. where filters metadata (department, language, source file). The response is list-of-lists: you may send several query_texts at once.


Wire-up to RAG

def retrieve(question: str, k: int = 4) -> str:
    hit = collection.query(query_texts=[question], n_results=k)
    docs = hit["documents"][0]
    metas = hit["metadatas"][0]
    parts = []
    for doc, meta in zip(docs, metas):
        parts.append(f"[{meta.get('source', '')}]\n{doc}")
    return "\n\n".join(parts)

context = retrieve("How many days do I have to request a refund?")
prompt = (
    "Answer only from context. If the context is silent, say you do not know.\n\n"
    f"{context}\n\nQuestion: How many days do I have to request a refund?"
)

Send prompt to any Chat API. Framework wrappers: LangChain RAG. Console RAG: Dify Knowledge Base.


When to switch stores

SituationConsider
Local folder, modest corpusStay on Chroma
Users, ACLs, and transactions already in Postgrespgvector
Payload filters, hybrid search, many clientsQdrant

Add ./chroma to .gitignore on Windows and Unix. Do not commit the store.


Next steps

评论