← Writing
AI & Generative AI

Exploring Retrievers with LangChain

Data Mastery Series — Episode 38: LangChain Website (Part 13 )

29 Dec 202426 min readLangChainDashboard
LangChain Series · Part 10 of 19

Exploring Retrievers with LangChain

Data Mastery Series — Episode 38: LangChain Website (Part 13 )

Connect with me and follow our journey: Linkedin, Facebook


Welcome to Episode 38 of the Data Mastery Series! Today, we continue our exploration of retrievers, diving into advanced techniques that further enhance information retrieval in LangChain-powered workflows.

Before we dive in, let’s recap our journey so far:

Note:This post is inspired by insights from the official LangChain documentation and represents my practical learning journey.

In the previous episode, we covered Vectorstore Retrievers, ParentDocument Retrievers, and Multi-Vector Retrievers. In this episode, we’ll delve into Self-Query Retrievers, Contextual Compression, and Time-Weighted Vectorstore Retrievers.

4. Self-Query Retrievers: Querying Metadata and Content

A self-querying retriever is like having an AI assistant that can refine your questions before searching for answers. Instead of just relying on keyword matching, it can understand the underlying structure of your query and use metadata to filter information more accurately. Think of it as adding precision to your search.

  • Index Type: Vectorstore
  • Uses an LLM: Yes
  • Use Cases: Ideal when your queries require filtering by metadata (like author, date, or category) rather than just relying on semantic content matching.

If a user’s question relates more to document metadata than the content, Self Query is the way to go.

Photo from web: https://python.langchain.com/v0.1/docs/modules/data%5Fconnection/retrievers/self%5Fquery/

Example - Self Query Retrievers

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
docs = []
for loader in loaders:
docs.extend(loader.load())

Create documents with metadata

docs_with_metadata = [
Document(
page_content=docs[0].page_content,
metadata={"title": "ลูกหมูสามตัว", "genre": "children", "year": 1990, "language": "Thai"}
),
Document(
page_content=docs[1].page_content,
metadata={"title": "Tortoise and the Hare", "genre": "children", "year": 1995, "language": "English"}
)
]

Define metadata fields

metadata_field_info = [
AttributeInfo(
name="title",
description="The title of the story",
type="string",
),
AttributeInfo(
name="genre",
description="The genre of the story, usually 'children'",
type="string",
),
AttributeInfo(
name="year",
description="The year the story was published",
type="integer",
),
AttributeInfo(
name="language",
description="The language of the story",
type="string",
),
]

Create VectorStore

vectorstore = Chroma.from_documents(docs_with_metadata, OpenAIEmbeddings(api_key=OPENAI_API_KEY))

Initialize SelfQueryRetriever

document_content_description = "A brief summary of the story"
llm = ChatOpenAI(temperature=0, api_key=OPENAI_API_KEY)

retriever = SelfQueryRetriever.from_llm(
llm,
vectorstore,
document_content_description,
metadata_field_info,
# enable_limit=True, # Filter K (optional)

)

result = retriever.invoke("I want a children story in Thai")

Example of output of result

