"""
Pattern 124: Ontology-Based Reasoning
Description:
Uses formal ontologies (OWL, RDF, SKOS) for domain knowledge
representation and semantic reasoning.
Components:
- Ontology definition
- Class hierarchies
- Property definitions
- Inference rules
Use Cases:
- Semantic reasoning
- Domain expertise
- Formal logic
LangChain Implementation:
Implements ontology concepts with LLM-based reasoning over formal structures.
"""
import os
from typing import List, Dict, Any, Set
from dataclasses import dataclass
from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
load_dotenv()
@dataclass
class OntologyClass:
"""Represents a class in the ontology."""
name: str
parent: str = None
properties: List[str] = None
description: str = ""
def __post_init__(self):
if self.properties is None:
self.properties = []
@dataclass
class OntologyProperty:
"""Represents a property in the ontology."""
name: str
domain: str # Class it belongs to
range: str # Type of value
description: str = ""
@dataclass
class OntologyInstance:
"""Represents an instance of a class."""
id: str
class_name: str
properties: Dict[str, Any]
class Ontology:
"""Formal ontology for domain knowledge."""
def __init__(self, name: str):
self.name = name
self.classes: Dict[str, OntologyClass] = {}
self.properties: Dict[str, OntologyProperty] = {}
self.instances: Dict[str, OntologyInstance] = {}
def add_class(self, ontology_class: OntologyClass):
"""Add a class to the ontology."""
self.classes[ontology_class.name] = ontology_class
def add_property(self, ontology_property: OntologyProperty):
"""Add a property to the ontology."""
self.properties[ontology_property.name] = ontology_property
def add_instance(self, instance: OntologyInstance):
"""Add an instance to the ontology."""
self.instances[instance.id] = instance
def get_subclasses(self, class_name: str) -> List[str]:
"""Get all subclasses of a class."""
subclasses = []
for cls_name, cls in self.classes.items():
if cls.parent == class_name:
subclasses.append(cls_name)
# Recursively get subclasses
subclasses.extend(self.get_subclasses(cls_name))
return subclasses
def get_superclasses(self, class_name: str) -> List[str]:
"""Get all superclasses of a class."""
superclasses = []
current = self.classes.get(class_name)
while current and current.parent:
superclasses.append(current.parent)
current = self.classes.get(current.parent)
return superclasses
def is_subclass_of(self, class1: str, class2: str) -> bool:
"""Check if class1 is a subclass of class2."""
return class2 in self.get_superclasses(class1)
def get_instances_of_class(self, class_name: str, include_subclasses: bool = True) -> List[OntologyInstance]:
"""Get all instances of a class."""
instances = [inst for inst in self.instances.values() if inst.class_name == class_name]
if include_subclasses:
subclasses = self.get_subclasses(class_name)
for subclass in subclasses:
instances.extend([inst for inst in self.instances.values() if inst.class_name == subclass])
return instances
class OntologyReasoningAgent:
"""Agent that performs reasoning using ontologies."""
def __init__(self, ontology: Ontology, model_name: str = "gpt-4"):
self.ontology = ontology
self.llm = ChatOpenAI(model=model_name, temperature=0.3)
def classify_instance(self, instance_description: str) -> str:
"""Classify an instance into ontology classes."""
# Get ontology structure
ontology_structure = "Ontology Classes:\n"
for cls_name, cls in self.ontology.classes.items():
ontology_structure += f"- {cls_name}"
if cls.parent:
ontology_structure += f" (subclass of {cls.parent})"
ontology_structure += f": {cls.description}\n"
classification_prompt = ChatPromptTemplate.from_messages([
("system", "Classify this instance into the most appropriate ontology class."),
("user", """{ontology_structure}
Instance to classify: {instance}
Most appropriate class:""")
])
chain = classification_prompt | self.llm | StrOutputParser()
return chain.invoke({
"ontology_structure": ontology_structure,
"instance": instance_description
})
def infer_properties(self, instance_id: str) -> Dict[str, Any]:
"""Infer properties of an instance based on ontology."""
instance = self.ontology.instances.get(instance_id)
if not instance:
return {}
# Get class hierarchy
superclasses = self.ontology.get_superclasses(instance.class_name)
# Collect inherited properties
inherited_properties = {}
for superclass in superclasses:
cls = self.ontology.classes.get(superclass)
if cls:
for prop in cls.properties:
if prop not in instance.properties:
inherited_properties[prop] = f"inherited from {superclass}"
inference_prompt = ChatPromptTemplate.from_messages([
("system", "Infer additional properties for this instance based on ontology."),
("user", """Instance: {instance_id}
Class: {class_name}
Superclasses: {superclasses}
Current Properties: {current_props}
Infer likely values for these properties:
{inherited_props}""")
])
chain = inference_prompt | self.llm | StrOutputParser()
inferences = chain.invoke({
"instance_id": instance_id,
"class_name": instance.class_name,
"superclasses": ", ".join(superclasses),
"current_props": str(instance.properties),
"inherited_props": ", ".join(inherited_properties.keys())
})
return {"inferences": inferences, "inherited": inherited_properties}
def answer_semantic_query(self, query: str) -> str:
"""Answer semantic queries using ontology."""
# Serialize ontology info
ontology_info = f"Ontology: {self.ontology.name}\n\n"
ontology_info += "Classes:\n"
for cls_name, cls in self.ontology.classes.items():
ontology_info += f"- {cls_name}: {cls.description}\n"
ontology_info += "\nInstances:\n"
for inst_id, inst in list(self.ontology.instances.items())[:10]: # Limit to 10
ontology_info += f"- {inst_id} ({inst.class_name}): {inst.properties}\n"
query_prompt = ChatPromptTemplate.from_messages([
("system", "Answer this semantic query using the ontology knowledge."),
("user", """{ontology_info}
Query: {query}
Answer:""")
])
chain = query_prompt | self.llm | StrOutputParser()
return chain.invoke({
"ontology_info": ontology_info,
"query": query
})
def check_consistency(self) -> str:
"""Check ontology consistency."""
consistency_prompt = ChatPromptTemplate.from_messages([
("system", "Check this ontology for logical consistency and conflicts."),
("user", """Ontology Structure:
{structure}
Check for:
1. Circular class hierarchies
2. Property domain/range conflicts
3. Instance classification errors
4. Missing definitions""")
])
structure = ""
for cls_name, cls in self.ontology.classes.items():
structure += f"{cls_name} -> {cls.parent}\n"
chain = consistency_prompt | self.llm | StrOutputParser()
return chain.invoke({"structure": structure})
def demonstrate_ontology_based_reasoning():
"""Demonstrate ontology-based reasoning pattern."""
print("=== Ontology-Based Reasoning Pattern ===\n")
# Create ontology
print("1. Building Domain Ontology")
print("-" * 50)
ontology = Ontology("VehicleOntology")
# Define class hierarchy
ontology.add_class(OntologyClass(
name="Vehicle",
description="Any mode of transportation"
))
ontology.add_class(OntologyClass(
name="LandVehicle",
parent="Vehicle",
description="Vehicles that travel on land"
))
ontology.add_class(OntologyClass(
name="WaterVehicle",
parent="Vehicle",
description="Vehicles that travel on water"
))
ontology.add_class(OntologyClass(
name="AirVehicle",
parent="Vehicle",
description="Vehicles that travel in air"
))
ontology.add_class(OntologyClass(
name="Car",
parent="LandVehicle",
properties=["num_wheels", "fuel_type", "num_doors"],
description="Four-wheeled motor vehicle"
))
ontology.add_class(OntologyClass(
name="Bicycle",
parent="LandVehicle",
properties=["num_wheels", "has_motor"],
description="Two-wheeled human-powered vehicle"
))
ontology.add_class(OntologyClass(
name="Boat",
parent="WaterVehicle",
properties=["propulsion_type", "max_speed"],
description="Watercraft of any size"
))
# Add instances
ontology.add_instance(OntologyInstance(
id="vehicle1",
class_name="Car",
properties={"num_wheels": 4, "fuel_type": "electric", "num_doors": 4}
))
ontology.add_instance(OntologyInstance(
id="vehicle2",
class_name="Bicycle",
properties={"num_wheels": 2, "has_motor": False}
))
print(f"✓ Created ontology with {len(ontology.classes)} classes")
print(f"✓ Added {len(ontology.instances)} instances")
print()
# Create reasoning agent
agent = OntologyReasoningAgent(ontology)
# Classification
print("2. Instance Classification")
print("-" * 50)
test_descriptions = [
"A motorized two-wheeler used for transportation",
"A watercraft with sails",
"A four-wheeled vehicle with an engine"
]
for desc in test_descriptions:
print(f"\nDescription: {desc}")
classification = agent.classify_instance(desc)
print(f"Classification: {classification}")
# Property inference
print("\n\n3. Property Inference")
print("-" * 50)
inferred = agent.infer_properties("vehicle1")
print(f"\nInferred properties for vehicle1:")
print(inferred)
# Semantic queries
print("\n\n4. Semantic Queries")
print("-" * 50)
queries = [
"What are all the types of land vehicles?",
"What properties do cars have?",
"How are bicycles and cars related?"
]
for query in queries:
print(f"\nQuery: {query}")
answer = agent.answer_semantic_query(query)
print(f"Answer: {answer}")
# Consistency check
print("\n\n5. Ontology Consistency Check")
print("-" * 50)
consistency = agent.check_consistency()
print(consistency)
print("\n=== Summary ===")
print("Ontology-based reasoning demonstrated with:")
print("- Formal class hierarchies")
print("- Property definitions")
print("- Instance classification")
print("- Property inference")
print("- Semantic queries")
print("- Consistency checking")
if __name__ == "__main__":
demonstrate_ontology_based_reasoning()
Agentic Design Pattern: Ontology Based Reasoning
Pattern 124: Ontology Based Reasoning – Line-by-Line Explanation
Overview
This document provides a detailed line-by-line explanation of the Ontology Based Reasoning pattern implementation.
Pattern Description
Pattern 124: Ontology-Based Reasoning
Description: Uses formal ontologies (OWL, RDF, SKOS) for domain knowledge representation and semantic reasoning.
Components: – Ontology definition – Class hierarchies – Property definitions – Inference rules
Use Cases: – Semantic reasoning – Domain expertise – Formal logic
LangChain Implementation: Implements ontology concepts with LLM-based reasoning over formal structures.
File Statistics
- Total Lines: 373
- Classes: 5
- Functions: 1
- Constants: 0
Module Documentation
"""
Pattern 124: Ontology-Based Reasoning
Description:
Uses formal ontologies (OWL, RDF, SKOS) for domain knowledge
representation and semantic reasoning.
Components:
- Ontology definition
- Class hierarchies
- Property definitions
- Inference rules
Use Cases:
- Semantic reasoning
- Domain expertise
- Formal logic
LangChain Implementation:
Implements ontology concepts with LLM-based reasoning over formal structures.
"""
Purpose: This module implements the Ontology Based Reasoning pattern, providing a structured approach to agent design and implementation.
Import Statements
Standard Library Imports
import dataclasses.dataclass
import langchain_core.output_parsers.StrOutputParser
import langchain_core.prompts.ChatPromptTemplate
import os
import typing.Any
import typing.Dict
import typing.List
import typing.Set
Purpose: Core Python functionality:
– dataclasses.dataclass: Simplified class definitions with @dataclass
– langchain_core.output_parsers.StrOutputParser: Regular expression operations
– langchain_core.prompts.ChatPromptTemplate: Regular expression operations
– os: Environment variable access and system operations
– typing.Any: Type hints for better code documentation (List, Dict, Any, Optional)
– typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional)
– typing.List: Type hints for better code documentation (List, Dict, Any, Optional)
– typing.Set: Type hints for better code documentation (List, Dict, Any, Optional)
LangChain Imports
from langchain_openai.ChatOpenAI
Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows
Other Imports
from dotenv.load_dotenv
Purpose: Additional dependencies for specialized functionality.
Classes
Class: OntologyClass
Lines 36-45
Purpose:
Represents a class in the ontology.
Methods:
__post_init__(self)
Lines 43-45
Purpose: Post Init operation.
Parameters:
Class: OntologyProperty
Lines 49-54
Purpose:
Represents a property in the ontology.
Class: OntologyInstance
Lines 58-62
Purpose:
Represents an instance of a class.
Class: Ontology
Lines 65-118
Purpose:
Formal ontology for domain knowledge.
Methods:
__init__(self, name)
Lines 68-72
Purpose: Initialize the class instance with required parameters.
Parameters:
– name: Name
add_class(self, ontology_class)
Lines 74-76
Purpose:
Add a class to the ontology.
Parameters:
– ontology_class: Ontology class
add_property(self, ontology_property)
Lines 78-80
Purpose:
Add a property to the ontology.
Parameters:
– ontology_property: Ontology property
add_instance(self, instance)
Lines 82-84
Purpose:
Add an instance to the ontology.
Parameters:
– instance: Instance
get_subclasses(self, class_name)
Lines 86-94
Purpose:
Get all subclasses of a class.
Parameters:
– class_name: Class name
Returns: <ast.Subscript object at 0x000001A06D749290>
get_superclasses(self, class_name)
Lines 96-103
Purpose:
Get all superclasses of a class.
Parameters:
– class_name: Class name
Returns: <ast.Subscript object at 0x000001A06D64BF50>
is_subclass_of(self, class1, class2)
Lines 105-107
Purpose:
Check if class1 is a subclass of class2.
Parameters:
– class1: Class1
– class2: Class2
Returns: bool
get_instances_of_class(self, class_name, include_subclasses)
Lines 109-118
Purpose:
Get all instances of a class.
Parameters:
– class_name: Class name
– include_subclasses: Include subclasses
Returns: <ast.Subscript object at 0x000001A06D60FF90>
Class: OntologyReasoningAgent
Lines 121-239
Purpose:
Agent that performs reasoning using ontologies.
Methods:
__init__(self, ontology, model_name)
Lines 124-126
Purpose: Initialize the class instance with required parameters.
Parameters:
– ontology: Ontology
– model_name: Language model instance
classify_instance(self, instance_description)
Lines 128-151
Purpose:
Classify an instance into ontology classes.
Parameters:
– instance_description: Instance description
Returns: str
infer_properties(self, instance_id)
Lines 153-191
Purpose:
Infer properties of an instance based on ontology.
Parameters:
– instance_id: Instance id
Returns: <ast.Subscript object at 0x000001A06D825C90>
answer_semantic_query(self, query)
Lines 193-218
Purpose:
Answer semantic queries using ontology.
Parameters:
– query: Input query or question
Returns: str
check_consistency(self)
Lines 220-239
Purpose:
Check ontology consistency.
Parameters:
Returns: str
Functions
demonstrate_ontology_based_reasoning()
Lines 242-368
Purpose:
Demonstrate ontology-based reasoning pattern.
Key Concepts Summary
Pattern Overview
The Ontology Based Reasoning pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.
Core Components
- Agent Class: Main implementation of the pattern logic
- Prompt Engineering: Carefully crafted prompts to guide LLM behavior
- Tool Integration: Optional tools for extended functionality
- Memory Management: State handling across interactions
- Output Parsing: Structured response processing
When to Use
✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability
❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)
Best Practices Demonstrated
1. Environment Configuration
- ✅ Use
.envfiles for API keys - ✅ Never hardcode credentials
- ✅ Validate environment variables on startup
2. Error Handling
- ✅ Graceful degradation on failures
- ✅ Informative error messages
- ✅ Logging for debugging
3. Prompt Engineering
- ✅ Clear, specific instructions
- ✅ Structured output formats
- ✅ Few-shot examples when beneficial
4. Code Organization
- ✅ Separation of concerns (class-based design)
- ✅ Type hints for clarity
- ✅ Docstrings for documentation
5. Performance Optimization
- ✅ Appropriate temperature settings
- ✅ Token budget management
- ✅ Caching where applicable
LangChain Components Used
- ChatOpenAI: OpenAI model interface
- Chains: Sequential component composition
Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development
Execution Flow
1. Initialize agent with configuration
↓
2. Load environment and dependencies
↓
3. Prepare prompt with input data
↓
4. Invoke LLM through LangChain
↓
5. Parse and validate response
↓
6. Return formatted result
Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation
Performance Considerations
Computational Cost
- LLM Calls: Primary cost factor (based on tokens)
- Latency: Depends on model and input size
- Memory: Minimal for most patterns
Optimization Strategies
- Prompt Optimization: Minimize tokens while maintaining clarity
- Caching: Cache repeated queries/responses
- Model Selection: Use smaller models when appropriate
- Batch Processing: Group similar requests
- Streaming: For long responses, stream tokens
Cost Estimates (Approximate)
- GPT-3.5-turbo: ~$0.002 per request
- GPT-4: ~$0.03 per request
- GPT-4-turbo: ~$0.01 per request
Actual costs vary based on input/output token counts
Common Pitfalls to Avoid
1. Prompt Design Issues
❌ Vague or ambiguous instructions
✅ Clear, specific, testable prompts
2. Error Handling
❌ Ignoring API errors or rate limits
✅ Implement retry logic and graceful degradation
3. Token Management
❌ Exceeding model token limits
✅ Truncate inputs, chunk processing
4. Security
❌ Exposing API keys in code
✅ Use environment variables
5. Testing
❌ Only testing happy paths
✅ Test edge cases, failures, malformed inputs
6. Cost Management
❌ Unmonitored API usage
✅ Implement budgets, alerts, and monitoring
Further Improvements
Production Enhancements
- Monitoring: Add logging, metrics, tracing
- Validation: Input/output schema validation
- Rate Limiting: Implement request throttling
- Caching: Redis or in-memory cache for responses
- Async Support: Non-blocking operations for scalability
Feature Extensions
- Multi-Model Support: Switch between providers
- Fallback Models: Graceful degradation strategy
- Custom Tools: Domain-specific function calling
- Advanced Memory: Vector stores for long-term memory
- Evaluation: Automated testing and benchmarking
Code Quality
- Testing: Unit tests, integration tests
- Documentation: API docs, usage examples
- Type Safety: Stricter type hints
- Configuration: Externalized config files
Related Patterns
- Pattern 123
- Pattern 125
Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…
Summary
The Ontology Based Reasoning pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:
✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns
🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability
📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/
File: 124_ontology_based_reasoning.py
Total Lines: 373
Pattern Category: AI Agent Design Pattern #124
Generated from actual implementation code
For educational and reference purposes