What is Time-Weighted Retrieval?
Time-Weighted Retrieval is an advanced technique used in Retrieval-Augmented Generation (RAG) systems that incorporates the temporal metadata of a document into its relevance score. In a standard RAG pipeline, documents are retrieved based solely on semantic similarity (e.g., cosine similarity between vector embeddings). Time-weighted retrieval modifies this score by applying a decay function based on the document's publication date.
Mathematically, the final retrieval score is often calculated as a weighted combination of semantic similarity and a time decay factor. The time decay factor typically follows an exponential decay formula, meaning that as a document gets older, its overall retrieval score decreases. This ensures that newer documents are favored when their semantic content is equally relevant to the user's query.
Why Time-Weighting Matters for News RAG
News is an inherently time-sensitive domain. When a user asks a Large Language Model (LLM) about "the current state of the economy" or "recent election results," the model needs the most up-to-date information available. A standard semantic search might retrieve a highly relevant article from five years ago simply because the vocabulary and context match the query perfectly.
Without time-weighting, a News RAG system risks providing stale, outdated, or factually incorrect information to users. By implementing time-weighted retrieval, developers can ensure that the RAG system prioritizes breaking news and recent updates, while still retaining the ability to pull historical context if the query specifically demands it.
How to Implement Time-Weighted Retrieval
Implementing time-weighted retrieval can be done by writing a custom scoring function or by leveraging existing frameworks like LangChain. Below, we will explore both approaches.
Approach 1: Custom Time-Weighted Scoring
To understand the mechanics, it is helpful to build a custom retrieval function. This example uses NumPy to calculate the exponential time decay and combines it with a basic semantic similarity score.
import numpy as np
from datetime import datetime
def calculate_time_weight(publish_date, current_date, decay_rate=0.05):
"""
Calculate exponential time decay.
decay_rate determines how fast the weight drops over time.
"""
# Calculate difference in days
time_diff_days = (current_date - publish_date).total_seconds() / (3600 * 24)
# Exponential decay: e^(-decay_rate * time)
return np.exp(-decay_rate * time_diff_days)
def retrieve_documents(query_vector, document_vectors, publish_dates, current_date, alpha=0.7):
"""
Retrieve documents based on a combination of semantic similarity and time weight.
alpha: weight given to semantic similarity (1.0 = only semantic, 0.0 = only time)
"""
scores = []
for i, doc_vec in enumerate(document_vectors):
# 1. Calculate semantic similarity (simplified dot product)
semantic_sim = np.dot(query_vector, doc_vec)
# 2. Calculate time weight
time_w = calculate_time_weight(publish_dates[i], current_date)
# 3. Combine scores
final_score = (alpha * semantic_sim) + ((1 - alpha) * time_w)
scores.append((i, final_score))
# Sort by final score descending
return sorted(scores, key=lambda x: x[1], reverse=True)
# Example usage:
current_date = datetime(2023, 10, 1)
dates = [datetime(2023, 9, 30), datetime(2023, 1, 1), datetime(2022, 10, 1)]
# Mock vectors (in reality, these would be high-dimensional embeddings)
q_vec = np.array([0.8, 0.2])
doc_vecs = [np.array([0.9, 0.1]), np.array([0.8, 0.2]), np.array([0.7, 0.3])]
top_docs = retrieve_documents(q_vec, doc_vecs, dates, current_date)
print("Top document indices and scores:", top_docs)
Approach 2: Using LangChain's TimeWeightedVectorStoreRetriever
For production applications, it is often easier to use established frameworks. LangChain provides a built-in TimeWeightedVectorStoreRetriever that handles this logic automatically. It requires your documents to have a last_accessed_at or creation date metadata field.
from langchain_community.vectorstores import FAISS
from langchain_community.retrievers import TimeWeightedVectorStoreRetriever
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document
from datetime import datetime, timedelta
# Initialize embeddings
embeddings = OpenAIEmbeddings()
# Create mock news documents with 'last_accessed_at' or creation metadata
# LangChain uses 'last_accessed_at' for its default decay logic
now = datetime.now()
docs = [
Document(page_content="The stock market hit a record high today.",
metadata={"last_accessed_at": now - timedelta(days=1)}),
Document(page_content="The stock market crashed during the 2008 financial crisis.",
metadata={"last_accessed_at": now - timedelta(days=365 * 15)}),
]
# Initialize a standard vector store
vectorstore = FAISS.from_documents(docs, embeddings)
# Wrap it in the TimeWeightedVectorStoreRetriever
# decay_rate: small value means slow decay (older docs stay relevant longer)
# k: number of documents to retrieve
retriever = TimeWeightedVectorStoreRetriever(
vectorstore=vectorstore,
decay_rate=0.01,
k=2
)
# Retrieve documents
query = "What is happening with the stock market?"
results = retriever.invoke(query)
for doc in results:
print(f"Content: {doc.page_content} | Date: {doc.metadata['last_accessed_at']}")
Best Practices for Time-Weighted News RAG
- Tune the Decay Rate Carefully: The decay rate is the most critical hyperparameter. If it is too high, the system will ignore highly relevant older articles. If it is too low, the system behaves like a standard semantic search. For a 24-hour news cycle, a higher decay rate is appropriate. For historical research, use a lower decay rate.
- Use Metadata Filtering for Hard Cutoffs: Sometimes exponential decay is not enough. If a user asks for "news from this week," you should use metadata filtering in your vector database to strictly exclude documents older than 7 days before applying semantic search.
- Balance the Alpha Weight: When combining semantic and time scores, the alpha parameter dictates the balance. A value of 0.7 or 0.8 usually works well, ensuring that semantic relevance remains the primary driver while recency acts as a tie-breaker or slight boost.
- Handle Different News Categories Differently: Financial and political news require aggressive time-weighting. However, opinion pieces, historical analyses, or evergreen content might not need time-weighting at all. Consider routing queries to different retrievers based on intent classification.
- Store Precise Timestamps: Ensure your ingestion pipeline accurately captures the exact publication time (down to the minute or second for breaking news) and stores it in a standardized format (like ISO 8601) in the vector database metadata.
Conclusion
Implementing time-weighted retrieval is a crucial step in building robust and reliable News RAG applications. By blending semantic similarity with an exponential time decay function, developers can ensure their systems prioritize fresh, accurate information without completely losing access to historical context. Whether you choose to build a custom scoring algorithm or leverage frameworks like LangChain, carefully tuning your decay rates and balancing your scoring weights will ultimately determine the success of your news retrieval pipeline.