Retrievers

View Source

Retrievers provide different strategies for finding relevant documents based on queries.

Overview

The library provides four retriever implementations:

RetrieverMethodQuery TypeBest For
SemanticVector similarityEmbeddingConceptual matching
FullTextKeyword matchingTextExact keywords
HybridRRF fusionBothBalanced results
GraphKnowledge graphBothEntity relationships

Retriever Behaviour

All retrievers implement the Rag.Retriever behaviour:

@callback retrieve(retriever, query, opts) :: {:ok, [result()]} | {:error, term()}

@type result :: %{
  id: any(),
  content: String.t(),
  score: float(),
  metadata: map()
}

Semantic Retriever

Uses pgvector L2 distance for vector similarity search.

alias Rag.Retriever
alias Rag.Retriever.Semantic

# Create retriever
retriever = %Semantic{repo: MyApp.Repo}

# Search with embedding
{:ok, results} = Retriever.retrieve(retriever, query_embedding, limit: 10)

Scoring:

  • Score = 1.0 - L2_distance
  • Range: 0.0 (dissimilar) to 1.0 (identical)

Capabilities:

  • supports_embedding?() - true
  • supports_text_query?() - false

Best for:

  • Finding conceptually similar content
  • When query meaning matters more than exact words

FullText Retriever

Uses PostgreSQL tsvector for keyword matching.

alias Rag.Retriever
alias Rag.Retriever.FullText

# Create retriever
retriever = %FullText{repo: MyApp.Repo}

# Search with text
{:ok, results} = Retriever.retrieve(retriever, "GenServer state", limit: 10)

Scoring:

  • Score from PostgreSQL ts_rank function
  • Multiple terms combined with AND

Capabilities:

  • supports_embedding?() - false
  • supports_text_query?() - true

Best for:

  • Finding documents with specific keywords
  • Technical term searches
  • When exact matches matter

Hybrid Retriever

Combines semantic and full-text using Reciprocal Rank Fusion (RRF).

alias Rag.Retriever
alias Rag.Retriever.Hybrid

# Create retriever
retriever = %Hybrid{repo: MyApp.Repo}

# Search with both embedding and text
{:ok, results} = Retriever.retrieve(retriever, {embedding, "search text"}, limit: 10)

Query Format:

  • Tuple of {embedding_vector, text_query}

Scoring (RRF):

RRF(d) = Σ 1 / (k + rank(d))  where k = 60
  • Documents in both result sets get combined scores
  • Balances semantic understanding with keyword precision

Capabilities:

  • supports_embedding?() - true
  • supports_text_query?() - true

Best for:

  • Balanced semantic + keyword search
  • When you want best of both methods
  • Production RAG systems

Graph Retriever

Uses knowledge graph structure for context-aware retrieval.

alias Rag.Retriever.Graph

# Create retriever
retriever = Graph.new(
  graph_store: graph_store,
  vector_store: vector_store,
  mode: :hybrid,
  depth: 2,
  local_weight: 0.7,
  global_weight: 0.3
)

# Search
{:ok, results} = Retriever.retrieve(retriever, query_embedding,
  limit: 10,
  embedding_fn: &embed/1
)

Search Modes

Local Search (:local)

  • Vector search on entity embeddings
  • Graph traversal to related entities
  • Collect source chunks from entities
  • Score by graph distance
{:ok, results} = Graph.local_search(retriever, query, limit: 10)

Global Search (:global)

  • Vector search on community summaries
  • Returns high-level context
  • Good for overview questions
{:ok, results} = Graph.global_search(retriever, query, limit: 10)

Hybrid Search (:hybrid)

  • Runs local and global in parallel
  • Combines with weighted RRF
  • Best of both approaches
{:ok, results} = Graph.hybrid_search(retriever, query, limit: 10)

Configuration

OptionDefaultDescription
graph_storerequiredGraph store module
vector_storerequiredVector store module
mode:localSearch mode
depth2Graph traversal depth
local_weight1.0Weight for local in hybrid
global_weight1.0Weight for global in hybrid

Scoring Comparison

RetrieverScore SourceRangeFormula
SemanticL2 distance0-11.0 - distance
FullTextts_rank0-1+PostgreSQL rank
HybridRRFvariesΣ 1/(60+rank)
Graph LocalDepth0-11/(1+depth)
Graph GlobalRank0-11/(1+rank)

Complete Example

alias Rag.Router
alias Rag.Retriever
alias Rag.Retriever.{Semantic, FullText, Hybrid}
alias Rag.Reranker
alias Rag.Reranker.LLM

# Setup
{:ok, router} = Router.new(providers: [:gemini])
query = "How does GenServer handle state?"

# Get query embedding
{:ok, [query_embedding], router} = Router.execute(router, :embeddings, [query], [])

# Semantic search
semantic_retriever = %Semantic{repo: Repo}
{:ok, semantic_results} = Retriever.retrieve(semantic_retriever, query_embedding, limit: 10)

# Full-text search
fulltext_retriever = %FullText{repo: Repo}
{:ok, fulltext_results} = Retriever.retrieve(fulltext_retriever, query, limit: 10)

# Hybrid search
hybrid_retriever = %Hybrid{repo: Repo}
{:ok, hybrid_results} = Retriever.retrieve(hybrid_retriever, {query_embedding, query}, limit: 10)

# Compare results
IO.puts("Semantic: #{length(semantic_results)} results")
IO.puts("FullText: #{length(fulltext_results)} results")
IO.puts("Hybrid: #{length(hybrid_results)} results")

# Rerank hybrid results
reranker = LLM.new(router: router)
{:ok, reranked} = Reranker.rerank(reranker, query, hybrid_results, top_k: 5)

# Use top results for RAG
context = Enum.map(reranked, & &1.content) |> Enum.join("\n\n")

Choosing a Retriever

Use Semantic when:

  • Query meaning matters more than keywords
  • Finding conceptually similar content
  • Working with paraphrased queries

Use FullText when:

  • Searching for specific terms
  • Technical/domain-specific queries
  • Exact keyword matching needed

Use Hybrid when:

  • Want balanced results
  • Building production RAG systems
  • Unsure which method works best

Use Graph when:

  • Entity relationships matter
  • Need multi-hop reasoning
  • Building knowledge-intensive applications

Pipeline Integration

# In a pipeline step
def retrieve_step(input, context, _opts) do
  retriever = %Hybrid{repo: context.repo}
  embedding = Context.get_step_result(context, :embed_query)
  query = context.query

  case Retriever.retrieve(retriever, {embedding, query}, limit: 10) do
    {:ok, results} -> {:ok, results}
    {:error, reason} -> {:error, reason}
  end
end

Next Steps

  • Rerankers - Improve retrieval quality with reranking
  • GraphRAG - Build knowledge graphs for retrieval
  • Pipeline - Combine retrievers in workflows