KBLayerComposer
KBLayerComposer.compose_recall composes recall across the four
canonical layer scopes:
| Scope | Source | Precedence |
|---|---|---|
PRIVATE | The tenant's own silo | 0 (highest) |
HIERARCHICAL | Parent organisation (read-only, replicated) | 1 |
MARKETPLACE | Subscribed external corpora (federated) | 2 |
GLOBAL | Curated read-only platform corpus | 3 (lowest) |
Smaller precedence wins.
Resolution flow
resolve_layers(ctx)returns the activeLayerHandlelist for the tenant. Defaults to[PRIVATE]; populates the other three when the tenant'skb_subscriptionsrows + parent-org link + global-corpus replication offsets are set.- For each layer, fan out the recall:
PRIVATEruns against the tenant's ownIMemoryEngine(defaultHierarchicalRAGAdapter).HIERARCHICAL/MARKETPLACE/GLOBALrun against thealphaswarm_kb_federationreverse-proxy when the layer has afederation_endpoint.
- Apply
BitemporalMerger.merge_layersto dedupe by entity id with precedence-aware ordering. Losers land inmetadata.dissenting_layers. - Re-rank by
(precedence, score, recency).
Conflict resolution
| Conflict | Resolution |
|---|---|
| Same entity, different value | Higher-precedence layer wins; loser exposed in dissenting_layers. |
Temporal disagreement (valid_to/valid_from overlap) | Both kept; downstream as_of reconstructs the timeline. |
| Edge contradiction (new edge supersedes old) | Old edge's expired_at = now(); not deleted. |
Caching + invalidation
- Per-subscriber result cache keyed by
(subscriber_tenant, source_tenant, dataset, query_hash)with a 60s TTL (default). - OpenFGA Watch events for
subscription.{granted,revoked,updated}flush impacted cache entries via thealphaswarm:kb:bitmapRedis pub/sub channel.
When NOT to use compose_recall
- Single-corpus recall against your tenant's own private corpus →
use
data.kb.recall(faster, no federation overhead). - Bulk re-indexing / improve / forget — these are always per-corpus.