An ontology you never write: the classes and relations your memory builds as you store
12 October 2026 · Semurg · 4 min read · Markdown
Verified: every command below ran on a fresh node of r11.78.20261012.bc0d94f (how)
knowledge-graphontologymemoryagents
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
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"}]}}'Expected: the facts are stored
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:
export SEMURG_API_TOKEN=<your token>
curl -sS "http://localhost:4000/v1/memory/status?scope=ontology-intro" \
-H "Authorization: Bearer $SEMURG_API_TOKEN"Expected: the ontology lists the classes person and company, and the relation WORKS_AT among its spellings
What the reply holds, under ontology:
typesandrelation_types: how many classes and relations this memory has.top_classes: the biggest classes, up to 50, each with its member count.members_boundsays what the count is:exact(the live members; forgetting one lowers it by one) orupper(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-casenameand itsspellings, the exact names it was stored under.merge: whether classes are merged automatically; it saysoffby default.classes_shownandrelations_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.
What it does not do yet
- Count how often each relation is used:
countsaysnot_counted. - Merge two classes that mean the same thing under different names, such as
CompanyandOrganisation:mergesaysoff, 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: the facts this ontology is built from.
- Graph RAG that says when it does not know: asking questions across the graph.
How these commands were checked
Our test farm ran each of the 3 command blocks marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.78.20261012.bc0d94f. A post is published only when every one of them passed there.