Recursive Graph Improvement — the shift from systems that observe to systems that reason, learn from their own outcomes, and know when to stop.
Each station closes a gap that the market treats as unsolvable. A skeptical architect needs the experiment, not the claim. The sixth — enrichment — is the mechanism that ties the other five together and sets up the data intelligence story.
Agentic frameworks dispatch work with heuristic routers. They decode — they don't reason about the situation. A 4% price variance on copper in Q3 is not the same event as 4% on steel in Q1.
Process mining shows WHERE. It cannot tell you WHY, fix it, or transfer the fix. Without reification, every bottleneck is a fresh 4–6 hour investigation.
You cannot change the engine on a flying airplane. Alibaba: AgentEvolver for static changes (offline). RGI's AgentEvolver: live deployments, conservation-gated.
Knowledge graphs store facts. No graph system brings judgment geometry. The distance function isn't cosmetic — it determines whether the system can distinguish boundary decisions.
Every enterprise wants AI that improves. No enterprise will approve automation that changes its own logic without proof it hasn't degraded.
Traditional RSI improves the model's weights. RGI enriches the context — and that enrichment compounds. The 1,000th investigation traverses a richer graph than the 1st.
This is the mechanism behind data intelligence: data that learns about itself.
The setup for data intelligence: once the context graph enriches itself from verified outcomes, the graph becomes a learning asset — not just a knowledge store. Source profilers update trust. Combination discoverers find new signals. Quality trajectories emerge. This is the substrate for data intelligence — data that gets smarter every day.
class CompoundingScorer:
"""Station ④: score → verify → reshape centroids.
~844 parameters. No LLM. No GPU. O(1) per decision."""
def score(self, factors: List[float], category: str) -> ScoreResult:
"""Softmax over L2 distances to category centroids."""
v = np.array(factors)
μ = self._graph_store.get_centroids(category)
d_sq = {a: np.sum((v - μ[category][a]) ** 2) for a in self._actions}
logits = {a: -d_sq[a] / self._tau for a in self._actions} # τ = 0.1
max_l = max(logits.values())
exp_l = {a: np.exp(logits[a] - max_l) for a in self._actions}
Z = sum(exp_l.values())
probs = {a: exp_l[a] / Z for a in self._actions}
best = max(probs, key=probs.get)
return ScoreResult(action=best, confidence=probs[best],
basis=self._graph_store.get_similar(v, category, limit=3))
def learn(self, factors, action_index, category, confirmed):
"""LVQ centroid update — conservation-gated."""
η = 0.05 if confirmed else 0.01
v = np.array(factors)
centroids = self._graph_store.get_centroids() # all categories
μ_old = centroids[category][action_index]
μ_new = μ_old + η * (v - μ_old)
# μ₀ = μ_canonical + ε_firm; update is LVQ: μ ← μ + η(v - μ)
new_centroids = centroids.copy()
new_centroids[category][action_index] = μ_new
gate = self._conservation_gate.check(centroids, new_centroids, category)
if not gate.passed:
return LearnResult(accepted=False, reason=gate.reason)
centroids[category][action_index] = μ_new
self._graph_store.save_centroids(centroids)
return LearnResult(accepted=True, displacement=gate.displacement)
class SituationAnalyzer:
"""Station ②: assemble cross-system context → factor vector.
Q-routed investigation — decides WHICH evidence and WHEN to stop."""
async def assemble(self, entity_id: str) -> SituationContext:
graph_ctx = await self._graph_store.get_context(entity_id)
erp_ctx = await self._sap_connector.get_entity(entity_id)
process_ctx = await self._celonis_connector.get_activities(entity_id)
ctx = SituationContext(graph=graph_ctx, erp=erp_ctx, process=process_ctx)
ctx.factors = self._extract_factors(ctx)
ctx.category = self._classify(ctx.factors)
return ctx
def select_investigation(self, v, category) -> List[str]:
"""Q-routed: precision × leverage × discriminative.
System reads the most informative evidence first."""
q_values = {
j: self._precision(j,v,category) * self._leverage(j,v,category)
* self._discriminative(j,v,category)
for j in self._available_factors
}
# Sorted by Q — top factors read first, stop when marginal Q drops
return sorted(q_values, key=q_values.get, reverse=True)
class ConservationGate:
"""Station ⑧: three layers, all fixed at deployment approval.
Learning cannot alter them. The gate protects the geometry."""
def check(self, old_centroids, new_centroids, category) -> GateResult:
α = self._current_phase()
V = self.tensor_volume # d × c × a
# Layer 1: sufficient verified volume (α·q·V must exceed θ_min)
if α * self._q * V < self.θ_min:
return GateResult(passed=False, reason="insufficient_volume")
# Displacement ceiling: centroid shift must stay within bounds
disp = np.linalg.norm(new_centroids[category] - old_centroids[category])
max_disp = 23.53 / (α * V)
if disp > max_disp:
return GateResult(passed=False, reason="displacement_exceeded")
# Layer 2: per-factor displacement (no single factor drifts too far)
for j in range(len(new_centroids[category])):
δ_j = abs(new_centroids[category][j] - old_centroids[category][j])
if δ_j > 0.7 * disp: # m_rel = 0.7×
return GateResult(passed=False, reason=f"per_factor_{j}")
# Layer 3: rate of change (sudden shifts trigger pause)
rate_short = self._rate(window=20)
rate_long = self._rate(window=self._q)
if rate_short > 1.15 * rate_long: # short-window 15% above long = regime shift
return GateResult(passed=False, reason="rate_of_change")
return GateResult(passed=True, displacement=disp)
# Per-deployment floors (not global):
# SOC: 0.764 (20:1) DataOps: 0.669 (10:1) S2P: 0.769 (5:1)
# Purchasing: 0.655 (3:1) Trading: 0.766 (2:1)
class GraphStore(Protocol):
"""45-method protocol. THE storage abstraction.
3 backends: InMemory (test), SQLite (dev), AGE (prod).
258 dedicated tests. Zero DecisionStore imports."""
# Decision lifecycle
def save_decision(self, decision: Decision) -> str: ...
def save_outcome(self, decision_id: str, outcome: Outcome) -> None: ...
def get_decisions(self, category: str, limit: int) -> List[Decision]: ...
# Centroid geometry (judgment memory)
def save_centroids(self, category: str, centroids: np.ndarray) -> None: ...
def get_centroids(self, category: str) -> Optional[np.ndarray]: ...
# Cross-graph attention (discovery)
def get_context(self, entity_id: str) -> GraphContext: ...
def traverse(self, start: str, edge_types: List[str],
max_depth: int = 3) -> List[Path]: ...
# Evolution (AgentEvolver)
def save_evolution(self, domain: str, key: str, payload: dict) -> None: ...
def get_evolution_log(self, domain: str) -> List[EvolutionEntry]: ...
RGI vs traditional RSI:
Five operations — Score, Investigate, Learn, Conserve, Compound — map one-to-one to the stations on the loop. This is the complete mathematical specification. Each equation has a corresponding evidence tier (the full technical paper): architectural, validated, or proven+confirmed.
The tradeoff is real: conservation-constrained accuracy is 6.5–11.1pp worse than unconstrained. That's the price. The return: 86.7% degradation detection at 0.87% false-pause. A global floor of 0.75 would pause 48% of healthy Purchasing decisions. Per-deployment calibration makes a system people actually use.
Departed expert's judgment → inspectable geometry. Centroids, not tribal knowledge.
Situational reasoning, not heuristic routing. Q-routed investigation. +5.9pp day-one.
Evolve while running. Shadow-test → promote/reject. AI systems + their targets. Conservation-gated.
Data learns about itself. Source trust · quality trajectories · combination discovery. n2.15 across domains.
Processes → editable graph objects. Celonis + SAP + EDW through reasoning, not dashboards. 847→312 (−63%).
Earns auto-decide rights · pulls back when accuracy drops · names where it stops. Queues shrink.
Market signal: the substrate is commoditizing.
None creates bounded recursive self-improvement with learned reasoning across enterprise decision domains.