forked from phoenix/litellm-mirror
add everyting for docs
This commit is contained in:
parent
de45a738ee
commit
0fe8799f94
1015 changed files with 185353 additions and 0 deletions
|
@ -0,0 +1,261 @@
|
|||
```python
|
||||
# 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)]))
|
||||
```
|
||||
|
||||
## Using a vanilla vector store retriever
|
||||
Let's start by initializing a simple vector store retriever and storing the 2023 State of the Union speech (in chunks). We can see that given an example question our retriever returns one or two relevant docs and a few irrelevant docs. And even the relevant docs have a lot of irrelevant information in them.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.text_splitter import CharacterTextSplitter
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
from langchain.document_loaders import TextLoader
|
||||
from langchain.vectorstores import FAISS
|
||||
|
||||
documents = TextLoader('../../../state_of_the_union.txt').load()
|
||||
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||
texts = text_splitter.split_documents(documents)
|
||||
retriever = FAISS.from_documents(texts, OpenAIEmbeddings()).as_retriever()
|
||||
|
||||
docs = retriever.get_relevant_documents("What did the president say about Ketanji Brown Jackson")
|
||||
pretty_print_docs(docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Document 1:
|
||||
|
||||
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.
|
||||
|
||||
Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
|
||||
|
||||
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
|
||||
|
||||
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 2:
|
||||
|
||||
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.
|
||||
|
||||
And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.
|
||||
|
||||
We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling.
|
||||
|
||||
We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers.
|
||||
|
||||
We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.
|
||||
|
||||
We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 3:
|
||||
|
||||
And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong.
|
||||
|
||||
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
|
||||
|
||||
While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.
|
||||
|
||||
And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things.
|
||||
|
||||
So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together.
|
||||
|
||||
First, beat the opioid epidemic.
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 4:
|
||||
|
||||
Tonight, I’m announcing a crackdown on these companies overcharging American businesses and consumers.
|
||||
|
||||
And as Wall Street firms take over more nursing homes, quality in those homes has gone down and costs have gone up.
|
||||
|
||||
That ends on my watch.
|
||||
|
||||
Medicare is going to set higher standards for nursing homes and make sure your loved ones get the care they deserve and expect.
|
||||
|
||||
We’ll also cut costs and keep the economy going strong by giving workers a fair shot, provide more training and apprenticeships, hire them based on their skills not degrees.
|
||||
|
||||
Let’s pass the Paycheck Fairness Act and paid leave.
|
||||
|
||||
Raise the minimum wage to $15 an hour and extend the Child Tax Credit, so no one has to raise a family in poverty.
|
||||
|
||||
Let’s increase Pell Grants and increase our historic support of HBCUs, and invest in what Jill—our First Lady who teaches full-time—calls America’s best-kept secret: community colleges.
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## Adding contextual compression with an `LLMChainExtractor`
|
||||
Now let's wrap our base retriever with a `ContextualCompressionRetriever`. We'll add an `LLMChainExtractor`, which will iterate over the initially returned documents and extract from each only the content that is relevant to the query.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.llms import OpenAI
|
||||
from langchain.retrievers import ContextualCompressionRetriever
|
||||
from langchain.retrievers.document_compressors import LLMChainExtractor
|
||||
|
||||
llm = OpenAI(temperature=0)
|
||||
compressor = LLMChainExtractor.from_llm(llm)
|
||||
compression_retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=retriever)
|
||||
|
||||
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
|
||||
pretty_print_docs(compressed_docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Document 1:
|
||||
|
||||
"One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
|
||||
|
||||
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence."
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 2:
|
||||
|
||||
"A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## More built-in compressors: filters
|
||||
### `LLMChainFilter`
|
||||
The `LLMChainFilter` is slightly simpler but more robust compressor that uses an LLM chain to decide which of the initially retrieved documents to filter out and which ones to return, without manipulating the document contents.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.retrievers.document_compressors import LLMChainFilter
|
||||
|
||||
_filter = LLMChainFilter.from_llm(llm)
|
||||
compression_retriever = ContextualCompressionRetriever(base_compressor=_filter, base_retriever=retriever)
|
||||
|
||||
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
|
||||
pretty_print_docs(compressed_docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Document 1:
|
||||
|
||||
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.
|
||||
|
||||
Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
|
||||
|
||||
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
|
||||
|
||||
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
### `EmbeddingsFilter`
|
||||
|
||||
Making an extra LLM call over each retrieved document is expensive and slow. The `EmbeddingsFilter` provides a cheaper and faster option by embedding the documents and query and only returning those documents which have sufficiently similar embeddings to the query.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
from langchain.retrievers.document_compressors import EmbeddingsFilter
|
||||
|
||||
embeddings = OpenAIEmbeddings()
|
||||
embeddings_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)
|
||||
compression_retriever = ContextualCompressionRetriever(base_compressor=embeddings_filter, base_retriever=retriever)
|
||||
|
||||
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
|
||||
pretty_print_docs(compressed_docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Document 1:
|
||||
|
||||
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.
|
||||
|
||||
Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
|
||||
|
||||
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
|
||||
|
||||
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 2:
|
||||
|
||||
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.
|
||||
|
||||
And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.
|
||||
|
||||
We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling.
|
||||
|
||||
We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers.
|
||||
|
||||
We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.
|
||||
|
||||
We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 3:
|
||||
|
||||
And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong.
|
||||
|
||||
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
|
||||
|
||||
While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.
|
||||
|
||||
And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things.
|
||||
|
||||
So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together.
|
||||
|
||||
First, beat the opioid epidemic.
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
# Stringing compressors and document transformers together
|
||||
Using the `DocumentCompressorPipeline` we can also easily combine multiple compressors in sequence. Along with compressors we can add `BaseDocumentTransformer`s to our pipeline, which don't perform any contextual compression but simply perform some transformation on a set of documents. For example `TextSplitter`s can be used as document transformers to split documents into smaller pieces, and the `EmbeddingsRedundantFilter` can be used to filter out redundant documents based on embedding similarity between documents.
|
||||
|
||||
Below we create a compressor pipeline by first splitting our docs into smaller chunks, then removing redundant documents, and then filtering based on relevance to the query.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.document_transformers import EmbeddingsRedundantFilter
|
||||
from langchain.retrievers.document_compressors import DocumentCompressorPipeline
|
||||
from langchain.text_splitter import CharacterTextSplitter
|
||||
|
||||
splitter = CharacterTextSplitter(chunk_size=300, chunk_overlap=0, separator=". ")
|
||||
redundant_filter = EmbeddingsRedundantFilter(embeddings=embeddings)
|
||||
relevant_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)
|
||||
pipeline_compressor = DocumentCompressorPipeline(
|
||||
transformers=[splitter, redundant_filter, relevant_filter]
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
compression_retriever = ContextualCompressionRetriever(base_compressor=pipeline_compressor, base_retriever=retriever)
|
||||
|
||||
compressed_docs = compression_retriever.get_relevant_documents("What did the president say about Ketanji Jackson Brown")
|
||||
pretty_print_docs(compressed_docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Document 1:
|
||||
|
||||
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
|
||||
|
||||
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 2:
|
||||
|
||||
As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential.
|
||||
|
||||
While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Document 3:
|
||||
|
||||
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
254
docs/snippets/modules/data_connection/retrievers/get_started.mdx
Normal file
254
docs/snippets/modules/data_connection/retrievers/get_started.mdx
Normal file
|
@ -0,0 +1,254 @@
|
|||
The public API of the `BaseRetriever` class in LangChain is as follows:
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, List
|
||||
from langchain.schema import Document
|
||||
from langchain.callbacks.manager import Callbacks
|
||||
|
||||
class BaseRetriever(ABC):
|
||||
...
|
||||
def get_relevant_documents(
|
||||
self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
|
||||
) -> List[Document]:
|
||||
"""Retrieve documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
callbacks: Callback manager or list of callbacks
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
...
|
||||
|
||||
async def aget_relevant_documents(
|
||||
self, query: str, *, callbacks: Callbacks = None, **kwargs: Any
|
||||
) -> List[Document]:
|
||||
"""Asynchronously get documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
callbacks: Callback manager or list of callbacks
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
It's that simple! You can call `get_relevant_documents` or the async `get_relevant_documents` methods to retrieve documents relevant to a query, where "relevance" is defined by
|
||||
the specific retriever object you are calling.
|
||||
|
||||
Of course, we also help construct what we think useful Retrievers are. The main type of Retriever that we focus on is a Vectorstore retriever. We will focus on that for the rest of this guide.
|
||||
|
||||
In order to understand what a vectorstore retriever is, it's important to understand what a Vectorstore is. So let's look at that.
|
||||
|
||||
By default, LangChain uses [Chroma](/docs/ecosystem/integrations/chroma.html) as the vectorstore to index and search embeddings. To walk through this tutorial, we'll first need to install `chromadb`.
|
||||
|
||||
```
|
||||
pip install chromadb
|
||||
```
|
||||
|
||||
This example showcases question answering over documents.
|
||||
We have chosen this as the example for getting started because it nicely combines a lot of different elements (Text splitters, embeddings, vectorstores) and then also shows how to use them in a chain.
|
||||
|
||||
Question answering over documents consists of four steps:
|
||||
|
||||
1. Create an index
|
||||
2. Create a Retriever from that index
|
||||
3. Create a question answering chain
|
||||
4. Ask questions!
|
||||
|
||||
Each of the steps has multiple sub steps and potential configurations. In this notebook we will primarily focus on (1). We will start by showing the one-liner for doing so, but then break down what is actually going on.
|
||||
|
||||
First, let's import some common classes we'll use no matter what.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.chains import RetrievalQA
|
||||
from langchain.llms import OpenAI
|
||||
```
|
||||
|
||||
Next in the generic setup, let's specify the document loader we want to use. You can download the `state_of_the_union.txt` file [here](https://github.com/hwchase17/langchain/blob/master/docs/extras/modules/state_of_the_union.txt)
|
||||
|
||||
|
||||
```python
|
||||
from langchain.document_loaders import TextLoader
|
||||
loader = TextLoader('../state_of_the_union.txt', encoding='utf8')
|
||||
```
|
||||
|
||||
## One Line Index Creation
|
||||
|
||||
To get started as quickly as possible, we can use the `VectorstoreIndexCreator`.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.indexes import VectorstoreIndexCreator
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
index = VectorstoreIndexCreator().from_loaders([loader])
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Running Chroma using direct local API.
|
||||
Using DuckDB in-memory for database. Data will be transient.
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
Now that the index is created, we can use it to ask questions of the data! Note that under the hood this is actually doing a few steps as well, which we will cover later in this guide.
|
||||
|
||||
|
||||
```python
|
||||
query = "What did the president say about Ketanji Brown Jackson"
|
||||
index.query(query)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
" The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He also said that she is a consensus builder and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
query = "What did the president say about Ketanji Brown Jackson"
|
||||
index.query_with_sources(query)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
{'question': 'What did the president say about Ketanji Brown Jackson',
|
||||
'answer': " The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, one of the nation's top legal minds, to continue Justice Breyer's legacy of excellence, and that she has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.\n",
|
||||
'sources': '../state_of_the_union.txt'}
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
What is returned from the `VectorstoreIndexCreator` is `VectorStoreIndexWrapper`, which provides these nice `query` and `query_with_sources` functionality. If we just wanted to access the vectorstore directly, we can also do that.
|
||||
|
||||
|
||||
```python
|
||||
index.vectorstore
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
<langchain.vectorstores.chroma.Chroma at 0x119aa5940>
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
If we then want to access the VectorstoreRetriever, we can do that with:
|
||||
|
||||
|
||||
```python
|
||||
index.vectorstore.as_retriever()
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
VectorStoreRetriever(vectorstore=<langchain.vectorstores.chroma.Chroma object at 0x119aa5940>, search_kwargs={})
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## Walkthrough
|
||||
|
||||
Okay, so what's actually going on? How is this index getting created?
|
||||
|
||||
A lot of the magic is being hid in this `VectorstoreIndexCreator`. What is this doing?
|
||||
|
||||
There are three main steps going on after the documents are loaded:
|
||||
|
||||
1. Splitting documents into chunks
|
||||
2. Creating embeddings for each document
|
||||
3. Storing documents and embeddings in a vectorstore
|
||||
|
||||
Let's walk through this in code
|
||||
|
||||
|
||||
```python
|
||||
documents = loader.load()
|
||||
```
|
||||
|
||||
Next, we will split the documents into chunks.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.text_splitter import CharacterTextSplitter
|
||||
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||
texts = text_splitter.split_documents(documents)
|
||||
```
|
||||
|
||||
We will then select which embeddings we want to use.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
embeddings = OpenAIEmbeddings()
|
||||
```
|
||||
|
||||
We now create the vectorstore to use as the index.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.vectorstores import Chroma
|
||||
db = Chroma.from_documents(texts, embeddings)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Running Chroma using direct local API.
|
||||
Using DuckDB in-memory for database. Data will be transient.
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
So that's creating the index. Then, we expose this index in a retriever interface.
|
||||
|
||||
|
||||
```python
|
||||
retriever = db.as_retriever()
|
||||
```
|
||||
|
||||
Then, as before, we create a chain and use it to answer questions!
|
||||
|
||||
|
||||
```python
|
||||
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=retriever)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
query = "What did the president say about Ketanji Brown Jackson"
|
||||
qa.run(query)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
" The President said that Judge Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He said she is a consensus builder and has received a broad range of support from organizations such as the Fraternal Order of Police and former judges appointed by Democrats and Republicans."
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
`VectorstoreIndexCreator` is just a wrapper around all this logic. It is configurable in the text splitter it uses, the embeddings it uses, and the vectorstore it uses. For example, you can configure it as below:
|
||||
|
||||
|
||||
```python
|
||||
index_creator = VectorstoreIndexCreator(
|
||||
vectorstore_cls=Chroma,
|
||||
embedding=OpenAIEmbeddings(),
|
||||
text_splitter=CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||
)
|
||||
```
|
||||
|
||||
Hopefully this highlights what is going on under the hood of `VectorstoreIndexCreator`. While we think it's important to have a simple way to create indexes, we also think it's important to understand what's going on under the hood.
|
|
@ -0,0 +1,161 @@
|
|||
# Implement a Custom Retriever
|
||||
|
||||
In this walkthrough, you will implement a simple custom retriever in LangChain using a simple dot product distance lookup.
|
||||
|
||||
All retrievers inherit from the `BaseRetriever` class and override the following abstract methods:
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, List
|
||||
from langchain.schema import Document
|
||||
from langchain.callbacks.manager import (
|
||||
AsyncCallbackManagerForRetrieverRun,
|
||||
CallbackManagerForRetrieverRun,
|
||||
)
|
||||
|
||||
class BaseRetriever(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def _get_relevant_documents(
|
||||
self, query: str, *, run_manager: CallbackManagerForRetrieverRun
|
||||
) -> List[Document]:
|
||||
"""Get documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
run_manager: The callbacks handler to use
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def _aget_relevant_documents(
|
||||
self,
|
||||
query: str,
|
||||
*,
|
||||
run_manager: AsyncCallbackManagerForRetrieverRun,
|
||||
) -> List[Document]:
|
||||
"""Asynchronously get documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
run_manager: The callbacks handler to use
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
```
|
||||
|
||||
|
||||
The `_get_relevant_documents` and async `_get_relevant_documents` methods can be implemented however you see fit. The `run_manager` is useful if your retriever calls other traceable LangChain primitives like LLMs, chains, or tools.
|
||||
|
||||
|
||||
Below, implement an example that fetches the most similar documents from a list of documents using a numpy array of embeddings.
|
||||
|
||||
|
||||
```python
|
||||
from typing import Any, List, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from langchain.callbacks.manager import (
|
||||
AsyncCallbackManagerForRetrieverRun,
|
||||
CallbackManagerForRetrieverRun,
|
||||
)
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.schema import BaseRetriever, Document
|
||||
|
||||
|
||||
class NumpyRetriever(BaseRetriever):
|
||||
"""Retrieves documents from a numpy array."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
texts: List[str],
|
||||
vectors: np.ndarray,
|
||||
embeddings: Optional[Embeddings] = None,
|
||||
num_to_return: int = 1,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.embeddings = embeddings or OpenAIEmbeddings()
|
||||
self.texts = texts
|
||||
self.vectors = vectors
|
||||
self.num_to_return = num_to_return
|
||||
|
||||
@classmethod
|
||||
def from_texts(
|
||||
cls,
|
||||
texts: List[str],
|
||||
embeddings: Optional[Embeddings] = None,
|
||||
**kwargs: Any,
|
||||
) -> "NumpyRetriever":
|
||||
embeddings = embeddings or OpenAIEmbeddings()
|
||||
vectors = np.array(embeddings.embed_documents(texts))
|
||||
return cls(texts, vectors, embeddings)
|
||||
|
||||
def _get_relevant_documents_from_query_vector(
|
||||
self, vector_query: np.ndarray
|
||||
) -> List[Document]:
|
||||
dot_product = np.dot(self.vectors, vector_query)
|
||||
# Get the indices of the min 5 documents
|
||||
indices = np.argpartition(
|
||||
dot_product, -min(self.num_to_return, len(self.vectors))
|
||||
)[-self.num_to_return :]
|
||||
# Sort indices by distance
|
||||
indices = indices[np.argsort(dot_product[indices])]
|
||||
return [
|
||||
Document(
|
||||
page_content=self.texts[idx],
|
||||
metadata={"index": idx},
|
||||
)
|
||||
for idx in indices
|
||||
]
|
||||
|
||||
def _get_relevant_documents(
|
||||
self, query: str, *, run_manager: CallbackManagerForRetrieverRun
|
||||
) -> List[Document]:
|
||||
"""Get documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
run_manager: The callbacks handler to use
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
vector_query = np.array(self.embeddings.embed_query(query))
|
||||
return self._get_relevant_documents_from_query_vector(vector_query)
|
||||
|
||||
async def _aget_relevant_documents(
|
||||
self,
|
||||
query: str,
|
||||
*,
|
||||
run_manager: AsyncCallbackManagerForRetrieverRun,
|
||||
) -> List[Document]:
|
||||
"""Asynchronously get documents relevant to a query.
|
||||
Args:
|
||||
query: string to find relevant documents for
|
||||
run_manager: The callbacks handler to use
|
||||
Returns:
|
||||
List of relevant documents
|
||||
"""
|
||||
query_emb = await self.embeddings.aembed_query(query)
|
||||
return self._get_relevant_documents_from_query_vector(np.array(query_emb))
|
||||
```
|
||||
|
||||
The retriever can be instantiated through the class method `from_texts`. It embeds the texts and stores them in a numpy array. To look up documents, it embeds the query and finds the most similar documents using a simple dot product distance.
|
||||
Once the retriever is implemented, you can use it like any other retriever in LangChain.
|
||||
|
||||
|
||||
```python
|
||||
retriever = NumpyRetriever.from_texts(texts= ["hello world", "goodbye world"])
|
||||
```
|
||||
|
||||
You can then use the retriever to get relevant documents.
|
||||
|
||||
```python
|
||||
retriever.get_relevant_documents("Hi there!")
|
||||
|
||||
# [Document(page_content='hello world', metadata={'index': 0})]
|
||||
```
|
||||
|
||||
```python
|
||||
retriever.get_relevant_documents("Bye!")
|
||||
# [Document(page_content='goodbye world', metadata={'index': 1})]
|
||||
```
|
|
@ -0,0 +1,124 @@
|
|||
```python
|
||||
import faiss
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from langchain.docstore import InMemoryDocstore
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
from langchain.retrievers import TimeWeightedVectorStoreRetriever
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores import FAISS
|
||||
```
|
||||
|
||||
## Low Decay Rate
|
||||
|
||||
A low `decay rate` (in this, to be extreme, we will set close to 0) means memories will be "remembered" for longer. A `decay rate` of 0 means memories never be forgotten, making this retriever equivalent to the vector lookup.
|
||||
|
||||
|
||||
```python
|
||||
# Define your embedding model
|
||||
embeddings_model = OpenAIEmbeddings()
|
||||
# Initialize the vectorstore as empty
|
||||
embedding_size = 1536
|
||||
index = faiss.IndexFlatL2(embedding_size)
|
||||
vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})
|
||||
retriever = TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, decay_rate=.0000000000000000000000001, k=1)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
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")])
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
['d7f85756-2371-4bdf-9140-052780a0f9b3']
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# "Hello World" is returned first because it is most salient, and the decay rate is close to 0., meaning it's still recent enough
|
||||
retriever.get_relevant_documents("hello world")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
[Document(page_content='hello world', metadata={'last_accessed_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 678341), 'created_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 279596), 'buffer_idx': 0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## High Decay Rate
|
||||
|
||||
With a high `decay rate` (e.g., several 9's), the `recency score` quickly goes to 0! If you set this all the way to 1, `recency` is 0 for all objects, once again making this equivalent to a vector lookup.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Define your embedding model
|
||||
embeddings_model = OpenAIEmbeddings()
|
||||
# Initialize the vectorstore as empty
|
||||
embedding_size = 1536
|
||||
index = faiss.IndexFlatL2(embedding_size)
|
||||
vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})
|
||||
retriever = TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, decay_rate=.999, k=1)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
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")])
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
['40011466-5bbe-4101-bfd1-e22e7f505de2']
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# "Hello Foo" is returned first because "hello world" is mostly forgotten
|
||||
retriever.get_relevant_documents("hello world")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
[Document(page_content='hello foo', metadata={'last_accessed_at': datetime.datetime(2023, 4, 16, 22, 9, 2, 494798), 'created_at': datetime.datetime(2023, 4, 16, 22, 9, 2, 178722), 'buffer_idx': 1})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## Virtual Time
|
||||
|
||||
Using some utils in LangChain, you can mock out the time component
|
||||
|
||||
|
||||
```python
|
||||
from langchain.utils import mock_now
|
||||
import datetime
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Notice the last access time is that date time
|
||||
with mock_now(datetime.datetime(2011, 2, 3, 10, 11)):
|
||||
print(retriever.get_relevant_documents("hello world"))
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
[Document(page_content='hello world', metadata={'last_accessed_at': MockDateTime(2011, 2, 3, 10, 11), 'created_at': datetime.datetime(2023, 5, 13, 21, 0, 27, 279596), 'buffer_idx': 0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
|
@ -0,0 +1,88 @@
|
|||
```python
|
||||
from langchain.document_loaders import TextLoader
|
||||
loader = TextLoader('../../../state_of_the_union.txt')
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from langchain.text_splitter import CharacterTextSplitter
|
||||
from langchain.vectorstores import FAISS
|
||||
from langchain.embeddings import OpenAIEmbeddings
|
||||
|
||||
documents = loader.load()
|
||||
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
||||
texts = text_splitter.split_documents(documents)
|
||||
embeddings = OpenAIEmbeddings()
|
||||
db = FAISS.from_documents(texts, embeddings)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
Exiting: Cleaning up .chroma directory
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
retriever = db.as_retriever()
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
|
||||
```
|
||||
|
||||
## Maximum Marginal Relevance Retrieval
|
||||
By default, the vectorstore retriever uses similarity search. If the underlying vectorstore support maximum marginal relevance search, you can specify that as the search type.
|
||||
|
||||
|
||||
```python
|
||||
retriever = db.as_retriever(search_type="mmr")
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
|
||||
```
|
||||
|
||||
## Similarity Score Threshold Retrieval
|
||||
|
||||
You can also a retrieval method that sets a similarity score threshold and only returns documents with a score above that threshold
|
||||
|
||||
|
||||
```python
|
||||
retriever = db.as_retriever(search_type="similarity_score_threshold", search_kwargs={"score_threshold": .5})
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
|
||||
```
|
||||
|
||||
## Specifying top k
|
||||
You can also specify search kwargs like `k` to use when doing retrieval.
|
||||
|
||||
|
||||
```python
|
||||
retriever = db.as_retriever(search_kwargs={"k": 1})
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
len(docs)
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
1
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
|
@ -0,0 +1,201 @@
|
|||
## Get started
|
||||
We'll use a Pinecone vector store in this example.
|
||||
|
||||
First we'll want to create a `Pinecone` VectorStore and seed it with some data. We've created a small demo set of documents that contain summaries of movies.
|
||||
|
||||
To use Pinecone, you to have `pinecone` package installed and you must have an API key and an Environment. Here are the [installation instructions](https://docs.pinecone.io/docs/quickstart).
|
||||
|
||||
NOTE: The self-query retriever requires you to have `lark` package installed.
|
||||
|
||||
|
||||
```python
|
||||
# !pip install lark pinecone-client
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
import pinecone
|
||||
|
||||
|
||||
pinecone.init(api_key=os.environ["PINECONE_API_KEY"], environment=os.environ["PINECONE_ENV"])
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from langchain.schema import Document
|
||||
from langchain.embeddings.openai import OpenAIEmbeddings
|
||||
from langchain.vectorstores import Pinecone
|
||||
|
||||
embeddings = OpenAIEmbeddings()
|
||||
# create new index
|
||||
pinecone.create_index("langchain-self-retriever-demo", dimension=1536)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
docs = [
|
||||
Document(page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose", metadata={"year": 1993, "rating": 7.7, "genre": ["action", "science fiction"]}),
|
||||
Document(page_content="Leo DiCaprio gets lost in a dream within a dream within a dream within a ...", metadata={"year": 2010, "director": "Christopher Nolan", "rating": 8.2}),
|
||||
Document(page_content="A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea", metadata={"year": 2006, "director": "Satoshi Kon", "rating": 8.6}),
|
||||
Document(page_content="A bunch of normal-sized women are supremely wholesome and some men pine after them", metadata={"year": 2019, "director": "Greta Gerwig", "rating": 8.3}),
|
||||
Document(page_content="Toys come alive and have a blast doing so", metadata={"year": 1995, "genre": "animated"}),
|
||||
Document(page_content="Three men walk into the Zone, three men walk out of the Zone", metadata={"year": 1979, "rating": 9.9, "director": "Andrei Tarkovsky", "genre": ["science fiction", "thriller"], "rating": 9.9})
|
||||
]
|
||||
vectorstore = Pinecone.from_documents(
|
||||
docs, embeddings, index_name="langchain-self-retriever-demo"
|
||||
)
|
||||
```
|
||||
|
||||
## Creating our self-querying retriever
|
||||
Now we can instantiate our retriever. To do this we'll need to provide some information upfront about the metadata fields that our documents support and a short description of the document contents.
|
||||
|
||||
|
||||
```python
|
||||
from langchain.llms import OpenAI
|
||||
from langchain.retrievers.self_query.base import SelfQueryRetriever
|
||||
from langchain.chains.query_constructor.base import AttributeInfo
|
||||
|
||||
metadata_field_info=[
|
||||
AttributeInfo(
|
||||
name="genre",
|
||||
description="The genre of the movie",
|
||||
type="string or list[string]",
|
||||
),
|
||||
AttributeInfo(
|
||||
name="year",
|
||||
description="The year the movie was released",
|
||||
type="integer",
|
||||
),
|
||||
AttributeInfo(
|
||||
name="director",
|
||||
description="The name of the movie director",
|
||||
type="string",
|
||||
),
|
||||
AttributeInfo(
|
||||
name="rating",
|
||||
description="A 1-10 rating for the movie",
|
||||
type="float"
|
||||
),
|
||||
]
|
||||
document_content_description = "Brief summary of a movie"
|
||||
llm = OpenAI(temperature=0)
|
||||
retriever = SelfQueryRetriever.from_llm(llm, vectorstore, document_content_description, metadata_field_info, verbose=True)
|
||||
```
|
||||
|
||||
## Testing it out
|
||||
And now we can try actually using our retriever!
|
||||
|
||||
|
||||
```python
|
||||
# This example only specifies a relevant query
|
||||
retriever.get_relevant_documents("What are some movies about dinosaurs")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
query='dinosaur' filter=None
|
||||
|
||||
|
||||
[Document(page_content='A bunch of scientists bring back dinosaurs and mayhem breaks loose', metadata={'genre': ['action', 'science fiction'], 'rating': 7.7, 'year': 1993.0}),
|
||||
Document(page_content='Toys come alive and have a blast doing so', metadata={'genre': 'animated', 'year': 1995.0}),
|
||||
Document(page_content='A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea', metadata={'director': 'Satoshi Kon', 'rating': 8.6, 'year': 2006.0}),
|
||||
Document(page_content='Leo DiCaprio gets lost in a dream within a dream within a dream within a ...', metadata={'director': 'Christopher Nolan', 'rating': 8.2, 'year': 2010.0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# This example only specifies a filter
|
||||
retriever.get_relevant_documents("I want to watch a movie rated higher than 8.5")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
query=' ' filter=Comparison(comparator=<Comparator.GT: 'gt'>, attribute='rating', value=8.5)
|
||||
|
||||
|
||||
[Document(page_content='A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea', metadata={'director': 'Satoshi Kon', 'rating': 8.6, 'year': 2006.0}),
|
||||
Document(page_content='Three men walk into the Zone, three men walk out of the Zone', metadata={'director': 'Andrei Tarkovsky', 'genre': ['science fiction', 'thriller'], 'rating': 9.9, 'year': 1979.0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# This example specifies a query and a filter
|
||||
retriever.get_relevant_documents("Has Greta Gerwig directed any movies about women")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
query='women' filter=Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='director', value='Greta Gerwig')
|
||||
|
||||
|
||||
[Document(page_content='A bunch of normal-sized women are supremely wholesome and some men pine after them', metadata={'director': 'Greta Gerwig', 'rating': 8.3, 'year': 2019.0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# This example specifies a composite filter
|
||||
retriever.get_relevant_documents("What's a highly rated (above 8.5) science fiction film?")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
query=' ' filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='genre', value='science fiction'), Comparison(comparator=<Comparator.GT: 'gt'>, attribute='rating', value=8.5)])
|
||||
|
||||
|
||||
[Document(page_content='Three men walk into the Zone, three men walk out of the Zone', metadata={'director': 'Andrei Tarkovsky', 'genre': ['science fiction', 'thriller'], 'rating': 9.9, 'year': 1979.0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
|
||||
```python
|
||||
# This example specifies a query and composite filter
|
||||
retriever.get_relevant_documents("What's a movie after 1990 but before 2005 that's all about toys, and preferably is animated")
|
||||
```
|
||||
|
||||
<CodeOutputBlock lang="python">
|
||||
|
||||
```
|
||||
query='toys' filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Comparison(comparator=<Comparator.GT: 'gt'>, attribute='year', value=1990.0), Comparison(comparator=<Comparator.LT: 'lt'>, attribute='year', value=2005.0), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='genre', value='animated')])
|
||||
|
||||
|
||||
[Document(page_content='Toys come alive and have a blast doing so', metadata={'genre': 'animated', 'year': 1995.0})]
|
||||
```
|
||||
|
||||
</CodeOutputBlock>
|
||||
|
||||
## Filter k
|
||||
|
||||
We can also use the self query retriever to specify `k`: the number of documents to fetch.
|
||||
|
||||
We can do this by passing `enable_limit=True` to the constructor.
|
||||
|
||||
|
||||
```python
|
||||
retriever = SelfQueryRetriever.from_llm(
|
||||
llm,
|
||||
vectorstore,
|
||||
document_content_description,
|
||||
metadata_field_info,
|
||||
enable_limit=True,
|
||||
verbose=True
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# This example only specifies a relevant query
|
||||
retriever.get_relevant_documents("What are two movies about dinosaurs")
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue