DeepFellow DOCS

Knowledge Graph Ontology

Control which entities and relations Knowledge Graph extracts, and auto-discover a project-specific ontology from sample files.

An ontology is the schema of a project's knowledge graph: the entity types to look for, the relation types that connect them, and which entity types each relation is allowed to connect. Ingestion only extracts entities and relations that fit the project's ontology, so the ontology decides what the graph contains and which questions it answers well.

Every project starts with a generic default ontology. To tailor extraction to your own documents, for example to find Contract, Party, and Clause entities in legal documents, let the server draft a project-specific ontology from a few sample files, review and edit the draft, and approve it.

Ontology Structure

GET /v1/graph/ontology returns, and PUT /v1/graph/ontology/draft accepts, ontologies in GraphRAG-SDK's own Ontology JSON shape:

{
    "entities": [
        {
            "label": "Person",
            "description": "A named individual (author, employee, participant, contact).",
            "properties": [
                { "name": "role", "type": "STRING", "description": "Job title or role." }
            ]
        },
        {
            "label": "Organization",
            "description": "A company, agency, team, or other named group entity.",
            "properties": []
        }
    ],
    "relations": [
        {
            "label": "WORKS_FOR",
            "description": "Person is employed by or affiliated with an Organization.",
            "patterns": [["Person", "Organization"]],
            "properties": [
                { "name": "start_date", "type": "DATE", "description": "When the employment started." }
            ]
        }
    ]
}
FieldDescription
entities[].labelName of the entity type, for example Person. GLiNER receives these labels as the entity types to look for.
entities[].descriptionWhat counts as this entity type. The extraction prompts include it, so a precise description improves extraction.
relations[].labelName of the relation type, for example WORKS_FOR.
relations[].descriptionWhat the relation means.
relations[].patternsAllowed [source label, target label] pairs. The extractor drops relations whose entity types match none of the pairs, and reports them in the ingestion job's warnings. An empty list, as on RELATED_TO, allows any pair.
propertiesTyped attributes the extractor captures on an entity or a relation.
properties[].nameAttribute name in snake_case, such as role or founded_year. See Draft Sanitization.
properties[].typeOne of STRING, INTEGER, FLOAT, BOOLEAN, DATE, or LIST. Defaults to STRING.

Every entity also carries a name attribute that GraphRAG-SDK manages itself, whether or not the ontology declares it. Do not use Document or Chunk as a label: GraphRAG-SDK uses them for its own nodes.

Default Ontology

A project uses the default ontology until it saves or approves its own. The default declares no properties.

Entity TypeDescription
PersonA named individual: author, employee, participant, or contact.
OrganizationA company, agency, team, or other named group.
TechnologyA named technology, platform, framework, or standard.
ProductA named product, service, or deliverable.
LocationA named place: city, country, region, address, or venue.
DateA specific date, time, or date range.
EventA named occurrence: meeting, release, incident, milestone, or transaction.
ConceptAn abstract idea, topic, category, or named process.
LawA named law, regulation, standard, contract clause, or policy.
DatasetA named collection of data.
MethodA named technique, algorithm, or procedure.
Relation TypeAllowed Patterns
WORKS_FORPerson → Organization
LOCATED_INOrganization, Person, Event, Product, or Location → Location
PART_OFOrganization → Organization, Product → Organization, Technology → Product, Concept → Concept, Location → Location
CREATED_BYProduct, Technology, or Dataset → Organization or Person
USESOrganization → Technology or Product, Method → Technology, Product → Technology
PARTICIPATED_INPerson or Organization → Event
OCCURRED_ONEvent → Date
GOVERNED_BYOrganization, Product, Event, or Concept → Law
COLLABORATES_WITHOrganization → Organization, Person → Person
RELATED_TOAny pair. A fallback for relevant associations that fit no other relation.

Ontology Lifecycle

Each project has at most one ontology record, which holds two ontologies:

  • ontology is the current draft. Discovery writes it, and you edit it.
  • approved_ontology is the active ontology, which ingestion, search, and completion use. It stays null, and the project keeps using the default ontology, until you save or approve a draft.

The record's state is one of the following:

