> ## 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.

# Graph Visualization

> Step-by-step guide to rendering interactive knowledge graphs

A minimal guide to rendering your current knowledge graph to an interactive HTML file with one call.

**Before you start:**

* Complete [Quickstart](getting-started/quickstart) to understand basic operations
* Have some remembered data or any existing knowledge graph

## What Graph Visualization Shows

* Nodes (entities, types, chunks, summaries) with color coding
* Edges with labels and weights; tooltips show extra edge properties
* Interactive features: drag nodes, zoom/pan, hover edges for details

## Code in Action

### Step 1: Create Your Knowledge Graph

```python theme={null}
await cognee.forget(everything=True)

await cognee.remember(
    ["Alice knows Bob.", "NLP is a subfield of CS."],
    self_improvement=False,
)
```

This starts from a clean state, then uses `remember()` to ingest the text and build the graph in one call.

### Step 2: Generate Visualization

```python theme={null}
visualize_graph_path = os.path.join(
    os.path.dirname(__file__), ".artifacts", "graph_after_remember.html"
)
await visualize_graph(visualize_graph_path)
```

This creates an interactive HTML file with your knowledge graph. You can specify a custom path or use the default location.

## Quick Options

### Default Location

```python theme={null}
from cognee.api.v1.visualize.visualize import visualize_graph

# Writes HTML to your home directory by default
await visualize_graph()
```

### Custom Path

```python theme={null}
from cognee.api.v1.visualize.visualize import visualize_graph

# Writes to the provided file path (created/overwritten)
await visualize_graph("./my_graph.html")
```

## Tabs

Every rendered HTML file opens with a tab bar of four views — **Graph**, **Schema**, **Memory**, and **Semantic** — all computed from the same graph payload:

<AccordionGroup>
  <Accordion title="Graph — classic topology">
    The default view: nodes and edges laid out by structure. Drag nodes, zoom/pan, and hover edges for details. On-screen controls let you:

    * Switch the layout — **Story** (fixed pipeline columns: Documents → Chunks → Entities → Types → Summaries), **Flow** (columns by processing order; vertical position settles by connectivity), or **Force** (a physics simulation where clusters emerge; drag nodes to rearrange).
    * Set the label budget — **Key** (landmark nodes + high-importance entities), **All**, or **Off** (hover to peek).
    * Recolor nodes by **Type**, **Node set**, or **User**, and zoom or fit to view.
  </Accordion>

  <Accordion title="Schema — types at a glance">
    A by-type summary of the rendered graph: instance counts per semantic type and the relationship distribution between types, computed at render time. The same projection is available as a standalone data API with its own HTTP endpoint — useful for driving dashboards without rendering HTML — see [Schema Inventory](/guides/schema-inventory).
  </Accordion>

  <Accordion title="Memory — pipeline structure">
    A deterministic map of how the memory was built: documents, their chunks, and the entities extracted from them, plus the run timeline. Every list is ordered by keys intrinsic to the data, so the layout is reproducible and append-stable as the graph grows. Not to be confused with [Memory Provenance](/guides/memory-provenance), which is a separate projection of the relational database (tenants, users, agents, datasets, files) rendered to its own HTML file.
  </Accordion>

  <Accordion title="Semantic — layout by meaning">
    Instead of laying nodes out by their edges, the Semantic tab places each node at the 2‑D projection of its embedding, so semantically similar nodes sit together and clusters of related entities become visible at a glance. It reuses the vectors Cognee already stored during `cognify()` — nothing is re‑embedded at render time on the default LanceDB backend, and only 2‑D positions and precomputed neighbor lists are sent to the browser.

    Click **Semantic** in the tab bar, or append `#semantic` to the file URL to deep‑link straight to it. In the tab you can:

    * **Cluster / Type** — toggle recoloring nodes by semantic cluster or by ontology type.
    * **Hover** a node to light up its nearest neighbors and list its relations.
    * **Legend** entries filter to a single cluster or type; scroll or use the on‑screen controls to zoom.
    * **Semantic ⇄ Structural** — toggle between the pinned meaning‑space layout and a bounded force layout over the graph topology.
    * **Recall overlay** — light up the nodes a past recall query retrieved.

    **Choosing the projection.** By default the layout uses **PCA** (pure‑numpy, sign‑stabilized), which is deterministic — the same graph always renders the same layout. To use **UMAP** instead, install it and opt in with an environment variable:

    ```bash theme={null}
    pip install umap-learn
    export SEMANTIC_MAP_PROJECTION=umap
    ```

    UMAP is an optional dependency and a lazy import — when `umap-learn` is not installed, the layout silently falls back to PCA.

    <Note>
      The Semantic tab is best‑effort: if embeddings can't be fetched or the projection fails, the tab shows a friendly empty state and the classic render is never affected. Nodes that have no stored vector are placed at the centroid of their positioned neighbors.
    </Note>

    **Behavior on large graphs.** The semantic layout and clustering are bounded to **2000 nodes** (`SEMANTIC_NODE_CAP`). Graphs above that are reduced with a deterministic seeded sample, so results are approximate at scale but stable across runs. When sampling kicks in, a warning is logged.

    When vectors are fetched, an info‑level log reports the join hit‑rate, e.g. `resolved 128/150 node embeddings across 4 collection(s)`. If nothing resolves (a blank Semantic map), a warning names the missing collections and unmapped node types — the usual cause of a blank map is an id/collection‑name mismatch rather than a silent failure.
  </Accordion>