"""
Story 1:
ลูกหมูสามตัว
กาลครั้งหนึ่งนานมาแล้ว มีแม่หมูอยู่กับลูกหมูสามตัว ลูกหมูทั้งสามโตหมดแล้ว ถึงเวลาที่พวกเขาต้องสร้างบ้านของตัวเองและออกไปผจญภัยในโลกกว้าง
ลูกหมูโต ชื่อ ข้าวสวย- พี่ชายคนโต เป็นหมูที่ค่อนข้างขี้เกียจ เขาตัดสินใจสร้างบ้านอย่างรวดเร็ว เจอกองฟางข้าวใกล้ๆ นึกในใจว่า "แค่นี้ก็พอแล้ว!" จากนั้นเขาก็รีบๆ สร้างบ้านฟางฟางที่กางออกง่ายๆ
ลูกหมูรอง ชื่อ ข้าวหอม- น้องชายคนที่สอง ไม่ได้ขี้เกียจเท่าพี่ชาย เขาตัดสินใจสร้างบ้านที่แข็งแรงกว่า เขาเก็บกิ่งไม้และก้านไม้จากป่ามาสร้างบ้านไม้ มันไม่ใช่บ้านที่แข็งแรงที่สุด แต่เขาคิดว่ามันก็น่าจะเพียงพอ
ลูกหมูเล็กชื่อ ข้าวสุก - น้องชายคนสุดท้อง ขึ้นชื่อเรื่องการทำงานหนักและวางแผน เขาต้องการบ้านที่จะทำให้เขามีความปลอดภัยอย่างแท้จริง เขาใช้เวลาหลายวันในการเก็บอิฐที่แข็งแรงและสร้างบ้านอิฐที่แข็งแรงอย่างระมัดระวัง เขายังสร้างปล่องไฟและประตูที่เหมาะสมพร้อมกับกุญแจที่แข็งแรง
วันแดดสดใสวันหนึ่ง มีหมาป่าตัวใหญ่ใจร้ายมีนามว่า แกงส้ม เดินเลาะเลียบไปตามทาง มันได้กลิ่นหอมกรุ่นของหมูย่างและเดินตามกลิ่นนั้นไปจนถึงบ้านฟางของลูกหมูตัวแรก
"ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!" หมาป่าคำราม
"ไม่! ข้าจะไม่ให้เจ้าเข้ามา!" ลูกหมูตัวเล็กตกใจร้องเสียงแหลม
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านฟางจนพังทลาย! ลูกหมูตัวเล็กกรี๊ดร้องและวิ่งหนีเร็วที่สุดเท่าที่จะวิ่ง
เขาวิ่งไปที่บ้านไม้ของพี่ชาย หมาป่าวิ่งตามมาติดๆ และเมื่อมาถึงบ้านไม้ มันก็ร้องขอ "ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!"
"ไม่! พวกเราจะไม่ให้เจ้าเข้ามา!" ลูกหมูทั้งสองตัวร้องออกมาพร้อมกัน
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านไม้จนพังทลาย! ลูกหมูทั้งสองตัวกรี๊ดร้องและวิ่งหนีเร็วที่สุดเท่าที่จะวิ่ง ไปที่บ้านอิฐของน้องชายคนสุดท้อง
หมาป่าตอนนี้หิวโซกว่าเดิม วิ่งตามพวกมันไป เมื่อมาถึงบ้านอิฐ มันตะโกนว่า "ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!"
"ไม่! พวกเราจะไม่ให้เจ้าเข้ามา!" ลูกหมูทั้งสามตัวตะโกนออกมาพร้อมกัน ตอนนี้พวกเขารู้สึกกล้าหาญมากขึ้นเพราะพวกเขาปลอดภัยอยู่ข้างใน
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านอิฐจนสุดแรงเกิด มันเป่าแรงจนหน้าแดง แต่บ้านอิฐก็ไม่ขยับเขยื้อน หมาป่าหงุดหงิดและพ่ายแพ้ ยอมแพ้และเดินโซซัดเซไปในป่า
ลูกหมูทั้งสามตัว ปลอดภัยอยู่ในบ้านอิฐ พวกเขาได้เรียนบทเรียนอันมีค่าในวันนั้น ความขยันหมั่นเพียรและการวางแผนนั้นสำคัญ และรากฐานที่แข็งแกร่งสามารถช่วยให้คุณปลอดภัยจากอันตราย พวกเขาอยู่ด้วยกันอย่างมีความสุขตลอดไปในบ้านอิฐที่แข็งแรง ไม่เคยลืมความสำคัญของการเตรียมตัว
"""

5. Contextual Compression: Focused and Relevant Retrieval

Imagine you’re searching through a huge book for a specific quote. You wouldn’t want to read the entire book, just the relevant parts, right? That’s what contextual compression does for AI. It narrows down the information returned by retrievers to the most relevant bits, leading to faster and more focused responses.

  • Index Type: Flexible, can be used with various types of indices
  • Uses an LLM: Optional
  • Use Cases: When retrieved documents contain too much irrelevant information, potentially distracting LLMs.

