---
title: "An ontology you never write: the classes and relations your memory builds as you store"
slug: knowledge-layer-2-the-automated-ontology
date: 2026-10-12
author: Semurg
section: how-to
tags: [knowledge-graph, ontology, memory, agents]
summary: Semurg builds the ontology of each memory from what you store, with no schema to write first. Read its classes and relations with one call, reuse them so agents stay consistent, and check that it holds together.
status: published
constants: [50]
covers: [route:GET /v1/memory/status, route:POST /v1/memory/remember, mcp:memory_status, mcp:memory_remember]
---
Most knowledge graphs ask for a schema first: the kinds of thing you will store, and how they may connect. Semurg does not. Each memory
builds its ontology from what you store: every entity type you send becomes a class, and every relationship type becomes a relation.
One call reads it back, so the next agent, or the next session of the same one, can reuse what is already there instead of inventing a
near-duplicate.

## Store a few facts

```bash runnable kind=shell-node needs=token
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/remember \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scope": "ontology-intro", "source": "onboarding notes",
       "facts": {"entities": [{"name": "Priya Raman", "type": "Person"},
                              {"name": "Tom Okafor", "type": "Person"},
                              {"name": "Northwind Retail", "type": "Company"}],
                 "relations": [{"source": "Priya Raman", "target": "Northwind Retail", "type": "WORKS_AT"},
                               {"source": "Tom Okafor", "target": "Northwind Retail", "type": "WORKS_AT"}]}}'
```

```expect
expect: the facts are stored
assert: json:.ok==true
```

No schema came first. `person` and `company` are now classes in this memory (class names are kept in lower case, so letter case
never splits a class), and `WORKS_AT` is a relation.

## Read the ontology

The status call, given a scope, returns that memory's ontology beside the usual status:

```bash runnable kind=shell-node needs=token
export SEMURG_API_TOKEN=<your token>
curl -sS "http://localhost:4000/v1/memory/status?scope=ontology-intro" \
  -H "Authorization: Bearer $SEMURG_API_TOKEN"
```

```expect
expect: the ontology lists the classes person and company, and the relation WORKS_AT among its spellings
assert: json:([.ontology.top_classes[].name|ascii_downcase]|index("person"))!=null and ([.ontology.relations[].spellings[]]|index("WORKS_AT"))!=null
```

What the reply holds, under `ontology`:

- `types` and `relation_types`: how many classes and relations this memory has.
- `top_classes`: the biggest classes, up to 50, each with its member count. `members_bound` says what the count is: `exact` (the live members; forgetting one lowers it by one) or `upper` (a very large class, where the number is an upper bound). Read the bound before you rely on the count.
- `relations`: the relations in use, up to 50, each with a lower-case `name` and its `spellings`, the exact names it was stored under.
- `merge`: whether classes are merged automatically; it says `off` by default.
- `classes_shown` and `relations_shown`: how many of each the lists carry, so a capped list says it is capped.
- `coherence`: whether the ontology holds together (below).

How often a relation is used is not counted yet: each relation's `count` says `not_counted`, never a made-up zero.

## Reuse it, spelled exactly

A class matches letter case aside: `Person` and `person` are one class. A relation is listed the same way, `works_at` and `WORKS_AT`
under one name, but each spelling is kept as its own relationship record. So the same fact stored once as `works_at` and once as
`WORKS_AT` is stored twice, and the reply lists both spellings under the one relation so you can see it happened. Before an agent
invents a class or a relation, it reads the ontology and reuses what is there, spelled exactly as listed.

## Does it hold together?

Because the ontology grows as you store, it can drift: a class nothing uses, a chain of aliases, a loop. `coherence` reports it in one
of three states: `coherent`; `incoherent`, with each problem named; or `could_not_judge`, with the reason. It never answers an empty
list that would read as "no problems" when the check did not run.

### For coding agents: Rules for an agent using Semurg's automated ontology

- [ ] Before storing a new kind of entity or relationship, call memory_status with the scope and read ontology.top_classes and ontology.relations.
- [ ] Reuse an existing class when one fits; class names match letter case aside.
- [ ] Reuse an existing relation spelled exactly as one of its spellings; a different spelling is kept as a separate relationship record, so the same fact is stored twice.
- [ ] Read members_bound before using a class's member count; upper means the count is a bound, not exact.
- [ ] Treat coherence could_not_judge as unknown, never as coherent.

The check that proves it:

```bash
curl -sS "http://localhost:4000/v1/memory/status?scope=ontology-intro" -H "Authorization: Bearer $SEMURG_API_TOKEN"
```

Passes when: the ontology lists the relation WORKS_AT with its exact spelling

## What it does not do yet

- Count how often each relation is used: `count` says `not_counted`.
- Merge two classes that mean the same thing under different names, such as `Company` and `Organisation`: `merge` says `off`, and reuse is up to the writer, which is why the agent block asks for it.

## Next

- [A knowledge graph your agents build as they work](/blog/knowledge-layer-1-the-knowledge-graph): the facts this ontology is built from.
- [Graph RAG that says when it does not know](/blog/knowledge-layer-4-graph-rag): asking questions across the graph.
