Skip to main content

What is an ontology in Cognee?

An ontology is an optional RDF/OWL file you can provide to Cognee. It acts as a reference vocabulary, making sure that entity types (“classes”) and entity mentions (“individuals”) extracted from your data are linked to canonical, well-defined concepts.

How it works

  • You pass ontology_file_path="my_ontology.owl" when running Cognify.
  • Cognee parses the file with RDFLib and loads its classes and relationships.
  • During graph extraction, entities and types are checked against the ontology:
    • If a match is found, the node is marked ontology_valid=True.
    • Parent classes and object-property links from the ontology are attached as extra edges.
  • If no ontology is provided, extraction still works, just without validation or enrichment.

Why use an ontology

  • Consistency: standardize how entities and types are represented
  • Enrichment: bring in inherited relationships from a domain schema
  • Control: align Cognee’s graph with existing enterprise or scientific vocabularies

Where to get ontologies

Cognee works best with manually curated, focused ontologies that fit your dataset. Ontology design itself is outside the scope of Cognee, so if you need to create or model an ontology from scratch, use dedicated ontology tools and references first, then bring the resulting RDF/OWL file into Cognee. Public resources like Wikidata or DBpedia define millions of classes and entities, which makes them too big to use directly in Cognee. If you start from a public ontology, always work with a subset, not the full ontology:
  • Select only the pieces you need (specific classes, properties, or individuals)
  • Save the subset in a format Cognee can parse with rdflib
  • If needed, enrich the subset manually by adding extra classes or relationships relevant to your domain
  • Keep it small and relevant so matching stays precise and performance remains fast
  • General vocabularies: schema.org, Dublin Core Terms (DC/Terms), SKOS, PROV-O, FOAF
  • Knowledge graph backbones: DBpedia Ontology, Wikidata (Wikibase RDF ontology)
  • Domain examples:
    • Healthcare: SNOMED CT (licensed), ICD, UMLS, MeSH, HL7/FHIR RDF
    • Finance: FIBO (Financial Industry Business Ontology)
    • Geo/IoT: GeoSPARQL, SOSA/SSN, GeoNames
    • Units: QUDT
Every public ontology is too broad to ingest wholesale. Creating a subset is what makes them usable in Cognee:
  • Improves matching precision (fewer false matches when mapping LLM output)
  • Keeps performance acceptable (smaller graphs → faster resolution)
  • Lets you curate only the relevant parts of a domain
Different communities provide different ways to extract subsets (e.g., “slims” in OBO ontologies, WDumper for Wikidata, module extraction in Protégé). The details vary, but the general principle is the same:
  1. Pick the terms (classes or properties) you care about
  2. Extract those terms plus their immediate context (e.g. parent classes, related properties)
  3. Save the result in an rdflib-readable RDF format

Supported formats

Any format RDFLib can parse:
  • RDF/XML (.owl, .rdf)
  • Turtle (.ttl)
  • N-Triples, JSON-LD, and others

RDF read/write surface

Beyond consuming an ontology as extraction scaffolding, Cognee can preserve external IRIs end-to-end and treat the memory graph as RDF. This is aimed at teams that maintain knowledge natively as RDF and want Cognee as a complementary agentic-memory layer over their RDF knowledge base.
The RDF surface relies on rdflib, which ships with Cognee’s ontology support. Everything here is backward compatible: ontology_uri defaults to None and existing text-extraction ingestion is unchanged — nothing is required for existing workflows.

URI preservation

When an extracted entity or type matches your ontology, Cognee now keeps the matched IRI on the persisted node in DataPoint.ontology_uri instead of flattening it to a local label. Grounded Entity/EntityType nodes carry their stable external IRI; ungrounded nodes keep ontology_uri = None. The field never affects node identity.

Exporting the memory graph to RDF

The cognee.modules.graph.rdf module builds an RDF view over the live memory graph so you can serialize it or query it with SPARQL, decoupled from the underlying graph engine’s query language.
from cognee.modules.graph.rdf import (
    serialize_memory_graph,
    query_memory_graph_sparql,
    export_memory_graph_to_rdf,   # returns an rdflib.Graph
    graph_data_to_rdf,            # pure builder over (nodes, edges) tuples
)

# Serialize the whole memory graph to RDF (Turtle by default).
turtle = await serialize_memory_graph(rdf_format="turtle")