The idea is simple: instead of immediately returning retrieved documents as-is, you can compress them using the context of the given query, so that only the relevant information is returned. “Compressing” here refers to both compressing the contents of an individual document and filtering out documents completely.

5.1) Vanilla Vector Store Retriever

First, we will show what the return data from the normal retriever look like.

Example - Parent Document Retriever - Vanilla Vector Store Retriever

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
documents = []
for loader in loaders:
documents.extend(loader.load())

Helper function for printing docs

def pretty_print_docs(docs):
print(
f"\n{'-' * 100}\n".join(
[f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]
)
)

Split documents into smaller chunks

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

Create retriever

retriever = FAISS.from_documents(texts, OpenAIEmbeddings(api_key=OPENAI_API_KEY)).as_retriever()

Get documents from query

def pretty_print_docs(docs):
print(
f"\n{'-' * 100}\n".join(
[f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]
)
)

docs = retriever.invoke("What did the tortoise do in the race")
pretty_print_docs(docs)

Example of output of result

"""
Document 1:
Tortoise and the Hare
Deep within a sun-dappled clearing of the DC Forest, lived a hare named Piti, famed for his lightning speed. He would streak past the other animals, a blur of brown fur that left them breathless in his wake. One crisp morning, Piti was bragging about his agility, puffing out his chest and flicking his tail with unconcealed pride.
"There's no creature in this entire forest faster than me!" he declared, his voice echoing through the trees.
A slow, rumbling voice came from behind a nearby thicket. It was Donato, a tortoise known for his steady pace and unwavering determination.
"Speed isn't everything, Piti," Donato rumbled. "Even the slowest can achieve victory, if they set their mind to it."
Piti burst into laughter. "You? Win a race against me? Donato, that's the most ludicrous notion I've ever heard!"

Document 2:
He drifted off to sleep, picturing himself crossing the finish line first to a chorus of cheers. But time crawled by for the sleeping hare, while for Donato, it marched on relentlessly.
Donato, inch by inch, made his way towards the finish line. The animals, who had initially mocked him, now cheered him on, their voices echoing through the forest. They were impressed by his unwavering perseverance.
Finally, Donato, with a triumphant plod, crossed the finish line. Piti woke up with a start, his ears twitching in disbelief. He saw, to his utter humiliation, the crowd celebrating Donato's victory.
The hare had lost the race, not to speed, but to slow and steady determination. The cheers of the animals resonated through the DC Forest, a testament to the fact that slow and steady truly does win the race.

Document 3:
To everyone's surprise, Donato challenged Piti to a race. The other animals gathered around, buzzing with excitement at the prospect of such an unequal competition. Even the wise old owl hooted in amusement, his amber eyes twinkling with anticipation.
The race began. Piti shot off like a furry bullet, leaving Donato in a cloud of dust. The animals cheered for the hare, certain of his victory. But Piti, brimming with overconfidence, spotted a patch of wildflowers bursting with color. He darted off the track, unable to resist the temptation of a tasty treat.
Meanwhile, Donato plodded on steadily, never stopping, never wavering. He may have been slow, but his determination burned bright.
Back on the track, Piti, feeling sluggish from his snack, decided to take a nap under the shade of a towering oak. "Old Donato won't catch up to me anyway," he thought arrogantly.

Document 4:
"ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!" หมาป่าคำราม
"ไม่! ข้าจะไม่ให้เจ้าเข้ามา!" ลูกหมูตัวเล็กตกใจร้องเสียงแหลม
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านฟางจนพังทลาย! ลูกหมูตัวเล็กกรี๊ดร้องและวิ่งหนีเร็วที่สุดเท่าที่จะวิ่ง
เขาวิ่งไปที่บ้านไม้ของพี่ชาย หมาป่าวิ่งตามมาติดๆ และเมื่อมาถึงบ้านไม้ มันก็ร้องขอ "ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!"
"ไม่! พวกเราจะไม่ให้เจ้าเข้ามา!" ลูกหมูทั้งสองตัวร้องออกมาพร้อมกัน
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านไม้จนพังทลาย! ลูกหมูทั้งสองตัวกรี๊ดร้องและวิ่งหนีเร็วที่สุดเท่าที่จะวิ่ง ไปที่บ้านอิฐของน้องชายคนสุดท้อง
หมาป่าตอนนี้หิวโซกว่าเดิม วิ่งตามพวกมันไป เมื่อมาถึงบ้านอิฐ มันตะโกนว่า "ลูกหมู ๆ เปิดประตูให้ข้าเข้าไปสิ!"
"ไม่! พวกเราจะไม่ให้เจ้าเข้ามา!" ลูกหมูทั้งสามตัวตะโกนออกมาพร้อมกัน ตอนนี้พวกเขารู้สึกกล้าหาญมากขึ้นเพราะพวกเขาปลอดภัยอยู่ข้างใน
หมาป่าสูดลมพ่นออกแรง ๆ เป่าบ้านอิฐจนสุดแรงเกิด มันเป่าแรงจนหน้าแดง แต่บ้านอิฐก็ไม่ขยับเขยื้อน หมาป่าหงุดหงิดและพ่ายแพ้ ยอมแพ้และเดินโซซัดเซไปในป่า
"""