StateMeaningNext Steps
discoveringDiscovery is running.Poll GET /v1/graph/ontology, or cancel.
draftDiscovery finished, or you saved an edited draft.Edit, approve, or propose again.
approvedYou approved the draft.Propose again to change the ontology.
failedDiscovery failed. last_error says why.Edit the previous draft, or propose again.
cancelledYou cancelled discovery.Propose again.

The typical flow is:

Upload a few representative files with POST /v1/files.

Start discovery with POST /v1/graph/ontology/propose.

Poll GET /v1/graph/ontology until state is draft.

Review ontology, and edit it with PUT /v1/graph/ontology/draft if needed.

Approve the draft with POST /v1/graph/ontology/approve.

Ingest your files. To apply the new ontology to files you ingested earlier, ingest them again with ?force=true.

Changing the ontology never changes what the graph already contains. Entities and relations extracted under the previous ontology stay in the graph until you ingest their files again with POST /v1/graph/files/{file_id}/ingest?force=true. Removing an entity or relation type from the ontology only stops future extraction of that type.

Discover an Ontology

Pass 1 to 10 file IDs of already uploaded files, and an optional boundaries hint:

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/ontology/propose' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "file_ids": ["FILE_ID_1", "FILE_ID_2"],
  "boundaries": "legal contracts"
}'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/graph/ontology/propose",
    headers={"Authorization": "Bearer DEEPFELLOW_PROJECT_API_KEY"},
    json={
        "file_ids": ["FILE_ID_1", "FILE_ID_2"],
        "boundaries": "legal contracts",
    },
)

print(response.json())
const response = await fetch('https://deepfellow-server-host/v1/graph/ontology/propose', {
    method: 'POST',
    headers: {
        Authorization: 'Bearer DEEPFELLOW_PROJECT_API_KEY',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        file_ids: ['FILE_ID_1', 'FILE_ID_2'],
        boundaries: 'legal contracts'
    })
});

const data = await response.json();
console.log(data);

The endpoint returns 202 Accepted:

{
    "status": "discovering",
    "ontology_id": "68da445c5186deb8bca2bdeb"
}
FieldDescription
file_ids1 to graph.ontology_discovery_max_sample_files file IDs. The limit defaults to, and never exceeds, 10. A longer list returns 422 Unprocessable Entity.
boundariesOptional free-text hint that narrows the scope of the ontology, such as a document type or a domain.

Discovery runs in the background:

  1. The server converts each sample file with Doc Chunker, through the same pipeline ingestion uses, so discovery reads the same text that ingestion will.
  2. The server picks graph.ontology_discovery_sample_chunks_per_doc chunks from each file, spread evenly from its beginning to its end.
  3. graph.ontology_discovery_llm_model proposes entity and relation types from each sampled chunk, running up to graph.ontology_discovery_concurrency calls in parallel. The prompts use the starting ontology as a vocabulary to build on.
  4. GraphRAG-SDK merges the proposals across files, collapses synonyms, and merges the result into the starting ontology.
  5. The server cleans up the result and stores it as the draft.

The starting ontology is the project's approved ontology, or the default ontology if the project has none. The draft therefore always contains every type the project already uses, plus the types discovery added, so proposing again never drops a type you approved earlier. The same files always yield the same sampled chunks. The LLM's answers still vary between runs, so two runs over the same files produce similar but not always identical drafts.

propose overwrites the current draft once discovery finishes, and returns 409 Conflict while discovery is already running for the project. The active ontology stays untouched until you save or approve the new draft.

Choose sample files that cover the kinds of documents the project will hold. Since discovery reads only a few chunks per file, several short, varied files give it more to work with than one long file. To draft ontologies faster, or at a lower cost, set graph.ontology_discovery_llm_model to a different model than graph.llm_model.

Check Discovery Progress

curl -X 'GET' \
  'https://deepfellow-server-host/v1/graph/ontology' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

Response:

