SwarmAI Recall Architecture — The READ Path of a Self-Evolving Agent OS #79
xg-gh-25
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
SwarmAI Recall Architecture — The READ Path of a Self-Evolving Agent OS
TL;DR
recall_multi). They inject at two distinct moments, not one.ref_count, decay, and adormant→archivedlifecycle. 90 days unreferenced = auto-exit.1. The Mental-Model Correction: Recall Is Not One System
The most common misconception — including ours, until we traced it — is that an agent has "a memory." SwarmAI has five, each with its own store, its own retrieval algorithm, and its own injection timing. Trying to reason about "the recall system" as a monolith produces wrong fixes (we shipped 33 patches at the wrong layer for a different recurring bug; the lesson generalizes).
Expanding the two injection moments on their own — this is the single most
misunderstood part:
2. The Architecture Panorama
2.1 The five subsystems (and the wiring truth)
knowledge_chunks+knowledge_fts(FTS5 external-content) +knowledge_vec(sqlite-vec, 1024-d Titan v2)0.6·vector + 0.4·BM25, threshold 0.05session_routerpassesembed_fn; vector active when Bedrock up, graceful FTS5-only fallbackknowledge_store.py,recall_engine.py,embedding_client.pymemory_entries+memory_vec(sqlite-vec) + inline HTML-comment decay metadata0.6·vector + 0.4·BM25+ decay weighting, threshold 0.10memory_embeddings(defaultsFalse); hybrid is built-but-unwired. And MEMORY.md ~15K < 30K threshold → full injection, so the section-selection scorer doesn't even run todaymemory_index.py,memory_embeddings.py,context_recall.py,memory_decay.pymessages_fts(FTS5 external-content)density·0.4 + recency·0.35 + richness·0.25, ±10-msg windowsent=0so an undrained pending message is never injected as phantom contextsession_recall.pytranscript_chunks+transcript_fts(FTS5) +transcript_vec(sqlite-vec)content_hashembed_fnpassedtranscript_indexer.pyrecall_multi._codeintel_recallIndex inventory: 4 independent FTS5 tables (
knowledge_fts,messages_fts,transcript_fts, code-symbol FTS) + 3 sqlite-vec tables (knowledge_vec,memory_vec,transcript_vec).2.2 The aggregator:
recall_multiA read-only facade that fans out across five domains and returns a bucketed result:
Two load-bearing safety properties:
allow_embed=Falseby default → zero Bedrock embeds, zero writes. (So Library degrades to FTS5-only when reached via the aggregator — the hybrid path is the directsession_routerroute.)policy_excluded_filesprivacy gate propagates across ALL domains — this closed a real leak where--domainscould bypass the privacy that--fileenforced (caught by an adversarial gate, run_4358cc95).2.3 The injection asymmetry that surprises people
Resume context calls no recall subsystem. When a session resumes (20–150K tokens of context),
context_injector.build_resume_context()mechanically extracts checkpoint / assistant-conclusions / key-tool-results / last-30-turns straight from the DBmessagestable. It does not callsession_recall,knowledge_store, ormemory_index. Recall is augmentation, never on the resume critical path. This is deliberate: resume must be deterministic and offline-safe; recall is best-effort and can fail without breaking continuity.3. Positioning: Memory vs Knowledge vs DDD
Three layers, one routing question. From
s_persist's routing tree, the decisive test is:MEMORY.md,DailyActivity/,EVOLUTION.mdKNOWLEDGE.md+Knowledge/libraryPRODUCT.md/TECH.md/IMPROVEMENT.md/PROJECT.md3.1 The 7-type knowledge governance (MECE + Darwinian)
Every stored entry is one of 7 mutually-exclusive, collectively-exhaustive types (PRI01, Discussion #59):
principle·correction·decision·guideline·pitfall·process·modelThe taxonomy isn't decoration — it drives routing (where a new entry lands) and lifecycle (how it decays). Critically, WHERE and WHAT/lifespan are kept separate on purpose:
3.2 Darwinian decay: knowledge must eliminate itself
Mechanically: entries carry
<!-- ref:N | last:DATE | decay:STATE -->. Decay follows an Ebbinghaus-style curve with Hebbian potentiation —ref_countand access spacing extend stability; idle entries slideactive → dormant → archived. Superseded entries score0.1×in selection rather than being deleted. The design principle that makes it work:4. Design Philosophy
4.1 From Hard Drive to OS
The framing that organizes the whole system:
And the honest diagnosis that motivated the recall work:
4.2 The 6-link circuit — recall is one link, not the goal
Recall (link ④) only matters if current flows all the way around with a falling correction-count:
The empirically broken link is ⑤ APPLY — not storage, not retrieval. A lesson can be in context and still not change behavior. That is why SwarmAI leans on mechanical gates over reminders.
4.3 Reversible, never drop
A principle we adopted explicitly, and a mistake we watched someone else make and retire:
Corollary for retrieval quality: recall must never summarize — "NO summarization — that is silent 降智 (intelligence-degradation), forbidden." Recall returns a reversible exact slice, scoped to a
## section, queried by content (never by a stale offset that distillation would invalidate).4.4 Measurement is reality
5. Methodology — How We Decide What to Recall
5.1 The recall chain (target architecture, partially built)
5.2 Cross-domain ranking is BUCKETED, never globally mixed
5.3 Keyword and vector are COMPLEMENTARY — the lever is commensurability, not the ratio
We ran a spike (12 synonym/CJK-shifted queries against the live
memory_vec) before tuning anything:And a cargo-cult guard, because borrowing a magic number without its machinery is meaningless:
That last rule — a missing vector renormalizes to the available leg, never scores 0 — is what makes lazy embedding safe.
5.4 Lazy per-surface embedding, not a big-bang index
5.5 The justification is capability, NOT context savings
We were explicit about not solving a problem we don't have:
5.6 Compress at produce-time, never retroactively
For tool-output compression (a sibling READ-path concern), the cache-safety rule:
6. Built vs Designed (the honesty section)
We refuse to present the roadmap as the product.
embed_fn, graceful fallbackknowledge_ftsexternal-content corruption root-fix + auto-heal proberecall_multiread-only 5-domain aggregator + privacy gaterecall_contextreversible section-scoped recall for excluded MEMORY sectionsTwo known debts, flagged not hidden:
transcript_indexer.upsert_chunkcarries the same external-content write-bug class that corruptedknowledge_fts(FTS5'delete'must bind OLD stored values). Separate table → separate run.0.6·vector + 0.4·keywordhybrid + min-max renorm (Knowledge inrecall_engine.py, Memory inmemory_embeddings.py). Duplicate logic → merge candidate.7. Reference Sources — What Shaped This
0.6 vector / 0.4 keywordwith Okapi-BM25+IDF, min-max on the BM25 leg only, missing-vector renorm.run_*/ SHAs / paths); cache-stable prefix8. Open Questions (we'd genuinely like input on)
Built in the open. The architecture above is the READ path; the WRITE path (ingestion governance, 7-type routing, Darwinian decay) is Discussion #59. Recall is one link in a 6-link circuit — and a link you can't measure is assumed broken.
🐝 SwarmAI — Your AI Team, 24/7. Human directs, AI delivers.
All reactions