How to Use ContextGraph for Decision Recording and Tracing in Semantica

The ContextGraph class provides built-in record_decision() and trace_decision_causality() methods to capture structured decision metadata and traverse causal relationships through breadth-first search.

The ContextGraph is the core knowledge structure in semantica-agi/semantica that enables agentic systems to maintain an auditable history of choices. This guide demonstrates how to use ContextGraph for decision recording and tracing, with direct references to the implementation in [semantica/context/context_graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py).

Creating a ContextGraph with Decision Support

Before recording decisions, initialize a ContextGraph with optional analytics enabled. The constructor accepts flags for centrality analysis, community detection, and node embeddings—all useful for later exploration of decision clusters.

from semantica.context import ContextGraph

graph = ContextGraph(
    advanced_analytics=True,
    centrality_analysis=True,
    community_detection=True,
    node_embeddings=True,
)

These settings activate knowledge graph algorithms that power similarity searches and precedent discovery discussed later.

Recording Decisions with record_decision()

The record_decision() method (lines 4334–4369 in [context_graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)) creates a persistent decision node with comprehensive metadata.

Method Signature and Validation

def record_decision(
    self,
    category: str,
    scenario: str,
    reasoning: str,
    outcome: str,
    confidence: float,
    entities: Optional[List[str]] = None,
    decision_maker: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
    valid_from: Optional[Union[str, datetime]] = None,
    valid_until: Optional[Union[str, datetime]] = None,
    **kwargs
) -> str:

The implementation performs strict input validation (lines 4334–4348):

  • Required fields: category, scenario, reasoning, outcome, and confidence are checked for type, length constraints, and valid value ranges
  • Temporal normalization: valid_from and valid_until pass through _normalize_temporal_input() (lines 4327–4332), converting to tz-naive UTC ISO strings
  • UUID generation: Each decision receives a unique identifier with attached timestamp (lines 4349–4350)

Internal Graph Insertion

The private helper _add_decision_to_graph() (lines 4448–4664) handles three critical indexing operations:

  1. Node creation: Stores the decision with type "decision" containing all validated fields
  2. Index updates: Maintains _decision_index, _entity_index, and _temporal_index for fast lookups
  3. Entity linking: Creates edges between the decision node and any referenced entities

Complete Recording Example

decision_id = graph.record_decision(
    category="fraud_detection",
    scenario="Multiple failed login attempts from new device",
    reasoning="IP address flagged in threat intelligence blacklist",
    outcome="account_blocked",
    confidence=0.98,
    entities=["user_42", "ip_203.0.113.5", "device_mobile_android"],
    decision_maker="security_automation_engine",
    metadata={"alert_severity": "critical", " threat_intel_source": "MISP"},
    valid_from="2024-09-01T00:00:00",
    valid_until="2025-09-01T00:00:00",
)

print(f"Decision recorded with ID: {decision_id}")

# Output: 550e8400-e29b-41d4-a716-446655440000

The returned UUID serves as the stable anchor for all subsequent tracing and linking operations.

Building Causal Chains Between Decisions

Decisions gain explanatory power through relationships. The ContextGraph supports canonical causal edge types defined in _CAUSAL_EDGE_TYPES and _CAUSAL_EDGE_ALIASES.

Adding Causal Relationships

Use add_edge() with causal edge types to express influence:


# Link a precedent decision as direct cause

graph.add_edge(
    source_id=previous_decision_id,
    target_id=decision_id,
    edge_type="CAUSED",
    weight=1.0,
    metadata={"causal_strength": "definitive"}
)

# Or express partial influence

graph.add_edge(
    source_id="risk_model_v3_assessment_001",
    target_id=decision_id,
    edge_type="INFLUENCED",
    weight=0.75,
)

Supported causal edge types include CAUSED, INFLUENCED, ENABLED, PREVENTED, and their aliases—enabling nuanced provenance modeling.

Tracing Decision Causality with trace_decision_causality()

The trace_decision_causality() method (lines 4696–4738) reconstructs the full causal history of any decision through breadth-first search.

Method Signature

def trace_decision_causality(
    self,
    decision_id: str,
    limit: Optional[int] = None,
    depth: int = 5,
    include_entities: bool = True
) -> List[Dict[str, Any]]:

Tracing Behavior

  • Search scope: Follows only edges whose type appears in _CAUSAL_EDGE_TYPES or _CAUSAL_EDGE_ALIASES
  • Traversal direction: Backward from target to sources (provenance), with configurable depth (default 5 hops)
  • Result enrichment: Each returned dictionary contains source, target, type, weight, hop, and timestamp fields
  • Entity integration: When include_entities=True, adjacent entity nodes enrich the trace with contextual information

Practical Tracing Example


# Retrieve full causality chain

trace = graph.trace_decision_causality(
    decision_id=decision_id,
    depth=4,
    limit=50,
    include_entities=True
)

print("Causality trace:")
for hop in trace:
    print(f"  [{hop['hop']}] {hop['source']} → {hop['target']}")
    print(f"       Edge: {hop['type']} (weight: {hop.get('weight', 1.0)})")
    if 'timestamp' in hop:
        print(f"       Time: {hop['timestamp']}")

