Skip to content

Causal Queries

advanced

Ask 'what if?' and 'why did this happen?' to your memory. A hands-on guide to causal strength, direction, intervention, and counterfactual endpoints.

What you'll build

A system that answers causal questions — "How strongly does A cause B?", "What is the causal direction?", "What happens if we do X?", "Would Z have happened if we hadn't done W?" — using memories stored via the Engramma API.

Prerequisites

RequirementDetails
AccountStarter tier or above
API keyFrom your dashboard
ToolsPython requests library or cURL
KnowledgeFamiliarity with Causal Reasoning concepts
Time~15 minutes

The causal endpoints

EndpointPurposeExample question
/v1/memory/causal/strengthMeasure how strongly A causes B (and B causes A)"How much does traffic cause latency?"
/v1/memory/causal/directionDetermine which way causation flows"Does traffic cause latency, or latency cause traffic?"
/v1/memory/causal/intervenePredict effects of forcing a cause"What happens if we spike traffic?"
/v1/memory/causal/counterfactualReason about alternative worlds"Would latency have stayed low without the traffic spike?"
/v1/memory/causal/neighborsMap upstream and downstream causal graph"What are the causes and effects of database load?"

Steps

1

Store causal facts

Use /v1/memory/text/store to record facts describing cause-and-effect relationships. The engine embeds them and builds a causal graph over time.
2

Measure causal strength

Use /v1/memory/causal/strength to quantify how strongly one pattern causes another.
3

Determine causal direction

Use /v1/memory/causal/direction to confirm which way causation flows between two concepts.
4

Predict intervention effects

Use /v1/memory/causal/intervene to ask 'what happens if we force X?'
5

Explore counterfactuals

Use /v1/memory/causal/counterfactual to reason about alternative scenarios.
6

Map the causal graph

Use /v1/memory/causal/neighbors to discover upstream causes and downstream effects.

Step 1: Store causal facts

First, store facts that describe cause-and-effect relationships. The engine detects causal language and builds a causal graph from repeated observations.

import requests

BASE = "https://api.engramma-memory.com"
HEADERS = {
    "X-API-Key": "your-api-key-here",
    "Content-Type": "application/json"
}

causal_facts = [
    "High traffic spikes cause increased API latency",
    "Increased API latency triggers auto-scaling",
    "Auto-scaling adds new pods within 30 seconds",
    "New pods restore normal latency levels",
    "Cache misses increase database load",
    "Database load above 80% causes query timeouts",
    "Query timeouts trigger circuit breaker activation",
    "Circuit breaker reduces traffic to the database",
    "Reduced traffic allows database recovery",
]

for fact in causal_facts:
    resp = requests.post(
        f"{BASE}/v1/memory/text/store",
        headers=HEADERS,
        json={"text": fact, "metadata": {"domain": "infrastructure"}}
    )
    resp.raise_for_status()

print(f"Stored {len(causal_facts)} causal facts")

Step 2: Measure causal strength

Use the /v1/memory/causal/strength endpoint to measure how strongly one concept causes another. You can pass raw text (auto-embedded) or pre-computed pattern arrays.

# Measure causal strength between two concepts
resp = requests.post(
    f"{BASE}/v1/memory/causal/strength",
    headers=HEADERS,
    json={
        "text_a": "high traffic spikes",
        "text_b": "increased API latency"
    }
)
result = resp.json()

print(f"Strength A→B: {result['strength_a_to_b']:.2f}")
print(f"Strength B→A: {result['strength_b_to_a']:.2f}")
print(f"Direction: {result['direction']}")
print(f"Observations A→B: {result['observations_a_to_b']}")
print(f"Observations B→A: {result['observations_b_to_a']}")
print(f"Confounded: {result['confounded']}")

Example response:

{
  "strength_a_to_b": 0.78,
  "strength_b_to_a": 0.12,
  "direction": "a_to_b",
  "observations_a_to_b": 15,
  "observations_b_to_a": 3,
  "confounded": false
}

Step 3: Determine causal direction

When you are unsure which way causation flows, use /v1/memory/causal/direction. This uses temporal displacement and consistency analysis to determine directionality.

# Determine causal direction
resp = requests.post(
    f"{BASE}/v1/memory/causal/direction",
    headers=HEADERS,
    json={
        "text_a": "cache misses",
        "text_b": "database load"
    }
)
result = resp.json()

print(f"Direction: {result['direction']}")
print(f"Confidence: {result['confidence']:.2f}")
print(f"Confounded: {result['confounded']}")
print(f"Displacement consistency: {result['details']['displacement_consistency']:.2f}")

Example response:

{
  "direction": "a_causes_b",
  "confidence": 0.87,
  "confounded": false,
  "details": {
    "displacement_consistency": 0.92
  }
}

Step 4: Predict intervention effects

