# How to Build Bi-Temporal Knowledge Graphs with Semantica: Complete Developer Guide

> Learn to build bi-temporal knowledge graphs using Semantica. This guide covers TemporalModel, TemporalVersionManager, and temporal reasoning for time-aware queries.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Semantica provides a full-stack framework for creating and querying bi-temporal knowledge graphs through three integrated layers: the `TemporalModel` for data structures, `TemporalVersionManager` for version control, and temporal reasoning engines for time-aware queries.**

Building bi-temporal knowledge graphs requires tracking two distinct time dimensions: **valid time** (when a fact is true in the real world) and **transaction time** (when the fact was recorded in the system). Semantica, an open-source knowledge graph framework, implements this capability through a specialized `semantica/kg` package that combines low-level temporal modeling with high-level versioning and query interfaces.

This guide walks through the complete implementation pipeline using actual source code from the `semantica-agi/semantica` repository.

## Understanding Bi-Temporal Data in Semantica

### What Makes Semantica's Approach Distinctive

Unlike standard knowledge graphs that treat relationships as static, Semantica's **bi-temporal model** captures the full lifecycle of every edge. This enables powerful capabilities:

- **Point-in-time queries**: Retrieve the graph state as it existed at any specific moment
- **Rollback and auditing**: Trace when information entered the system and when it was superseded
- **"What-if" analysis**: Create hypothetical versions without affecting production data
- **Compliance and provenance**: Meet regulatory requirements for data lineage tracking

The implementation spans four core modules that work together seamlessly.

## The Temporal Model Layer: `BiTemporalFact`

The foundation lives in [`semantica/kg/temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_model.py). This module defines `BiTemporalFact`, a wrapper that normalizes temporal metadata on every relationship.

### Core Data Structures

A `BiTemporalFact` attaches four critical fields to each edge:

| Field | Time Dimension | Purpose |
|-------|---------------|---------|
| `valid_from` | Valid time | When the relationship became true in reality |
| `valid_until` | Valid time | When the relationship ceased being true |
| `recorded_at` | Transaction time | When the system first captured this fact |
| `superseded_at` | Transaction time | When a newer version replaced this record |

The `TemporalBound.OPEN` constant indicates an unbounded interval—either "still true" for valid time or "current record" for transaction time.

### Temporal Utilities

[`temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/temporal_model.py) provides essential helper functions:

- **`parse_temporal_value`**: Normalizes strings, numbers, or `datetime` objects to UTC-aware datetimes
- **`serialize_temporal_value`**: Converts internal representations to ISO-8601 format with compact "Z" suffix
- **`relationship_to_json_ready`**: Ensures round-trip serialization without losing temporal semantics

These utilities handle edge cases like timezone conversions and open-ended intervals automatically.

## Version Management with `TemporalVersionManager`

The versioning system in [`semantica/change_management/managers.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/change_management/managers.py) implements a **lightweight VCS for knowledge graphs**. This component is exposed through [`semantica/kg/temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_query.py) as the public `TemporalVersionManager` API.

### Key Operations

| Method | Function |
|--------|----------|
| `create_version(graph, timestamp, version_label)` | Captures a complete snapshot at a specific transaction time |
| `list_tags()` | Returns all stored version labels (e.g., "v2020", "v2023") |
| `tag_version(label)` | Assigns human-readable identifiers to snapshots |
| `validate_snapshot(snapshot)` | Enforces bi-temporal constraints and detects anomalies |

The manager persists snapshots to configurable backends including SQLite, PostgreSQL, or any custom `VectorStore` implementation.

### Snapshot Integrity

`validate_snapshot()` performs critical checks:

- No overlapping transaction intervals for the same edge
- Proper `TemporalBound.OPEN` usage for current records
- Consistent valid-time ranges within transaction-time boundaries

These validations prevent the data corruption patterns common in naive bi-temporal implementations.

## Temporal Reasoning and Querying

The reasoning layer in [`semantica/reasoning/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/temporal_reasoning.py) enables sophisticated time-aware analysis.

### Allen Interval Relations

Semantica implements the full set of **Allen interval algebra** relations for comparing temporal intervals:

```python
from semantica.reasoning.temporal_reasoning import TemporalReasoner

reasoner = TemporalReasoner()
relation = reasoner.allen_relation(fact_a, fact_b)

# Returns: 'before', 'meets', 'overlaps', 'starts', 'during', 'finishes', 'equal', etc.

```

Access this through Python API or the CLI: `semantica temporal allen ...`

### Graph Analytics

- **`version_history(entity_id)`**: Returns chronological state series for any entity
- **`compare_snapshots(v1, v2)`**: Identifies added, removed, and modified edges between versions

These operations support forensic analysis, change detection, and longitudinal studies of knowledge evolution.

## Visualization Capabilities

The [`semantica/visualization/temporal_visualizer.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/visualization/temporal_visualizer.py) module provides interactive rendering through Plotly.

### Available Views

