Embeddings
Every embedding-related knob — provider, model, dimensions, batch size, retries, and the constraints that link them to the HNSW index.
CortexDB calls an embedding service on every event ingest (one vector per chunk) and on every recall query (one for the query, plus N for HyDE-expanded queries). Embeddings are the single largest line item in the inference bill for most workloads, and vector recall does not work without a real provider configured.
The core constraint
The embedding service's output dimension must match the engine's vector index dimension. They are configured separately and are not cross-checked at startup — a mismatch produces silent recall failures (everything returns 0 results).
| Setting | Where | Default |
|---|---|---|
| Embedding output dim | CORTEX_EMBEDDING_DIMS env var | 1536 |
| Index storage dim | cortex.toml → [engine] vector_dimensions | 3072 |
The two dimension defaults don't match each other
The env-var default targets text-embedding-3-small (1536); the TOML default targets
text-embedding-3-large (3072). Pick one model and set both consistently. vector_dimensions
accepts only {256, 384, 512, 768, 1024, 1536, 3072} — anything else fails schema validation at
startup.
# cortex.toml
[engine]
vector_dimensions = 1536export CORTEX_EMBEDDING_MODEL=text-embedding-3-small
export CORTEX_EMBEDDING_DIMS=1536Provider selection
export CORTEX_EMBEDDING_PROVIDER=<empty> | mock | cohere | ollama| Value | Behavior | Required env |
|---|---|---|
| (empty / unset) | HTTP service against CORTEX_EMBEDDING_URL. Default. | OPENAI_API_KEY or LLM_API_KEY |
mock | Deterministic mock embeddings (384 d). Development only. | — |
cohere | Cohere's embed API. | COHERE_API_KEY |
ollama (also: any URL containing :11434) | Local Ollama daemon. | CORTEX_EMBEDDING_URL=http://localhost:11434 + a non-empty CORTEX_EMBEDDING_API_KEY (see below) |
Ollama still needs a non-empty embedding key
Although Ollama ignores auth, a non-empty CORTEX_EMBEDDING_API_KEY is still required — an empty
value fails the readiness check. Set a dummy value (e.g. ollama). Setting
CORTEX_EMBEDDING_URL=http://localhost:11434 without an explicit provider auto-selects Ollama mode.
The provider pin
On first boot the server writes an embedding_provider.pin file (recording provider:model:dims) into
the data directory. On every later start, the configured provider is checked against the pin — a
mismatch is startup-fatal, with guidance to re-index. A missing API key can no longer silently
downgrade a real corpus to mock vectors.
To deliberately migrate a data directory to a different provider/model, set
CORTEX_EMBEDDING_ALLOW_REPIN=1 for one restart (and plan to re-embed — vectors from different models
are not comparable).
When the API key is missing
If no OPENAI_API_KEY / LLM_API_KEY / COHERE_API_KEY is set and Ollama isn't detected, the binary
boots on mock embeddings (384-d) with a loud warning, and the data directory is pinned as mock.
GET /v1/admin/ready reports degraded: true and the pinned provider. Mock embeddings produce
meaningless recall — if you didn't ask for mock, check your startup logs and readiness output.
The full env-var surface
| Env var | Default | What it controls |
|---|---|---|
CORTEX_EMBEDDING_URL | https://api.openai.com/v1 | Base URL of the embedding HTTP API. |
CORTEX_EMBEDDING_MODEL | text-embedding-3-small | Model name passed to the provider. |
CORTEX_EMBEDDING_DIMS | 1536 | Output dimension. Must match engine.vector_dimensions. |
CORTEX_EMBEDDING_PROVIDER | (empty) | mock, cohere, ollama, or empty for OpenAI-compatible HTTP. |
CORTEX_EMBEDDING_API_KEY | (none) | Provider-specific embedding key. Required non-empty even for Ollama. |
CORTEX_EMBEDDING_ALLOW_REPIN | (unset) | Set =1 for one restart to change the pinned provider/model/dims. |
CORTEX_EMBEDDING_MAX_BATCH_ITEMS | 2048 | Max items per provider call before the client splits. |
CORTEX_EMBEDDING_RETRY_ATTEMPTS | 1 | Retries on transient error before failing the request. |
CORTEX_EMBEDDING_RETRY_BASE_DELAY_MS | 250 | Initial backoff between retries (exponential). |
CORTEX_EMBEDDING_HTTP_TIMEOUT_SECS | 30 | Per-request timeout for the embedding HTTP call. |
OPENAI_API_KEY | (none) | Primary key for OpenAI / HTTP mode. |
LLM_API_KEY | (none) | Generic fallback if OPENAI_API_KEY not set. |
COHERE_API_KEY | (none) | Required if CORTEX_EMBEDDING_PROVIDER=cohere. |
Choosing a model
| Model | Provider | Dims | Cost / 1M tok | When to pick it |
|---|---|---|---|---|
text-embedding-3-small | OpenAI | 1536 | $0.020 | Default. Used in the published 93.8% number. |
text-embedding-3-large | OpenAI | 3072 | $0.130 | ~+0.4pp on LongMemEval-S, ~3× cost. |
embed-multilingual-v3.0 | Cohere | 1024 | $0.100 | Strong on non-English content. |
nomic-embed-text | Ollama (local) | 768 | $0 | Local, free, ~5pp worse than OpenAI small. |
mxbai-embed-large | Ollama (local) | 1024 | $0 | Best local option; ~3pp worse than OpenAI small. |
bge-large-en-v1.5 | Ollama (local) | 1024 | $0 | Strong English-only local. |
Batch and retry tuning
The service packs pending requests into provider calls of up to CORTEX_EMBEDDING_MAX_BATCH_ITEMS
items. Larger batches improve throughput but raise the latency of the first request in a batch.
| Workload | MAX_BATCH_ITEMS | RETRY_ATTEMPTS | Why |
|---|---|---|---|
| Realtime / voice | 256 | 1 | Fail fast; small batches don't gather enough to wait for. |
| Default / mixed | 2048 | 1 | Compiled default. |
| Batch / ingest | 4096 | 3 | Pack OpenAI calls, tolerate 429s with backoff. |
| Cohere | 96 | 2 | Cohere's per-call cap is lower than OpenAI's. |
OpenAI's text-embedding-3-* models accept up to 2048 inputs per call — setting the batch higher
doesn't error (the client splits transparently) but stops amortizing per-batch overhead.
Local-first with Ollama
ollama pull nomic-embed-text
export CORTEX_EMBEDDING_URL=http://localhost:11434
export CORTEX_EMBEDDING_MODEL=nomic-embed-text
export CORTEX_EMBEDDING_DIMS=768
export CORTEX_EMBEDDING_API_KEY=ollama # non-empty required# cortex.toml — match the dim
[engine]
vector_dimensions = 768Expect ~30 ms / embedding on a modern CPU. See Self-hosting defaults for the full content-only setup.
Caching
HTTP embedding services are wrapped in an in-process LRU keyed by (model, text_hash). The cache
survives the process lifetime; it's lost on restart, and its size is not env-configurable (~10K
entries compiled default). Highly effective for re-embedding the same text (eval reruns), near-useless
for cold ingest.
Diagnostics
On startup the binary logs which service it picked:
info Using HTTP embedding service model=text-embedding-3-small dims=1536
info Using Ollama local embedding service model=nomic-embed-text dims=768 url=http://localhost:11434
warn No OPENAI_API_KEY or LLM_API_KEY set -- falling back to mock embeddings.Grep for "embedding service" after changing config, and check GET /v1/admin/ready — it reports the
pinned provider and degraded: true when on mock. Recall failures are usually a missing API key or a
dim mismatch.
Next steps
- LLM & Answer Generation — the extraction and answer LLMs
- Recall Tuning — how embeddings feed the recall pipeline
- Storage & Cluster — the HNSW index that consumes these vectors
Profiles & Presets
Seven copy-paste configurations — Benchmark / Max-Recall / Voice / Batch / Cost / Enterprise / Quickstart — for the most common CortexDB deployment shapes.
LLM & Answer Generation
All 18 LLM-related env vars — entity extraction, async enrichment, answer generation, verifier — plus how the fallback chain works.