"""
Pattern 208: Spatial Reasoning Pattern
Reasons about spatial relationships, locations, distances, orientations,
and navigation. Handles concepts like left/right, near/far, inside/outside.
"""
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
class SpatialRelation(Enum):
"""Types of spatial relationships."""
ABOVE = "above"
BELOW = "below"
LEFT_OF = "left_of"
RIGHT_OF = "right_of"
IN_FRONT_OF = "in_front_of"
BEHIND = "behind"
INSIDE = "inside"
OUTSIDE = "outside"
NEAR = "near"
FAR = "far"
TOUCHING = "touching"
ADJACENT = "adjacent"
@dataclass
class SpatialObject:
"""Object with spatial properties."""
name: str
location: Tuple[float, float, float] # x, y, z coordinates
size: Tuple[float, float, float] # width, height, depth
orientation: float = 0.0 # degrees
@dataclass
class SpatialQuery:
"""Query about spatial relationships."""
query_text: str
objects_involved: List[str]
query_type: str # "relation", "path", "distance", "visibility"
class SpatialReasoningAgent:
"""Agent that performs spatial reasoning."""
def __init__(self, name: str, model_name: str = "gpt-4"):
self.name = name
self.llm = ChatOpenAI(model=model_name, temperature=0.3)
self.objects: Dict[str, SpatialObject] = {}
def add_object(self, obj: SpatialObject):
"""Add object to spatial environment."""
self.objects[obj.name] = obj
def determine_relation(self, obj1_name: str, obj2_name: str) -> str:
"""Determine spatial relation between two objects."""
if obj1_name not in self.objects or obj2_name not in self.objects:
return "unknown"
obj1 = self.objects[obj1_name]
obj2 = self.objects[obj2_name]
dx = obj2.location[0] - obj1.location[0]
dy = obj2.location[1] - obj1.location[1]
dz = obj2.location[2] - obj1.location[2]
# Simple spatial reasoning based on coordinates
relations = []
if dz > 0.5:
relations.append("above")
elif dz 0.5:
relations.append("right of")
elif dx 0.5:
relations.append("in front of")
elif dy < -0.5:
relations.append("behind")
distance = (dx**2 + dy**2 + dz**2) ** 0.5
if distance 5.0:
relations.append("far from")
return ", ".join(relations) if relations else "at similar location"
def calculate_distance(self, obj1_name: str, obj2_name: str) -> float:
"""Calculate Euclidean distance between objects."""
if obj1_name not in self.objects or obj2_name not in self.objects:
return -1.0
obj1 = self.objects[obj1_name]
obj2 = self.objects[obj2_name]
dx = obj2.location[0] - obj1.location[0]
dy = obj2.location[1] - obj1.location[1]
dz = obj2.location[2] - obj1.location[2]
return (dx**2 + dy**2 + dz**2) ** 0.5
def find_path(self, start: str, goal: str, obstacles: List[str] = None) -> List[str]:
"""Find path from start to goal avoiding obstacles."""
print(f"\n {self.name}: Finding path from {start} to {goal}...")
if start not in self.objects or goal not in self.objects:
return []
prompt = ChatPromptTemplate.from_messages([
("system", "Plan a path from start to goal in 3D space."),
("human", """Start location: {start}
Goal location: {goal}
Obstacles: {obstacles}
Describe a path as a sequence of waypoints.""")
])
chain = prompt | self.llm
response = chain.invoke({
"start": str(self.objects[start].location),
"goal": str(self.objects[goal].location),
"obstacles": str([self.objects[o].location for o in (obstacles or [])])
})
waypoints = [line.strip() for line in response.content.split("\n") if line.strip()]
print(f" Path: {len(waypoints)} waypoints")
return waypoints
def check_visibility(self, observer: str, target: str) -> bool:
"""Check if target is visible from observer."""
if observer not in self.objects or target not in self.objects:
return False
obs = self.objects[observer]
tgt = self.objects[target]
# Simple line-of-sight check (no obstacles for now)
distance = self.calculate_distance(observer, target)
return distance Dict[str, Any]:
"""Reason about spatial scene from natural language."""
print(f"\n {self.name}: Reasoning about scene...")
prompt = ChatPromptTemplate.from_messages([
("system", "Analyze spatial relationships in scene description."),
("human", """Scene: {scene}
Identify:
1. Objects and their locations
2. Spatial relationships between objects
3. Possible navigation paths
Provide structured analysis.""")
])
chain = prompt | self.llm
response = chain.invoke({"scene": scene_description})
return {
"scene": scene_description,
"analysis": response.content,
"object_count": len(self.objects)
}
class SpatialReasoningPattern:
"""
Spatial Reasoning Pattern - Understand space and location.
Handles spatial relationships, distances, paths, visibility,
and scene understanding.
"""
def __init__(self):
self.agents: Dict[str, SpatialReasoningAgent] = {}
def register_agent(self, agent: SpatialReasoningAgent):
"""Register spatial reasoning agent."""
self.agents[agent.name] = agent
def run(self, queries: List[SpatialQuery]) -> Dict[str, Any]:
"""Execute spatial reasoning queries."""
print("=== Spatial Reasoning Pattern ===\n")
for agent in self.agents.values():
for query in queries:
if query.query_type == "relation" and len(query.objects_involved) >= 2:
relation = agent.determine_relation(
query.objects_involved[0],
query.objects_involved[1]
)
print(f"\n Relation: {query.objects_involved[0]} is {relation} {query.objects_involved[1]}")
elif query.query_type == "distance" and len(query.objects_involved) >= 2:
distance = agent.calculate_distance(
query.objects_involved[0],
query.objects_involved[1]
)
print(f"\n Distance: {distance:.2f} units")
elif query.query_type == "path" and len(query.objects_involved) >= 2:
agent.find_path(
query.objects_involved[0],
query.objects_involved[1]
)
elif query.query_type == "scene":
agent.reason_about_scene(query.query_text)
return {
"pattern": "SpatialReasoning",
"agents": len(self.agents),
"queries_processed": len(queries)
}
# Example usage
if __name__ == "__main__":
print("=== Spatial Reasoning Pattern Demo ===\n")
# Create pattern and agent
pattern = SpatialReasoningPattern()
agent = SpatialReasoningAgent("Navigator")
pattern.register_agent(agent)
# Add objects to spatial environment
print("1. Setting up spatial environment...\n")
agent.add_object(SpatialObject("Table", (0.0, 0.0, 0.0), (2.0, 1.0, 0.8)))
agent.add_object(SpatialObject("Chair", (-1.0, 0.0, 0.0), (0.5, 0.5, 1.0)))
agent.add_object(SpatialObject("Lamp", (0.0, 0.0, 1.5), (0.2, 0.2, 0.5)))
agent.add_object(SpatialObject("Door", (5.0, 0.0, 0.0), (1.0, 0.1, 2.0)))
agent.add_object(SpatialObject("Window", (0.0, 3.0, 1.0), (1.5, 0.1, 1.5)))
# Spatial reasoning queries
print("2. Executing spatial reasoning queries...\n")
queries = [
SpatialQuery(
"Where is the lamp relative to the table?",
["Lamp", "Table"],
"relation"
),
SpatialQuery(
"How far is the door from the chair?",
["Door", "Chair"],
"distance"
),
SpatialQuery(
"Find path from chair to door",
["Chair", "Door"],
"path"
),
SpatialQuery(
"The room has a table in the center with a lamp on it. "
"A chair is to the left, and a door is on the right wall.",
[],
"scene"
)
]
# Run spatial reasoning
summary = pattern.run(queries)
print(f"\n=== Summary ===")
print(f"Queries processed: {summary['queries_processed']}")
print(f"Objects in environment: {len(agent.objects)}")
print("\nā Spatial reasoning: understanding locations and relationships!")
Agentic Architecture Pattern: Spatial Reasoning
Pattern 208: 208 Spatial Reasoning Pattern – Line-by-Line Explanation
Overview
Pattern 208: Spatial Reasoning Pattern
Reasons about spatial relationships, locations, distances, orientations, and navigation. Handles concepts like left/right, near/far, inside/outside.
Code Structure Analysis
File Information
- Pattern Number: 208
- Pattern Name: 208 Spatial Reasoning Pattern
- File Path:
patterns\advanced_reasoning\208_spatial_reasoning_pattern.py - Total Lines: 278
Detailed Code Explanation
Module Docstring
"""
Pattern 208: Spatial Reasoning Pattern
Reasons about spatial relationships, locations, distances, orientations,
and navigation. Handles concepts like left/right, near/far, inside/outside.
"""
Explanation: This module docstring describes the purpose and key features of the 208 Spatial Reasoning Pattern pattern.
Imports Section
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass
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, Optional, Tuple: Type hints for better code documentation
– from dataclasses import dataclass: Data class decorators for clean data structures
– 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
SpatialRelation (Line 15)
class SpatialRelation(Enum):
ABOVE
BELOW
LEFT_OF
RIGHT_OF
IN_FRONT_OF
BEHIND
INSIDE
OUTSIDE
NEAR
FAR
TOUCHING
ADJACENT
Purpose: Types of spatial relationships.
Values:
– ABOVE: Represents above
– BELOW: Represents below
– LEFT_OF: Represents left of
– RIGHT_OF: Represents right of
– IN_FRONT_OF: Represents in front of
– BEHIND: Represents behind
– INSIDE: Represents inside
– OUTSIDE: Represents outside
– NEAR: Represents near
– FAR: Represents far
– TOUCHING: Represents touching
– ADJACENT: Represents adjacent
Classes
SpatialRelation (Line 15)
Description: Types of spatial relationships.
SpatialObject (Line 32)
Description: Object with spatial properties.
SpatialQuery (Line 41)
Description: Query about spatial relationships.
SpatialReasoningAgent (Line 48)
Description: Agent that performs spatial reasoning.
Methods:
__init__ (Line 51)
def __init__(self, name, model_name):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
– name: Identifier or label
– model_name: LLM model to use (e.g., “gpt-4”)
add_object (Line 56)
def add_object(self, obj):
"""Add object to spatial environment."""
Purpose: Add object to spatial environment.
Parameters:
– self: Instance reference
– obj: Parameter for obj
determine_relation (Line 60)
def determine_relation(self, obj1_name, obj2_name):
"""Determine spatial relation between two objects."""
Purpose: Determine spatial relation between two objects.
Parameters:
– self: Instance reference
– obj1_name: Parameter for obj1_name
– obj2_name: Parameter for obj2_name
calculate_distance (Line 97)
def calculate_distance(self, obj1_name, obj2_name):
"""Calculate Euclidean distance between objects."""
Purpose: Calculate Euclidean distance between objects.
Parameters:
– self: Instance reference
– obj1_name: Parameter for obj1_name
– obj2_name: Parameter for obj2_name
find_path (Line 111)
def find_path(self, start, goal, obstacles):
"""Find path from start to goal avoiding obstacles."""
Purpose: Find path from start to goal avoiding obstacles.
Parameters:
– self: Instance reference
– start: Parameter for start
– goal: Parameter for goal
– obstacles: Parameter for obstacles
check_visibility (Line 138)
def check_visibility(self, observer, target):
"""Check if target is visible from observer."""
Purpose: Check if target is visible from observer.
Parameters:
– self: Instance reference
– observer: Parameter for observer
– target: Parameter for target
reason_about_scene (Line 150)
def reason_about_scene(self, scene_description):
"""Reason about spatial scene from natural language."""
Purpose: Reason about spatial scene from natural language.
Parameters:
– self: Instance reference
– scene_description: Parameter for scene_description
SpatialReasoningPattern (Line 176)
Description: Spatial Reasoning Pattern – Understand space and location.
Handles spatial relationships, distances, paths, visibility, and scene understanding.
Methods:
__init__ (Line 184)
def __init__(self):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
register_agent (Line 187)
def register_agent(self, agent):
"""Register spatial reasoning agent."""
Purpose: Register spatial reasoning agent.
Parameters:
– self: Instance reference
– agent: Parameter for agent
run (Line 191)
def run(self, queries):
"""Execute spatial reasoning queries."""
Purpose: Execute spatial reasoning queries.
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 = SpatialRelation(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