Before You Start
- This is a low-level infrastructure exercise, not a replacement for
cognee.remember()or Cognee pipelines — direct graph writes like this skip vector indexing, relational records, dataset provenance, and normal access-control behavior. - This guide defines a custom
Person(DataPoint)model — see DataPoints for the concept, or Custom Data Models and Custom Graph Model to go deeper.
Code in Action
What Just Happened
Step 1: Configure an Isolated Graph and Get the Engine
graph_file_path points Ladybug at a throwaway directory so this exercise never touches your default Cognee graph. graph_database_subprocess_enabled=False keeps Ladybug running inside your Python process instead of as a separate background process — simpler for a short script like this. get_graph_engine() is async and returns whatever adapter the configuration selected — here, a Ladybug adapter — typed as GraphDBInterface. Everything from this point on is written against that interface, not against Ladybug specifically.
Step 2: Define a Minimal Node Model
Person inherits from DataPoint, Cognee’s base model for graph nodes. Declaring "identity_fields": ["name"] gives each Person a deterministic id based on its name, so Person(name="Alice") always resolves to the same node id instead of a random one.
Step 3: Create Nodes and an Edge
add_nodes(nodes) takes a list of DataPoint instances and writes them in a single batch. add_edge(source_id, target_id, relationship_name, edge_properties) creates one directed edge — here, Alice -[knows]-> Bob with a small property dictionary. Both take plain string ids, so DataPoint.id (a UUID) is converted with str(...) before being passed in.
Step 4: Read Data Back
get_node(node_id) returns a single node’s properties as a dictionary, or None if it does not exist. get_neighbors(node_id) returns the properties of every node connected to the given node — here, Bob’s only neighbor is Alice. has_edge(source_id, target_id, relationship_name) checks whether a specific directed, labeled edge exists, returning a plain bool.
Step 5: Clean Up
delete_nodes(node_ids) removes the listed nodes and any edges attached to them. Running it in a finally block ensures the example nodes are removed even if an earlier step raises, leaving the isolated graph empty again.
One Interface, Many Databases
Cognee can store its graph in several different backends — Ladybug (the default local, file-based engine), Postgres, Neo4j, and others (see Graph Stores for how to configure each one). Application code that queries or writes to the graph should not need a different code path for each one. Cognee solves this with three pieces:- Configuration picks which provider is active (e.g.
"ladybug"or"postgres"). get_graph_engine()is a factory function. It reads the configuration and returns an object implementing the interface — you never construct an adapter yourself.GraphDBInterfaceis an abstract base class that declares the methods every adapter must provide (add_node,get_node,has_edge, and so on).- Concrete adapters (
LadybugAdapter,PostgresAdapter, …) implement that interface against a specific database.
get_graph_engine() works unchanged no matter which provider is configured.
Comparing Adapters
LadybugAdapter and PostgresAdapter both implement add_nodes, add_edge, get_node, get_neighbors, and has_edge from GraphDBInterface — but the two implementations look nothing alike internally. Ladybug builds parameterized Cypher-style statements against an embedded Kuzu database; Postgres issues SQL against relational tables that model nodes and edges. Neither difference is visible to code written against the interface, which is the point: swapping graph_database_provider from "ladybug" to "postgres" (with matching connection settings) does not require changing any of the code above.
DataPoints
Learn more about DataPoint, the base model Person builds on
BaseRetriever Guide
See the same abstract-contract pattern applied to retrievers