Use /v1/memory/causal/intervene to answer "What happens if we force X?" The engine traces forward through the causal graph to predict downstream effects.

# Predict what happens if we force a traffic spike
resp = requests.post(
    f"{BASE}/v1/memory/causal/intervene",
    headers=HEADERS,
    json={
        "text": "high traffic spike occurs"
    }
)
result = resp.json()

print("Predicted effects:")
for effect in result["predicted_effects"]:
    print(f"  → effect_key: {effect['effect_key'][:3]}...")
    print(f"    predicted_value: {effect['predicted_value'][:3]}...")
    print(f"    causal_strength: {effect['causal_strength']:.2f}")
    print()

Example response:

{
  "predicted_effects": [
    {
      "effect_key": [0.23, 0.87, ...],
      "predicted_value": [0.45, 0.12, ...],
      "causal_strength": 0.78
    },
    {
      "effect_key": [0.11, 0.65, ...],
      "predicted_value": [0.33, 0.91, ...],
      "causal_strength": 0.64
    }
  ]
}
Info

The intervene endpoint returns raw embedding vectors for effect keys and values. To get human-readable text, pass the effect_key arrays to /v1/memory/text/retrieve to find the closest stored memories.

Step 5: Explore counterfactuals

Use /v1/memory/causal/counterfactual to reason about alternative scenarios: "Would Y have happened if X had been different?"

# Would latency have stayed low if traffic hadn't spiked?
resp = requests.post(
    f"{BASE}/v1/memory/causal/counterfactual",
    headers=HEADERS,
    json={
        "text_cause": "high traffic spike",
        "text_effect": "increased API latency",
        "text_counterfactual": "traffic remains at normal levels"
    }
)
result = resp.json()

print(f"Counterfactual effect: {result['counterfactual_effect'][:3]}...")
print(f"Computable: {result['computable']}")

Example response:

{
  "counterfactual_effect": [0.02, 0.11, 0.03, ...],
  "computable": true
}
Info

The counterfactual_effect is an embedding vector representing the predicted effect in the alternative world. Compare it to stored patterns using /v1/memory/text/retrieve to interpret the result. If computable is false, the causal graph lacks sufficient evidence to reason about this counterfactual.

Step 6: Map the causal graph

Use /v1/memory/causal/neighbors to discover what causes a concept (parents) and what it causes (children).

# Find causes and effects of database load
resp = requests.post(
    f"{BASE}/v1/memory/causal/neighbors",
    headers=HEADERS,
    json={
        "text": "database load",
        "direction": "both",
        "min_strength": 0.3
    }
)
result = resp.json()

print("Parents (what causes database load):")
for parent in result["parents"]:
    print(f"  ← key: {parent['key'][:3]}... (strength: {parent['strength']:.2f})")

print("\nChildren (what database load causes):")
for child in result["children"]:
    print(f"  → key: {child['key'][:3]}... (strength: {child['strength']:.2f})")

Example response:

{
  "parents": [
    {"key": [0.45, 0.23, ...], "strength": 0.82}
  ],
  "children": [
    {"key": [0.71, 0.14, ...], "strength": 0.65}
  ]
}

Complete end-to-end example

Here is a full workflow combining storage, retrieval, and all causal endpoints.

import requests

BASE = "https://api.engramma-memory.com"
HEADERS = {
    "X-API-Key": "your-api-key-here",
    "Content-Type": "application/json"
}

# --- Step 1: Store causal facts ---
facts = [
    "DNS provider had a 10-minute outage at 14:00",
    "DNS outage caused service discovery failures",
    "Service discovery failures caused 503 errors for users",
    "503 errors triggered PagerDuty incident INC-1234",
]

for fact in facts:
    requests.post(
        f"{BASE}/v1/memory/text/store",
        headers=HEADERS,
        json={"text": fact}
    ).raise_for_status()

print(f"Stored {len(facts)} facts\n")

# --- Step 2: Retrieve related memories ---
resp = requests.post(
    f"{BASE}/v1/memory/text/retrieve",
    headers=HEADERS,
    json={"text": "service failures", "top_k": 3}
)
print("Related memories:")
for r in resp.json()["results"]:
    print(f"  [{r['similarity']:.2f}] {r['text']} (id: {r['pattern_id']})")

# --- Step 3: Check causal strength ---
resp = requests.post(
    f"{BASE}/v1/memory/causal/strength",
    headers=HEADERS,
    json={
        "text_a": "DNS outage",
        "text_b": "service discovery failures"
    }
)
strength = resp.json()
print(f"\nCausal strength DNS→service discovery: {strength['strength_a_to_b']:.2f}")
print(f"Confounded: {strength['confounded']}")

# --- Step 4: Confirm direction ---
resp = requests.post(
    f"{BASE}/v1/memory/causal/direction",
    headers=HEADERS,
    json={
        "text_a": "DNS outage",
        "text_b": "503 errors"
    }
)
direction = resp.json()
print(f"\nDirection: {direction['direction']} (confidence: {direction['confidence']:.2f})")

# --- Step 5: Intervene ---
resp = requests.post(
    f"{BASE}/v1/memory/causal/intervene",
    headers=HEADERS,
    json={"text": "DNS provider goes down"}
)
effects = resp.json()
print(f"\nIntervention predicts {len(effects['predicted_effects'])} downstream effects")
for e in effects["predicted_effects"]:
    print(f"  → causal_strength: {e['causal_strength']:.2f}")

# --- Step 6: Counterfactual ---
resp = requests.post(
    f"{BASE}/v1/memory/causal/counterfactual",
    headers=HEADERS,
    json={
        "text_cause": "DNS outage",
        "text_effect": "503 errors for users",
        "text_counterfactual": "DNS provider remains healthy"
    }
)
cf = resp.json()
print(f"\nCounterfactual computable: {cf['computable']}")

# --- Step 7: Map causal neighborhood ---
resp = requests.post(
    f"{BASE}/v1/memory/causal/neighbors",
    headers=HEADERS,
    json={"text": "service discovery failures", "direction": "both", "min_strength": 0.3}
)
graph = resp.json()
print(f"\nCausal neighbors of 'service discovery failures':")
print(f"  Parents: {len(graph['parents'])}")
print(f"  Children: {len(graph['children'])}")

Using raw pattern arrays

All causal endpoints also accept pre-computed embedding arrays instead of text. This is useful when you already have pattern vectors from a previous retrieve call.

# If you already have pattern embeddings from a retrieve call
pattern_a = [0.23, 0.87, 0.45, ...]  # embedding for concept A
pattern_b = [0.11, 0.65, 0.33, ...]  # embedding for concept B

# Strength with raw patterns
resp = requests.post(
    f"{BASE}/v1/memory/causal/strength",
    headers=HEADERS,
    json={"pattern_a": pattern_a, "pattern_b": pattern_b}
)

# Direction with raw patterns
resp = requests.post(
    f"{BASE}/v1/memory/causal/direction",
    headers=HEADERS,
    json={"pattern_a": pattern_a, "pattern_b": pattern_b}
)

# Intervene with raw patterns
resp = requests.post(
    f"{BASE}/v1/memory/causal/intervene",
    headers=HEADERS,
    json={"cause_key": pattern_a, "cause_value": pattern_b}
)

# Counterfactual with raw patterns
resp = requests.post(
    f"{BASE}/v1/memory/causal/counterfactual",
    headers=HEADERS,
    json={
        "cause_key": pattern_a,
        "effect_key": pattern_b,
        "counterfactual_cause_value": [0.05, 0.02, 0.01, ...]
    }
)

# Neighbors with raw pattern
resp = requests.post(
    f"{BASE}/v1/memory/causal/neighbors",
    headers=HEADERS,
    json={"pattern_key": pattern_a}
)

Causal links are not static. They strengthen or weaken based on accumulated evidence:

# Store reinforcing evidence — strengthens the causal link
requests.post(f"{BASE}/v1/memory/text/store", headers=HEADERS,
    json={"text": "Traffic spike at 2pm caused 500ms latency increase"})
requests.post(f"{BASE}/v1/memory/text/store", headers=HEADERS,
    json={"text": "Yesterday's spike triggered auto-scaling within 25s"})

# Store contradicting evidence — weakens the link
requests.post(f"{BASE}/v1/memory/text/store", headers=HEADERS,
    json={"text": "Traffic spike at 3pm did NOT cause latency increase (cache was warm)"})

# Check updated strength after more evidence is stored
resp = requests.post(
    f"{BASE}/v1/memory/causal/strength",
    headers=HEADERS,
    json={
        "text_a": "traffic spikes",
        "text_b": "API latency increase"
    }
)
result = resp.json()
print(f"Updated strength: {result['strength_a_to_b']:.2f}")
print(f"Observations: {result['observations_a_to_b']}")
print(f"Confounded: {result['confounded']}")
# strength may drop from 0.91 to 0.78 due to contradicting evidence
Warning

Causal reasoning requires evidence. Store at least 2-3 related facts before expecting reliable causal strength measurements. The more observations the engine accumulates, the more accurate the confounded detection and direction confidence become.

Interpreting the confounded flag

When confounded is true, the engine has detected that a third variable may explain the correlation between A and B. This means the causal link may be spurious.

resp = requests.post(
    f"{BASE}/v1/memory/causal/strength",
    headers=HEADERS,
    json={"text_a": "ice cream sales", "text_b": "drowning incidents"}
)
result = resp.json()
# result["confounded"] == True
# Both are caused by hot weather, not by each other

Next steps