# Or run SPARQL directly over an RDF view of the graph.
rows = await query_memory_graph_sparql(
    "SELECT ?s WHERE { ?s a <http://example.org/mm#CNCMachine> }"
)
How nodes and edges map to RDF:
  • Grounded nodes are emitted under their preserved ontology_uri. Ungrounded nodes get a minted IRI under DEFAULT_BASE_IRI (https://cognee.ai/graph/…) so the RDF stays well-formed and nothing is dropped.
  • A node’s name becomes an rdfs:label.
  • The is_a relationship resolves to rdf:type for an individual→class link and to rdfs:subClassOf for a class→class link. Other relationships become predicate IRIs — a minted …/prop/<name> IRI, or, for RDF-ingested edges that carry a predicate_uri, that original RDF predicate IRI.
export_memory_graph_to_rdf / serialize_memory_graph / query_memory_graph_sparql materialize the whole graph into an in-memory rdflib store on each call. This is convenient for querying and export but costs memory proportional to graph size — keep that in mind for very large graphs.

Ingesting RDF into datapoints

The cognee.modules.ontology.rdf_xml.rdf_ingest module ingests an RDF T-Box + A-Box directly into Cognee datapoints, keeping the external IRIs verbatim rather than canonicalizing entities into a local vocabulary.
from cognee.modules.ontology.rdf_xml.rdf_ingest import ingest_rdf

# Accepts a file path, list of paths, file-like object, or a parsed rdflib.Graph.
data_points = await ingest_rdf("knowledge_base.ttl")
  • OWL classes become EntityType nodes; individuals typed by a known class become Entity nodes. rdf:type and rdfs:subClassOf are kept as is_a relationships.
  • Node identity is derived from the IRI (not the label), so distinct IRIs stay distinct and re-ingesting the same RDF is idempotent — this is an open-world model with no fuzzy canonicalization.
  • Object-property assertions between two ingested individuals become explicit graph edges that preserve the original RDF predicate IRI as predicate_uri, so they round-trip back out on export.
  • Parsing reuses the ontology resolver, so any RDF syntax RDFLib understands (RDF/XML, Turtle, N-Triples, JSON-LD, …) is supported.
Lower-level helpers are available when you need them: load_rdf_graph(source) parses a source into an rdflib.Graph, and build_datapoints_from_rdf(graph) / build_graph_from_rdf(graph) turn a parsed graph into datapoints (and custom edges) without persisting.
Scope / limits. RDF ingestion preserves object-property assertions only when both endpoints are ingested individuals. Blank nodes, arbitrary literal/data-property round-trip, and RDF reasoning/entailment (e.g. OWL RL) are out of scope — no inference is applied to the ingested graph.

Additional details and examples

Once you have your subset file, integrating it into Cognee is simple:
import cognee

await cognee.cognify(
    datasets=["my_dataset"],
    ontology_file_path="subset.owl",  # your curated subset here
)
Cognee does not ship with or apply a built-in default ontology. When you don’t pass ontology_file_path (or an ontology resolver), Cognee runs with an empty resolver, so there is no reference vocabulary to validate or enrich against.In that mode, node labels and relationship names are produced directly by the LLM during graph extraction. There is no fixed, predefined list of relationship types — the model infers them from your text. Consistency is guided by the extraction prompt rather than enforced, so the same concept may occasionally surface under slightly different labels across chunks. The prompt asks the model to:
  • Use basic node types (e.g. label a person as Person, not Mathematician or Scientist — those become properties).
  • Use snake_case relationship names (e.g. acted_in).
  • Apply coreference resolution so an entity referred to by different names or pronouns maps to a single, consistent node.
Typical relationship names the model produces are plain, real-world verbs and roles derived from the text, for example:
  • People: married_to, parent_of, friend_of, works_at, colleague_of
  • Ownership and roles: owns, owned_by, member_of, founder_of, employed_by
  • Things and places: produces, located_in, part_of, created_by, acted_in
Provide an ontology when you need these labels to be standardized and validated against a fixed vocabulary instead of inferred per document.
Cognee can load several OWL files and merge them into one in-memory graph, which is useful when you split a large ontology into focused modules.Environment variable — comma-separated paths:
ONTOLOGY_FILE_PATH=/path/to/domain.owl,/path/to/entities.owl
Python API — list of paths:
from cognee.modules.ontology.rdf_xml.RDFLibOntologyResolver import RDFLibOntologyResolver
from cognee.modules.ontology.ontology_config import Config

resolver = RDFLibOntologyResolver(
    ontology_file=["/path/to/domain.owl", "/path/to/entities.owl"]
)

config: Config = {"ontology_config": {"ontology_resolver": resolver}}
await cognee.cognify(config=config)
Files that cannot be found or parsed are skipped with a warning; at least one valid file is required for ontology grounding to take effect.How the merge works.RDFLibOntologyResolver parses each file with RDFLib and adds all triples to the same in-memory rdflib.Graph. The result is a single unified graph — Cognee does not create disjoint subgraphs, even when the ontologies share no classes or properties. There is no explicit conflict-resolution step: RDFLib performs an additive merge of triples, and any classes or properties that happen to share IRIs across files naturally coexist in the same graph.One ontology per cognify() run.The ontology resolver is configured at the cognify() call level (via ONTOLOGY_FILE_PATH or the ontology_config in the Config payload), not per dataset or per document. If you need different vocabularies for different data, run cognify() separately for each dataset with its own resolver instance.
Cognee does not provide ontology-authoring features. If you need to create, edit, or validate an ontology, use dedicated RDF/OWL tooling such as Protégé or your team’s existing ontology workflow, then load the resulting file into Cognee.When preparing a file for Cognee:
  • Keep the ontology focused on the classes, properties, and individuals relevant to your dataset
  • Prefer a curated subset over a large general-purpose ontology
  • Save it in a format RDFLib can parse, such as RDF/XML (.owl, .rdf) or Turtle (.ttl)
For more detailed examples of working with ontologies in Cognee, check out the demo scripts in the repository: