cognee-recall · git:20261029.0989319 · 2026-10-29 · sha256 13994db1ddde0700
cognee-recall git:20261029.0989319A
Immutable. This exact content never changes and is served at /api/v1/blob/13994db1ddde0700.
---
name: cognee-recall
description: Use when querying cognee memory with recall() (or search()) — picking a search type, understanding auto-routing, scoping to datasets, node sets or sessions, getting context or citations instead of an answer, reading the results, or debugging empty or unexpected results.
---
# Query memory with recall()
`recall()` is cognee's query API. It picks a search strategy, searches the
graph and (with a session) the session cache, and returns a list of tagged
results.
```python
import cognee
results = await cognee.recall("Where was Einstein born?")
for r in results:
print(r.source, r.text) # e.g. "graph", "Einstein was born in Ulm."
```
Without `datasets` it covers every dataset the user can read, but dataset
routing (always on, no off switch) scores those datasets
against the query on their global context index and searches only the best
`DATASET_ROUTING_TOP_K` (default 1), plus any dataset that has no index yet.
A dataset farther than `DATASET_ROUTING_DISTANCE_CUTOFF` (cosine distance,
default 0.8) is skipped, and when none is within it all are searched; datasets
within `DATASET_ROUTING_TIE_MARGIN` (default 0.001) of the last routed one are
searched too. A routed search that finds nothing is widened to the rest. The
search log prints every dataset's distance. Routing assumes
one embedding model across datasets; after an `EMBEDDING_MODEL` change it
logs a warning and every readable dataset is searched. Pass
`datasets=["research"]` (names) or `dataset_ids=[...]` (UUIDs, which win) to
pin the scope yourself; an explicit scope is never routed.
## Use it
### How the search type is picked
1. An explicit `query_type=SearchType.X` always wins.
2. With no usable LLM key nothing can write an answer, so on both paths
`HYBRID_COMPLETION`, `GRAPH_COMPLETION` and `RAG_COMPLETION` run with
`only_context=True` (the assembled prompt instead of an answer), and a routed
`TEMPORAL` or `BROAD` becomes `HYBRID_COMPLETION` with `only_context=True`;
a routed `CODE` needs no LLM and runs as it is.
3. Without an explicit type, the router (`auto_route=True`, the default). It is five regex
rules, first match wins, no LLM call:
- the whole query is one `"quoted phrase"` → `CHUNKS_LEXICAL`
- mentions coding rules/standards/conventions or code-review guidelines
→ `CODING_RULES`
- code phrasing right next to an identifier (`what calls parse_config`,
`where is CodeRetriever defined`, `callers of foo()`) → `CODE`, running
its `ask` operation over the identifiers
- opens with `how many` / `total number of` → `BROAD`
- an absolute date behind a time preposition (`in 2019`, `before 1900`,
`on 7 November 1867`) → `TEMPORAL`
4. Everything else → `HYBRID_COMPLETION`.
A routed type (never a pinned one) that the backend rejects, or a routed
`CHUNKS_LEXICAL` / `CODING_RULES` / `CODE` / `BROAD` that returns nothing, is
retried once as `HYBRID_COMPLETION`. So is a routed `CODE` with no matching
symbol or no code graph, and a routed `BROAD` that cannot write a working
query, hits a limit or finds no document text; any other "no data" is not
retried. The router never picks `CYPHER`.
```python
from cognee import SearchType
await cognee.recall("What changed in v2?", query_type=SearchType.GRAPH_COMPLETION)
```
### Search types
The full list is `cognee/modules/search/types/SearchType.py`; the
type-to-retriever table is `cognee/modules/retrieval/README.md`.
| Type | LLM? | Use for |
|---|---|---|
| `HYBRID_COMPLETION` (default) | yes | General questions: document passages plus entity neighbourhoods, then an answer |
| `GRAPH_COMPLETION` | yes | Answers from graph relationships |
| `GRAPH_COMPLETION_COT`, `_CONTEXT_EXTENSION`, `_DECOMPOSITION` | yes | Harder multi-hop questions (more LLM calls) |
| `GRAPH_SUMMARY_COMPLETION` | yes | Summarizes the retrieved graph edges at query time (extra LLM call), then answers |
| `RAG_COMPLETION` | yes | Classic chunk RAG |
| `TEMPORAL` | yes | Time questions; hybrid retrieval reranked by the dates in the question |
| `CHUNKS` | no | Raw passages by vector similarity |
| `CHUNKS_LEXICAL` | no | Keyword / exact-phrase match |
| `SUMMARIES` | no | Document summaries |
| `CODE` | no | Code-graph operations via `code_query={...}`; needs `scope="code"` in recall |
| `SKILLS` | no | Discover skill playbooks; exactly one dataset |
| `CYPHER` | no | Raw Cypher. Off by default; `ALLOW_CYPHER_QUERY=true` enables it. It can write, so only pass user-authored queries deliberately |
| `NATURAL_LANGUAGE` | yes | LLM writes Cypher, then runs it (same flag) |
| `GRAPH_REPORT` | partly | Graph insight report: hubs, cross-set links, suggested questions |
| `FEELING_LUCKY` | yes | An LLM picks the type |
| `AGENTIC_COMPLETION` | yes | Multi-step loop with skills/tools; exactly one dataset. Use `search()` for its parameters |
### Scope: which sources are searched
`scope` is one of, or a list of: `graph`, `session`, `session_first`,
`trace`, `session_context`, `all`, `tools`, `code`. `all` means graph +
session + trace + session_context; `tools` and `code` are never included
implicitly.
When `scope` is omitted:
| You pass | Sources |
|---|---|
| `session_id` only | Session first; a session hit skips the graph |
| `session_id` + `datasets` | Session and graph both contribute |
| `session_id` + `query_type` | **Graph only** — pinning a type drops the session |
| no `session_id` | Graph only |
Session and trace search is keyword overlap, not embeddings.
### Filters and knobs
- `top_k=15`: per dataset, not in total. The default HYBRID caps each lane
at `min(top_k, 10)`; set `chunks_top_k` / `entities_top_k` /
`facts_top_k` in `retriever_specific_config` to go higher.
- `node_name=["AI"]` (+ `node_name_filter_operator="OR"|"AND"`): restricts
graph/chunk/completion types to data remembered with that `node_set`.
SUMMARIES, CHUNKS_LEXICAL, GRAPH_REPORT, CYPHER, NATURAL_LANGUAGE, CODE and
SKILLS ignore it; CODING_RULES treats it as the rules node-set name.
- `system_prompt` / `system_prompt_path`: change the answering prompt.
- `response_model=MyPydanticModel`: structured answer, on `r.structured`.
- `include_references=True`: attach the document chunks that support each
graph edge used (needs `EDGE_EVIDENCE_ENABLED=true`, the default).
- `only_context=True`: return what the LLM would have received instead of
an answer. `r.text` is the rendered user prompt, `r.system_prompt` the
system prompt. Pin `query_type` when you use it.
- `retriever_specific_config={...}`: retriever-only options. For the
agentic extras (`skills`, `tools`, `max_iter`) and `node_type`, call
`cognee.search()` instead, which takes them as parameters.
### Reading the results
Each item is a Pydantic model with a `source` discriminator: `graph`,
`session`, `trace`, `session_context`, `code`, `tools`, `skills`, or
`system`. Graph items carry `text` (always renderable), `search_type`,
`kind`, `score`, `dataset_id` / `dataset_name`, `metadata`, `raw`, and
`structured`. A `system` item is a status
marker, not data (see "memory warming up" below).
When a query sounds procedural ("how do I…", "runbook", "steps to…") and
exactly one dataset is targeted, recall also runs a small `SKILLS` lookup
and appends hits with `source="skills"`. Disable with
`SKILL_GATE_ENABLED=false`.
## Pitfalls
- **Permissions change what you get back.** With no `datasets`, recall
searches only datasets the user can read, so a user without grants gets
one `source="system"` `memory_warming_up` marker from a graph-only recall
(with `only_context=True` or `RECALL_WARMUP_SHORTCIRCUIT=false` it gets
`[]`; with session sources included only the graph lane is empty, and
session and trace hits still come back). Asking for a dataset **id** the user cannot read raises
`PermissionDeniedError` (HTTP 403). Dataset **names** resolve only among
the user's own datasets, so a name that is not theirs (even one shared
with them) raises `DatasetNotFoundError`; use `dataset_ids` for shared
datasets. See the `cognee-permissions` skill.
- **"Memory warming up".** On an empty graph, a graph-only recall (no
session sources, not `only_context`) returns one `source="system"` item
with `status="memory_warming_up"` (or `"build_failed"` plus
`error_message`) instead of results; a multi-source recall just returns
no graph results. Wait for the
remember to finish, or check why it failed.
- **Hybrid silently becomes graph completion** when you pass
`neighborhood_depth` or `feedback_influence > 0` (including a nonzero
`DEFAULT_FEEDBACK_INFLUENCE`), or the chunk collection is missing.
`node_name` stays on hybrid, which filters to that node set. `search()`
also defers for a custom `node_type` or `node_name` with `node_type=None`;
`recall()` has no `node_type`. `wide_search_top_k` and `triplet_distance_penalty`
with hybrid raise `InvalidHybridSearchConfig`; pin
`GRAPH_COMPLETION` to use them.
- **`SKILLS` and `AGENTIC_COMPLETION` need exactly one dataset.** For
`SKILLS`, `search()` raises unless exactly one dataset is given.
`recall()` runs SKILLS per dataset with access control on (zero datasets
gives `[]`, not an error) and raises only with access control off and not
exactly one dataset. For
`AGENTIC_COMPLETION` only `search()` checks it up front, so call it through
`search()` with one dataset.
- **`code_query` without `scope="code"` raises**, and `scope="tools"` also
needs `TOOL_CALLS_ENABLED=true`.
- **Latency.** Most completion types make one LLM call; COT,
DECOMPOSITION, CONTEXT_EXTENSION, GRAPH_SUMMARY_COMPLETION, TEMPORAL,
FEELING_LUCKY (one call to pick the type, then the chosen type's) and
AGENTIC_COMPLETION (a loop of up to `max_iter`, default 6) make more. NATURAL_LANGUAGE makes one (no answer call) and retries only on
an empty or failed query, up to 3 attempts. With `CACHING` and
`AUTO_FEEDBACK` on (defaults), each answered turn adds one analysis call,
even without a `session_id`. Set
`AUTO_FEEDBACK=false` for low-latency reads (see the `cognee-performance`
skill).
## recall() or search()?
Use `recall()`. Drop to `cognee.search()` only for agentic parameters
(`skills`, `tools`, `max_iter`, `node_type`) as first-class arguments, raw
`SearchResult` objects, or a pinned type with no router. `search()` never
searches the session cache; its `session_id` only adds conversation history
to the prompt. Full guide: `docs/recall-vs-search.md`.
## How it works
`recall()` resolves scope and search type, then calls the same authorized
search `search()` uses: datasets resolve through the permission layer with
`read`, one search per dataset runs concurrently, and results are
normalized and tagged.
- Entry point, scope and type resolution: `cognee/api/v1/recall/recall.py`
- Router: `cognee/api/v1/recall/query_router.py`
- Skill gate: `cognee/api/v1/recall/skill_gate.py`
- Result types: `cognee/modules/recall/types/RecallResponse.py`,
`SearchResultItem.py`
- Scope names: `cognee/memory/entries.py:normalize_scope`
- Warm-up config (`RECALL_WARMUP_*`): `cognee/modules/recall/config.py`
- Core search and fan-out: `cognee/modules/search/methods/search.py`
- Hybrid fallback rules: `cognee/modules/search/methods/hybrid_deferral.py`
- Registry: `cognee/modules/search/methods/get_search_type_retriever_instance.py`
Examples in `examples/guides/`: `recall_core.py`,
`hybrid_retrieval_recall.py`, `references_example.py`, `temporal_recall.py`,
`sessions.py`.
## Extending it
Adding a search type, per `cognee/modules/retrieval/README.md`:
1. Write the retriever in `cognee/modules/retrieval/` (subclass
`BaseRetriever` or a completion base).
2. Add the `SearchType` member and its `search_core_registry` entry.
3. Add a row to the README table. A unit test
(`cognee/tests/unit/modules/retrieval/retriever_readme_index_test.py`)
fails if the table and registry disagree.
4. Optional: add it to `SEARCH_TYPE_CHOICES` in `cognee/cli/config.py` for
the CLI, or a regex rule to `query_router.py` for auto-routing. Never
route a type that can write.