cognee.recall()
Description
recall() is the main retrieval entry point in Cognee v1.0.
- It auto-routes queries by default when you do not specify
query_type. Routing is rule-based (no LLM call) and deliberately narrow: a fully quoted query runsCHUNKS_LEXICAL, an explicit coding-rules or code-review phrase runsCODING_RULES, and everything else — including every ordinary question — runsHYBRID_COMPLETION. Every other search type takes an explicitquery_type. See Auto-routing behavior for the rule table, the retry when a routed type comes up empty, and when to override. - It can search the permanent graph, session memory, or both.
- It returns
RecallResponseitems sourced from graph retrieval, session retrieval, or both depending on the request.
Prerequisites
recall() only reads from memory that already exists — it does not initialize anything on its own. Populate memory first with remember() (or the legacy add() + cognify() sequence). The first ingestion run creates the relational, vector, and graph databases and the default user.
Parameters
str
required
Natural-language query to run against memory.
SearchType | None
default:"None"
Forces a specific retrieval strategy instead of using auto-routing, and bypasses the router entirely. Pass it for any type the three routing rules do not cover —
GRAPH_COMPLETION_COT, GRAPH_COMPLETION_CONTEXT_EXTENSION, GRAPH_SUMMARY_COMPLETION, and CYPHER among them — and for TEMPORAL on questions without an absolute date. Leaving it unset when no usable LLM is configured resolves to SearchType.CHUNKS (plain vector search over chunks) instead of a completion, because nothing could write a completion answer. This is decided by LLM availability alone — the same key rule the provider preflight applies — and never by which extractor built the graph: a GLiNER-built graph answers completions normally as long as a key is present. An explicit query_type still selects any search type.list[str] | None
default:"None"
Restricts graph retrieval to the named datasets. Dataset names are resolved only against datasets owned by the current user, and strictly: a name that resolves to no such dataset raises
DatasetNotFoundError (HTTP 404) listing the names that did not resolve, before any retrieval runs, rather than being dropped from the scope. dataset_ids is unaffected — passing it skips name resolution outright, even alongside datasets — and so is a recall that never resolves names at all (see scope). When both datasets and dataset_ids are omitted, retrieval spans every dataset the current user has read access to — not just a single default dataset. Pass this to narrow the search to specific datasets.list[UUID] | None
default:"None"
Restricts graph retrieval by dataset UUIDs instead of names. Use this for shared datasets that the current user can access but did not create. When provided, this takes precedence over
datasets and the name-to-UUID lookup is skipped. Leaving both datasets and dataset_ids unset searches all of the user’s readable datasets.int
default:"15"
Maximum number of results to return.
bool
default:"True"
When
True, Cognee chooses a retrieval strategy automatically if query_type is not set, using the three-rule query router: CHUNKS_LEXICAL for a fully quoted query, CODING_RULES for an explicit coding-rules or code-review phrase, TEMPORAL for a question with a time preposition followed by an absolute date (in 2019, before July 1969), HYBRID_COMPLETION for everything else. When the backend rejects a routed type, or a routed CHUNKS_LEXICAL or CODING_RULES returns nothing, recall retries once as HYBRID_COMPLETION; a query_type you pinned is never retried that way. Set auto_route to False to always use HYBRID_COMPLETION. An explicit query_type always takes precedence over routing. With no usable LLM configured, the no-query_type default is CHUNKS under either setting — see query_type above.str | list[str] | None
default:"None"
Which sources retrieval reads from. Accepts a single value or a list:
"graph"— the permanent knowledge graph."session"— session-cache Q&A history, matched by keyword."trace"— recorded agent traces, matched by keyword across function name, parameters, return value, and error message. See Agent Session Traces."session_context"— the session’s active guidance, rendered read-only for the profile named bycontext_profile. Serving it never stamps or ages an entry."session_first"— session plus graph, with a session hit short-circuiting the graph search. It expands to["session", "graph"](any other value you list is kept after those two) and makes the short-circuit requestable by name, instead of only reachable by passing asession_idwith nodatasetsand noquery_type."all"— expands tograph,session,trace, andsession_context."auto"— the default whenscopeis omitted. Resolves tographalone when there is nosession_id, or whensession_idis combined with an explicitquery_type; otherwise tosessionplusgraph. It never selectstraceorsession_context— ask for those by name.
"tools" and "code" are explicit opt-in only: neither "auto" nor "all" includes them, so name them yourself (scope=["all", "tools"] works). An unrecognized name raises ValueError. "graph_context" is a deprecated alias for "graph".Only the graph and code lanes resolve dataset names strictly, so the behavior described under datasets applies only when one of them runs. A recall scoped purely to session, trace, or session_context never resolves names — an unknown name there is neither an error nor a filter.Additional keyword options
Structured output with response_model
Pass a Pydantic model class to get a validated, parsed answer instead of free text — each result carries the validated payload as a dict in its structured field. All completion-style search types support it (GRAPH_COMPLETION and its variants except GRAPH_SUMMARY_COMPLETION, RAG_COMPLETION, TRIPLET_COMPLETION, HYBRID_COMPLETION, TEMPORAL, AGENTIC_COMPLETION):
response_model is shorthand for retriever_specific_config={"response_model": ...} — Cognee folds the parameter into the config before dispatching, so the dict form still works. Pass it in one place: supplying the same model class through both is allowed, but different classes raise CogneeValidationError (HTTP 422).
Remote mode. A Python class cannot cross the HTTP boundary, so against a remote server (see
serve()) the SDK forwards response_model.model_json_schema() as the response_schema field of POST /api/v1/recall, and the server rebuilds a validation model from it. Only the schema’s structure travels — custom validators and value constraints are not enforced server-side; rehydrate on the client (NLPFacts.model_validate(results[0].structured)) when you need them. See Search & Recall — response_schema for the supported schema subset and rejection rules.Return value
recall() returns a list of RecallResponse items. Depending on the request, results may come from session memory, permanent graph retrieval, or both.
These items are Pydantic objects, not plain dictionaries — read fields with attribute access (result.text), not result.get("text") or result["text"]. Calling .get() on a result raises AttributeError: 'ResponseGraphEntry' object has no attribute 'get'.
The concrete type of each item is set by its source field (import from cognee.modules.recall.types.RecallResponse):
Warming-up marker
Before running graph retrieval,recall() checks how far the target datasets got through a Cognee pipeline. This check is a single indexed relational query — it never spins up a graph or vector engine. It classifies the datasets into three readiness states:
For the two cold states the graph lane returns immediately, with a marker instead of a graph search plus an LLM call that could only come back empty:
source, add a "system" case — code that previously saw an empty list for a cold dataset now sees a one-item list carrying this marker. text is populated so consumers that just render text still display something sensible. If you branch on status, treat build_failed as a failure to surface to the user, not as a transient “still warming up” state that will resolve on its own: it only clears once a graph-writing run succeeds.
Details and exceptions:
- The marker only appears when graph is the sole source. In a multi-source recall (for example a session-scoped call that reads both session memory and the graph), a cold graph contributes
[]instead, so the other sources — and thetoolson_emptyfallback — behave exactly as if graph retrieval had returned nothing. only_context=Truebypasses the check and always runs normal retrieval, since those callers expect context rather than a marker.- Populated datasets are unaffected. A dataset with a completed graph-writing run reads as warm. So does one that has only been
add()-ed but not yet cognified — staged-only datasets fail safe to warm, and a failedadd()never produces abuild_failedmarker, since staging says nothing about the graph. - A build still in flight reads as warm. The never-built verdict requires that no pipeline run exists at all, so once a first
cognify()has started — a background run, for example — recall runs a normal search that comes back empty rather than returning amemory_warming_upmarker. - Only warm verdicts are cached. Both cold states are re-probed on every recall, so the first recall after a successful rebuild sees the truth immediately.
- The check fails open. A probe or configuration error falls through to a normal search, so it can never block a real answer.
- It can be turned off with
RECALL_WARMUP_SHORTCIRCUIT=false(orcognee.config.set("recall_warmup_shortcircuit", False)), which restores the previous behavior exactly. See Recall warm-up for that variable and its two companions.
Skill gate
When a query reads like a request for a procedure,recall() also looks for matching skill playbooks and appends them to the results as "skills" entries. The gate is a fixed set of weighted regexes — no LLM call, no I/O — that fires on phrasings such as how do I …, steps to …, walk me through, playbook, runbook, checklist, workflow, or the word skill. An operational verb alone (set up, install, configure, deploy, migrate, troubleshoot, and similar) is too weak to fire the gate by itself and only counts alongside one of those phrasings.
Chinese phrasings fire the gate the same way. Because Chinese is written without spaces, the English rules — all anchored on word boundaries — could never match inside a Chinese sentence, so such a query previously scored zero and never reached the skills lane. A separate set of boundary-free rules now covers the same tiers: an interrogative followed closely by an operational verb (怎么配置 …, 如何部署 …, 怎么排查这个报错), a request followed closely by one (帮我配置 …, 麻烦帮忙排查 …), and document nouns such as 操作手册, 运维手册, 配置指南, 检查清单, or 操作流程. As in English, a bare operational verb (安装, 部署, 配置, 排查, …) or 步骤 on its own is too weak to fire the gate — and since they share one rule, they do not add up either, so 部署步骤 alone does not fire. English behavior is unchanged.
A negated phrase does not count: the gate ignores a match when a negation word appears within 20 characters before it — not, n't, no, never, without, lack in English, and 不, 别, 勿, 未, 无需, 无须 in Chinese, so 别帮我部署 … does not fire. The Chinese negators match anywhere, including inside unrelated words such as 不同 or 区别, so 不同环境怎么部署 does not fire either. Bare 没 is deliberately not a negator, because it also occurs inside the ordinary affirmative-interrogative 有没有 (“is there …”), which would suppress a large share of normal Chinese questions. That suppression belongs to the gate alone — the query router applies no such rule.
When it fires, recall() starts a SKILLS search for up to three skills concurrently with the main retrieval and appends the hits after the other sources. Each hit is a ResponseSkillEntry whose skill dict holds the skill’s metadata only — the procedure body stays behind the load_skill tool or GET /api/v1/skills/{skill_id}.
- Additive and fail-safe. The main lanes never wait on the gate, it never replaces or blocks the main answer, and any error in the lookup contributes nothing rather than failing the recall. A dataset with no skills ingested yet simply adds no entries.
- Single-dataset only. Skills are scoped per dataset, so the gate runs only when exactly one dataset is targeted through
datasetsordataset_ids. Otherwise it is skipped silently. - Skipped when it would be redundant. It does not run when
"graph"is not in scope, whenonly_context=True, or whenquery_typeis alreadySKILLSorAGENTIC_COMPLETION. - It can be turned off with
SKILL_GATE_ENABLED=false.
Source provenance in metadata
For chunk and summary results (CHUNKS, CHUNKS_LEXICAL, SUMMARIES), the metadata dict carries stable source identifiers so you can map a result back to the data you ingested and inspect the exact cited chunk. Only the keys present in the underlying payload are included:
Completion-style results carry an empty
metadata dict unless include_references=True (see include_references); then, for RAG_COMPLETION and graph-completion results, metadata.evidence lists structured references to what was placed in the LLM context, and the same ids are also surfaced inline in the Evidence: block. RAG_COMPLETION bullets are rendered as - chunk N of document NAME (data_id: …, chunk_id: …): "snippet", built from the retrieved chunk payloads with no DB migration required. GRAPH_COMPLETION bullets are rendered as - chunk N of document NAME (data_id: …, chunk_id: …) with no snippet, resolved through the edge-evidence sidecar, which needs Alembic revision f3a7b9c1d2e4 on an existing deployment.
Not every completion type fills metadata.evidence. With include_references=True, what you get depends on the search type, not on the database backend:
So when a result’s
search_type is HYBRID_COMPLETION (the default when auto-routing matches no rule), it has the Evidence: bullets in text but an empty metadata dict, and running migrations does not change that. A hybrid request that defers to GRAPH_COMPLETION (for example with neighborhood_depth or feedback_influence set) reports search_type="GRAPH_COMPLETION" and does carry metadata.evidence. To get structured references in metadata, pass query_type=SearchType.RAG_COMPLETION or SearchType.GRAPH_COMPLETION.
Each metadata.evidence entry is a dict with a kind (segment, graph_node, or graph_edge), an artifact_id, a role (used_as_context, or supports_assertion for source chunks resolved through the sidecar), the source locators that apply to its kind, and the rank and score fields:
rank— the 0-based position of the artifact in the context Cognee built, not a relevance grade. ForRAG_COMPLETIONit is the chunk’s position in the retrieval results; duplicate chunks are dropped without renumbering, so ranks can skip. For graph completion, nodes come first in order of appearance, then edges, then the sidecar’s source chunks, all on one numbering.score— the chunk’s raw vector distance, with the same meaning as the item-levelscore(lower is better). OnlyRAG_COMPLETIONchunk entries set it; graph node, graph edge, and sidecar chunk entries leave itNone.
The
text_result, context_result, objects_result, user_prompt_result, and system_prompt_result keys come from the legacy search(verbose=True) API, which returns plain dicts. recall() does not produce those keys — an only_context call’s system prompt is on the item’s own system_prompt field. For a graph-backed recall item, result.text is the display-ready value. result.raw preserves the normalized payload for that item; for completion-style searches, it is not the same thing as objects_result.Relevance in score
CHUNKS, SUMMARIES, and SKILLS results carry the retriever’s own relevance number in score (and, for chunks and summaries, under raw["score"] alongside the rest of the payload), so you can rank or fuse results across retrievers instead of trusting return order alone.
The value is the raw backend distance, not a normalized similarity — cosine distance for the built-in vector adapters — so a lower number is a better match, and the range depends on the vector store you run. Do not compare a score across two different backends, and do not read it as a 0–1 confidence.
score is None for search types whose payload carries no numeric score: completion-style search types, and CHUNKS_LEXICAL — whose BM25 ranking orders results but does not populate this field. Branch on score is not None before doing arithmetic with it.