June 2026Level 5 · AdmissibilityNaming & vocabulary

Compiling an Ontology Into a Living Agent

A small, verified proof-of-concept that turns an OWL class into a typed Python object — and the roadmap that turns that object into an LLM agent whose database schema is the ontology.

The Idea In One Sentence

If your ontology already says exactly what a thing is — its properties, their types, their constraints — then you should be able to generate the code for it, run that code as a live object, and let an LLM be that object while a database persists its state.

One axis: OWL → Python → DB. The ontology is the schema. The generated class is the body. The agent is the mind. The database is the memory. Four lenses on a single entity.

This post is careful about one thing above all else: what runs today versus what is the roadmap. I built and ran a small piece of this. The rest is a clearly-marked plan, not a claim. I'll mark every section [RUNS TODAY] or [ROADMAP] so you never have to guess.


What Runs Today

[RUNS TODAY — VERIFIED]

There is one Python file, owl22python.py, and it does exactly one demonstrable thing: it reads an OWL2/XML ontology, generates a typed Pydantic class from it, instantiates that class with real data, and roundtrips it back out as an OWL2 individual plus typed RDF triples. I ran it. It exits clean.

Step 1 — Hand it a tiny OWL2 ontology

The input is small, hand-written OWL2/XML. One class, three data properties, each with a declared range:

<Class IRI="#Owl"/>
<DataProperty IRI="#species"/>       <!-- range: xsd:string  -->
<DataProperty IRI="#wingspan_cm"/>   <!-- range: xsd:decimal -->
<DataProperty IRI="#nocturnal"/>     <!-- range: xsd:boolean -->

That OWL declares the shape of a thing. It does not, by itself, give you anything you can run. That's the gap this closes.

Step 2 — Generate a typed Python class from the restrictions

The parser walks each DataProperty restriction and maps its xsd range to a Python type — xsd:string → str, xsd:decimal → float, xsd:boolean → bool. It then calls Pydantic's create_model(__base__=RenderablePiece) to synthesize a real class at runtime, one typed field per OWL property. The class it generates looks like this:

class Owl(RenderablePiece):
    """Generated from OWL2 class <http://example.org/birds#Owl>."""
    species: str       = Field(..., description="OWL DataProperty species (xsd:string)")
    wingspan_cm: float = Field(..., description="OWL DataProperty wingspan_cm (xsd:decimal)")
    nocturnal: bool    = Field(..., description="OWL DataProperty nocturnal (xsd:boolean)")

This is not a string of source code that a human pastes. The class is built live, in memory, by create_model — the generated class's fields confirm it at runtime:

>>> GENERATED CLASS (live): fields = {'species': 'str', 'wingspan_cm': 'float', 'nocturnal': 'bool'}

The base class is RenderablePiece from MetaStack (the installed pydantic_stack_core library) — so the generated class isn't an inert struct, it's a composable, self-rendering typed piece. A second library, template_mixins, handles the runtime class synthesis side. These are real installed dependencies, not mocks.

Step 3 — Instantiate, then roundtrip back to the ontology

Hand the generated class some data and it validates against the OWL-derived types. Then render() emits the entity back out as an OWL2 individual in RDF/XML, plus the typed RDF triples:

>>> INSTANTIATED: {'species': 'Great Horned Owl', 'wingspan_cm': 120.0, 'nocturnal': True}

OWL INDIVIDUAL (RDF/XML):
<Owl rdf:about="http://example.org/birds#owl_individual">
  <species rdf:datatype="xsd:string">Great Horned Owl</species>
  <wingspan_cm rdf:datatype="xsd:decimal">120.0</wingspan_cm>
  <nocturnal rdf:datatype="xsd:boolean">true</nocturnal>
</Owl>

TRIPLES:
  #owl_individual  rdf:type            #Owl
  #owl_individual  #species      "Great Horned Owl"^^xsd:string
  #owl_individual  #wingspan_cm  "120.0"^^xsd:decimal
  #owl_individual  #nocturnal    "true"^^xsd:boolean

The loop closes: ontology in, typed Python object in the middle, ontology-typed individual out. Class membership (rdf:type) and one typed-literal triple per property. That's the whole demonstrated artifact.

OWL → Python → typed-RDF roundtrip. It runs, it exits 0, every type is carried end to end. That part is real.

The honest caveat

This is a proof-of-concept, not a production ontology pipeline. Two things to be clear about:

So: the mechanism — OWL DataProperty restrictions becoming typed Pydantic fields and roundtripping to typed RDF — is verified. The claim is that narrow and that solid. It is the seed, not the tree.


Where This Goes

[ROADMAP — VISION, NOT BUILT]

Everything from here down is the plan. None of it is being claimed as working. It's the arc the roundtrip points at, written out so the direction is legible — not so you mistake it for a shipped system.

1. Agentify the generated model

The generated class has data. The next move is to give it behavior — methods like hoot(), hunt(prey), move(location). The vision: lift those methods into stateless functions that all operate over a single shared, pickled state object. Each call loads the state, applies the change, saves it back. The entity stops being a snapshot and becomes a thing that does.

2. Expose the behaviors as a "soma_simulate_owl" MCP

Wrap each agentified function as a tool in an MCP server. Now the entity's verbs — hunt, hoot, move — are callable tools that any LLM agent can invoke. The ontology's class hasn't just become code; its code has become an interface.

3. Give the tools to an "OwlSimulator" agent

Hand that MCP to a Heaven LLM agent whose system prompt is, simply, to be the Owl. The agent doesn't describe an owl. It is the running entity, acting through the tools the ontology generated. Its decisions are owl-decisions because the only verbs it has are the owl's verbs.

4. Persist to a database whose schema IS the OWL

Every change the agent makes writes through to a database — and the database's schema is itself ingested from the same OWL spec. There is no impedance mismatch between "what the thing is" (ontology), "what the thing does" (agent), and "what the thing remembers" (DB), because all three are projections of one source. Change the ontology, and the code, the tools, and the schema all change with it.

The net of the vision: an OWL2 class becomes a live LLM agent with persisted state, where the database schema is the ontology. One definition, four runnable faces.

The Four Lenses

The reason this is worth building toward: a single ontology entity, fully realized, is viewable through four lenses at once — and they're the same thing seen from four sides.

Today, exactly one lens is verified end to end: Code Object, generated from Domain (the OWL). The roundtrip is the bridge between those two. The other lenses — Agent and Place — are the roadmap above. This is the smallest honest version of a much larger idea: ontologies as runnable worlds. An ontology stops being a static document you query and becomes a place you can compile, run, and inhabit.


Why Start So Small

Because the difference between a demo that's real and a demo that's theater is whether you can run it and watch the types survive the trip. I'd rather show you three OWL properties becoming three typed fields and roundtripping cleanly than hand you a grand diagram of a simulator that doesn't exist yet.

The roundtrip is the load-bearing claim. Everything above it — the agent, the MCP, the schema-equals-ontology database, the visitable world — is the direction the load-bearing claim points. When those are built and verified, they'll get their own post, with their own run logs. Until then they stay exactly where they belong: labeled as roadmap.

That discipline — sharply separating what runs from what's envisioned — is the same one every system here is built on. The proof is never the diagram. The proof is the thing that runs.

← All posts L5: Admissibility →

Next note: SANCTOT: A Complete L5 Example →

See one actually run.

The fastest way to judge any of this is to watch the engine do it.

Watch a world run →