LLM & Answer Generation
All 18 LLM-related env vars — entity extraction, async enrichment, answer generation, verifier — plus how the fallback chain works.
CortexDB calls LLMs in four distinct places, each independently configurable. Mixing them is the norm — e.g. GPT-4o-mini for cheap entity extraction on every write, a strong model for the answer the user sees, an optional verifier on top.
The four LLM call sites
| Call site | When | Default model | Why this one |
|---|---|---|---|
| Entity extraction | In the ingest pipeline, off the write path (does not block the captured ACK) | gpt-4o-mini | Cheap, fast, structured-output-reliable |
| Async enrichment | Background job, only if explicitly enabled | (none — disabled) | Heavyweight KG enrichment; opt-in |
| Answer generation | /v1/answer | claude-opus-4-6 | Highest score on multi-session in our A/B |
| Verifier | Post-answer cross-check; on whenever OPENAI_API_KEY or CORTEX_VERIFIER_API_KEY is set | gpt-4.1 | Different family → catches model-specific failure modes |
Each has its own model, URL, API key, and disable switch — route them to entirely different providers
if you need to. The image bakes a URL and model for the extraction (gpt-4o-mini), verifier (gpt-4.1)
and answer (anthropic / claude-opus-4-6) lanes, so a bare provider key switches a lane on:
OPENAI_API_KEY alone enables extraction and the verifier, and ANTHROPIC_API_KEY alone enables
/v1/answer on claude-opus-4-6.
1. Entity extraction LLM
Runs on /v1/experience ingest to seed the knowledge graph. If disabled, fact/belief layers degrade to
text-only matching.
Extraction does not block the write
A write returns captured (202) at the durable WAL accept point in ~10 ms — before any
extraction. The gpt-4o-mini extraction runs as a pipeline stage after that ACK (it can't complete
inside a 10 ms write), and heavier fact/belief consolidation is fully asynchronous. Pass
?wait=indexed or ?wait=consolidated to block the client until those later stages finish. See
Lifecycle.
| Env var | Default | What it controls |
|---|---|---|
CORTEX_LLM_DISABLE | (unset) | Set to 1/true to skip extraction. Faster writes; weaker recall. |
CORTEX_LLM_URL | https://api.openai.com/v1 | Endpoint. Any OpenAI-compatible API. |
CORTEX_LLM_MODEL | gpt-4o-mini | Model name passed to the provider. |
CORTEX_ENTITY_API_KEY | (falls back to OPENAI_API_KEY) | Separate extraction key. |
Change it for benchmarks (CORTEX_LLM_DISABLE=1 for pure-text recall), systematic extraction errors
(CORTEX_LLM_MODEL=gpt-4o, ~10× cost), or local extraction
(CORTEX_LLM_URL=http://localhost:11434/v1 + a local model).
2. Async enrichment LLM (optional)
A heavyweight pipeline doing deeper KG enrichment (multi-hop fact linking, cross-session entity
disambiguation). Off by default — it needs the model, an endpoint key and
CORTEX_ENRICHMENT_DELAY_SECONDS; without the delay variable the boot log says enrichment OFF and
Facts/Beliefs stay empty even with the model and key set.
| Env var | Default | What it controls |
|---|---|---|
CORTEX_ENRICHMENT_MODEL | (empty = disabled) | Model name. |
CORTEX_ENRICHMENT_DELAY_SECONDS | (unset = enrichment off) | Seconds after a write before enriching; required to turn the scanner on (0 = as fast as allowed). |
CORTEX_ENRICHMENT_URL | (falls back to CORTEX_LLM_URL) | Endpoint. |
CORTEX_ENRICHMENT_API_KEY | (falls back to OPENAI_API_KEY / LLM_API_KEY, not CORTEX_ENTITY_API_KEY) | Separate key. |
Enrichment is what populates Facts/Beliefs on a self-host
On a content-only self-hosted instance, the Facts/Beliefs/Understanding layers stay empty until
enrichment is enabled here. The async pipeline drains on the scheduler's
enrichment_drain_interval_secs (default 30 s). See
Self-hosting defaults.
export CORTEX_ENRICHMENT_MODEL=gpt-4o-mini
export CORTEX_ENRICHMENT_URL=https://api.openai.com/v1
export CORTEX_ENRICHMENT_API_KEY=sk-...
export CORTEX_ENRICHMENT_DELAY_SECONDS=5gpt-4o-mini is the recommended enrichment model: the cheapest reliable extraction model, about
$0.40 per 1,000 events. See Cost Planning.
3. Answer generation LLM
Used by POST /v1/answer to turn a recall pack into a cited natural-language answer.
| Env var | Default | What it controls |
|---|---|---|
CORTEX_ANSWER_PROVIDER | anthropic | anthropic, openai, google, bedrock, ollama |
CORTEX_ANSWER_MODEL | claude-opus-4-6 | Model name. Provider-specific. |
CORTEX_ANSWER_URL | (provider default) | Override endpoint (proxies, gateways). |
CORTEX_ANSWER_API_KEY | (falls back to provider-specific env) | ANTHROPIC_API_KEY, OPENAI_API_KEY, etc. |
CORTEX_ANSWER_MAX_TOKENS | 1500 | Generation budget. |
ANTHROPIC_API_KEY | (none) | Fallback when provider is anthropic and CORTEX_ANSWER_API_KEY unset. |
Self-hosted, the answer lane must be set — the default model is a cloud value
claude-opus-4-6 is the managed-cloud default. Self-hosted, /v1/answer is disabled (returns
503 ANSWER_UNAVAILABLE) until you set the CORTEX_ANSWER_* group to your own provider; the model
name resolves to whatever you configure. Setting ANTHROPIC_API_KEY alone also enables it, on the
baked claude-opus-4-6. Setting only CORTEX_LLM_* does not route /v1/answer —
that's the extraction lane. See Self-hosting defaults.
Recommended (September 2026): gpt-5.6-terra for answers (about 2 to 4 cents each), gpt-5.6-luna
for high-volume or simple questions (about 0.2 to 0.4 cents), and gpt-5.6-sol or gpt-6-astra only where
answer quality is the product. Terra, Luna and Sol all work as CORTEX_ANSWER_MODEL with
CORTEX_ANSWER_PROVIDER=openai. For premium answers use Sol: gpt-6-astra expects
max_completion_tokens rather than the max_tokens CortexDB sends, so it isn't usable on the answer
lane yet. Costs, monthly estimates and the
price-book caveat are on Cost Planning.
Earlier model choices (internal A/B deltas on a 150-question slice — directional, not definitive; the GPT-5.6 models have no published delta on this slice):
| Model | Provider | Per-query cost | LongMemEval-S delta vs Opus 4.6 |
|---|---|---|---|
claude-opus-4-6 | Anthropic | ~$0.03 | 0 (baseline, 93.8%) |
claude-sonnet-4-6 | Anthropic | ~$0.006 | ~-2 pp |
gpt-4o | OpenAI | ~$0.015 | ~-3 pp |
gpt-4o-mini | OpenAI | ~$0.001 | ~-8 pp |
gemini-2.0-flash | ~$0.002 | ~-5 pp (estimated) |
Provider notes. Anthropic: set CORTEX_ANSWER_URL to a Bedrock/Vertex endpoint to route hosted
Claude. OpenAI: CORTEX_ANSWER_PROVIDER=openai, CORTEX_ANSWER_MODEL=gpt-5.6-terra. Bedrock/Google: set
CORTEX_ANSWER_URL to the regional endpoint; auth via the provider's native env vars. Ollama:
CORTEX_ANSWER_PROVIDER=ollama, CORTEX_ANSWER_URL=http://localhost:11434,
CORTEX_ANSWER_MODEL=qwen2.5:14b (~3–15 s / answer).
4. Verifier LLM (on whenever an OpenAI key is present)
A second LLM call that critiques the answer for hallucination against the citation pack. Doubles
latency and cost when enabled. The image bakes CORTEX_VERIFIER_URL=https://api.openai.com/v1 and
CORTEX_VERIFIER_MODEL=gpt-4.1, so it switches on as soon as OPENAI_API_KEY (or
CORTEX_VERIFIER_API_KEY) is set; set CORTEX_VERIFIER_URL= (empty) to keep it off.
| Env var | Default | What it controls |
|---|---|---|
CORTEX_VERIFIER_MODEL | gpt-4.1 | Use a different family from the answer model. |
CORTEX_VERIFIER_URL | https://api.openai.com/v1 (baked into the image) | Endpoint; set empty to disable the lane. |
CORTEX_VERIFIER_API_KEY | (falls back to OPENAI_API_KEY, not CORTEX_ANSWER_API_KEY) | Key. |
CORTEX_VERIFIER_MAX_TOKENS | 16384 | Verifier output budget. |
Configure the lane to use it:
export CORTEX_VERIFIER_URL=https://api.openai.com/v1
export CORTEX_VERIFIER_API_KEY=$OPENAI_API_KEY
export CORTEX_VERIFIER_MODEL=gpt-4.1Using a different family from the answer model avoids sharing the generator's blind spots.
Configuring the verifier
The verifier is configured only by the four CORTEX_VERIFIER_* variables above; setting them turns it
on for every question. There is no separate enable flag or per-question-type setting (names such as
CORTEX_ANSWER_USE_VERIFIER have no effect).
The fallback chain (in cortex.toml)
The [llm] section declares a fallback chain any LLM call can use when the primary provider is
unreachable:
[llm]
provider = "openai"
endpoint = "" # empty → resolve from provider default
api_key = "" # empty → resolve from OPENAI_API_KEY env
model = "gpt-4o-mini"
fallback_provider = "anthropic"
fallback_endpoint = "" # empty → https://api.anthropic.com/v1
fallback_api_key = "" # empty → ANTHROPIC_API_KEY env
fallback_model = "claude-sonnet-4-6"
fallback_chain = ["openai", "anthropic", "google"] # tried in order on cascading failures
max_extraction_batch_size = 8 # entity-extraction items per LLM call
extraction_timeout_ms = 30000 # per-call timeoutResolution: TOML api_key wins, else the env var for the named provider
(OPENAI_API_KEY/ANTHROPIC_API_KEY/LLM_API_KEY); same for endpoint. Call-site CORTEX_*
overrides take precedence over the TOML defaults — the TOML chain is a floor, the env vars a ceiling.
Cost ladder
Estimated monthly model spend for an agent writing 10K events a day (300K a month) and answering 1K questions a day (30K a month), using the per-event and per-answer figures on Cost Planning (enrichment about $0.40 per 1,000 events; answers at the rates listed there; OpenAI list prices, September 2026).
| Configuration | Embedding | Enrichment | Answer | Total |
|---|---|---|---|---|
| Content-only, answers on Luna | negligible | — | ~$60–120 | ~$60–120 |
| Enrichment on, answers on Luna | negligible | ~$120 | ~$60–120 | ~$180–240 |
| Recommended: enrichment on, answers on Terra | negligible | ~$120 | ~$600–1,200 | ~$720–1,320 |
| Premium answers on Sol | negligible | ~$120 | ~$1,200–2,100 | ~$1,320–2,220 |
| Premium answers on GPT-6 Astra | negligible | ~$120 | ~$3,300–5,400 | ~$3,420–5,520 |
Question volume drives the cost, not corpus size: choosing the answer model (and routing simple questions to Luna) is the largest cost lever. The previous version of this table, inherited from the v1 docs, was internally inconsistent on the answer column.
Diagnostics
The model is at diagnostics.answer_model — there is no model_used field
The /v1/answer response top-level keys are { pack_id, answer, citations, provenance, diagnostics, as_of }. The model is reported at diagnostics.answer_model — there is no top-level
model_used field.
Choosing the answer model
The answer lane always calls its configured CORTEX_ANSWER_MODEL. /v1/answer accepts an
answer_model field (and the SDKs expose it), but it is only echoed in diagnostics.answer_model. To
answer with a different model, change CORTEX_ANSWER_MODEL (or run a second deployment).
{
"answer": "...",
"citations": [ "..." ],
"diagnostics": { "answer_model": "gpt-5.6-terra", "recall_ms": 42, "llm_ms": 830, "...": "..." }
}Without a per-request override, if diagnostics.answer_model doesn't match what you set: a typo made the binary fall back to the
compiled default; the fallback chain triggered (primary provider unreachable); or a scope-level policy
override is forcing a different model — check /v1/policy/effective?actor=….
Next steps
- Embeddings — the vector side of the inference bill
- Recall Tuning — feed the answer model better context
- Profiles & Presets — the Cost-Optimized profile for an all-mini deployment
- Cost Planning — recommended models and what they cost