"""
Pattern 210: Counterfactual Reasoning Pattern
Reasons about "what if" scenarios and alternative possibilities.
Explores hypothetical situations contrary to actual facts.
"""
from typing import Dict, Any, List, Optional
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 CounterfactualType(Enum):
"""Types of counterfactual reasoning."""
WHAT_IF = "what_if" # What if X had happened?
IF_ONLY = "if_only" # If only X had been different
ALTERNATIVE = "alternative" # Alternative possible outcomes
MINIMAL_CHANGE = "minimal_change" # Minimal change for different outcome
@dataclass
class ActualWorld:
"""The actual state of affairs."""
facts: List[str]
outcome: str
context: Dict[str, Any] = field(default_factory=dict)
@dataclass
class CounterfactualWorld:
"""A hypothetical alternative world."""
change: str # What's different from actual
predicted_facts: List[str]
predicted_outcome: str
plausibility: float = 0.5 # 0.0 to 1.0
reasoning: str = ""
@dataclass
class CounterfactualQuery:
"""Query about counterfactual scenario."""
actual_world: ActualWorld
counterfactual_change: str
question: str
class CounterfactualReasoningAgent:
"""Agent that performs counterfactual reasoning."""
def __init__(self, name: str, model_name: str = "gpt-4"):
self.name = name
self.llm = ChatOpenAI(model=model_name, temperature=0.7)
self.scenarios: List[CounterfactualWorld] = []
def generate_counterfactual(self, actual: ActualWorld,
change: str) -> CounterfactualWorld:
"""Generate counterfactual scenario with change."""
print(f"\n {self.name}: Generating counterfactual: '{change}'...")
actual_facts = "\n".join([f"- {fact}" for fact in actual.facts])
prompt = ChatPromptTemplate.from_messages([
("system", "You are a counterfactual reasoning expert. Imagine alternative "
"scenarios and predict consequences."),
("human", """Actual World:
Facts:
{facts}
Outcome: {outcome}
Counterfactual Change: {change}
If this change had occurred, what would be different?
Provide:
1. New facts (what changes)
2. Alternative outcome
3. Plausibility (0.0-1.0)
4. Reasoning chain
Format:
FACTS:
OUTCOME:
PLAUSIBILITY:
REASONING: """)
])
chain = prompt | self.llm
response = chain.invoke({
"facts": actual_facts,
"outcome": actual.outcome,
"change": change
})
# Parse response
new_facts = []
new_outcome = ""
plausibility = 0.5
reasoning = ""
current_section = None
for line in response.content.split("\n"):
line = line.strip()
if line.startswith("FACTS:"):
current_section = "facts"
continue
elif line.startswith("OUTCOME:"):
new_outcome = line.replace("OUTCOME:", "").strip()
current_section = "outcome"
continue
elif line.startswith("PLAUSIBILITY:"):
try:
plausibility = float(line.replace("PLAUSIBILITY:", "").strip())
except:
plausibility = 0.5
current_section = "plausibility"
continue
elif line.startswith("REASONING:"):
current_section = "reasoning"
reasoning = line.replace("REASONING:", "").strip()
continue
if current_section == "facts" and line.startswith("-"):
new_facts.append(line.lstrip("- "))
elif current_section == "reasoning" and line:
reasoning += " " + line
counterfactual = CounterfactualWorld(
change=change,
predicted_facts=new_facts if new_facts else ["Unknown changes"],
predicted_outcome=new_outcome or "Unknown outcome",
plausibility=plausibility,
reasoning=reasoning.strip() or "Hypothetical alternative"
)
self.scenarios.append(counterfactual)
print(f" Predicted outcome: {counterfactual.predicted_outcome[:80]}...")
print(f" Plausibility: {counterfactual.plausibility}")
return counterfactual
def compare_worlds(self, actual: ActualWorld,
counterfactual: CounterfactualWorld) -> Dict[str, Any]:
"""Compare actual and counterfactual worlds."""
print(f"\n {self.name}: Comparing worlds...")
prompt = ChatPromptTemplate.from_messages([
("system", "Compare actual and counterfactual scenarios."),
("human", """Actual Outcome: {actual_outcome}
Counterfactual Outcome: {counterfactual_outcome}
What are the key differences? What insights does this comparison reveal?""")
])
chain = prompt | self.llm
response = chain.invoke({
"actual_outcome": actual.outcome,
"counterfactual_outcome": counterfactual.predicted_outcome
})
comparison = {
"actual": actual.outcome,
"counterfactual": counterfactual.predicted_outcome,
"analysis": response.content,
"change": counterfactual.change
}
return comparison
def identify_critical_factors(self, actual: ActualWorld) -> List[str]:
"""Identify factors critical to the actual outcome."""
print(f"\n {self.name}: Identifying critical factors...")
facts_text = "\n".join(actual.facts)
prompt = ChatPromptTemplate.from_messages([
("system", "Identify which factors were most critical to the outcome."),
("human", """Facts:
{facts}
Outcome: {outcome}
Which factors were most critical? List 2-3 key factors.""")
])
chain = prompt | self.llm
response = chain.invoke({
"facts": facts_text,
"outcome": actual.outcome
})
factors = [line.strip().lstrip("0123456789.-) ")
for line in response.content.split("\n")
if line.strip()]
print(f" Identified {len(factors)} critical factors")
return factors[:3]
def explore_minimal_changes(self, actual: ActualWorld,
desired_outcome: str) -> CounterfactualWorld:
"""Find minimal change needed for desired outcome."""
print(f"\n {self.name}: Finding minimal change for desired outcome...")
facts_text = "\n".join(actual.facts)
prompt = ChatPromptTemplate.from_messages([
("system", "Find the minimal change that would lead to desired outcome."),
("human", """Current Facts:
{facts}
Current Outcome: {current_outcome}
Desired Outcome: {desired_outcome}
What is the SMALLEST change that would lead to the desired outcome?
Explain the change and why it would work.""")
])
chain = prompt | self.llm
response = chain.invoke({
"facts": facts_text,
"current_outcome": actual.outcome,
"desired_outcome": desired_outcome
})
minimal_change = CounterfactualWorld(
change=response.content.split("\n")[0],
predicted_facts=[],
predicted_outcome=desired_outcome,
plausibility=0.7,
reasoning=response.content
)
print(f" Minimal change: {minimal_change.change[:80]}...")
return minimal_change
class CounterfactualReasoningPattern:
"""
Counterfactual Reasoning Pattern - Explore "what if" scenarios.
Generates alternative scenarios, compares outcomes, identifies
critical factors, and explores minimal changes.
"""
def __init__(self):
self.agents: Dict[str, CounterfactualReasoningAgent] = {}
def register_agent(self, agent: CounterfactualReasoningAgent):
"""Register counterfactual reasoning agent."""
self.agents[agent.name] = agent
def run(self, queries: List[CounterfactualQuery]) -> Dict[str, Any]:
"""Execute counterfactual reasoning queries."""
print("=== Counterfactual Reasoning Pattern ===\n")
comparisons = []
for agent in self.agents.values():
for query in queries:
# Generate counterfactual
counterfactual = agent.generate_counterfactual(
query.actual_world,
query.counterfactual_change
)
# Compare worlds
comparison = agent.compare_worlds(
query.actual_world,
counterfactual
)
comparisons.append(comparison)
return {
"pattern": "CounterfactualReasoning",
"agents": len(self.agents),
"queries_processed": len(queries),
"scenarios_explored": sum(len(a.scenarios) for a in self.agents.values()),
"comparisons": comparisons
}
# Example usage
if __name__ == "__main__":
print("=== Counterfactual Reasoning Pattern Demo ===\n")
# Create pattern and agent
pattern = CounterfactualReasoningPattern()
agent = CounterfactualReasoningAgent("WhatIfAnalyst")
pattern.register_agent(agent)
# Actual world scenarios
print("1. Defining actual world scenarios...\n")
# Scenario 1: Historical event
actual1 = ActualWorld(
facts=[
"Columbus sailed west in 1492",
"He had support from Spanish monarchy",
"Navigation technology was available"
],
outcome="European discovery of the Americas in 1492"
)
# Scenario 2: Business decision
actual2 = ActualWorld(
facts=[
"Company invested heavily in mobile apps",
"Market shifted to mobile-first",
"Competitors were slow to adapt"
],
outcome="Company became market leader in mobile space"
)
# Counterfactual queries
print("2. Exploring counterfactual scenarios...\n")
queries = [
CounterfactualQuery(
actual_world=actual1,
counterfactual_change="What if the Spanish monarchy had refused funding?",
question="Would the Americas still be discovered in 1492?"
),
CounterfactualQuery(
actual_world=actual2,
counterfactual_change="What if the company had focused on desktop instead?",
question="Would they still be market leader?"
)
]
# Run counterfactual reasoning
summary = pattern.run(queries)
print(f"\n=== Summary ===")
print(f"Queries processed: {summary['queries_processed']}")
print(f"Scenarios explored: {summary['scenarios_explored']}")
print(f"\n=== Counterfactual Analysis ===")
for i, comp in enumerate(summary['comparisons'], 1):
print(f"\nScenario {i}:")
print(f" Change: {comp['change'][:60]}...")
print(f" Actual: {comp['actual'][:60]}...")
print(f" Counterfactual: {comp['counterfactual'][:60]}...")
# Additional: Identify critical factors
print(f"\n=== Critical Factors ===")
critical = agent.identify_critical_factors(actual1)
for factor in critical:
print(f" - {factor}")
print("\nā Counterfactual reasoning: exploring alternative realities!")
Agentic Architecture Pattern: Counterfactual Reasoning
Pattern 210: 210 Counterfactual Reasoning Pattern – Line-by-Line Explanation
Overview
Pattern 210: Counterfactual Reasoning Pattern
Reasons about “what if” scenarios and alternative possibilities. Explores hypothetical situations contrary to actual facts.
Code Structure Analysis
File Information
- Pattern Number: 210
- Pattern Name: 210 Counterfactual Reasoning Pattern
- File Path:
patterns\advanced_reasoning\210_counterfactual_reasoning_pattern.py - Total Lines: 352
Detailed Code Explanation
Module Docstring
"""
Pattern 210: Counterfactual Reasoning Pattern
Reasons about "what if" scenarios and alternative possibilities.
Explores hypothetical situations contrary to actual facts.
"""
Explanation: This module docstring describes the purpose and key features of the 210 Counterfactual Reasoning Pattern pattern.
Imports Section
from typing import Dict, Any, List, Optional
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, Optional: 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
CounterfactualType (Line 16)
class CounterfactualType(Enum):
WHAT_IF
IF_ONLY
ALTERNATIVE
MINIMAL_CHANGE
Purpose: Types of counterfactual reasoning.
Values:
– WHAT_IF: Represents what if
– IF_ONLY: Represents if only
– ALTERNATIVE: Represents alternative
– MINIMAL_CHANGE: Represents minimal change
Classes
CounterfactualType (Line 16)
Description: Types of counterfactual reasoning.
ActualWorld (Line 25)
Description: The actual state of affairs.
CounterfactualWorld (Line 33)
Description: A hypothetical alternative world.
CounterfactualQuery (Line 43)
Description: Query about counterfactual scenario.
CounterfactualReasoningAgent (Line 50)
Description: Agent that performs counterfactual reasoning.
Methods:
__init__ (Line 53)
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”)
generate_counterfactual (Line 58)
def generate_counterfactual(self, actual, change):
"""Generate counterfactual scenario with change."""
Purpose: Generate counterfactual scenario with change.
Parameters:
– self: Instance reference
– actual: Parameter for actual
– change: Parameter for change
compare_worlds (Line 144)
def compare_worlds(self, actual, counterfactual):
"""Compare actual and counterfactual worlds."""
Purpose: Compare actual and counterfactual worlds.
Parameters:
– self: Instance reference
– actual: Parameter for actual
– counterfactual: Parameter for counterfactual
identify_critical_factors (Line 172)
def identify_critical_factors(self, actual):
"""Identify factors critical to the actual outcome."""
Purpose: Identify factors critical to the actual outcome.
Parameters:
– self: Instance reference
– actual: Parameter for actual
explore_minimal_changes (Line 201)
def explore_minimal_changes(self, actual, desired_outcome):
"""Find minimal change needed for desired outcome."""
Purpose: Find minimal change needed for desired outcome.
Parameters:
– self: Instance reference
– actual: Parameter for actual
– desired_outcome: Parameter for desired_outcome
CounterfactualReasoningPattern (Line 240)
Description: Counterfactual Reasoning Pattern – Explore “what if” scenarios.
Generates alternative scenarios, compares outcomes, identifies critical factors, and explores minimal changes.
Methods:
__init__ (Line 248)
def __init__(self):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
register_agent (Line 251)
def register_agent(self, agent):
"""Register counterfactual reasoning agent."""
Purpose: Register counterfactual reasoning agent.
Parameters:
– self: Instance reference
– agent: Parameter for agent
run (Line 255)
def run(self, queries):
"""Execute counterfactual reasoning queries."""
Purpose: Execute counterfactual 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 = CounterfactualType(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