5.2) Contextual Compression with an LLMChainExtractor

This method uses an LLM chain to extract only the content from each retrieved document that is most relevant to the query, filtering out the rest.

Example - Parent Document Retriever - Contextual Compression Retriever

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
documents = []
for loader in loaders:
documents.extend(loader.load())

Split documents

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

Create retriever

retriever = FAISS.from_documents(texts, OpenAIEmbeddings(api_key=OPENAI_API_KEY)).as_retriever()

Create Contextual Compression

llm = OpenAI(temperature=0, api_key=OPENAI_API_KEY)
compressor = LLMChainExtractor.from_llm(llm)

Initialize ContextualCompressionRetriever

compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)

Set the question

question = "What did the tortoise do in the race"
compressed_docs = compression_retriever.invoke(question)
pretty_print_docs(compressed_docs)

Example of output of result

"""
Document 1:
Tortoise and the Hare
Donato, a tortoise known for his steady pace and unwavering determination.
"Speed isn't everything, Piti," Donato rumbled. "Even the slowest can achieve victory, if they set their mind to it."

Document 2:
Donato, inch by inch, made his way towards the finish line. The animals, who had initially mocked him, now cheered him on, their voices echoing through the forest. They were impressed by his unwavering perseverance.
Finally, Donato, with a triumphant plod, crossed the finish line.

Document 3:

  • Donato challenged Piti to a race.
  • The race began.
  • Piti shot off like a furry bullet, leaving Donato in a cloud of dust.
  • Piti, brimming with overconfidence, spotted a patch of wildflowers bursting with color.
  • He darted off the track, unable to resist the temptation of a tasty treat.
  • Meanwhile, Donato plodded on steadily, never stopping, never wavering.
  • Piti, feeling sluggish from his snack, decided to take a nap under the shade of a towering oak.
    """

More built-in compressors: filters

5.3) Contextual Compression with an LLMChainFilter

This compressor is a simpler approach, using an LLM to decide which of the initially returned documents to filter out, without altering the document contents.

Example - Parent Document Retriever - LLMChainFilter

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
documents = []
for loader in loaders:
documents.extend(loader.load())

Split documents

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

Create retriever

retriever = FAISS.from_documents(texts, OpenAIEmbeddings(api_key=OPENAI_API_KEY)).as_retriever()

Create LLMChainFilter

llm = OpenAI(temperature=0, api_key=OPENAI_API_KEY)
_filter = LLMChainFilter.from_llm(llm)

Initialize ContextualCompressionRetriever

compression_retriever = ContextualCompressionRetriever(
base_compressor=_filter, base_retriever=retriever
)

Set the question

question = "What did the tortoise do in the race"
compressed_docs = compression_retriever.invoke(question)
pretty_print_docs(compressed_docs)

