Ontology Extension¶
SynthOrg's semantic ontology lives at synthorg.ontology. It holds the
canonical definitions of the concepts the platform reasons about, such as
Task, Project, Department, and Artifact. Each definition pairs a
free-text description with typed fields, constraints, disambiguation text,
and relationships. The injection layer threads these definitions into agent
context so every agent shares one consistent vocabulary. This guide covers
the two ways to add an entity and shows how a definition reaches an agent.
Concepts¶
- Entity definition: an
EntityDefinitionvalue object with aname, adefinition, typedfields,constraints,disambiguation, andrelationships. Definitions are immutable; updates create new versions through the versioning service. - Tier:
EntityTier.COREfor framework-provided definitions (protected from user edits) orEntityTier.USERfor domain definitions you add. - Source: how the definition entered the ontology.
AUTO(the@ontology_entitydecorator),CONFIG(a company template), orAPI(the REST endpoint). - Injection strategy: how definitions reach an agent. The
OntologyInjectionConfig.strategysetting selectsprompt,tool,hybrid(the default), ormemory.
Option A: a framework entity via @ontology_entity¶
Decorate any Pydantic BaseModel with @ontology_entity. The decorator
registers the class in a module-level registry; OntologyService.bootstrap()
later derives an EntityDefinition from it. The class docstring becomes the
definition, and every field that carries a Field(description=...) becomes
an EntityField. The default tier is CORE and the default source is AUTO.
from pydantic import BaseModel, Field
from synthorg.core.types import NotBlankStr
from synthorg.ontology import ontology_entity
@ontology_entity
class CostCentre(BaseModel):
"""An accounting bucket that attributes spend to a budget owner."""
name: NotBlankStr = Field(description="Human-readable cost-centre label")
owner: NotBlankStr = Field(
description="Identifier of the budget owner accountable for spend",
)
Override the derived name, tier, or source by calling the decorator with arguments:
@ontology_entity(entity_name="Approval", tier=EntityTier.CORE)
class ApprovalItem(BaseModel):
"""A pending human decision gating an agent action."""
Framework models defined inside synthorg.core import the decorator from
synthorg.ontology.decorator rather than the package root, which keeps the
core import graph free of the heavier persistence and security edges.
Application code outside core can use the package-level
from synthorg.ontology import ontology_entity.
Only fields with a description are derived, because the description is what an agent reads. A field without one is omitted from the definition.
Option B: a domain entity via a company template¶
Domain entities that have no backing model are declared in a company template
(YAML), the ontology's ingestion format. They register at the USER tier with
the CONFIG source. Each entry maps to an
EntityEntry; the fields mapping is field name to description.
ontology:
entities:
entries:
- name: CostCentre
definition: >-
An accounting bucket that attributes spend to a budget owner.
fields:
owner: Identifier of the budget owner accountable for spend.
constraints:
- Every cost centre maps to exactly one budget owner.
disambiguation: Not a general-ledger account code.
OntologyService.bootstrap_from_config() reads the parsed EntitiesConfig,
builds an EntityDefinition per entry, and registers each one. Registration
is idempotent: an entity whose name already exists is skipped.
How a definition reaches an agent¶
The injection strategy turns registered definitions into agent context. With
the default hybrid strategy, CORE definitions are injected as a system
message and the rest stay available through an on-demand tool.
PromptInjectionStrategy lists the CORE-tier entities, renders each with
format_entity(), and packs them into a single system message up to the
configured core_token_budget:
from synthorg.ontology.injection.prompt import format_entity
text = format_entity(definition)
# ## CostCentre
# An accounting bucket that attributes spend to a budget owner.
# Fields:
# - owner: str -- Identifier of the budget owner accountable for spend
ToolBasedInjectionStrategy registers the lookup_entity tool instead, so an
agent fetches a definition by exact name or free-text query only when it needs
one. The hybrid strategy combines both: canonical CORE definitions up
front, everything else on demand. Select a strategy through configuration:
ontology:
injection:
strategy: hybrid # prompt | tool | hybrid | memory
core_token_budget: 2000
tool_name: lookup_entity
Inspecting and managing at runtime¶
OntologyService is the orchestration entry point. It wraps the backend
repository and the versioning service:
# Every @ontology_entity model registered in the backend.
registered = await ontology_service.bootstrap()
# Read paths.
cost_centre = await ontology_service.get("CostCentre")
core_only = await ontology_service.list_entities(tier=EntityTier.CORE)
matches = await ontology_service.search("budget")
# Version history (definitions are immutable; updates snapshot).
manifest = await ontology_service.get_version_manifest()
history = await ontology_service.list_versions("CostCentre")
The same operations are reachable over the REST API and the MCP tool surface for operators and agents that act on the ontology directly.
Observability¶
ontology.entity.decorator_registered(debug): a model registered through the decorator.ontology.bootstrap.completed(info): bootstrap finished, withtotal,registered, andskippedcounts.ontology.config.loaded(info): company-template entities registered.ontology.entity.registered/ontology.entity.updated/ontology.entity.deleted: backend writes.ontology.injection.prepared(debug): definitions packed into agent context, withagent_id,entity_count, andstrategy.ontology.tool.lookup(debug): an agent invoked thelookup_entitytool.ontology.sync.published/ontology.sync.skipped: a definition published to organisational memory, or skipped because its content was unchanged.
Event-name constants live in synthorg.observability.events.ontology.
See docs/design/ontology.md for the subsystem architecture, the drift-detection and delegation-guard layers, and the resolution rules.