Cognee Custom Graph Models logo

Cognee Custom Graph Models

OrganizationPopular
topoteretes
cognee-custom-graph-models

Use when defining the shape of cognee's knowledge graph with graph_model= — writing DataPoint node classes, choosing identity and index fields so nodes merge and are searchable, declaring typed Edge fields and FromIdentity references, building a model from a JSON schema, or debugging duplicated nodes, missing edges, or InvalidReferenceTypeError.

Overview

Publishertopoteretes
Repositorycognee
Skill namecognee-custom-graph-models
Stars
32K
Forks
3.3K
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by topoteretes on GitHub. Read the source before you install it.

Installation

Install the Cognee Custom Graph Models AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/topoteretes/cognee/tree/main/.agents/skills/cognee-custom-graph-models
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/topoteretes/cognee.git /tmp/cognee
mkdir -p .claude/skills
cp -r /tmp/cognee/.agents/skills/cognee-custom-graph-models .claude/skills/cognee-custom-graph-models
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Cognee Custom Graph Models in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Cognee Custom Graph Models on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Cognee Custom Graph Models is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Custom graph models

By default cognee extracts a generic KnowledgeGraph of entities and relationships. Pass your own model with graph_model= and the LLM fills your node and edge types instead.

python
from typing import Annotated, Literal
import cognee
from cognee.low_level import DataPoint, Edge, FromIdentity


class Role(DataPoint):
    name: str
    metadata: dict = {"index_fields": ["name"], "identity_fields": ["name"]}


class Person(DataPoint):
    name: str
    is_a: Annotated[Role, FromIdentity()] | None = None  # reference by name
    reports_to: list[Edge["Person", "Person"]] = []  # edge owned by Person
    metadata: dict = {"index_fields": ["name"], "identity_fields": ["name"]}


class PeopleGraph(DataPoint):  # the root the LLM fills
    people: list[Person]
    friends_with: list[Edge[Person, Person]] = []
    family: list[Edge[Person, Person, Literal["married_to", "sibling_of"]]] = []
    metadata: dict = {"index_fields": [], "transparent": True}


await cognee.remember(text, graph_model=PeopleGraph, custom_prompt="Extract every person...")

Full example: examples/guides/custom_graph_model.py.

Use it

Nodes: DataPoint classes

Every node type subclasses DataPoint (from cognee.low_level import DataPoint). Its fields become:

  • Node properties: scalars, strings, dicts, and anything that is not a DataPoint.
  • Edges named after the field: a field holding a DataPoint or a list of DataPoints. members: list[Person] becomes members edges.

dict[str, Person], sets and plain tuples are stored as properties, not edges.

Identity and search: metadata

KeyWhat it does
identity_fieldsThe node id is derived from these field values (normalized: lowercased, spaces to _, apostrophes removed). The same entity from two chunks or two runs becomes one node.
index_fieldsEach field gets a vector collection named <ClassName>_<field>, so recall can find the node.
transparentThe node is not stored; its children take its place. Use it for a root container like PeopleGraph.

Without identity_fields every node gets a random id, so the same person is duplicated in every chunk and every run. Set it on every node type that represents a real-world entity.

Write metadata explicitly, as in the examples above. There is also an annotation shortcut (from cognee.infrastructure.engine import Dedup, Embeddable; name: Annotated[str, Embeddable(), Dedup()]), but today only half of it works:

  • Dedup() works: ids are derived from the marked fields.
  • Embeddable() does not index. The markers update the class-level default, but each instance still carries {"index_fields": []}, and indexing reads the instance, so no vector collection is created and recall cannot find the node.

Markers are also ignored entirely when the class declares metadata itself.

Typed edges: list[Edge[Source, Target, Name]]

The LLM answers edges as flat rows of identity strings (source, target), and cognee resolves them to the extracted nodes. The third parameter controls the relationship name:

DeclarationRelationship name
list[Edge[Person, Person]]The field name (friends_with)
list[Edge[Person, Person, Literal["a", "b"]]]The LLM picks one value
list[Edge[Person, Person, str]]Free-form from the LLM, normalized
  • Where to declare: on the root model for relationships with no obvious owner, or on the owning node. On the owner, endpoints of the owner's own type must be strings (Edge["Person", "Person"]), because the class is not defined yet inside its own body.
  • Always a list: Edge[...] or Edge[...] | None on its own raises.
  • Both endpoint types need exactly one identity_fields entry.

References: Annotated[Target, FromIdentity()]

Instead of a nested object, the LLM answers the identity string of a node (is_a: "engineer"), and cognee links to that node. Supported spellings: Target, Target | None, list[Target], list[Target] | None. Anything else raises InvalidReferenceTypeError. The target needs exactly one identity field, and its other required fields need defaults.

Edge values you build by hand

Edge(source=..., target=..., relationship_type=..., weight=..., properties={...}). An omitted source falls back to the node declaring the field; on a parametrized field that node must be the declared Source type, or it raises. On a root container, always pass source=. The tuple form (Edge(weight=0.8), target_node) attaches edge properties to a plain DataPoint field (examples/guides/custom_data_models.py).

From JSON instead of Python

  • cognee.low_level.graph_model_from_spec(spec): a small entity/relation spec (names, fields, one/many relations) compiled to DataPoint classes, with identity and index on name by default. Example: examples/guides/graph_model_from_json.py.
  • cognee.low_level.graph_schema_to_graph_model(json_schema): a JSON Schema (needs a top-level title; only internal # refs).
  • HTTP: POST /api/v1/remember takes a graph_model form field (JSON schema string); POST /api/v1/cognify takes a graphModel JSON object. POST /api/v1/llm/infer-schema proposes a schema from sample text.

Neither JSON path can express typed Edge fields or FromIdentity; use Python classes for those.

Pitfalls

  • Duplicated nodes → missing identity_fields.
  • Node never shows up in recall → no index_fields, or the recalling process never imported the model class (graph completion searches the collections of DataPoint classes loaded in that process).
  • Edge rows silently missing → an unresolved row is dropped with a warning. Endpoints resolve by exact type against nodes extracted from the same chunk: a subclass instance does not match where its base is declared, and a node from another chunk or an earlier run is not a candidate.
  • InvalidReferenceTypeError, "declares Edge in a shape the LLM extraction cannot fill" → an unsupported FromIdentity or Edge spelling. These are raised when the model is converted during extraction, not at class definition, so they appear mid-pipeline.
  • String endpoints resolve only to the owning model itself or a module-level class. A string naming another class defined inside a function raises InvalidReferenceTypeError, so define models at module level.
  • Field names that collide with DataPoint's own fields (id, type, version, metadata, created_at, belongs_to_set, …) are stripped from what the LLM sees. Rename them.
  • A subclass that overrides metadata replaces the parent's entirely. Dropping identity_fields only logs a warning. A subclass also hashes ids under its own class name.
  • Write a custom_prompt. Without one the generic knowledge-graph prompt is used; your schema reaches the LLM only as structured output.
  • Not with GLiNER. extractor="gliner_demo" (alias "gliner") raises with a custom graph_model, and so does the default GRAPH_EXTRACTOR=auto when no LLM key is configured (it resolves to gliner_demo).
  • Remote mode drops it. After cognee.serve(url), remember() and cognify() do not forward graph_model; the server builds a generic graph.
  • A custom model skips the generic path's ontology resolution, per-graph node dedup, and functional_relationships. Summaries still run.

How it works

extract_content_graph converts a DataPoint model into a plain Pydantic model for the LLM: infrastructure fields and metadata are stripped, typed edge fields become row lists (FriendsWithEdge with source/target strings), and FromIdentity fields become strings. The answer is converted back into DataPoint instances with ids from identity_fields, edge rows are resolved against the nodes in that answer, and the result is attached to the chunk (chunk.contains) and stored by add_data_points. Ownership is recorded per document, so forget(data_id=...) removes a custom-model document's nodes while shared nodes survive.

  • LLM boundary, both directions: cognee/shared/llm_graph_model.py
  • DataPoint, metadata, ids: cognee/infrastructure/engine/models/DataPoint.py
  • Markers: cognee/infrastructure/engine/models/FieldAnnotations.py
  • Edge: cognee/infrastructure/engine/models/Edge.py
  • Property vs edge decision: cognee/modules/graph/utils/field_edges.py
  • Custom-model branch of extraction: cognee/tasks/graph/extract_graph_from_data.py
  • JSON schema / spec paths: cognee/shared/graph_model_utils.py, cognee/modules/graph_models/
  • Vector collections: cognee/tasks/storage/index_data_points.py

Extending it

  • Tests for the LLM round trip: cognee/tests/unit/modules/graph/test_content_graph_to_data_point.py. Edge typing: cognee/tests/unit/interfaces/graph/test_typed_edge_model.py, test_typed_edges_graph.py. Identity: cognee/tests/unit/infrastructure/engine/test_identity_fields.py. Property vs edge: cognee/tests/unit/modules/graph/test_field_edges.py.
  • Deletion of custom-model nodes: cognee/tests/test_delete_custom_graph.py.
  • A new Edge or FromIdentity spelling must be handled in both directions in llm_graph_model.py, and rejected with InvalidReferenceTypeError when unsupported, never silently accepted.

Frequently asked questions

What does the Cognee Custom Graph Models AI skill do?

Use when defining the shape of cognee's knowledge graph with graph_model= — writing DataPoint node classes, choosing identity and index fields so nodes merge and are searchable, declaring typed Edge fields and FromIdentity references, building a model from a JSON schema, or debugging duplicated nodes, missing edges, or InvalidReferenceTypeError.

Why use Cognee Custom Graph Models on TypingMind?

Because you install it once and use it with any model. Cognee Custom Graph Models is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Cognee Custom Graph Models in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/topoteretes/cognee/tree/main/.agents/skills/cognee-custom-graph-models. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Cognee Custom Graph Models?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Cognee Custom Graph Models?

As many as you like. As long as a model supports skills, you can use Cognee Custom Graph Models with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Cognee Custom Graph Models AI skill free?

Yes. It is published on GitHub by topoteretes under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