Example of output of result

"""
Document 1:
Tortoise and the Hare
Deep within a sun-dappled clearing of the DC Forest, lived a hare named Piti, famed for his lightning speed. He would streak past the other animals, a blur of brown fur that left them breathless in his wake. One crisp morning, Piti was bragging about his agility, puffing out his chest and flicking his tail with unconcealed pride.
"There's no creature in this entire forest faster than me!" he declared, his voice echoing through the trees.
A slow, rumbling voice came from behind a nearby thicket. It was Donato, a tortoise known for his steady pace and unwavering determination.
"Speed isn't everything, Piti," Donato rumbled. "Even the slowest can achieve victory, if they set their mind to it."
Piti burst into laughter. "You? Win a race against me? Donato, that's the most ludicrous notion I've ever heard!"

Document 2:
He drifted off to sleep, picturing himself crossing the finish line first to a chorus of cheers. But time crawled by for the sleeping hare, while for Donato, it marched on relentlessly.
Donato, inch by inch, made his way towards the finish line. The animals, who had initially mocked him, now cheered him on, their voices echoing through the forest. They were impressed by his unwavering perseverance.
Finally, Donato, with a triumphant plod, crossed the finish line. Piti woke up with a start, his ears twitching in disbelief. He saw, to his utter humiliation, the crowd celebrating Donato's victory.
The hare had lost the race, not to speed, but to slow and steady determination. The cheers of the animals resonated through the DC Forest, a testament to the fact that slow and steady truly does win the race.

Document 3:
To everyone's surprise, Donato challenged Piti to a race. The other animals gathered around, buzzing with excitement at the prospect of such an unequal competition. Even the wise old owl hooted in amusement, his amber eyes twinkling with anticipation.
The race began. Piti shot off like a furry bullet, leaving Donato in a cloud of dust. The animals cheered for the hare, certain of his victory. But Piti, brimming with overconfidence, spotted a patch of wildflowers bursting with color. He darted off the track, unable to resist the temptation of a tasty treat.
Meanwhile, Donato plodded on steadily, never stopping, never wavering. He may have been slow, but his determination burned bright.
Back on the track, Piti, feeling sluggish from his snack, decided to take a nap under the shade of a towering oak. "Old Donato won't catch up to me anyway," he thought arrogantly.
"""

5.4) Contextual Compression with an EmbeddingsFilter

This method provides a cost-effective alternative by using embeddings to filter out documents that are not semantically similar to the query.

Example - Parent Document Retriever - EmbeddingsFilter

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
documents = []
for loader in loaders:
documents.extend(loader.load())

Split documents

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

Create retriever

embeddings = OpenAIEmbeddings(api_key=OPENAI_API_KEY)
retriever = FAISS.from_documents(texts, embeddings).as_retriever()

Create EmbeddingsFilter by using OpenAIEmbeddings

embeddings_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)

Initialize ContextualCompressionRetriever

compression_retriever = ContextualCompressionRetriever(
base_compressor=embeddings_filter, base_retriever=retriever
)

Set the question

question = "What did the tortoise do in the race?"
compressed_docs = compression_retriever.invoke(question)
pretty_print_docs(compressed_docs)

Example of output of result

