"""
Pattern 67: Ontology-Based Pattern
Formal knowledge representation using ontologies with classes, properties, relationships,
inheritance, and logical reasoning capabilities.
"""
from typing import Dict, Any, List, Set, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
class PropertyType(Enum):
"""Types of ontology properties."""
DATA_PROPERTY = "data_property" # Property with literal value
OBJECT_PROPERTY = "object_property" # Property relating to another entity
FUNCTIONAL = "functional" # Has exactly one value
TRANSITIVE = "transitive" # If A->B and B->C then A->C
SYMMETRIC = "symmetric" # If A->B then B->A
@dataclass
class OntologyProperty:
"""A property definition in ontology."""
name: str
property_type: PropertyType
domain: Optional[str] = None # Which class it belongs to
range: Optional[str] = None # What type of value it has
restrictions: Dict[str, Any] = field(default_factory=dict)
@dataclass
class OntologyClass:
"""A class in the ontology."""
name: str
parent: Optional[str] = None
properties: Dict[str, OntologyProperty] = field(default_factory=dict)
instances: Set[str] = field(default_factory=set)
constraints: List[str] = field(default_factory=list)
def get_all_properties(self, ontology: 'OntologyBasedPattern') -> Dict[str, OntologyProperty]:
"""Get all properties including inherited ones."""
props = dict(self.properties)
# Add parent properties
if self.parent and self.parent in ontology.classes:
parent_class = ontology.classes[self.parent]
parent_props = parent_class.get_all_properties(ontology)
# Parent properties come first (can be overridden)
for name, prop in parent_props.items():
if name not in props:
props[name] = prop
return props
@dataclass
class OntologyRelation:
"""A relationship between entities."""
subject: str
predicate: str
object: str
properties: Dict[str, Any] = field(default_factory=dict)
timestamp: datetime = field(default_factory=datetime.now)
@dataclass
class OntologyInstance:
"""An instance of an ontology class."""
name: str
class_name: str
properties: Dict[str, Any] = field(default_factory=dict)
relations: List[OntologyRelation] = field(default_factory=list)
class OntologyReasoner:
"""Reasoning engine for ontology."""
def __init__(self, ontology: 'OntologyBasedPattern'):
self.ontology = ontology
self.llm = ontology.llm
def is_subclass_of(self, child: str, parent: str) -> bool:
"""Check if child is a subclass of parent."""
if child == parent:
return True
if child not in self.ontology.classes:
return False
current = self.ontology.classes[child]
while current.parent:
if current.parent == parent:
return True
if current.parent not in self.ontology.classes:
break
current = self.ontology.classes[current.parent]
return False
def is_instance_of(self, instance_name: str, class_name: str) -> bool:
"""Check if instance is of given class (including subclasses)."""
if instance_name not in self.ontology.instances:
return False
instance = self.ontology.instances[instance_name]
return self.is_subclass_of(instance.class_name, class_name)
def infer_transitive_relations(self, predicate: str) -> List[OntologyRelation]:
"""Infer transitive relations (if A->B and B->C then A->C)."""
# Find property definition
prop = None
for onto_class in self.ontology.classes.values():
if predicate in onto_class.properties:
prop = onto_class.properties[predicate]
break
if not prop or prop.property_type != PropertyType.TRANSITIVE:
return []
# Build relation map
relations_map: Dict[str, List[str]] = {}
for rel in self.ontology.relations:
if rel.predicate == predicate:
if rel.subject not in relations_map:
relations_map[rel.subject] = []
relations_map[rel.subject].append(rel.object)
# Infer transitive closures
inferred = []
for start, targets in relations_map.items():
visited = set()
queue = list(targets)
while queue:
current = queue.pop(0)
if current in visited:
continue
visited.add(current)
# Add inferred relation
if current not in targets: # Not direct relation
inferred.append(OntologyRelation(
subject=start,
predicate=predicate,
object=current,
properties={"inferred": True, "type": "transitive"}
))
# Add next level
if current in relations_map:
queue.extend(relations_map[current])
return inferred
def query_with_reasoning(self, query: str) -> str:
"""Answer query using ontology reasoning."""
# Get ontology context
context_parts = [
"Ontology Classes:",
*[f"- {name}: {cls.parent or 'root'}" for name, cls in self.ontology.classes.items()],
"\nInstances:",
*[f"- {inst.name} (type: {inst.class_name})" for inst in self.ontology.instances.values()],
"\nRelations:",
*[f"- {rel.subject} {rel.predicate} {rel.object}" for rel in self.ontology.relations[:20]]
]
context = "\n".join(context_parts)
prompt = ChatPromptTemplate.from_messages([
("system", "You are an ontology reasoning expert. Use the ontology structure to answer questions."),
("human", f"Ontology:\n{context}\n\nQuery: {query}\n\nProvide a reasoned answer based on the ontology.")
])
chain = prompt | self.llm
response = chain.invoke({})
return str(response.content)
class OntologyBasedPattern:
"""
Ontology-Based Pattern - Formal knowledge representation with reasoning.
Provides structured knowledge representation using classes, properties,
instances, and relationships with inheritance and logical inference.
"""
def __init__(self, model_name: str = "gpt-4"):
self.llm = ChatOpenAI(model=model_name, temperature=0.2)
self.classes: Dict[str, OntologyClass] = {}
self.relations: List[OntologyRelation] = []
self.instances: Dict[str, OntologyInstance] = {}
self.reasoner = OntologyReasoner(self)
def add_class(self, name: str, parent: Optional[str] = None,
properties: Optional[Dict[str, OntologyProperty]] = None,
constraints: Optional[List[str]] = None):
"""Add a class to ontology."""
onto_class = OntologyClass(
name=name,
parent=parent,
properties=properties or {},
constraints=constraints or []
)
self.classes[name] = onto_class
print(f"📚 Class added: {name}" + (f" (extends {parent})" if parent else ""))
def add_instance(self, class_name: str, instance_name: str, properties: Optional[Dict[str, Any]] = None):
"""Add an instance of a class."""
if class_name not in self.classes:
raise ValueError(f"Class {class_name} not found in ontology")
instance = OntologyInstance(
name=instance_name,
class_name=class_name,
properties=properties or {}
)
self.classes[class_name].instances.add(instance_name)
self.instances[instance_name] = instance
print(f"➕ Instance added: {instance_name} of class {class_name}")
def add_relation(self, subject: str, predicate: str, obj: str):
"""Add a relation between entities."""
relation = OntologyRelation(subject, predicate, obj)
self.relations.append(relation)
print(f"🔗 Relation: {subject} {predicate} {obj}")
def query(self, question: str) -> str:
"""Query the ontology using natural language."""
ontology_desc = self._describe_ontology()
prompt = ChatPromptTemplate.from_messages([
("system", """You are an ontology reasoner. Use the ontology to answer questions.
Ontology:
{ontology}"""),
("human", "{question}")
])
chain = prompt | self.llm
response = chain.invoke({
"ontology": ontology_desc,
"question": question
})
return str(response.content)
def _describe_ontology(self) -> str:
"""Describe the ontology structure."""
desc = []
desc.append("Classes:")
for name, cls in self.classes.items():
parent_info = f" extends {cls.parent}" if cls.parent else ""
desc.append(f" - {name}{parent_info}")
if cls.properties:
desc.append(f" Properties: {list(cls.properties.keys())}")
desc.append("\nInstances:")
for inst_name, inst_data in self.instances.items():
desc.append(f" - {inst_name}: {inst_data.class_name}")
desc.append("\nRelations:")
for rel in self.relations:
desc.append(f" - {rel.subject} {rel.predicate} {rel.object}")
return "\n".join(desc)
def run(self, queries: List[str]) -> Dict[str, Any]:
"""Run queries against the ontology."""
results = []
for query in queries:
answer = self.query(query)
results.append({"query": query, "answer": answer})
return {
"ontology_stats": {
"classes": len(self.classes),
"instances": len(self.instances),
"relations": len(self.relations)
},
"queries": results
}
# Example usage
if __name__ == "__main__":
print("\n=== Ontology-Based Pattern Example ===")
onto = OntologyBasedPattern()
# Build ontology
onto.add_class("Animal")
onto.add_class("Mammal", parent="Animal")
onto.add_class("Bird", parent="Animal")
onto.add_class("Dog", parent="Mammal")
onto.add_class("Cat", parent="Mammal")
# Add instances
onto.add_instance("Dog", "Buddy", {"age": 5, "breed": "Golden Retriever"})
onto.add_instance("Cat", "Whiskers", {"age": 3, "breed": "Siamese"})
onto.add_instance("Bird", "Tweety", {"species": "Canary"})
# Add relations
onto.add_relation("Buddy", "likes", "playing fetch")
onto.add_relation("Whiskers", "likes", "sleeping")
onto.add_relation("Dog", "is_a", "Mammal")
# Query
queries = [
"What animals are mammals?",
"Tell me about Buddy",
"What properties do mammals have?"
]
result = onto.run(queries)
print("\n=== Results ===")
print(f"Ontology: {result['ontology_stats']}")
print("\nQuery Results:")
for q in result['queries']:
print(f"\nQ: {q['query']}")
print(f"A: {q['answer']}")
Agentic Architecture Pattern: Ontology Based
Pattern 67: 67 Ontology Based – Line-by-Line Explanation
Overview
Pattern 67: Ontology-Based Pattern
Formal knowledge representation using ontologies with classes, properties, relationships, inheritance, and logical reasoning capabilities.
Code Structure Analysis
File Information
- Pattern Number: 67
- Pattern Name: 67 Ontology Based
- File Path:
patterns\memory_knowledge\67_ontology_based.py - Total Lines: 328
Detailed Code Explanation
Module Docstring
"""
Pattern 67: Ontology-Based Pattern
Formal knowledge representation using ontologies with classes, properties, relationships,
inheritance, and logical reasoning capabilities.
"""
Explanation: This module docstring describes the purpose and key features of the 67 Ontology Based pattern.
Imports Section
from typing import Dict, Any, List, Set, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI # First 20 imports shown
Explanation: The module imports necessary dependencies:
– from typing import Dict, Any, List, Set, Optional, Tuple: Type hints for better code documentation
– from dataclasses import dataclass, field: Data class decorators for clean data structures
– from datetime import datetime: Date and time utilities
– from enum import Enum: Enumeration support for fixed sets of values
– from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration
– from langchain_openai import ChatOpenAI: LangChain framework integration
Enumerations
PropertyType (Line 16)
class PropertyType(Enum):
DATA_PROPERTY
OBJECT_PROPERTY
FUNCTIONAL
TRANSITIVE
SYMMETRIC
Purpose: Types of ontology properties.
Values:
– DATA_PROPERTY: Represents data property
– OBJECT_PROPERTY: Represents object property
– FUNCTIONAL: Represents functional
– TRANSITIVE: Represents transitive
– SYMMETRIC: Represents symmetric
Classes
PropertyType (Line 16)
Description: Types of ontology properties.
OntologyProperty (Line 26)
Description: A property definition in ontology.
OntologyClass (Line 36)
Description: A class in the ontology.
Methods:
get_all_properties (Line 44)
def get_all_properties(self, ontology):
"""Get all properties including inherited ones."""
Purpose: Get all properties including inherited ones.
Parameters:
– self: Instance reference
– ontology: Parameter for ontology
OntologyRelation (Line 61)
Description: A relationship between entities.
OntologyInstance (Line 71)
Description: An instance of an ontology class.
OntologyReasoner (Line 79)
Description: Reasoning engine for ontology.
Methods:
__init__ (Line 82)
def __init__(self, ontology):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
– ontology: Parameter for ontology
is_subclass_of (Line 86)
def is_subclass_of(self, child, parent):
"""Check if child is a subclass of parent."""
Purpose: Check if child is a subclass of parent.
Parameters:
– self: Instance reference
– child: Parameter for child
– parent: Parameter for parent
is_instance_of (Line 104)
def is_instance_of(self, instance_name, class_name):
"""Check if instance is of given class (including subclasses)."""
Purpose: Check if instance is of given class (including subclasses).
Parameters:
– self: Instance reference
– instance_name: Parameter for instance_name
– class_name: Parameter for class_name
infer_transitive_relations (Line 112)
def infer_transitive_relations(self, predicate):
"""Infer transitive relations (if A->B and B->C then A->C)."""
Purpose: Infer transitive relations (if A->B and B->C then A->C).
Parameters:
– self: Instance reference
– predicate: Parameter for predicate
query_with_reasoning (Line 159)
def query_with_reasoning(self, query):
"""Answer query using ontology reasoning."""
Purpose: Answer query using ontology reasoning.
Parameters:
– self: Instance reference
– query: Parameter for query
OntologyBasedPattern (Line 184)
Description: Ontology-Based Pattern – Formal knowledge representation with reasoning.
Provides structured knowledge representation using classes, properties, instances, and relationships with inheritance and logical inference.
Methods:
__init__ (Line 192)
def __init__(self, model_name):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
– model_name: LLM model to use (e.g., “gpt-4”)
add_class (Line 199)
def add_class(self, name, parent, properties, constraints):
"""Add a class to ontology."""
Purpose: Add a class to ontology.
Parameters:
– self: Instance reference
– name: Identifier or label
– parent: Parameter for parent
– properties: Parameter for properties
– constraints: Parameter for constraints
add_instance (Line 212)
def add_instance(self, class_name, instance_name, properties):
"""Add an instance of a class."""
Purpose: Add an instance of a class.
Parameters:
– self: Instance reference
– class_name: Parameter for class_name
– instance_name: Parameter for instance_name
– properties: Parameter for properties
add_relation (Line 227)
def add_relation(self, subject, predicate, obj):
"""Add a relation between entities."""
Purpose: Add a relation between entities.
Parameters:
– self: Instance reference
– subject: Parameter for subject
– predicate: Parameter for predicate
– obj: Parameter for obj
query (Line 233)
def query(self, question):
"""Query the ontology using natural language."""
Purpose: Query the ontology using natural language.
Parameters:
– self: Instance reference
– question: Parameter for question
_describe_ontology (Line 253)
def _describe_ontology(self):
"""Describe the ontology structure."""
Purpose: Describe the ontology structure.
Parameters:
– self: Instance reference
run (Line 273)
def run(self, queries):
"""Run queries against the ontology."""
Purpose: Run queries against the ontology.
Parameters:
– self: Instance reference
– queries: Parameter for queries
Design Patterns Used
- Strategy Pattern: Different execution strategies for various scenarios
- State Pattern: Managing different states in the workflow
- Chain of Responsibility: Sequential processing through workflow nodes
- Observer Pattern: Tracking and monitoring pattern execution
- Factory Pattern: Creating instances with proper configuration
Usage Example
# Initialize the pattern
pattern = PropertyType(name="example", model_name="gpt-4")
# Configure and execute
result = pattern.execute(data={"key": "value"})
# Check results
print(result)
Key Concepts
- LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
- Workflow Orchestration: LangGraph manages execution flow through nodes
- State Management: TypedDict defines workflow state structure
- Error Handling: Comprehensive error handling and fallback mechanisms
- Extensibility: Designed for easy customization and extension
Extension Points
This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities
Leave a Reply