What You’ll Build
Four small files — two technical documents (an API reference and an architecture guide) and two sales-call transcripts (where a rep promises “unlimited requests” and “instant sync”) — are ingested three ways: everything in one dataset, one dataset with node-set tags, and two walled-off datasets with node sets inside each. The same questions run under every layout, and each recall prints as a comparableQUERY / SCOPE / ANSWER block, so you can see exactly what each layout changes: the unfiltered pile blends promise with spec, node sets scope a query to one group while still answering cross-domain questions, and datasets make leakage impossible. The run ends with the rule of thumb the demo exists to teach: node sets are tags, datasets are walls.
The complete runnable demo — the script plus the four data files it reads — is
examples/demos/organizing_your_data/ —
this page walks through its key moments rather than reproducing it.
The three sections below cover one layout each: the calls that build it, the output those calls produce in a real run, and what the layout is good and bad at. Output excerpts are verbatim from one run, trimmed with .... Every ANSWER is a live LLM call, so the wording varies from run to run; each ANSWER line is prefixed with the dataset it came from, so the same question can be compared across layouts by eye.
Features in Play
- NodeSets — tags each source with soft, overlappable groups at write time;
node_namescopes retrieval to a tag at read time - Datasets — the hard boundary with its own permissions and storage that keeps sales transcripts out of technical answers entirely
- Remember — ingests the four bundled files under each layout’s labels
- Recall — runs the same questions under every layout, scoped by
datasetsandnode_name
Everything in One Dataset
The baseline, and the layout that produces the complaint the page opens with. All four files land in the default dataset with no labels, and one recall runs with no filter.NOTE line names the competition to watch for.
- The simplest layout: one
remember()call, no labeling decisions, nothing to keep consistent later. - Every fact can link to every other, so any question can draw on the whole corpus.
- There is no way to scope a query. Every recall sees everything, and sources that disagree compete in one retrieval pool.
- Whether the answer favors the spec or the promise is left to the LLM, so a correct answer in one run is not a guarantee for the next.
- One set of permissions for the whole pile: you cannot let one audience read the transcripts and another only the docs.
- A corpus that covers one domain for one audience, where every question should see everything.
- Prototypes and small personal memories, before you know which groupings matter.
One Dataset with Node Sets
The same dataset and the same graph, but every item now carries node-set tags. Tags are soft and overlappable: everything is also taggedacme_api, so a shared grouping cuts across the docs/calls split — something no folder-style hierarchy could express.
node_name restricts retrieval to the tagged subgraph, so the same question now answers from the documentation alone. The sales calls are still in the graph — they just don’t participate in this query.
tech_docs, the answer is the documented limit. Scoped to sales_calls with the same call shape, it is what the rep said, with no mention of the spec.
- Scoped and cross-domain questions both work:
node_namenarrows a recall to one group, and leaving it out lets the graph connect the groups. - Tags overlap. One item can belong to several groups at once, like
tech_docsandacme_apihere. - Cheap to adopt: the same
remember()call with anode_setargument, no new datasets or permissions to manage.
- The boundary is soft. An unscoped recall still sees everything, so keeping the promise out of the spec is the caller’s job on every query.
- Tags are assigned at write time, so a new grouping means re-ingesting the affected items.
- No separate permissions, storage, or forget per tag — everything still lives in one dataset.
- Mixed content that shares entities you still want linked, like documentation and call transcripts about the same product.
- Memory that has to answer both narrow questions (“what does the spec say?”) and cross-cutting ones (“where do the two disagree?”).
- Slicing one team’s memory by topic, source, or account without splitting it up.
Separate Datasets
The third layout gives each domain its own dataset. Graphs are built independently, permissions and storage are separate, and either side can be forgotten without touching the other. Node sets still work inside each dataset — here the sales dataset tags each call by account.datasets picks the wall, node_name picks the tag inside it, and the answer covers one account’s calls only.
[tech_docs] prefix on the first answer shows it came from that dataset alone — the sales calls cannot leak in. The combined datasets plus node_name filter narrows the second answer to the Initech call.
recall() without datasets spans every dataset you can read, so separating data at write time is not enough — queries must opt into the scope they want.
datasets returns one ANSWER per readable dataset, [tech_docs] and [sales_calls], so the rep’s promise is back in the result. The run then closes with the TAKEAWAY banner.
- Hard isolation: a recall scoped to one dataset can never surface content from another.
- Each dataset has its own permissions and storage, and can be forgotten independently.
- Node sets still slice inside a dataset, so you lose nothing from the previous layout within each wall.
- Graphs are built independently, so there is no cross-dataset reasoning. An unscoped recall returns one answer per dataset rather than one answer that connects them.
- The wall only holds when the query names it:
recall()withoutdatasetsspans every dataset you can read. - Dataset scoping walls datasets off only in multi-user mode, which the default Ladybug and LanceDB stack turns on automatically — keep
ENABLE_BACKEND_ACCESS_CONTROLat its default or set it totrue.
- Domains that must never contaminate each other’s answers, like the spec and the sales promises here.
- Content with different access control or retention, where one audience or one lifecycle applies per dataset.
- Multi-tenant memory, with one dataset per customer or team.
Run It
data/ folder, and every answer is a live LLM call. Ladybug (the default) and Neo4j both support node-set filtering out of the box. The script calls cognee.forget(everything=True) before each of its three sections, so point it at a scratch instance rather than memory you want to keep — see Forget.
Choosing a Layout
Start with the simplest layout that answers your queries cleanly. One dataset with no labels is fine while the corpus covers a single domain for a single audience. Reach for node sets the moment distinct content types share entities you still want linked — scoped questions stay clean, and cross-domain questions keep working. Move a domain into its own dataset when it must never contaminate the other’s answers, or when it needs different access control or retention — and remember the two compose, so datasets can carry node sets inside them.NodeSets
How node-set labels are written and how
node_name filters retrieval by them.Datasets
What a dataset isolates — documents, permissions, and processing.
NodeSet Grouping
The minimal single-feature guide to tagging memories with node sets.
Agentic Procurement Decisions
A sibling demo where node-set scoping powers an agent’s research phase.