"""
Document 1:
Tortoise and the Hare
Deep within a sun-dappled clearing of the DC Forest, lived a hare named Piti, famed for his lightning speed. He would streak past the other animals, a blur of brown fur that left them breathless in his wake. One crisp morning, Piti was bragging about his agility, puffing out his chest and flicking his tail with unconcealed pride.
"There's no creature in this entire forest faster than me!" he declared, his voice echoing through the trees.
A slow, rumbling voice came from behind a nearby thicket. It was Donato, a tortoise known for his steady pace and unwavering determination.
"Speed isn't everything, Piti," Donato rumbled. "Even the slowest can achieve victory, if they set their mind to it."
Piti burst into laughter. "You? Win a race against me? Donato, that's the most ludicrous notion I've ever heard!"

Document 2:
He drifted off to sleep, picturing himself crossing the finish line first to a chorus of cheers. But time crawled by for the sleeping hare, while for Donato, it marched on relentlessly.
Donato, inch by inch, made his way towards the finish line. The animals, who had initially mocked him, now cheered him on, their voices echoing through the forest. They were impressed by his unwavering perseverance.
Finally, Donato, with a triumphant plod, crossed the finish line. Piti woke up with a start, his ears twitching in disbelief. He saw, to his utter humiliation, the crowd celebrating Donato's victory.
The hare had lost the race, not to speed, but to slow and steady determination. The cheers of the animals resonated through the DC Forest, a testament to the fact that slow and steady truly does win the race.

Document 3:
To everyone's surprise, Donato challenged Piti to a race. The other animals gathered around, buzzing with excitement at the prospect of such an unequal competition. Even the wise old owl hooted in amusement, his amber eyes twinkling with anticipation.
The race began. Piti shot off like a furry bullet, leaving Donato in a cloud of dust. The animals cheered for the hare, certain of his victory. But Piti, brimming with overconfidence, spotted a patch of wildflowers bursting with color. He darted off the track, unable to resist the temptation of a tasty treat.
Meanwhile, Donato plodded on steadily, never stopping, never wavering. He may have been slow, but his determination burned bright.
Back on the track, Piti, feeling sluggish from his snack, decided to take a nap under the shade of a towering oak. "Old Donato won't catch up to me anyway," he thought arrogantly.
"""

5.5) Stringing compressors and document transformers together

Using the DocumentCompressorPipeline, we can combine multiple compressors and document transformers in sequence.

Example - Parent Document Retriever - EmbeddingsFilter

Load and split documents

loaders = [
TextLoader("../../Tortoise and the Hare.txt"),
TextLoader("../../ลูกหมูสามตัว.txt"),
]
documents = []
for loader in loaders:
documents.extend(loader.load())

Split documents

text_splitter = CharacterTextSplitter(chunk_size=300, chunk_overlap=0, separator=". ")
texts = text_splitter.split_documents(documents)

Create retriever

embeddings = OpenAIEmbeddings(api_key=OPENAI_API_KEY)
retriever = FAISS.from_documents(texts, embeddings).as_retriever()

Create EmbeddingsRedundantFilter (delete duplicate document)

redundant_filter = EmbeddingsRedundantFilter(embeddings=embeddings)
relevant_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)

Create DocumentCompressorPipeline

pipeline_compressor = DocumentCompressorPipeline(
transformers=[text_splitter, redundant_filter, relevant_filter]
)

Initialize ContextualCompressionRetriever

compression_retriever = ContextualCompressionRetriever(
base_compressor=pipeline_compressor, base_retriever=retriever
)

Set the question

question = "What did the tortoise do in the race?"
compressed_docs = compression_retriever.invoke(question)
pretty_print_docs(compressed_docs)

Example of output of result

"""
Document 1:
Tortoise and the Hare
Deep within a sun-dappled clearing of the DC Forest, lived a hare named Piti, famed for his lightning speed. He would streak past the other animals, a blur of brown fur that left them breathless in his wake

Document 2:
It was Donato, a tortoise known for his steady pace and unwavering determination.
"Speed isn't everything, Piti," Donato rumbled. "Even the slowest can achieve victory, if they set their mind to it."
Piti burst into laughter

Document 3:
Even the wise old owl hooted in amusement, his amber eyes twinkling with anticipation.
The race began. Piti shot off like a furry bullet, leaving Donato in a cloud of dust. The animals cheered for the hare, certain of his victory

Document 4:
He saw, to his utter humiliation, the crowd celebrating Donato's victory.
The hare had lost the race, not to speed, but to slow and steady determination. The cheers of the animals resonated through the DC Forest, a testament to the fact that slow and steady truly does win the race.
"""

6) Time-Weighted Vector Store Retriever: Prioritizing Recency

