# Your agent's context is only as precise as your entity types

By Preston Rasmussen · Nov 10, 2026 · https://www.getzep.com/blog/type-hierarchies-zep-ontology/ · Tags: Context graphs, Product updates

Zep type hierarchies arrange custom entity and edge types as subtypes. Nodes get every label in their chain, and parent filters match subtypes.

**Key takeaways**

- **A flat ontology makes the agent's context too vague or too brittle.** With one broad type, such as `Vehicle`, an agent cannot retrieve only the hybrid cars. With many narrow types, the agent must list every type to retrieve all cars, and overlapping types are applied inconsistently.
- **A type hierarchy removes this trade-off.** It arranges custom entity types or edge types as types and subtypes. For example, `Vehicle` has the subtypes `Car` and `Truck`, and `Car` has the subtypes `ElectricCar` and `HybridCar`.
- **Zep classifies in two stages.** Extraction selects a top-level type from a short list. Then one LLM call for each level selects a subtype, and the walk stops at a leaf type or when no subtype fits.
- **A node gets every label in its chain.** An `ElectricCar` node has the labels `Entity`, `Vehicle`, `Car`, and `ElectricCar`, so a filter on `Car` returns cars of all kinds. Edge types use the same model: a filter on `EMPLOYED_BY` also returns `WORKS_FULL_TIME_FOR` and `CONTRACTS_FOR`.
- **Type hierarchies are in closed beta.** The feature is on by default on BYOC. Other accounts can ask Zep for access.

An agent is only as precise as the context that it retrieves. A dealership agent that answers "Which hybrids do we have in stock?" must retrieve only hybrid cars. The same agent that answers "Which vehicles did Ana look at?" must retrieve every vehicle. A flat ontology lets the agent do one of these tasks well, but not both.