</AccordionGroup>

## Tips

* **Large graphs**: Rendering a very big graph can be slow. Consider building subsets (e.g., smaller datasets) before visualizing
* **Edge weights**: If present, control line thickness; multiple weights are summarized and shown in tooltips
* **Static HTML**: Files are static HTML; you can open them in any modern browser or share them as artifacts

## Related projections

Two companion projections summarize your memory without rendering every node — both run end-to-end without an LLM:

* [Schema Inventory](/guides/schema-inventory) — `get_schema_inventory()` summarizes the graph by semantic type: per-type counts, sample names, and relationship distribution.
* [Memory Provenance](/guides/memory-provenance) — `visualize_memory_provenance()` renders the ownership and data-flow story (Tenant → User → Agent → Dataset → file) from the relational database.

## Additional information

<Accordion title="Bounded subgraph by default">
  `visualize_graph()` renders a **bounded, relevant subgraph** by default instead of the entire graph: it picks a small set of seed nodes, expands their *k*-hop neighborhood, and caps the result at `max_nodes`. This keeps renders fast and readable on large graphs. Pass `full=True` to restore the legacy whole-graph render.

  Seeds are resolved by priority — the first of these that produces nodes wins:

  1. `seed_node_ids` — explicit node ids you pass.
  2. `recall_result` — a `recall()` or search result whose graph provenance (`used_graph_element_ids`) seeds the subgraph, i.e. "show me the subgraph behind this answer".
  3. `query` — a query string whose nearest vector hits (distance-ranked, nearest first) seed the subgraph.
  4. Highest-degree nodes — the fallback so a bare `visualize_graph()` call still shows a representative view.

  If none of these resolve any seeds, an empty graph is rendered.

  The new parameters are **keyword-only**, so existing positional calls keep working unchanged:

  ```python theme={null}
  from cognee.api.v1.visualize.visualize import visualize_graph

  # Default: bounded subgraph around the highest-degree nodes
  await visualize_graph("./graph.html")

  # Seed the subgraph from a query
  await visualize_graph("./graph.html", query="natural language processing")

  # Show the subgraph behind a recall answer
  result = await cognee.recall("What does Alice know?")
  await visualize_graph("./graph.html", recall_result=result)

  # Legacy whole-graph render
  await visualize_graph("./graph.html", full=True)
  ```

  | Parameter                 | Default | Description                                                              |
  | ------------------------- | ------- | ------------------------------------------------------------------------ |
  | `full`                    | `False` | Render the entire graph (legacy behavior).                               |
  | `query`                   | `None`  | Query string; its nearest vector hits seed the subgraph.                 |
  | `seed_node_ids`           | `None`  | Explicit seed node ids for neighborhood expansion.                       |
  | `recall_result`           | `None`  | A recall/search result whose `used_graph_element_ids` seed the subgraph. |
  | `neighborhood_depth`      | `2`     | *k*-hop expansion depth around the seeds (must be ≥ 1).                  |
  | `neighborhood_seed_top_k` | `10`    | Maximum number of seed nodes (must be ≥ 1).                              |
  | `max_nodes`               | `500`   | Hard cap on rendered nodes after expansion (must be ≥ 1).                |

  When the neighborhood exceeds `max_nodes`, nodes are kept by hop distance from the seeds (seeds first) and edges survive only when both endpoints do, so no dangling edges remain.

  **Over HTTP.** `GET /api/v1/visualize` exposes the same controls as query params: `full`, `query`, `seed_node_ids`, `neighborhood_depth`, `neighborhood_seed_top_k`, and `max_nodes` (`recall_result` is Python-only). For example, `GET /api/v1/visualize?dataset_id=<id>&full=true` returns the whole-graph render.
</Accordion>