{
    "id": "68da445c5186deb8bca2bdeb",
    "project_id": "68da445c5186deb8bca2bdec",
    "state": "draft",
    "ontology": {
        "entities": [
            { "label": "Person", "description": "A named individual (author, employee, participant, contact).", "properties": [] },
            { "label": "Contract", "description": "A legally binding agreement between parties.", "properties": [
                { "name": "effective_date", "type": "DATE", "description": "Date the contract takes effect." }
            ] }
        ],
        "relations": [
            { "label": "PARTY_TO", "description": "An organization or person that signed a contract.", "patterns": [["Organization", "Contract"], ["Person", "Contract"]], "properties": [] }
        ]
    },
    "approved_ontology": null,
    "source_file_ids": ["FILE_ID_1", "FILE_ID_2"],
    "boundaries": "legal contracts",
    "last_error": null,
    "warnings": [],
    "created_at": "2026-08-03T10:00:00Z",
    "updated_at": "2026-08-03T10:05:00Z"
}

The example shortens ontology for readability. A real draft also lists every type of the default ontology.

GET /v1/graph/ontology returns 404 Not Found until the project runs discovery for the first time. While state is discovering, ontology still holds the previous draft, or null on a first run.

warnings lists the discovery steps that failed without failing the whole discovery, for example a sampled chunk the LLM returned no valid proposal for, or a skipped synonym-merging pass. The draft is still usable, but it reflects less of your files. Each new propose clears the list.

Cancel Discovery

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/ontology/cancel' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

Cancelling sets state to cancelled and returns the ontology record. It returns 409 Conflict if state is not discovering. A queued discovery that has not started yet is skipped. A discovery that is already running is not interrupted: once it finishes, it still stores its draft and sets state to draft. To start over, propose again.

Discovery Failures

The server retries each LLM call that fails, for example on a rate limit or a temporary DeepFellow Infra error, up to graph.ontology_discovery_llm_call_attempts times, waiting 1, 2, 4, and more seconds between attempts.

A discovery fails in the following cases:

  • A sample file does not belong to the project or cannot be converted.
  • An LLM call still fails after its last attempt.
  • No text could be sampled from the sample files.
  • Every sampled chunk failed to produce a proposal. Discovery then has nothing to add to the starting ontology.

state becomes failed, and last_error holds the error. Nightshift first retries discovery with backoff until it reaches graph.ontology_discovery_max_attempts deliveries. After that, propose again to retry.

Edit the Draft

Replace the draft with an edited ontology:

curl -X 'PUT' \
  'https://deepfellow-server-host/v1/graph/ontology/draft' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "ontology": {
    "entities": [
      { "label": "Contract", "description": "A legally binding agreement between parties.", "properties": [] },
      { "label": "Organization", "description": "A company, agency, team, or other named group.", "properties": [] }
    ],
    "relations": [
      { "label": "PARTY_TO", "description": "An organization that signed a contract.", "patterns": [["Organization", "Contract"]], "properties": [] }
    ]
  }
}'

Send the complete ontology every time. The request replaces the draft as a whole, so any type you leave out is removed from it.

Saving a draft makes it the active ontology at once: the server writes it to both ontology and approved_ontology, and the next ingested document already uses it. Approval is not required for an edit to take effect.

Before the server stores the draft, it runs the same compatibility check as approval, so an edit never breaks ingestion or search. The endpoint returns the updated ontology record with state set to draft, or one of these errors:

StatusCause
404 Not FoundThe project never ran discovery. Propose first.
409 Conflictstate is discovering, approved, or cancelled. Only a draft or failed ontology accepts edits.
409 ConflictThe draft changes the type of a property FalkorDB already holds for the project.
422 Unprocessable EntityThe body is not a valid Ontology, for example a property with an unknown type.

To change an approved ontology, propose again, then edit and approve the new draft.

Approve the Draft

curl -X 'POST' \
  'https://deepfellow-server-host/v1/graph/ontology/approve' \
  -H "Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY"

Approval runs the compatibility check, copies the draft to approved_ontology, sets state to approved, and returns the ontology record. It returns 404 Not Found if the project never ran discovery, and 409 Conflict if state is not draft or if the check fails.

Since a saved draft is already active, approval mostly marks the ontology as final. After approval, the draft no longer accepts edits until the next propose.

Compatibility with Ingested Data

