Skip to content

Read Path

Retrieval uses concurrent fan-out across multiple search strategies, fuses the results, optionally reranks them, and scores with a unified function.

Sequence

Search strategies

ANN (approximate nearest neighbour) search using the query embedding. Returns candidates ranked by cosine similarity.

  • Memory backend: brute-force scan
  • BadgerDB backend: HNSW index (M=16, efConstruction=200)
  • Postgres backend: pgvector <=> operator with IVFFlat index
  • Qdrant backend: native ANN with label metadata filtering

When Labels are specified in the request, vector search applies label push-down filtering. Only nodes with all specified labels are returned.

Graph walk

Breadth-first traversal from seed nodes, following edges outward. Discovers contextually related nodes that may not be close in vector space.

Traversal strategies:

StrategyBehaviour
BFSStandard breadth-first, all edges
BeamKeep top-K candidates at each depth
WaterCircleExpanding ring with edge weight decay

Session context

Recent nodes from the current session (via KVStore). Provides conversational continuity.

Auto-embedding for text queries

When a RetrieveRequest includes Text instead of (or in addition to) Vector, the text is automatically embedded using the configured Embedder. This enables natural-language queries without requiring callers to manage their own embedding pipeline.

go
results, _ := ns.Retrieve(ctx, client.RetrieveRequest{
    Text: "What changed in Go 1.22?",
    TopK: 5,
})

Fusion

The fusion step merges results from all paths:

  1. Deduplicate by node ID
  2. Tag each result with its retrieval source ("vector", "graph", "fused")
  3. Nodes found by multiple paths are tagged "fused"

Reranking

When an LLM reranker is configured and the query includes text, fusion results are passed through a cross-encoder-style reranker before scoring. The reranker uses the LLM to assess relevance of each candidate to the original query text, producing a more semantically accurate ordering.

go
engine := retrieval.Engine{
    // ...
    Reranker: retrieval.NewLLMReranker(llmProvider, "gpt-4o-mini"),
}

If the reranker fails or is not configured, candidates proceed to scoring in their original fusion order.

Label filtering

The Labels field in RetrieveRequest filters results to only include nodes that have all specified labels:

go
results, _ := ns.Retrieve(ctx, client.RetrieveRequest{
    Vector: queryVec,
    Labels: []string{"Claim", "Verified"},
    TopK:   10,
})

Label filtering is pushed down to the vector search backend when supported (Qdrant, Postgres). For other backends, it is applied as a post-filter.

Scoring

Each candidate is scored by the composite function:

score = w_sim * similarity + w_conf * confidence + w_rec * recency + w_util * utility

See Scoring Function for details on weights and decay.

Streaming (gRPC)

The gRPC API supports server-streamed retrieval via StreamRetrieve. Results are sent one at a time as they are scored, allowing clients to begin processing before the full result set is ready.

rpc StreamRetrieve(GRPCRetrieveRequest) returns (stream GRPCRetrieveResponse)

Hybrid strategy weights

The retrieval engine balances vector vs. graph results:

go
strategy := retrieval.HybridStrategy{
    VectorWeight:  0.45,  // how much to trust vector results
    GraphWeight:   0.40,  // how much to trust graph results
    SessionWeight: 0.15,  // how much to trust session context
    Traversal:     store.StrategyWaterCircle,
    MaxDepth:      3,
}

These defaults come from the namespace mode and can be overridden per-query.

Temporal filtering

All results are filtered by temporal validity:

  • Nodes where ValidFrom > AsOf are excluded
  • Nodes where ValidUntil < AsOf are excluded
  • Nodes failing IsValidAt(AsOf) receive a score of 0

This means point-in-time queries automatically see only facts that were valid at the requested time.

Released under the MIT License.