<Accordion title="Troubleshooting: empty graph after cognify">
  If `visualize_graph()` logs `No nodes found in the database` (or the HTML opens empty) even though `add()` and `cognify()` ran without errors, the most common causes are:

  * **Graph path mismatch.** With the default Ladybug backend, the graph is stored on disk under `<SYSTEM_ROOT_DIRECTORY>/databases/`. By default, `SYSTEM_ROOT_DIRECTORY` is an absolute `.cognee_system` path under Cognee's package root. In notebooks like Colab, it is safer to set explicit absolute paths before running `add()`, `cognify()`, and `visualize_graph()` so every step uses the same persisted location across cells and runtime changes:

    ```python theme={null}
    import cognee

    cognee.config.system_root_directory("/content/cognee_system")
    cognee.config.data_root_directory("/content/cognee_data")
    ```

  * **`cognify()` produced no nodes.** A run can finish "successfully" yet extract nothing — for example if no data was actually ingested, or graph extraction silently returned empty results (often a misconfigured or failing LLM/embedding provider). Don't rely on the absence of an error; verify the graph was populated.

  * **Data was pruned in between.** Calling `cognee.forget(everything=True)` (or `cognee.prune`) after `cognify()` clears the graph, so a later `visualize_graph()` sees nothing.

  ### Verify the graph was populated

  Before visualizing, query the graph engine directly. `get_graph_data()` returns a `(nodes, edges)` tuple, and `get_graph_metrics()` reports the node/edge counts:

  ```python theme={null}
  from cognee.infrastructure.databases.graph import get_graph_engine

  graph_engine = await get_graph_engine()

  nodes, edges = await graph_engine.get_graph_data()
  print(f"nodes={len(nodes)}, edges={len(edges)}")

  metrics = await graph_engine.get_graph_metrics()
  print(metrics)  # {'num_nodes': ..., 'num_edges': ..., ...}
  ```

  If `len(nodes)` is `0` here, the problem is upstream in `add()`/`cognify()` (or a path mismatch), not in visualization. A non-zero count from the same process that then reports `No nodes found` points to a path/config mismatch between steps.
</Accordion>

## Full Examples

Additional examples about Cognee are available on our [github](https://github.com/topoteretes/cognee/tree/main/examples/guides).

<Accordion title="Basic visualization guide">
  ```python theme={null}
  import asyncio
  import cognee
  import os
  from cognee.api.v1.visualize.visualize import visualize_graph


  async def main():
      # Prune data and system metadata before running, only if we want "fresh" state.
      await cognee.forget(everything=True)

      await cognee.remember(
          ["Alice knows Bob.", "NLP is a subfield of CS."],
          self_improvement=False,
      )

      visualize_graph_path = os.path.join(
          os.path.dirname(__file__), ".artifacts", "graph_after_remember.html"
      )
      await visualize_graph(visualize_graph_path)


  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Accordion title="Semantic memory map">
  ```python theme={null}
  import asyncio
  import os

  import cognee
  from cognee.api.v1.visualize.visualize import visualize_graph

  DEST = os.path.join(os.path.expanduser("~"), "semantic_memory_map.html")

  # A few short, deliberately multi-topic passages so distinct clusters emerge:
  # computing pioneers, jazz, and ocean science.
  TEXT = """
  Ada Lovelace worked with Charles Babbage on the Analytical Engine in London.
  Alan Turing formalized computation and broke ciphers at Bletchley Park.
  Grace Hopper built the first compiler and worked on the Harvard Mark I.

  Miles Davis recorded Kind of Blue, a landmark modal jazz album, in New York.
  John Coltrane played saxophone with the Miles Davis Quintet before A Love Supreme.
  Bill Evans, the pianist on Kind of Blue, shaped its impressionistic harmony.

  Marine biologists study coral reefs, which host a quarter of all ocean species.
  Rising sea temperatures cause coral bleaching, threatening reef ecosystems.
  Phytoplankton in the ocean produce a large share of the planet's oxygen.
  """


  async def main():
      await cognee.prune.prune_data()
      await cognee.prune.prune_system(metadata=True)

      await cognee.add(TEXT)
      await cognee.cognify()

      html = await visualize_graph(destination_file_path=DEST)

      has_semantic = 'data-view="semantic"' in html
      has_positions = "window._semanticPositions = null" not in html
      print(f"\nSaved: {DEST}")
      print(f"Semantic tab present:   {has_semantic}")
      print(f"Semantic positions set: {has_positions}")
      print("\nOpen the file and click the Semantic tab (or append #semantic to the URL).")


  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Accordion title="Legacy guide">
  ```python theme={null}
  import asyncio
  import cognee
  import os
  from cognee.api.v1.visualize.visualize import visualize_graph


  async def main():
      await cognee.add(["Alice knows Bob.", "NLP is a subfield of CS."])
      await cognee.cognify()

      visualize_graph_path = os.path.join(
          os.path.dirname(__file__), ".artifacts", "graph_visualization.html"
      )
      await visualize_graph(visualize_graph_path)


  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Note>
  This simple example uses basic text data for demonstration. In practice, you can visualize complex knowledge graphs with thousands of nodes and relationships.
</Note>

<Columns cols={3}>
  <Card title="Core Concepts" icon="brain" href="/core-concepts/overview">
    Understand knowledge graph fundamentals
  </Card>

  <Card title="Custom Data Models" icon="circle-stop" href="/guides/custom-data-models">
    Learn about custom data models
  </Card>
</Columns>