A [custom entity type](https://www.getzep.com/blog/entity-types-structured-agent-memory/) tells Zep which kinds of things to extract from your data, such as a product, a patient, or a contract. Until now, the custom types in a Zep ontology were flat. Each node got the label `Entity` and at most one custom type label. With type hierarchies, you can now arrange custom entity types and edge types as types and subtypes, and Zep classifies each entity or fact into the most specific type that fits.

## Why flat types give your agent imprecise context

A flat ontology makes you select one level of detail for each kind of thing. A dealership agent can use one broad type, `Vehicle`. With one broad type, an agent cannot filter the graph for electric cars only. The agent can also use many narrow types, such as `ElectricCar`, `HybridCar`, `PickupTruck`, and `BoxTruck`. With many narrow types, the agent cannot filter for all cars in one query, and every narrow type counts against the entity type limit of your plan.

Narrow types also make classification harder. Zep selects a type from the description of each type. When descriptions overlap, no single type is correct, and Zep can classify the same kind of item inconsistently. The [custom ontology guide](https://help.getzep.com/v4/customizing-graph-structure#ontology-design-best-practices) says to make the types at one level mutually exclusive. A long, flat list of similar types makes that rule difficult to follow.

Programming languages and schema.org solve this with a type hierarchy. In a type hierarchy, `ElectricCar` is a kind of `Car`, and `Car` is a kind of `Vehicle`. An item of a subtype is also an item of each parent type.

## Define an entity type hierarchy

Every type in the hierarchy is an ordinary custom entity type, with its own name, description, and properties. The new optional field `entity_type_hierarchy` holds only the structure. Its value is a dictionary of dictionaries. Each key names an entity type in the same request, and `{}` marks a leaf type.

```python
from zep_cloud.client import Zep
from zep_cloud.types import EntityProperty, EntityType

client = Zep(api_key="YOUR_API_KEY")

details = [EntityProperty(name="details", type="text", description="Details about the vehicle.")]

entity_types = [
    EntityType(name="Vehicle", description="A vehicle for sale or in service, such as a car or a truck. Do not use for a part or an accessory.", properties=details),
    EntityType(name="Car", description="A passenger car, such as a sedan or an SUV. Do not use for a pickup truck or a van.", properties=details),
    EntityType(name="ElectricCar", description="A car with only a battery and an electric motor, such as a Tesla Model 3. Do not use for a hybrid.", properties=details),
    EntityType(name="HybridCar", description="A car with an engine and an electric motor, such as a Toyota Prius. Do not use for a car with only a battery.", properties=details),
    EntityType(name="Truck", description="A truck for cargo, such as a pickup truck or a box truck. Do not use for an SUV.", properties=details),
]

client.project.set_ontology(
    entity_types=entity_types,
    entity_type_hierarchy={
        "Vehicle": {
            "Car": {"ElectricCar": {}, "HybridCar": {}},
            "Truck": {},
        },
    },
)
```

Each type description gives a membership rule, an example, and a similar item that does not belong. The parent type `Vehicle` contains its subtypes by design. The siblings `ElectricCar` and `HybridCar` must not overlap.

A type can have up to 10 direct subtypes, and a hierarchy can have up to 4 subtype levels below a top-level type. Subtypes do not count against the entity type limit of your plan. One ontology can have up to 100 entity types in total. The [entity type hierarchy guide](https://help.getzep.com/v4/customizing-graph-structure#entity-type-hierarchy) has the TypeScript and Go versions and the full validation rules.

## How Zep classifies an entity into a subtype

Zep classifies each entity in two stages.

1. **Top-level extraction.** The extraction prompt shows only the top-level types. In the example, extraction sees `Vehicle` and the other top-level types of the ontology, and it does not see `Car` or `ElectricCar`. The list of choices stays short.
2. **Subtype classification.** For each entity with a top-level type that has subtypes, Zep walks down the hierarchy. At each level, one LLM call selects one subtype, or "none" when no subtype fits. The walk stops at a leaf type or at "none".

The input to each call is the entity name, the chain selected so far, the candidate subtypes with their descriptions, and the episode text that the entity came from. For example, "the Prius that Ana test-drove" becomes `Vehicle`, then `Car`, then `HybridCar`. A mention of "a new car" with no other detail stops at `Car`, because no subtype fits.

Subtype classification runs before deduplication. Deduplication, attribute extraction, and summaries see the full chain. An entity whose top-level type has no subtypes makes no classification calls, and an ontology without a hierarchy adds no LLM calls.

## Labels, filters, and properties

A classified node gets the label of every type in its chain. A `HybridCar` node has the labels `Entity`, `Vehicle`, `Car`, and `HybridCar`. A node never has a subtype label without the labels of all its parent types.

A label filter matches a node when any label of the node matches. A filter on `Car` returns `Car`, `ElectricCar`, and `HybridCar` nodes, and a filter on `ElectricCar` returns only electric cars:

```python
cars = client.graph.node.list(
    "YOUR_GRAPH_UUID",
    filters={"node_labels": ["Car"]},
)
```

![Car sits inside Vehicle, with ElectricCar and HybridCar inside Car. A filter on Car returns a Model 3, the Prius, and a new car, but not a box truck.](https://www.getzep.com/blog/type-hierarchies-zep-ontology/parent-filter-nesting.png)

A Prius classified as HybridCar is also a Car and a Vehicle, so a filter on Car returns it.

A subtype does not inherit properties or identity properties from its parent type. A node uses the properties of the last type in its chain only. Declare every property that a subtype needs on the subtype itself. To match subtype nodes by an identity value, such as a vehicle identification number, set `identity_properties` on each subtype.

## Edge type hierarchies

Facts get the same model. The optional field `edge_type_hierarchy` has the same shape as `entity_type_hierarchy`. For example, the edge type `EMPLOYED_BY` can have the subtypes `WORKS_FULL_TIME_FOR` and `CONTRACTS_FOR`. Extraction selects a top-level edge type, and then the subtype walk selects the most specific edge type that fits.

An edge has one type name, which is the name of the last type in its chain. Search expands an edge type filter through the hierarchy. A filter on `EMPLOYED_BY` returns `EMPLOYED_BY`, `WORKS_FULL_TIME_FOR`, and `CONTRACTS_FOR` edges. A filter on `CONTRACTS_FOR` returns only contract work. The `exclude_edge_types` filter expands in the same way.

| Behavior | Entity type hierarchy | Edge type hierarchy |
|---|---|---|
| Ontology field | `entity_type_hierarchy` | `edge_type_hierarchy` |
| Saved type | Every label in the chain | Last type in the chain |
| Filter on a parent type | Matches all subtypes | Matches all subtypes |
| Property inheritance | None | None |

Entity and edge type hierarchies classify the same way and store the result differently. Source: the Zep custom ontology guide.

## Ontology changes and existing data

A change to a hierarchy applies to episodes that Zep processes after the change. Existing nodes and edges keep their labels and names, and Zep does not reclassify them. Each ontology write replaces the full ontology. A request without `entity_type_hierarchy` removes the stored hierarchy. A graph ontology replaces the project ontology for that graph, and Zep does not merge the two hierarchies.

## Availability of type hierarchies

Entity and edge type hierarchies are in closed beta. They are on by default on BYOC deployments. For other accounts, Zep enables the feature for each account. Without access, the API rejects a non-empty hierarchy with HTTP `400`. To ask for access, [contact Zep](https://www.getzep.com/pricing/).

To learn how types fit with the other kinds of context in a graph, read [Zep context types](https://www.getzep.com/blog/zep-context-types/). For facts that relate three or more entities, read about [hyperedges](https://www.getzep.com/blog/hyperedges-facts-across-many-entities/).

## FAQ

### What is a type hierarchy in a knowledge graph?

A type hierarchy arranges entity types or relationship types as types and subtypes. Each item of a subtype is also an item of its parent types. In Zep, `ElectricCar` can be a subtype of `Car`, and `Car` can be a subtype of `Vehicle`. Zep classifies each entity into the most specific subtype that fits, and a filter on a parent type also returns the items of its subtypes.

### Does a subtype inherit the properties of its parent type?

No. Each type in a Zep hierarchy declares its own properties and identity properties. A node uses the properties of the last type in its type chain only. A `HybridCar` node does not get a property that only `Vehicle` declares. Declare on each subtype every property that the subtype needs, and set `identity_properties` on each subtype when you match nodes by an identity value.

### How many LLM calls does subtype classification add?

Subtype classification makes one LLM call for each level that Zep walks, for each entity or edge. A hierarchy has at most 4 subtype levels below a top-level type, so one entity makes at most four calls. Calls for different entities run in parallel. An entity whose top-level type has no subtypes makes no calls, and an ontology without a hierarchy adds no calls.

### Does Zep reclassify existing nodes when I change a hierarchy?

No. A hierarchy change applies to episodes that Zep processes after the change. Existing nodes keep their labels, and existing edges keep their names. When a new extraction merges with an existing node, and the existing chain is a prefix of the new chain, the node gets the longer chain. For example, an existing `Car` node can become a `HybridCar` node.
