Causal Queries
advancedAsk '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
| Requirement | Details |
|---|---|
| Account | Starter tier or above |
| API key | From your dashboard |
| Tools | Python requests library or cURL |
| Knowledge | Familiarity with Causal Reasoning concepts |
| Time | ~15 minutes |
The causal endpoints
| Endpoint | Purpose | Example question |
|---|---|---|
/v1/memory/causal/strength | Measure how strongly A causes B (and B causes A) | "How much does traffic cause latency?" |
/v1/memory/causal/direction | Determine which way causation flows | "Does traffic cause latency, or latency cause traffic?" |
/v1/memory/causal/intervene | Predict effects of forcing a cause | "What happens if we spike traffic?" |
/v1/memory/causal/counterfactual | Reason about alternative worlds | "Would latency have stayed low without the traffic spike?" |
/v1/memory/causal/neighbors | Map upstream and downstream causal graph | "What are the causes and effects of database load?" |
Steps
Store causal facts
Measure causal strength
Determine causal direction
Predict intervention effects
Explore counterfactuals
Map the causal graph
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
}
]
}
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
}
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}
)How causal links strengthen
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 evidenceCausal 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
- Triggering Consolidation — Strengthen causal links through sleep cycles
- Explainability — Inspect reasoning chains at any detail level
- Causal Reasoning — Deep dive into causal inference theory