| Method | Output |
|--------|--------|
| `visualize_timeline(kg, output="interactive")` | Interactive timeline of all temporal edges |
| `visualize_version_history(history, timestamps, output="interactive")` | Entity property evolution over transaction time |

These visualizations are exercised in [`tests/visualization/reproduce_notebooks.py`](https://github.com/semantica-agi/semantica/blob/main/tests/visualization/reproduce_notebooks.py) with comprehensive examples.

## Complete Implementation Examples

### Creating a Bi-Temporal Knowledge Graph

```python
from semantica.kg.knowledge_graph import KnowledgeGraph
from semantica.kg.temporal_model import BiTemporalFact

kg = KnowledgeGraph()

# Add a bi-temporal edge with explicit valid-time bounds

edge = {
    "source": "Alice",
    "target": "AcmeCorp",
    "type": "EMPLOYED_BY",
    "valid_from": "2020-01-01",
    "valid_until": "2022-06-30"
    # recorded_at auto-populated; superseded_at defaults to OPEN

}
kg.add_relationship(edge)

```

The `add_relationship` call internally wraps the dict in `BiTemporalFact` to normalize temporal fields.

### Capturing Version Snapshots

```python
from semantica.kg.temporal_query import TemporalVersionManager

tvm = TemporalVersionManager(storage_path="tmp/semantica.db")

# Snapshot at specific transaction time with semantic label

snapshot = tvm.create_version(
    kg,
    timestamp="2021-03-15",
    version_label="v2021_q1"
)

```

Transaction time (`recorded_at`) is set to the snapshot timestamp for all new edges.

### Querying Entity History

```python
history = tvm.version_history(entity_id="Alice")

for version, ts in history:
    print(f"At {ts.isoformat()} -> {version['relationships']}")

```

Returns `(state, timestamp)` tuples in chronological order.

### Rendering Interactive Timelines

```python
from semantica.visualization.temporal_visualizer import TemporalVisualizer

viz = TemporalVisualizer()
viz.visualize_timeline(snapshot, output="interactive")

```

Outputs a Plotly figure showing valid-time intervals colored by relationship type.

### Comparing Historical States

```python
v1 = tvm.create_version(kg, timestamp="2020-12-31", version_label="v2020")
v2 = tvm.create_version(kg, timestamp="2022-12-31", version_label="v2022")
diff = tvm.compare_snapshots(v1, v2)

print("Added edges:", diff["added"])
print("Removed edges:", diff["removed"])
print("Changed edges:", diff["changed"])

```

The diff structure identifies structural changes between any two transaction-time points.

## Summary

Building bi-temporal knowledge graphs with Semantica requires understanding three integrated layers:

- **Model layer**: `BiTemporalFact` in [`temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/temporal_model.py) normalizes valid-time and transaction-time metadata on every edge
- **Version layer**: `TemporalVersionManager` in [`managers.py`](https://github.com/semantica-agi/semantica/blob/main/managers.py) provides snapshot creation, tagging, and persistence with configurable storage backends
- **Reasoning layer**: [`temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/temporal_reasoning.py) implements Allen-interval relations, history queries, and snapshot comparison

The framework enforces bi-temporal constraints automatically, supports interactive visualization, and exposes both Python API and CLI interfaces for diverse workflow integration.

## Frequently Asked Questions

### What is the difference between valid time and transaction time in Semantica?

**Valid time** represents when a fact was true in the modeled world (e.g., Alice worked at AcmeCorp from January 2020 to June 2022). **Transaction time** represents when the system recorded that fact (e.g., the employment relationship was entered on March 15, 2021). Semantica tracks both through independent interval fields on every `BiTemporalFact`, enabling queries like "What did we believe on date X about the world on date Y?"

### How does `TemporalVersionManager` store multiple versions efficiently?

The manager stores complete snapshots with copy-on-write semantics: edges are annotated with `recorded_at` and `superseded_at` timestamps rather than duplicated. When a new version is created, existing edges receive `superseded_at` markers only if modified. This approach, implemented in [`semantica/change_management/managers.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/change_management/managers.py), balances query performance with storage efficiency across SQLite, PostgreSQL, or custom backends.

### Can I migrate an existing knowledge graph to bi-temporal storage?

Yes. When adding relationships to a `KnowledgeGraph` without temporal fields, `BiTemporalFact` automatically assigns `recorded_at` to the current UTC time and sets both `valid_from` and `valid_until` to `None`. For historical data, populate `valid_from` and `valid_until` explicitly before calling `add_relationship`. The `parse_temporal_value` function in [`temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/temporal_model.py) accepts ISO-8601 strings, Unix timestamps, or `datetime` objects for flexible data ingestion.

### How do I query the graph as it existed at a specific point in history?

Use `TemporalVersionManager.create_version()` with your target timestamp as both the snapshot time and filter predicate. The resulting snapshot contains only edges where `recorded_at <= timestamp < superseded_at` (transaction-time filter) and optionally where `valid_from <= timestamp < valid_until` (valid-time filter). For ad-hoc queries without persisting a snapshot, the [`temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/temporal_query.py) module provides helper functions that apply these filters dynamically.