You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ruby RAG Tutorial: Build Production-Ready Retrieval Systems with DSPy.rb
name
Retrieval Augmented Generation (RAG)
description
Step-by-step guide to implementing Retrieval Augmented Generation in Ruby. Vector search, Rails integration, and evaluation metrics. Better than Python's LangChain.
breadcrumb
name
url
Advanced
/advanced/
name
url
RAG
/advanced/rag/
prev
name
url
Multi-stage Pipelines
/advanced/pipelines/
next
name
url
Custom Metrics
/advanced/custom-metrics/
date
2025-07-10 00:00:00 +0000
Retrieval Augmented Generation (RAG)
DSPy.rb supports building RAG (Retrieval Augmented Generation) applications by combining retrieval systems with LLM-powered reasoning. While the framework doesn't provide built-in vector stores or embedding models, you can integrate external retrieval services and build sophisticated RAG pipelines.
Overview
RAG in DSPy.rb involves:
External Retrieval Integration: Connect to vector databases and search services
Context-Aware Signatures: Design signatures that work with retrieved context
Multi-Step Reasoning: Chain retrieval and generation steps
Manual Implementation: Build custom RAG workflows using DSPy modules
Basic RAG Implementation
Simple RAG with External Service
# Define a signature for context-based question answeringclassContextualQA < DSPy::Signaturedescription"Answer questions using provided context"inputdoconst:question,Stringconst:context,Stringendoutputdoconst:answer,Stringconst:confidence,Floatendend# Basic RAG moduleclassSimpleRAG < DSPy::Moduledefinitialize(retrieval_service)super@retrieval_service=retrieval_service@qa_predictor=DSPy::Predict.new(ContextualQA)enddefforward(question:)# Step 1: Retrieve relevant contextcontext=@retrieval_service.search(question)# Step 2: Generate answer with contextresult=@qa_predictor.call(question: question,context: context){question: question,context: context,answer: result.answer,confidence: result.confidence}endend# Example retrieval service integrationclassColBERTRetrieverdefinitialize(base_url)@base_url=base_urlenddefsearch(query,k: 5)# Integration with external ColBERT serviceresponse=HTTP.get("#{@base_url}/search",params: {query: query,k: k})ifresponse.success?results=JSON.parse(response.body)results['passages'].join('\n\n')else""endendend# Usageretriever=ColBERTRetriever.new("http://localhost:8080")rag_system=SimpleRAG.new(retriever)result=rag_system.call(question: "What is machine learning?")puts"Answer: #{result[:answer]}"puts"Context used: #{result[:context][0..100]}..."
classFilteredRAG < DSPy::Moduledefinitialize(retrieval_service)super@retrieval_service=retrieval_service@relevance_filter=DSPy::Predict.new(RelevanceFilter)@context_ranker=DSPy::Predict.new(ContextRanker)@qa_predictor=DSPy::Predict.new(ContextualQA)enddefforward(question:,max_passages: 3)# Step 1: Initial retrieval (get more than needed)raw_passages=@retrieval_service.search(question,k: max_passages * 2)passages=raw_passages.split('\n\n')# Step 2: Filter for relevancefiltered_passages=[]passages.eachdo |passage|
relevance=@relevance_filter.call(question: question,passage: passage)ifrelevance.is_relevantfiltered_passages << {text: passage,relevance_score: relevance.score}endend# Step 3: Rerank passagesiffiltered_passages.size > max_passages# Use top passages by relevance scoretop_passages=filtered_passages.sort_by{ |p| -p[:relevance_score]}.first(max_passages)context=top_passages.map{ |p| p[:text]}.join('\n\n')elsecontext=filtered_passages.map{ |p| p[:text]}.join('\n\n')end# Step 4: Generate answerresult=@qa_predictor.call(question: question,context: context){question: question,context: context,answer: result.answer,confidence: result.confidence,passages_used: filtered_passages.size,passages_filtered: passages.size - filtered_passages.size}endendclassRelevanceFilter < DSPy::Signaturedescription"Determine if passage is relevant to question"inputdoconst:question,Stringconst:passage,Stringendoutputdoconst:is_relevant,T::Booleanconst:score,Floatconst:reasoning,StringendendclassContextRanker < DSPy::Signaturedescription"Rank context passages by relevance"inputdoconst:question,Stringconst:passages,T::Array[String]endoutputdoconst:ranked_passages,T::Array[String]const:relevance_scores,T::Array[Float]endend
Integration with External Services
Vector Database Integration
# Example integration with a vector databaseclassVectorDBRetrieverdefinitialize(connection_string,collection_name)@connection_string=connection_string@collection_name=collection_name# Initialize your vector DB client hereenddefsearch(query,k: 5,similarity_threshold: 0.7)# Implement vector similarity search# This is a placeholder - actual implementation depends on your vector DBbegin# Convert query to embeddingembedding=generate_embedding(query)# Search vector databaseresults=vector_db_search(embedding,k: k)# Filter by similarity thresholdrelevant_results=results.select{ |r| r[:similarity] >= similarity_threshold}# Extract text contentrelevant_results.map{ |r| r[:text]}.join('\n\n')rescue=>eputs"Vector search failed: #{e.message}"""endendprivatedefgenerate_embedding(text)# Integrate with your embedding service# e.g., OpenAI embeddings, SentenceTransformers, etc.enddefvector_db_search(embedding,k:)# Implement actual vector database search# Return array of { text: "...", similarity: 0.85 }endend
Hybrid Search Implementation
classHybridRetrieverdefinitialize(vector_retriever,keyword_retriever)@vector_retriever=vector_retriever@keyword_retriever=keyword_retrieverenddefsearch(query,k: 5,vector_weight: 0.7)# Perform both vector and keyword searchvector_results=@vector_retriever.search(query,k: k)keyword_results=@keyword_retriever.search(query,k: k)# Combine and deduplicate resultscombined_passages=[vector_results.split('\n\n'),keyword_results.split('\n\n')].flatten.uniq# Take top k passagescombined_passages.first(k).join('\n\n')endendclassKeywordRetrieverdefinitialize(search_service)@search_service=search_serviceenddefsearch(query,k: 5)# Implement keyword-based search (e.g., Elasticsearch, Solr)results=@search_service.search(query,limit: k)results.map{ |r| r['content']}.join('\n\n')endend
# Good: Clear context handlingclassContextualSummary < DSPy::Signaturedescription"Summarize information from multiple sources"inputdoconst:topic,Stringconst:sources,String# Multiple passages separated by \n\nendoutputdoconst:summary,Stringconst:key_points,T::Array[String]const:source_coverage,Floatendend
2. Handle Retrieval Failures
defforward_with_fallback(question:)begincontext=@retrieval_service.search(question)ifcontext.empty?# Fallback to general knowledgereturngenerate_general_answer(question)endgenerate_contextual_answer(question,context)rescue=>eputs"Retrieval failed: #{e.message}"generate_general_answer(question)endend