FalkorDB keeps a copy of each project's ontology, which grows as the project ingests documents. Saving or approving a draft compares the draft against that copy:

  • Additive changes are applied. A new entity type, relation type, property, or relation pattern is written to FalkorDB before the draft becomes active.
  • Removals are allowed. A type that the draft leaves out stays in FalkorDB and in the existing graph, but is no longer extracted.
  • Retyped properties are rejected. A property that keeps its name but changes its type on an existing entity or relation type, for example founded_year from INTEGER to STRING, would leave the data already in the graph inconsistent with the new schema. The request returns 409 Conflict, and nothing changes.

A project that has not ingested anything yet passes the check with any draft. The 409 Conflict error message lists every conflict, for example:

{
    "error": {
        "message": "Ontology conflicts with the already-registered ontology: Entity 'Organization': property 'founded_year' type change INTEGER -> STRING not allowed.",
        "type": "invalid_request_error",
        "param": null,
        "code": null
    }
}

To resolve a conflict, keep the property's original type, or give the property a new name.

Draft Cleanup

When discovery finishes, the server cleans up the draft before it stores it.

Draft Sanitization

Discovery LLMs occasionally propose a value they saw in the text, such as John Smith or 1976, as a property name. The server drops every property whose name does not match ^[a-z][a-z0-9_]*$: a lowercase letter followed by lowercase letters, digits, or underscores. It sanitizes the draft when discovery finishes, when you save a draft, and when you approve one, and it drops such properties silently, without an error.

Near-Duplicate Types

A new type whose name differs from an existing one only by letter case, punctuation, or a plural ending, such as Organizations or organization next to Organization, is merged into the existing type. Its properties and relation patterns move to the existing type. The types of the starting ontology always keep their names and are never merged into each other, even if two of them differ only this way. Between two new types, the first one discovery proposed keeps its name. The check covers regular English plurals only, so People is not merged into Person.

Definitions of Known Types

GraphRAG-SDK's synonym-merging pass sometimes rewrites the descriptions of types the starting ontology already has, or changes the type of their properties. The server restores the original descriptions, since the extraction prompts rely on them, and the original property types and descriptions, so approving the draft never fails the compatibility check on a property you did not change. New properties that discovery adds to a known type stay in the draft.

Limits on New Types

If graph.ontology_discovery_max_new_entities or graph.ontology_discovery_max_new_relations is above 0, the draft keeps at most that many new entity or relation types. Types of the starting ontology never count toward the limit. The server keeps the new entity types that the most relation patterns reference, and the new relation types with the most patterns. Relation patterns that reference a dropped entity type are removed, and a new relation type left with no patterns is dropped too.

Use the Web UI

Open the project details page and click Ontology in the Toolboxes section header. The page has three parts:

  1. Choose sample files. Select up to 10 of the project's files, enter optional Boundaries, and click Propose. While discovery runs, the page refreshes by itself and shows a Cancel button. If you have unsaved edits, the page asks for confirmation first, since the new draft will overwrite them.
  2. Review & edit draft. Add, edit, and remove entities, relations, patterns, and properties. Save draft saves your edits and makes them active at once. Approve is enabled only for a saved draft in the draft state.
  3. Approved ontology. Lists the labels of the active ontology, and notes when a newer draft exists.

Configuration

These are dynamic settings. See Knowledge Graph Settings for the full list.

SettingDescriptionDefault
graph.ontology_discovery_llm_modelModel on DeepFellow Infra that drafts ontologies. Independent of graph.llm_model.gpt-4o-mini
graph.ontology_discovery_sample_chunks_per_docNumber of chunks sampled from each sample file.3
graph.ontology_discovery_concurrencyNumber of LLM calls discovery runs in parallel.4
graph.ontology_discovery_max_sample_filesMaximum number of sample files per discovery. Values above 10 have no effect.10
graph.ontology_discovery_max_attemptsNightshift deliveries a discovery gets before it is marked failed.3
graph.ontology_discovery_llm_call_attemptsAttempts each discovery LLM call gets before it fails. At least 1.4
graph.ontology_discovery_max_new_entitiesMaximum number of new entity types one discovery adds. 0 means no limit.0
graph.ontology_discovery_max_new_relationsMaximum number of new relation types one discovery adds. 0 means no limit.0

We use cookies on our website. We use them to ensure proper functioning of the site and, if you agree, for purposes such as analytics, marketing, and targeting ads.