* feat(mragent): MRAgent graph memory over RuVector with Darwin optimization
Add ADR-269 and a runnable reference implementation of MRAgent ("Memory is
Reconstructed, Not Retrieved") on RuVector, optimized by Meta-Harness Darwin
Mode under the "freeze the model, evolve the harness" invariant.
- Frozen model: deterministic Cue-Tag-Content memory substrate mirroring
RuVector hybrid (RRF) search + bounded-depth Cypher traversal semantics
(examples/mragent/agent/memory.mjs)
- Evolved harness: 10-gene reconstruction genome (cueK, efSearch, hybridAlpha,
fusion, traversalDepth, tagFanout, pruneThreshold, maxContent, rerank,
promptStrategy) in DARWIN_MUTABLE_BLOCK regions (agent/harness.mjs)
- Darwin evolution loop with mapLimit/paretoFront and ADR-150 graceful fallback
when @metaharness/darwin is absent (optimize.mjs)
- scorePolicy.ts fitness mirroring ADR-266; benchmark + probe + 7 deterministic
acceptance gates
- eval corpus with chained multi-hop "bridge" tasks so traversal depth, fan-out
and pruning are genuinely load-bearing
Runs with zero optional deps: baseline 83.3% -> evolved 100% accuracy, faster
and ~33% smaller context. Darwin discovers traversalDepth=3 (LINKED_TO*1..3).
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_017MDmEV4svuFxuDBGg8zek2
* feat(mragent): self-reconstructing graph memory, beyond SOTA (ADR-270)
Extend the MRAgent harness past the paper into calibrated, adaptive,
self-reorganizing memory, co-evolved by Darwin. Also fixes the corpus being
silently excluded by the root .gitignore data/ rule (the example was missing
its eval set).
Beyond-SOTA mechanisms (each a tunable gene Darwin evolves):
- Adaptive depth (haltConfidence): halt traversal once evidence is decisive
- Abstention + risk-adjusted utility (abstainThreshold): refuse on weak
evidence instead of hallucinating; graded on calibrated utility, not raw acc
- Consolidation/replay (agent/consolidate.mjs): store reorganizes its own
topology, laying Cue->shortcut->Content edges (RuVector self-learning GNN)
Substrate upgrades:
- Concept layer (agent/concepts.mjs): dense (concept) vs sparse (token) signals
genuinely decoupled, so hybridAlpha/fusion become load-bearing
- Hardened 24-task corpus, 6 classes (semantic/lexical/hybrid/bridge/
distractor/unanswerable) synthesized from structured signal specs
- All 12 genes proven load-bearing (some via epistatic interaction)
- Memetic optimizer: GA (mapLimit/paretoFront) + multi-start coordinate-descent
polish that reliably finds the narrow calibration optimum
Measured (deterministic, zero optional deps): baseline acc 81% / risk 0.708 /
halluc 0.13 -> evolved 100% / risk 1.000 / halluc 0.00; consolidation -25%
hops at 100% accuracy. 11 acceptance gates pass. ADR-150 compliant.
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_017MDmEV4svuFxuDBGg8zek2
* feat(mragent): generalization protocol (train/test/CV) + overfit fixes
Add a held-out evaluation regime that proves the evolved harness GENERALIZES
rather than memorizing the eval set, and fix the overfitting it surfaced.
Protocol:
- Scale corpus to 60 tasks via a deterministic generator (tools/genCorpus.mjs,
npm run gen-corpus), 10 per class, difficulty-varied (1-hop AND 2-hop bridges,
1-3 ranking-distractors) so train constrains every gene
- Optimizer evolves on a class-stratified TRAIN split, selects via 3-fold
cross-validation with a variance penalty (mean - 0.5*range), and reports a
held-out TEST split it never saw
- Generalization gate = does evolution improve the unseen split
Overfit fixes uncovered by held-out eval:
- Abstention confidence now derives from the answer's RAW relevance, not its
decay^depth path score, so deep-but-relevant bridge answers aren't mistaken
for weak ones (b-test confidence 0.39 -> 0.79); abstention generalizes across
depths. Adaptive-depth halt uses the same raw-relevance signal.
- Larger difficulty-varied corpus + CV variance penalty stop the optimizer
shaving under-constrained genes (maxContent->1) to train-fragile settings
Result (held-out test, reproducible): baseline ~30% acc / risk 0.25 / halluc
0.17 -> evolved ~65% / 0.81 / 0.04 (+35pt acc, +0.56 risk). Honest ceiling
(~80%) documented: synthetic embedding noise + one global hybridAlpha can't
serve both dense- and sparse-keyed queries. 12 acceptance gates pass.
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_017MDmEV4svuFxuDBGg8zek2
* feat(mragent): GPU LLM write-layer for the Darwin optimizer (local RTX 5080)
Adds the directed-proposal layer the GA lacks (ADR-260 'real Darwin write-layer
proposes leaps from failure traces'): agent/llmMutator.mjs shows a local,
GPU-served code model (qwen2.5-coder via an OpenAI-compatible endpoint) the
current genome + its failing cases and asks for improved genomes. Every proposal
is clamped to the declared gene bounds (coerceGenome) before entering the
population, so untrusted LLM output can only ever be a safe genome — never an
unsafe gene. Wired into optimize.mjs every 3rd generation; folded into the
archive so GPU candidates compete in polish + acceptance.
Fully opt-in + gracefully degrading (ADR-150): MRAGENT_LLM=off or no reachable
endpoint => identical deterministic GA+coordinate-descent run as before. Auto-
detects http://localhost:11434/v1 (ollama) by default; MRAGENT_LLM_URL/MODEL
override.
Measured (RTX 5080, qwen2.5-coder:7b): 8 genomes proposed across gens, bounds-
safe; the deterministic polish still wins on this small synthetic corpus (the
GA+grid already enumerates the optimum), so the write-layer is a no-regression
enhancement that matters on larger corpora the grid can't cover. 14/14 tests
pass (2 new coerceGenome safety tests).
Co-Authored-By: claude-flow <ruv@ruv.net>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ruvnet <ruvnet@gmail.com>
7.7 KiB
MRAgent — Self-Reconstructing Graph Memory over RuVector, evolved by Darwin
A runnable reference implementation of MRAgent ("Memory is Reconstructed, Not Retrieved: Graph Memory for LLM Agents") on RuVector — and then past the paper. A Meta-Harness Darwin loop evolves the reconstruction harness while the memory substrate stays frozen ("freeze the model, evolve the harness").
Frozen model: the RuVector Cue-Tag-Content memory graph (
agent/memory.mjs). Evolved harness: a 12-gene reconstruction genome (agent/harness.mjs).
ADRs: ADR-269 (the MRAgent baseline) and ADR-270 (this beyond-SOTA version).
Beyond the paper
MRAgent reconstructs an answer over a static graph: search cues → traverse cue→tag→content → prune → synthesize. This implementation adds three mechanisms a 25-year-out memory system needs, each a tunable gene Darwin co-evolves:
- Adaptive depth (
haltConfidence) — stop traversing once evidence is decisive, so easy queries cost fewer hops (ACT-style adaptive computation). - Abstention + calibration (
abstainThreshold) — answer "I don't know" when reconstructed evidence is too weak, instead of confidently hallucinating. Graded by a risk-adjusted utility, not raw accuracy: a confident wrong answer scores worse than an honest abstention. - Consolidation / replay (
agent/consolidate.mjs) — the store reorganizes its own topology from workload (the self-learning GNN RuVector describes), laying Cue→shortcut→Content edges so a 3-hop query resolves in 1 hop tomorrow.
The 12-gene reconstruction genome
| Gene | Range | RuVector mapping |
|---|---|---|
cueK |
1–12 | # cue vectors from hybridSearch |
efSearch |
16–256 | HNSW search depth |
hybridAlpha |
0–1 | RRF sparse↔dense weight |
fusion |
rrf · linear · dbsf | hybrid fusion strategy |
traversalDepth |
1–4 | Cypher LINKED_TO*1..N hops |
tagFanout |
1–8 | tags expanded per node |
pruneThreshold |
0–0.6 | path-evidence floor |
maxContent |
1–20 | content LIMIT to synthesis |
haltConfidence |
0.2–0.9 | adaptive-depth halt |
rerank |
gnn · none | corroboration-aware rerank |
promptStrategy |
terse · evidence-first · prune-explicit | synthesis prompt |
abstainThreshold |
0–0.6 | abstention / calibration |
Every gene is proven load-bearing in test/harness.test.mjs — some only via
interaction (distractor tasks are solved by evidence-first or by
terse + gnn + fanout≥2, an epistatic landscape).
The hardened corpus (60 tasks, 6 classes, difficulty-varied)
data/eval-set.json is generated by tools/genCorpus.mjs (npm run gen-corpus) as structured signal specs; agent/memory.mjs synthesizes the
Cue/Tag/Content node texts so difficulty is guaranteed, not dependent on fragile
English. A concept layer (agent/concepts.mjs) gives the dense embedding real
semantics decoupled from lexical overlap. 10 instances per class, with varied
difficulty (1-hop AND 2-hop bridges, 1–3 ranking-distractors) so a train/test
split constrains every gene:
| Class | Stresses |
|---|---|
| semantic | hybridAlpha→dense (paraphrase, no shared tokens) |
| lexical | hybridAlpha→sparse (rare identifier, generic concept) |
| hybrid | fusion / RRF (needs both signals) |
| bridge | traversalDepth (1–2 intermediate hops) |
| distractor | rerank / tagFanout / promptStrategy (ranking-distractor content) |
| unanswerable | abstainThreshold (no correct content exists → abstain) |
Generalization, not overfitting (train / test / CV)
The optimizer evolves on a train split and reports a held-out test split it
never saw — proving the genome generalizes rather than memorizing the eval set.
Selection uses 3-fold cross-validation with a variance penalty (mean − ½·range
across folds) so a knife-edge gene that wins one fold but collapses on another is
rejected. A subtle bug this surfaced — confidence was depressed by decay^depth,
making deep-but-relevant answers look weak and breaking abstention across depths —
is fixed by deriving abstention confidence from the answer's raw relevance, not
its decayed path score (agent/memory.mjs).
accuracy risk halluc
baseline (test) ~30% ~0.25 0.17
evolved (test) ~65% ~0.81 0.04 ← held out, never seen in evolution
+35pt +0.56 generalizes
(The synthetic toy embedding has per-instance noise, and one global hybridAlpha
cannot perfectly serve both dense- and sparse-keyed queries, so the test ceiling
is ~80%, not 100% — the gate asks whether evolution transfers, which it does.)
Results on the full corpus (zero optional deps, deterministic)
config accuracy risk halluc latency hops
baseline 50.0% 0.417 0.17 2.81 1.23
evolved (ref) 70.0% 0.775 0.03 3.09 1.08
evolved+replay 70.0% 0.775 0.03 3.16 1.00
evolved vs baseline: accuracy +20.0pt · risk +0.358 · hallucination 0.17 → 0.03
consolidation: shortcuts → fewer hops at equal accuracy
npm run optimize (full GA + memetic polish) reaches +33pt train accuracy /
risk 0.94 and writes the evolved genome to optimize.report.json, which
npm run benchmark then picks up. The optimizer is memetic: a genetic loop
(Darwin mapLimit/paretoFront) explores broadly, then deterministic
coordinate descent refines narrow optima (e.g. the abstention band).
Run it
cd examples/mragent
npm test # 12 deterministic gates, every gene proven load-bearing
npm run benchmark # baseline vs evolved vs evolved+replay
npm run optimize # Darwin loop + memetic polish + consolidation + held-out test
npm run gen-corpus # regenerate data/eval-set.json (deterministic)
npm run probe # inspect @metaharness/darwin exports (optional)
Nothing requires network, an API key, or native bindings. The substrate is a
deterministic in-process graph with the same semantics as a live RuVector
.rvf index (concept-dense + token-sparse hybrid RRF search, bounded-depth
prunable Cypher traversal, GNN-style corroboration rerank), so an evolved genome
transfers to production unchanged.
With the real Darwin write-layer (optional)
npm i -D @metaharness/darwin@latest
npx metaharness evolve . --generations 12 --children 3 --eval-cmd "node benchmark.mjs"
harness/scorePolicy.ts is the fitness metaharness evolve calls per mutation.
ADR-150 compliance
@metaharness/darwin and ruvector are optionalDependencies only; every
touch is try/catch guarded; npm test, npm run benchmark, and npm run optimize all pass with no optional deps installed (the CI gate).
Layout
examples/mragent/
├── agent/
│ ├── concepts.mjs # concept layer (dense semantics ≠ sparse tokens)
│ ├── memory.mjs # FROZEN: Cue-Tag-Content store (RuVector semantics)
│ ├── harness.mjs # EVOLVED: 12-gene genome + reasoning loop
│ └── consolidate.mjs # replay → self-reorganizing topology
├── harness/scorePolicy.ts# Darwin fitness (accuracy + risk + cost)
├── data/eval-set.json # 60-task structured corpus (generated)
├── tools/genCorpus.mjs # deterministic corpus generator
├── optimize.mjs # GA + CV + memetic polish + held-out test + consolidation
├── benchmark.mjs # baseline vs evolved vs replay
├── probeDarwin.mjs # probe optional @metaharness/darwin
└── test/harness.test.mjs # 12 acceptance gates