This produces a traversable audit trail showing every decision and entity that contributed to the final outcome.

Finding Decision Precedents

Beyond explicit causal tracing, the ContextGraph supports semantic precedent discovery through find_precedents() and find_precedents_by_scenario(). These methods leverage:

  • Vector similarity: Embedding-based matching of scenario descriptions
  • Entity overlap: Shared entities between past and current decisions
  • Category clustering: Decisions from the same functional domain

Locate these utilities in the same [context_graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) file, typically after the core traversal methods.

End-to-End Decision Workflow

from semantica.context import ContextGraph

# Initialize

graph = ContextGraph(advanced_analytics=True, node_embeddings=True)

# Stage 1: Record initial risk assessment

risk_decision = graph.record_decision(
    category="risk_assessment",
    scenario="Failed security challenge",
    reasoning="Behavioral biometrics anomaly detected",
    outcome="elevated_risk_score",
    confidence=0.87,
    entities=["session_abc123", "user_42"],
    decision_maker="risk_engine_v2",
)

# Stage 2: Record follow-up action, causally linked

block_decision = graph.record_decision(
    category="access_control",
    scenario="Elevated risk score triggered step-up auth",
    reasoning="Risk score exceeded dynamic threshold",
    outcome="session_terminated",
    confidence=0.95,
    entities=["session_abc123", "user_42"],
    decision_maker="access_policy_engine",
)

# Establish causality

graph.add_edge(
    source_id=risk_decision,
    target_id=block_decision,
    edge_type="CAUSED",
    weight=1.0
)

# Stage 3: Trace complete provenance

audit_trail = graph.trace_decision_causality(block_decision, depth=3)

print(f"\nFull audit for {block_decision[:8]}...")
for entry in audit_trail:
    print(f"  Hop {entry['hop']}: {entry['type']} from {entry['source'][:8]}")

MCP Tool Integration

The repository provides higher-level wrappers in [semantica/mcp/tools/decisions.py](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/decisions.py) that expose record_decision as an MCP tool for multi-context processor environments. Similarly, [semantica/mcp/tools/graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/graph.py) wraps causal edge operations for remote graph manipulation.

Source Reference Summary

Component Location Purpose
Core decision recording [semantica/context/context_graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) lines 4334–4664 record_decision() and _add_decision_to_graph()
Causality tracing [semantica/context/context_graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) lines 4696–4738 trace_decision_causality() BFS implementation
MCP decision tools [semantica/mcp/tools/decisions.py](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/decisions.py) Exposed tool interface
MCP graph tools [semantica/mcp/tools/graph.py](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/graph.py) Edge manipulation helpers
Integration tests [tests/vector_store/test_end_to_end_decision_tracking.py](https://github.com/semantica-agi/semantica/blob/main/tests/vector_store/test_end_to_end_decision_tracking.py) Workflow validation

Summary

  • record_decision() creates validated, indexed decision nodes with UUIDs, temporal bounds, and entity references
  • trace_decision_causality() performs controlled BFS across causal edges to reconstruct decision provenance
  • Causal edge types (CAUSED, INFLUENCED, etc.) enable fine-grained modeling of decision relationships
  • Dual indexing (_decision_index, _temporal_index) ensures efficient retrieval at scale
  • MCP tooling exposes these capabilities to external agent orchestration systems

Frequently Asked Questions

What makes a recorded decision retrievable for tracing?

The record_decision() method automatically indexes decisions in _decision_index by UUID, _entity_index by referenced entities, and _temporal_index by validity period. Without these indexes, trace_decision_causality() could not efficiently locate starting nodes or traverse relationships. The implementation guarantees O(1) UUID lookups and O(n) traversal bounded by depth and limit parameters.

How does temporal validation work for decisions?

The _normalize_temporal_input() helper (lines 4327–4332) converts valid_from and valid_until to standardized tz-naive UTC ISO strings. This normalization ensures consistent comparisons during temporal range queries and prevents timezone-related provenance errors. Invalid or malformed temporal inputs raise ValueError before any graph mutation occurs.

Can I trace forward from a decision to its effects?

The current trace_decision_causality() implementation traverses backward (provenance direction) following incoming causal edges. To trace forward effects, you would either reverse the edge direction in the search or construct an inverse index of outgoing edges. The underlying graph structure in NetworkX supports both directions—adapt the BFS logic in a subclass or wrapper for downstream impact analysis.

What confidence threshold should I set for decision recording?

The confidence parameter accepts any float in [0.0, 1.0] with no enforced minimum threshold in the core implementation. However, downstream analytics in find_precedents() and similarity scoring may weight decisions by confidence. The integration tests in [test_end_to_end_decision_tracking.py](https://github.com/semantica-agi/semantica/blob/main/tests/vector_store/test_end_to_end_decision_tracking.py) demonstrate filtering precedent searches by confidence ≥ 0.7 for high-reliability historical matching.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →