> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Inspecting Graph Completion Context

> See how GRAPH_COMPLETION turns retrieved graph triplets into an answer's context, and what a memory fragment is

This guide shows you how `SearchType.GRAPH_COMPLETION` — Cognee's graph-based search type — turns triplets (pairs of connected nodes plus the relationship between them) into the context behind an answer, before that context ever reaches the language model.

## Before You Start

* Complete the [Understand Recall with RAG Completion guide](/guides/rag-recall) to see the shared `recall()` parameters this guide builds on (`query_text`, `datasets`, `top_k`, `only_context`) — this guide does not repeat that explanation.
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) configured.

## Code in Action

```python theme={null}
import asyncio

import cognee
from cognee import SearchType

DATASET_NAME = "graph_completion_demo"
QUERY = "Who works on Cognee, and how do Alice and Bob collaborate?"

DOCUMENTS = [
    "Alice is a Cognee engineer.",
    "Bob is Cognee's product manager.",
    "Cognee turns documents into AI memory.",
    "Alice builds Cognee hybrid retrieval.",
    "Alice and Bob meet weekly on Cognee demos.",
    "Bob sends Cognee feedback to Alice.",
]


async def main():
    await cognee.remember(
        DOCUMENTS,
        dataset_name=DATASET_NAME,
        self_improvement=False,
    )

    context = await cognee.recall(
        query_text=QUERY,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET_NAME],
        top_k=20,
        only_context=True,
    )
    print("Retrieved context:\n")
    print(context[0].text)

    narrow_context = await cognee.recall(
        query_text=QUERY,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET_NAME],
        top_k=20,
        wide_search_top_k=1,
        only_context=True,
    )
    print("\nNarrow candidate pool (wide_search_top_k=1):\n")
    print(narrow_context[0].text)

    wide_context = await cognee.recall(
        query_text=QUERY,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET_NAME],
        top_k=20,
        wide_search_top_k=100,
        only_context=True,
    )
    print("\nWide candidate pool (wide_search_top_k=100):\n")
    print(wide_context[0].text)

    answer = await cognee.recall(
        query_text=QUERY,
        query_type=SearchType.GRAPH_COMPLETION,
        datasets=[DATASET_NAME],
        top_k=20,
    )
    print("\nGenerated answer:\n")
    print(answer[0].text)


if __name__ == "__main__":
    asyncio.run(main())
```

## What Just Happened

### Step 1: Ingest the Example Dataset

```python theme={null}
await cognee.remember(
    DOCUMENTS,
    dataset_name=DATASET_NAME,
    self_improvement=False,
)
```

Six short sentences about Alice, Bob, and Cognee give the graph something to connect: two people, their roles, and how they interact with each other and with Cognee.

### Step 2: Retrieve Only the Graph Context

```python theme={null}
context = await cognee.recall(
    query_text=QUERY,
    query_type=SearchType.GRAPH_COMPLETION,
    datasets=[DATASET_NAME],
    top_k=20,
    only_context=True,
)
print("Retrieved context:\n")
print(context[0].text)
```

With `only_context=True`, `recall()` skips the completion step and returns the formatted graph context it would otherwise have sent to the language model.

`recall()` returns a list with one entry per query. This example passes a single query, so the list always has exactly one item: `context[0]`. By printing `context[0].text` instead of just `context[0]`, we make the output readable — otherwise it would print the full result object.

The printed text has two parts: a `Nodes:` section listing every node touched by a retrieved triplet, and a `Connections:` section rendering each triplet as `source --[relationship]--> target`. What ends up here is determined by `top_k=20`, which keeps the 20 most relevant triplets (two connected nodes plus the relationship between them) found by the search.

Lines like `alice --[works_at]--> cognee` or `alice --[attends_weekly_on]--> cognee demos` show relationships Graph Completion decided were relevant to the query.

### Step 3: Compare Narrow vs. Wide Candidate Pools

```python theme={null}
narrow_context = await cognee.recall(
    query_text=QUERY,
    query_type=SearchType.GRAPH_COMPLETION,
    datasets=[DATASET_NAME],
    top_k=20,
    wide_search_top_k=1,
    only_context=True,
)
print("\nNarrow candidate pool (wide_search_top_k=1):\n")
print(narrow_context[0].text)

wide_context = await cognee.recall(
    query_text=QUERY,
    query_type=SearchType.GRAPH_COMPLETION,
    datasets=[DATASET_NAME],
    top_k=20,
    wide_search_top_k=100,
    only_context=True,
)
print("\nWide candidate pool (wide_search_top_k=100):\n")
print(wide_context[0].text)
```

Unlike Step 2, both calls here add one more parameter: `wide_search_top_k`. We call `recall()` twice with the same `top_k=20`, but with two very different `wide_search_top_k` limits — `1` and `100` — to see its effect directly.

`wide_search_top_k` caps how many candidates the vector-search step contributes before the memory fragment is built (see [Under the Hood](#under-the-hood) below). Here, `wide_search_top_k=1` produces a fragment of only 19 nodes and 18 edges — short of the requested 20 triplets. `wide_search_top_k=100` produces a larger fragment of 33 nodes and 48 edges, enough to fill the full `top_k=20`. This is the performance/quality tradeoff: a narrower pool is cheaper but can miss relevant relationships; a wider pool costs more but is less likely to.

### Step 4: Generate the Final Answer

```python theme={null}
answer = await cognee.recall(
    query_text=QUERY,
    query_type=SearchType.GRAPH_COMPLETION,
    datasets=[DATASET_NAME],
    top_k=20,
)
print("\nGenerated answer:\n")
print(answer[0].text)
```

Dropping `only_context=True` lets `recall()` complete the answer using that same context — nothing changes between the two calls except whether the completion step runs. The context inspected in Step 2 is exactly what was sent to the LLM to produce this answer.

## Under the Hood

<AccordionGroup>
  <Accordion title="How Graph Completion Selects Triplets">
    Graph Completion does not just return the first nodes and edges it finds. Internally, it runs the same pipeline for every query:

    1. **Vector search** — search the indexed node and edge collections (entity names, text summaries, document chunks, and relationship labels) for the query, scoring the nodes and relationship labels that matched. `wide_search_top_k` (default `100`) caps how many candidates come back from *each* collection at this stage — it is a separate knob from `top_k`, which only comes into play at the very end, in step 5.
    2. **Project a memory fragment** — a memory fragment is a temporary, in-memory copy of the graph, built restricted to the node ids that matched in step 1 (see the next accordion for the full picture). An edge is only carried over if both of the nodes it connects were selected — edges are never chosen on their own.
    3. **Map distances onto the fragment** — attach each match's vector distance from step 1 to the corresponding node or edge inside that fragment.
    4. **Score each triplet** — a triplet's two endpoint nodes and its edge each carry their own importance weight (and, if configured, a feedback weight from past corrections); these are combined into one score for the triplet as a whole.
    5. **Keep the top `top_k`** — the best-scoring triplets are what you saw resolved into the `Nodes:` / `Connections:` text above.

    * **`wide_search_top_k` (performance vs. quality)** — a higher value scores more candidate nodes and edges for the memory fragment, making it more likely to catch the relevant relationship, but takes longer; a lower value is faster but more likely to miss one. `None` scores every node and edge in the graph — the best quality, and the slowest.
  </Accordion>

  <Accordion title="What a Memory Fragment Is">
    A memory fragment is a temporary, in-memory graph object that Cognee builds fresh for one query and discards once triplets are selected — it is never read directly from, or written back to, the full persisted graph.

    This guide's example passes a single `query_text` with no `node_name` or `neighborhood_depth` — so Cognee always builds the fragment the same way here: **ID-filtered**, using only the node ids the vector search already scored, plus the edges between them.

    Three other projections exist, chosen by which parameters you pass to `recall()`:

    1. **Full-graph** — every stored node and edge, used when there's no useful pre-filter (`wide_search_top_k=None`).
    2. **Node-set** — restricted to nodes matching a given `node_name`.
    3. **Neighborhood** — starts from a few nodes (via `neighborhood_depth`) and includes their direct connections, then those connections' own connections, up to a set limit.

    In every mode, an edge is included only if both of its endpoint nodes were — the node selection always comes first, and the edges follow from it.
  </Accordion>
</AccordionGroup>

<Columns cols={2}>
  <Card title="Recall" icon="search" href="/core-concepts/main-operations/recall">
    Understand recall()'s full parameter surface and auto-routing behavior
  </Card>

  <Card title="SearchType" icon="list" href="/python-api/search-type">
    See every search type, including GRAPH\_COMPLETION's variants
  </Card>
</Columns>
