DataPoint model gives you.
Before You Start
- Complete Quickstart to understand basic operations
- Ensure you have LLM Providers configured — the cognify step extracts the graph with an LLM
- Read Custom Graph Model for the Python-class equivalent of what the JSON compiles to
- Read DataPoints for how graph nodes are identified and indexed
Code in Action
What Just Happened
Step 1: Declare the Schema as JSON
entities in the graph and names one of them as the root — the entity the generated top-level model is built from (the first entity, when root is omitted). Each field is either a primitive value, an enum, or a relation pointing at another declared entity, and a relation’s cardinality decides whether it holds one target or a list. collaborates_with targets Person itself, so an entity may relate to its own type. Every description is compiled into the schema and onto the generated model’s fields, but cognee rebuilds that model into a plain extraction schema before the LLM call and drops them along the way — a description documents the spec, it does not steer the LLM. Use custom_prompt for that.
Step 2: Compile the Spec into a Model
graph_model_from_spec validates the spec, compiles it to the JSON Schema cognee’s model generator accepts, and returns a DataPoint-derived Pydantic class. An invalid spec — an unknown relation target, a duplicate entity name, a field colliding with a DataPoint infrastructure field — raises ValidationError here, before any LLM call is made. graph_spec_to_json_schema, also exported from cognee.low_level, stops one step earlier and returns that JSON Schema, which is useful for inspecting what the spec compiled into.
Step 3: Extract with the Generated Model
graph_model argument: pass it to cognify() (or remember()) and it becomes the structured-output schema the LLM must fill, so the graph can only contain the entities and relations the JSON declared.
Step 4: Search the Resulting Graph
Advanced Usage
Node identity and indexing
Node identity and indexing
Each entity compiles with a Both lists may only reference
metadata default of index_fields and identity_fields, both defaulting to ["name"]. identity_fields is what makes nodes extracted from different chunks and different runs merge into one graph node when their identity values match, so Ada Lovelace mentioned twice stays a single node. Set "identity_fields": [] on an entity to opt out and give every extracted node a random id.name or a declared primitive/enum field — never a relation, and so may primary_label_field, a third entity-level key the UI editor writes. primary_label_field is validated for that parity but never compiled into the generated model.identity_fields is a Python-side extension of the DSL. The cognee UI graph-model editor produces the same JSON shape but never emits it, so models built in the frontend do not merge nodes.Field kinds
Field kinds
primitive— a scalar value, withprimitive_typeone of"string"(the default),"number","boolean", or"date"(an ISO date string).enum— a string restricted to the non-emptyenum_valueslist.relation— an edge named after the field, pointing atrelation.target_entity_namewithrelation.cardinalityof"one"or"many".
primitive and enum fields accept "required": true to force the LLM to supply a value; a relation accepts required too, for frontend parity, but it is never compiled. Field names are snake_case with camelCase aliases accepted (primitiveType, targetEntityName), so one document works for both the UI editor and Python.Validation limits
Validation limits
Validation is also the safety gate — the generated model is built by executing generated code, so names are restricted and size is capped:
- Entity and field names must be plain identifiers: letters, digits, and underscores, starting with a letter.
- At most 50 entities per spec, and 40 fields per entity.
- Entity names must be unique, and may not collide with the
{Name}Typemarker the compiler generates for another entity. - Field names must be unique within their entity.
root, when given, must name a declared entity, and every relation must target one.- A declared
namefield must be a string primitive — it is the node’s primary identifier. - Unknown keys are rejected rather than ignored, so a typo in a key name surfaces immediately.
What a custom graph model skips
What a custom graph model skips
Extraction with any model other than the default
KnowledgeGraph — whether written in Python or compiled from JSON — bypasses ontology grounding and the extra node/edge dedup passes cognee runs on the default path, and functional_relationships is not supported here: nothing raises if you pass it, but that pass is built for the default path’s entity nodes and will not reliably act on a custom model’s graph. Stay on the default path when you need those, and use a custom model when you need a predictable, domain-specific shape instead.Custom Graph Model
Write the same schema as Python DataPoint classes
DataPoints
How graph nodes are identified, indexed, and merged
Custom Prompts
Tell the LLM what to look for inside the shape you declared