Fabriq
1.x
Docs/Fabriq/Recall
Open

Reading6 min
Updated2 Aug 2026
Sourcev1/(ai-agents)/recall.mdx

Recall is the agent toolkit's headline capability: hand it a natural-language query and a token budget, and it returns a ranked, budget-fitted context pack assembled across the vector, search, and graph planes. It is the difference between a vector store and a brain — recall is a pipeline, and fabriq has every stage as a first-class port.

type RecallRequest struct {
    Query    string      // natural-language; embedded + lexical-searched
    Budget   int         // token budget for the returned pack
    Entities []string    // entity types to recall from
    K        int         // candidates per channel (default 24)
    Hops     int         // graph expansion depth (default 1)
    Filters  query.Where // optional structured constraints
}

pack, err := tk.Recall(ctx, agent.RecallRequest{
    Query: "overheating pumps", Budget: 8000, Entities: []string{"asset"},
})

The pipeline01

  1. Semantic — embed Query (via the Embedder) and run Vector().Similar per entity.

  2. LexicalSearch().Search per searchable entity (full-text).

  3. Graph — expand the top seeds one or more hops along each entity's declared edges (and, optionally, reverse edges).

  4. Fuse — combine the channels with Reciprocal Rank Fusion.

  5. Hydrate — load the fused ids' authoritative rows from the relational plane (the source of truth), batched per entity.

  6. Pack — greedily include rows in fused-rank order until the next would exceed Budget; the rest are reported as Omitted.

type ContextItem struct {
    Entity, ID string
    Row        json.RawMessage // the hydrated, current row
    Score      float64         // fused rank score
    Source     []string        // provenance: "vector","search","graph"
    Tokens     int
}

type ContextPack struct {
    Items    []ContextItem
    Omitted  int      // candidates that didn't fit the budget
    Tokens   int      // total estimate used
    Warnings []string // degraded-channel notes — observable, not silent
}

Why Reciprocal Rank Fusion

RRF combines the channels using only rank position, never raw scores — score = Σ weight / (60 + rank) across channels. So cosine similarity, BM25 relevance, and graph hop-distance never have to be normalized against each other, which is the single biggest source of fragility in hybrid retrieval. An item that surfaces in multiple channels rises; per-channel weights are tunable via Config.ChannelWeights.

Note

Recall is lenient by default: if a channel errors (search engine unreachable, no embedder), it is logged, dropped, and surfaced in ContextPack.Warnings — recall returns what the surviving channels found. Set Config.Strict = true to make any channel failure a hard error instead.

Graph expansion02

The graph channel expands from the top seeds along each seed entity's declared edges, staying inside fabriq's confirmed openCypher subset (MATCH (n:Label {id:$id})-[:Rel]->(m:Target) RETURN m.id). Neighbours map back to entities through the edge's target — no label-to-entity reverse lookup needed. Knobs:

  • Hops — depth; > 1 emits a variable-length path ([:Rel*1..H]).

  • Config.GraphReverse — also expand incoming edges (opt-in; off by default to bound fan-out).

  • Config.GraphSeeds — how many top-ranked seeds to expand (default 8).

Token budgeting03

Config.Tokenizer func([]byte) int estimates a row's token cost; the default is a dependency-free bytes/4 heuristic. Wire an exact tokenizer (tiktoken, etc.) for precise budgets. The packer stops at the first row that would overflow, preserving fused-rank order, and reports the remainder as Omitted.

Altitude-aware recall04

When context distillation is enabled, RecallRequest accepts an altitude parameter that controls which layer of the digest tree is consulted before (or instead of) raw entity rows:

type RecallRequest struct {
    Query    string
    Budget   int
    Entities []string
    K        int
    Hops     int
    Filters  query.Where
    Altitude agent.Altitude // AltAuto (default) | AltEntity | AltScope | AltTenant
}
ValueBehaviour
AltAuto (default, zero value)Token budget drives descent: if the candidate entity rows fit within Budget, recall descends to raw entity rows (AltEntity); otherwise it climbs to the tenant-root digest summary (AltTenant).
AltEntityRaw entity rows only — digest items are dropped.
AltScopeScope/cluster digest summaries are kept; the entity rows they cover are dropped (when at least one digest is present).
AltTenantThe tenant-root digest only; covered entity rows are dropped (when a digest is present).

In all non-AltAuto modes, a digest and any entity row it covers are never both included in the same ContextPack. If no digest items are present the behaviour gracefully falls back to returning whatever was found.

// Tight budget: AltAuto climbs to the tenant digest when entity rows don't fit.
pack, err := tk.Recall(ctx, agent.RecallRequest{
    Query:    "north site equipment status",
    Budget:   1500,
    Entities: []string{"asset"},
    Altitude: agent.AltAuto,
})

// Generous budget: AltAuto descends to entity rows when they fit.
pack, err := tk.Recall(ctx, agent.RecallRequest{
    Query:    "north site equipment status",
    Budget:   12000,
    Entities: []string{"asset"},
    Altitude: agent.AltAuto,
})
Note

Auto-distillation rides the same write events as auto-embedding: the proj:distill worker consumes the same event stream as proj:embed, so a write that triggers re-indexing also triggers re-summarisation. No extra wiring is needed to keep the two in sync.

Read primitives05

For agents that want manual control, the same tool surface exposes the raw planes as individual tools (see MCP for the agent-facing names):

ToolPlaneNotes
recallall (fused)the auto-context front door
vector_similarvectorsemantic nearest-neighbour by query text
searchsearchfull-text over indexed fields
graph_traversegraphread-only openCypher (caller-supplied)
getrelationalone row by id
Warning

graph_traverse passes caller-supplied cypher straight to the graph engine. A read-only guard rejects mutating clauses (CREATE/MERGE/DELETE/SET/…) as defense-in-depth, but deployments exposing it to untrusted callers should also use a read-only graph connection.