Sometimes, recent information is more valuable than older data. The Time-Weighted Vector Store Retriever factors in the recency of the information when ranking results, ensuring that the most relevant and up-to-date documents are prioritized.

  • Index Type: Vectorstore
  • Uses an LLM: No
  • Use Cases: When you need to retrieve the most recent and relevant information from a time-sensitive dataset.

The core formula is:
semantic_similarity + (1.0 — decay_rate) ^ hours_passed

Here, hours_passed refers to the time since the document was last accessed (not the creation time). Let’s examine with low and high decay rates.

6.1) Low Decay Rate

Setting a low decay rate (close to 0) means that memories are “remembered” for longer. A decay rate of 0 makes this retriever equivalent to a vector lookup.

Example - Time-weighted vector store retriever - Low decay rate

Define embedding model

embeddings_model = OpenAIEmbeddings(api_key=OPENAI_API_KEY)

Initialize vectorstore

embedding_size = 1536
index = faiss.IndexFlatL2(embedding_size)
vectorstore = FAISS(embeddings_model, index, InMemoryDocstore({}), {})

Initialize TimeWeightedVectorStoreRetriever with very low decay_rate

retriever = TimeWeightedVectorStoreRetriever(
vectorstore=vectorstore, decay_rate=0.0000000000000000000000001, k=1
)

Add documents with access times

yesterday = datetime.now() - timedelta(days=1)
retriever.add_documents(
[Document(page_content="hello world", metadata={"last_accessed_at": yesterday})]
)
retriever.add_documents([Document(page_content="hello foo")])

Retrieve and print result

result = retriever.invoke("hello world")
print(result)

Example of output

"""
[Document(metadata={'last_accessed_at': datetime.datetime(2024, 12, 29, 4, 55, 19, 937951), 'created_at': datetime.datetime(2024, 12, 29, 4, 55, 19, 67516), 'buffer_idx': 0}, page_content='hello world')]
"""

6.2) High Decay Rate

Conversely, a high decay rate (close to 1) means that the recency score quickly diminishes, making the retriever prioritize semantic similarity more.

Example - Time-weighted vector store retriever - High decay rate

Define embedding model

embeddings_model = OpenAIEmbeddings(api_key=OPENAI_API_KEY)

Initialize vectorstore

embedding_size = 1536
index = faiss.IndexFlatL2(embedding_size)
vectorstore = FAISS(embeddings_model, index, InMemoryDocstore({}), {})

Initialize TimeWeightedVectorStoreRetriever with high decay_rate

retriever = TimeWeightedVectorStoreRetriever(
vectorstore=vectorstore, decay_rate=0.999, k=1
)

Add documents with access times

yesterday = datetime.now() - timedelta(days=1)
retriever.add_documents(
[Document(page_content="hello world", metadata={"last_accessed_at": yesterday})]
)
retriever.add_documents([Document(page_content="hello foo")])

Retrieve and print result

result = retriever.invoke("hello world")
print(result)

Example of output

"""
[Document(metadata={'last_accessed_at': datetime.datetime(2024, 12, 29, 5, 53, 1, 467568), 'created_at': datetime.datetime(2024, 12, 29, 5, 53, 1, 134997), 'buffer_idx': 1}, page_content='hello foo')]
"""

Key Takeaways

In this episode, we explored:

  • Self-Query Retrievers: Perfect for metadata and structured queries.
  • Contextual Compression Retrievers: Ideal for refining noisy document sets.
  • Time-Weighted Retrievers: Effective for time-sensitive retrieval needs.

In Episode 39, we’ll explore remaining other retrieval techniques such as Multi-Query Retriever, Ensemble and Long-Context Reorder. Stay tuned to unlock the next level of intelligent data retrieval! 🚀


Data Science Explore the world of data science with Donato_Story

Dashboard Discover the power of data visualization with Donato_Story

Donato_Journey Join me on my journey (Thai version)

Course_Review Discover the training courses with Donato_Story (Thai version)

Let’s Connect!

Your thoughts and feedback are invaluable. Feel free to share them in the comments or connect with me on

Originally published on Medium

Related