Intermediate7 min read14 of 40

Fusion Retrieval - Multi-Strategy Search

Combine multiple retrieval strategies (vector, keyword, graph) with reciprocal rank fusion for robust search results across diverse query types.

Read first:Simple RAG

Overview

Fusion Retrieval combines multiple search strategies (vector, keyword, graph) and merges results using reciprocal rank fusion. This approach is more robust across diverse query types than any single method.

Pattern #3 in the 37 RAG Patterns Collection

Why Fusion?

Search TypeStrengthWeakness
VectorSemantic similarityMisses exact matches
KeywordExact term matchingNo semantic understanding
GraphRelationship awareRequires structured data

Fusion combines all strengths while minimizing weaknesses.

Architecture

graph LR
    A[Query] --> B[Vector Search]
    A --> C[Keyword Search]
    A --> D[Graph Search]
    
    B --> E[Result Set 1]
    C --> F[Result Set 2]
    D --> G[Result Set 3]
    
    E --> H[Reciprocal Rank Fusion]
    F --> H
    G --> H
    
    H --> I[Merged Results]
    I --> J[LLM Generation]
    
    style A fill:#f9f,stroke:#333
    style H fill:#ff9,stroke:#333
    style J fill:#9f9,stroke:#333

Reciprocal Rank Fusion

The magic formula that combines rankings:

RRF(d) = Σ(1 / (k + rank_i(d)))

Where:

  • d = document
  • rank_i(d) = rank of document in retriever i
  • k = constant (typically 60)

Implementation

Step 1: Multiple Retrievers

python
def multi_retrieval(query):
    # Vector search
    vector_results = opensearch.search(
        index="embeddings",
        body={
            "query": {"knn": {"vector": embed(query), "k": 10}}
        }
    )
    
    # Keyword search (BM25)
    keyword_results = opensearch.search(
        index="text",
        body={
            "query": {"match": {"content": query}}
        }
    )
    
    # Graph search (if applicable)
    graph_results = traverse_knowledge_graph(query)
    
    return vector_results, keyword_results, graph_results

Step 2: Reciprocal Rank Fusion

python
def reciprocal_rank_fusion(results_lists, k=60):
    """Combine multiple ranked lists using RRF"""
    scores = {}
    
    for results in results_lists:
        for rank, doc_id in enumerate(results, start=1):
            if doc_id not in scores:
                scores[doc_id] = 0
            scores[doc_id] += 1 / (k + rank)
    
    # Sort by combined score
    ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    return [doc_id for doc_id, score in ranked]

Step 3: Complete Pipeline

python
def fusion_rag(query):
    # Get results from multiple strategies
    vector, keyword, graph = multi_retrieval(query)
    
    # Combine with RRF
    fused_ids = reciprocal_rank_fusion([
        [r['_id'] for r in vector],
        [r['_id'] for r in keyword],
        [r['id'] for r in graph]
    ])
    
    # Fetch top documents
    context = fetch_documents(fused_ids[:5])
    
    # Generate response
    response = bedrock.invoke_model(
        modelId="meta.llama3-70b-instruct-v1:0",
        body=json.dumps({
            "prompt": f"Context: {context}\n\nQuestion: {query}"
        })
    )
    return response

Complete Notebook

🚀 Run the full implementation:

📓 Fusion Retrieval AWS Notebook

Includes:

  • Multiple retrieval strategies setup
  • RRF implementation with tuning
  • Benchmark comparisons
  • OpenSearch hybrid configuration
  • Performance optimization

Performance

MetricValue
Accuracy+20% vs single-strategy
Latency~400ms (parallel)
Cost/query$0.10
RobustnessHigh across query types

When to Use

Best For

  • Production applications requiring high reliability
  • Diverse query types (semantic + exact matches)
  • Critical accuracy where single-method failure is costly
  • General-purpose search without query type prediction

Combine With

  • Reranking (#4) - Further refine fused results
  • Caching (#33) - Cache fusion results for speed
  • Adaptive RAG (#8) - Route queries to best fusion mix

Tuning Tips

1. Adjust k Parameter

python
# Higher k = more equal weight distribution
k = 60  # Standard
k = 20  # More weight to top ranks
k = 100 # More democratic fusion

2. Weight Different Strategies

python
def weighted_rrf(results_lists, weights, k=60):
    scores = {}
    for results, weight in zip(results_lists, weights):
        for rank, doc_id in enumerate(results, start=1):
            if doc_id not in scores:
                scores[doc_id] = 0
            scores[doc_id] += weight / (k + rank)
    return scores

# Example: favor vector over keyword
fused = weighted_rrf(
    [vector_results, keyword_results],
    weights=[0.7, 0.3]
)

3. Filter Low-Confidence Results

python
# Only include docs appearing in multiple retrievers
def multi_retriever_filter(fused_ids, min_appearances=2):
    return [
        doc_id for doc_id in fused_ids 
        if count_appearances(doc_id) >= min_appearances
    ]

Related Papers

Next Steps

  1. Try it: Run the Fusion Retrieval notebook
  2. Add reranking: Implement Pattern #4 for refinement
  3. Optimize: Use Caching (#33) for frequently fused queries
  4. Scale: Try Hybrid Search (#34) for production

Part of: 37 RAG Patterns Collection
See also: Complete Pattern Index

Go Deeper With Live Instruction

Covered in depth in our llm engineering program.