- What is the Temporal Graph?
- Episode Grouping Logic
- Why Temporal Graphs Are Revolutionary
- Comparison with Traditional Approaches
- Hybrid Architecture: Temporal + Regular Graphs
- Real-World Applications
- Technical Implementation
The Temporal Graph is a sophisticated bi-temporal knowledge graph that revolutionizes how we understand customer journeys by tracking both:
- Valid Time: When a fact was actually true in the real world
- Transaction Time: When the fact was recorded in your database
Example:
- Alice's address changed on Jan 1st (valid time)
- But you recorded this change on Jan 5th (transaction time)
- Customers, products, orders, addresses, etc.
- Each has
valid_fromandvalid_totimestamps - Properties stored as JSONB for flexibility
- Vector embeddings for semantic search
- "customer PURCHASED product"
- "customer LIVES_AT address"
- Also have
valid_fromandvalid_totimestamps
- Raw customer actions (purchase, support request, etc.)
- Trigger the creation/updating of nodes and edges
Primary Rule: Time-based grouping within 1 hour
- Events are grouped into episodes if they occur within 1 hour of each other
- The threshold is defined as
episode_threshold = timedelta(hours=1)
-
Sorting: All events are first sorted by their timestamp in chronological order
-
Sequential Grouping: The algorithm processes events one by one:
- The first event starts a new episode
- Each subsequent event is compared to the last event in the current episode
- If the time difference is ≤ 1 hour, the event is added to the current episode
- If the time difference is > 1 hour, the current episode is finalized and a new episode starts
-
Episode Creation: When an episode is finalized, it includes:
- All events within the time window
- Start time: timestamp of the first event
- End time: timestamp of the last event
- Summary: description of event types (e.g., "Episode with 3 events: purchase, support_request")
Alice Johnson has these events:
10:00 AM - purchase (laptop)
10:15 AM - support_request (setup help)
10:30 AM - purchase (accessories)
2:00 PM - support_request (warranty question)
2:15 PM - purchase (extended warranty)
This creates 2 episodes:
- Episode 1 (10:00-10:30 AM): purchase → support_request → purchase
- Episode 2 (2:00-2:15 PM): support_request → purchase
The gap between 10:30 AM and 2:00 PM (3.5 hours) exceeds the 1-hour threshold, so they become separate episodes.
- See exactly what your system "knew" at any point in time
- Track how customer relationships evolved
- Understand causality: "Did the price change cause the purchase spike?"
- No data loss - every change is preserved
- Audit trail for compliance
- Can "replay" business decisions with historical context
- Track when relationships started/ended
- "Alice was a VIP customer from Jan-Mar, then churned"
- Understand patterns: "Support requests often precede cancellations"
- "Find customers who were active 6 months ago but are quiet now"
- "What products were trending during the holiday season?"
- "Which support issues led to customer churn?"
- Your system "remembers" context across time
- Can answer questions like: "Why did Alice's behavior change?"
- Builds a rich, evolving understanding of each customer
- Semantic search with temporal filters
- "Find customers similar to Alice's profile from last year"
- Hybrid search combining meaning, keywords, and time
-- Traditional approach
UPDATE customers SET status='VIP', tier='Gold' WHERE id='alice';
-- ❌ LOST FOREVER: When did Alice become VIP? What was her previous tier?-- Traditional approach
SELECT * FROM customers WHERE status='VIP';
-- ❌ CAN'T ANSWER: "Who were VIPs 6 months ago but churned since?"
-- ❌ CAN'T ANSWER: "What caused Alice to become VIP?"// Traditional graph
CREATE (alice:Customer)-[:PURCHASED]->(laptop:Product)
// ❌ MISSING: When? For how long was this relationship valid?
// ❌ MISSING: What was Alice's state BEFORE this purchase?# GraphRAG approach
vector_search("customers similar to Alice")
# ❌ MISSING: "Customers similar to Alice's profile from LAST YEAR"
# ❌ MISSING: "How Alice's behavior CHANGED over time"# Your system CAN do this:
graph.query_at_time("customer", datetime(2024, 12, 1))
# Returns: "Alice was 'Standard' tier, had 2 support tickets, $500 lifetime value"
graph.query_at_time("customer", datetime(2025, 6, 1))
# Returns: "Alice is 'VIP' tier, 0 open tickets, $5000 lifetime value"
# INSIGHT: Alice's transformation from Standard→VIP over 6 months!# Your system tracks the SEQUENCE:
# Dec 15: Alice purchases laptop ($1200)
# Dec 16: Support request (setup help)
# Dec 17: Support resolved positively
# Dec 18: Alice leaves 5-star review
# Dec 20: Alice's tier upgraded to VIP
# Dec 25: Alice purchases accessories ($800)
# INSIGHT: Great support experience → VIP upgrade → higher purchases# Traditional: "Alice bought products X, Y, Z"
# Your system:
# "Alice's relationship with ProductX:
# - Purchased (Dec 2024)
# - Had issues (Dec 2024-Jan 2025)
# - Resolved satisfaction (Jan 2025)
# - Became advocate/reviewer (Feb 2025)"# Your system can identify:
# "Customers who have 2+ support tickets within 30 days
# have 60% churn probability in the following 90 days"
# "VIP customers who go 45+ days without purchases
# typically downgrade within 2 months"❌ "Alice is a VIP customer with $5000 lifetime value"
✅ "Alice started as Standard tier (Dec 2024), had initial product issues requiring 2 support contacts, but after positive resolution became highly engaged, upgraded to VIP (Jan 2025), and now has $5000 LTV with 95% satisfaction trend. Prediction: Likely to remain VIP for 12+ months based on similar temporal patterns."
# Find customers following Alice's early pattern (pre-VIP)
similar_journeys = graph.find_similar_temporal_patterns(
customer_id="alice",
time_period="first_60_days"
)
# Proactively help customers showing early Alice-like issues# "What was Alice interested in 3 months ago vs now?"
interests_then = graph.query_at_time("interests", 3.months.ago)
interests_now = graph.query_at_time("interests", now)
# Send contextual offers based on interest evolution# "What product issues emerge 30-60 days after purchase?"
post_purchase_issues = graph.query_temporal_patterns(
event_sequence=["purchase", "support_request"],
time_gap=(30, 60, "days")
)GraphRAG is great for semantic retrieval but lacks:
- ❌ Temporal reasoning: "How did this relationship change over time?"
- ❌ Causality tracking: "What sequence of events led to this outcome?"
- ❌ Historical context: "What was true 6 months ago vs now?"
- ❌ Predictive patterns: "Based on temporal patterns, what happens next?"
Your system uses BOTH temporal and regular graph storage in a smart hybrid approach:
-- Your main temporal storage tables:
temporal_graph.nodes -- TemporalNodes (customers, products, etc.)
temporal_graph.edges -- TemporalEdges (relationships with time)
temporal_graph.events -- Raw events (purchases, support requests, etc.)-- Apache AGE tables (regular graph database):
temporal_graph._ag_label_vertex -- Regular graph vertices
temporal_graph._ag_label_edge -- Regular graph edgesRaw Event → Temporal Processing → Dual Storage
↓ ↓ ↓
Customer Extract Store in both:
Action Entities & • temporal_graph.*
Relationships • _ag_label_*
# Time-based questions
graph.query_at_time("customer", last_month)
graph.get_entity_history("alice_johnson")
graph.query_in_time_range("purchases", holiday_season)-- Traditional graph questions
-- "Find all customers who bought products similar to what Alice bought"
-- "What's the shortest path from Alice to Product X?"
-- "Which customers are most central in the network?"- Temporal queries → Use temporal tables (optimized for time ranges)
- Graph algorithms → Use AGE (optimized for traversals)
- "When did this happen?" → Temporal graph
- "Who is connected to whom?" → Regular graph
- "How did relationships evolve?" → Temporal graph
- "Find the shortest path" → Regular graph
- Time-series analysis → Temporal storage excels
- Network analysis → Traditional graph excels
- Combined insights → Use both together!
# Complex query using BOTH systems:
# 1. Temporal: Find Alice's VIP upgrade time
vip_date = temporal_graph.query("SELECT valid_from FROM nodes
WHERE type='customer' AND properties->>'tier'='VIP'")
# 2. Regular Graph: Find customers with similar network position to Alice
similar_customers = age_graph.query("MATCH (a:Customer)-[:SIMILAR_TO]-(c:Customer)
WHERE a.name = 'Alice' RETURN c")
# 3. Temporal: Check when those customers became VIP
for customer in similar_customers:
vip_timeline = temporal_graph.get_entity_history(customer.id)
# Compare Alice's timeline vs others- Episode Analysis: Group related customer actions within time windows
- Behavioral Patterns: Identify sequences that lead to conversions or churn
- Temporal Segmentation: Group customers by journey patterns, not just demographics
- Churn Prediction: Identify patterns that historically led to customer loss
- Upsell Opportunities: Find temporal patterns that indicate readiness for upgrades
- Support Optimization: Predict which issues will escalate based on historical sequences
- Causality Analysis: Understand what events actually drive business outcomes
- Trend Analysis: See how customer behavior evolves over time
- Performance Attribution: Track how changes in process/product affect customer journeys
class TemporalNode(Base):
__tablename__ = "nodes"
id = Column(PG_UUID(as_uuid=True), primary_key=True)
type = Column(String(50), nullable=False)
properties = Column(JSONB)
valid_from = Column(DateTime(timezone=True), nullable=False)
valid_to = Column(DateTime(timezone=True))
recorded_at = Column(DateTime(timezone=True))
embedding = Column(Vector(1536)) # For semantic searchclass TemporalEdge(Base):
__tablename__ = "edges"
id = Column(PG_UUID(as_uuid=True), primary_key=True)
source_node_id = Column(PG_UUID(as_uuid=True), ForeignKey('nodes.id'))
target_node_id = Column(PG_UUID(as_uuid=True), ForeignKey('nodes.id'))
relationship_type = Column(String(50), nullable=False)
properties = Column(JSONB)
valid_from = Column(DateTime(timezone=True), nullable=False)
valid_to = Column(DateTime(timezone=True))
recorded_at = Column(DateTime(timezone=True))class Event(Base):
__tablename__ = "events"
id = Column(PG_UUID(as_uuid=True), primary_key=True)
event_type = Column(String(50), nullable=False)
event_data = Column(JSONB, nullable=False)
timestamp = Column(DateTime(timezone=True), nullable=False)
processed = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True))def query_at_time(self, query: str, timestamp: datetime) -> List[TemporalNode]:
"""Query graph state at specific time."""
nodes = session.query(TemporalNode).filter(
and_(
TemporalNode.type == query,
TemporalNode.valid_from <= timestamp,
or_(
TemporalNode.valid_to.is_(None),
TemporalNode.valid_to > timestamp
)
)
).all()
return nodesdef get_entity_history(self, entity_id: str) -> List[Tuple[TemporalNode, TimeRange]]:
"""Get complete history of entity changes."""
nodes = session.query(TemporalNode).filter(
TemporalNode.properties['identifier'].astext == entity_id
).order_by(TemporalNode.valid_from).all()
history = []
for node in nodes:
time_range = TimeRange(node.valid_from, node.valid_to)
history.append((node, time_range))
return historyThe temporal graph system represents a paradigm shift from static data storage to dynamic, time-aware intelligence. By preserving the full history of customer interactions and relationships, it enables:
- Deep Understanding: See not just what happened, but when, why, and what came next
- Predictive Power: Use historical patterns to predict future behavior
- Contextual Intelligence: Understand the full story behind each customer journey
- Business Impact: Make data-driven decisions based on temporal causality, not just correlations
This system transforms customer analytics from simple reporting to temporal intelligence - understanding customers as they evolve through time, not just as static profiles. 🚀
Generated on June 19, 2025 - Graphiti Temporal Graph Analysis