Find answers from the community

Updated 3 months ago

Filtering

I currently need to perform search that might use a Vector DB Search or an SQL search depending on the query. It seems like Llama Index can handle that with the SQL Router Query Engine.
I am using Qdrant as my Vector DB and I have metadata such as id and I want to be able to dynamically filter metadata
So if the query is give me info about something based on docs belonging to the user id I want to be able to pas that id as filter to Qdrant. Is that possible?
L
t
c
53 comments
We have something called an auto vector retriever. It will dynamically set the filters as needed by asking the LLM to write the filters

Actually, I think this notebook is exactly for your usecase

https://gpt-index.readthedocs.io/en/stable/examples/query_engine/SQLAutoVectorQueryEngine.html#define-sqlautovectorqueryengine
is this possible with the airtable reader? i'd love to be able to filter the nodes returned in the document
If you set metadata in your documents returned by the reader (not sure if the airtable reader sets up metadata or not), then yea you can use metadata filtering at query time to reduce the search space
yes it does - ok I will try & inevitably come back with questions. could be a gamechanger 4 me
apologies @Logan M - more inevitable than I thought...

what is the relevant part of the documentation for this?

my nodes look like this. so I think I've got metadata in person/ url/ tool. so I should be able to filer by each of those?

ideally I want to add a button that allows the user to select the person/ tool & filter by them

[NodeWithScore(node=TextNode(id_='c57bfbb7-cdb4-4f67-a557-92e2820544f0', embedding=None, metadata={'Person': 'Marc Andreessen', 'url': 'https://pmarchive.com/', 'tool': 'Technique'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, hash='700bc7de4274245678638212412c1fabd87f2be3375541fc3542e951de895fec', text='Each night before you go to bed, prepare a 3x5 index card with a short list of 3 to 5 things that you will do the next day.\nAnd then, the next day, do those things. I sit down at my desk before I go to sleep, pull up my Todo List (which I keep in Microsoft Word’s outline mode, due to long habit), and pick out the 3 to 5 things I am going to get done tomorrow. I write those things on a fresh 3x5 card, lay the card out with my card keys, and go to bed. Then, the next day, I try like hell to get just those things done. If I do, it was a successful day.\n', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n'), score=0.8491035803980768),
correct! In that case yea, the user can filter before searching πŸ’ͺ

Which vector store backend are you using right now? Just the default, or something else?
vector store index
but are you using chroma or pinecone or anything in the backend?
should I be..?
well everything is stored in airtable
and it loads from airtable using airtable reader
and then i guess the vector is just hosted on my server
Oh so it's creating indexes on the fly?
i mean it runs and loads when i launch/ relaunch the server
but the point of the app is to constantly add to it and for users to be able to so for the moment that suits
neat! Ok so filtering for the base vector store isn't actually implemented yet -- but there's a PR for it! I think i just need to finish the PR myself at this point though, it's been sitting for a bit πŸ˜… https://github.com/jerryjliu/llama_index/pull/7564
no problem at all! please do let me know when it's ready
and thanks again for your help
sorry I can't be of more help on the PRs
no worries! Should be able to get it in this weekend πŸ‘
& then how would it work?

there can just be some code that is basically if a variable = bla then filter by bla?

& I can make a button with the var for users to hit?
& sorry for follow up. does the filtering work for multiple values e.g. filter by tool + person not just person
Yea so how it works is something like this

Plain Text
from llama_index.vector_stores.types import ExactMatchFilter, MetadataFilters

filters = MetadataFilters(filters=[ExactMatchFilter(key="tool", value="name"), ExactMatchFilter(key="person", value="name")])

query_engine = index.as_query_engine(filters=filters)
@Logan M Thank you. I will give auto vector retriever a shot. To me it's kind of mind boggling that a LLM can create filters for Qdrant dynamically.

That brings me to 2 questions.
  1. Is there some kind of prompt template added to auto retriever to give it that ability because I would imagine unlike SQL generating, OPEN AI's LLM isn't trained on how to create filters for Qdrant.
  1. My use case includes filtering by id or by date . With the SQL Auto Vector Query Engine, is there some way to see the prompt template it provides and for me to further edit / enhance it?
Right, the example I gave above hardcodes the filters. Using VectorIndexAutoRetriever with the sql vector thing linked above in that notebook, it can dynamically write filters.

You are correct, there is a template, if you take a look at this setup

Plain Text
vector_store_info = VectorStoreInfo(
    content_info="articles about different cities",
    metadata_info=[
        MetadataInfo(name="title", type="str", description="The name of the city"),
    ],
)
vector_auto_retriever = VectorIndexAutoRetriever(
    vector_index, vector_store_info=vector_store_info
)

retriever_query_engine = RetrieverQueryEngine.from_args(
    vector_auto_retriever, service_context=service_context
)


You see that we define a VectorStoreInfo object. It also has an optional prompt_template_str argument.

tbh though, the template is quite complex to modify haha. But here it is if you are curious, it combines a prefix, examples, and suffix
https://github.com/jerryjliu/llama_index/blob/502a4d66f8d2beff20e481396ec23949cfc03aa2/llama_index/indices/vector_store/retrievers/auto_retriever/prompts.py#L18

And again, this is just controling the vector index side. The SQL auto vector thing also assumes you have a sql query engine setup as well, that connects to a db for text2sql queries
@Logan M
Thanks a lot. So if I pass prompt_template to the VectorStoreInfo I can override the default prompt. I now see that from L40 to L61, you give examples to the prompt on how to filter. That's awesome. https://github.com/jerryjliu/llama_index/blob/502a4d66f8d2beff20e481396ec23949cfc03aa2/llama_index/indices/vector_store/retrievers/auto_retriever/prompts.py#L40

And again, this is just controling the vector index side. The SQL auto vector thing also assumes you have a sql query engine setup as well, that connects to a db for text2sql queries
Is my understanding correct then that there is a default prompt setup for vector and another default prompt setup for sql side? And then there's I guess the parent prompt which helps the agent determine which engine to use? = a total of 3 prompts for this SQL Auto Query Engine setup.
exactly -- well actually, theres more prompts I think πŸ˜… tbh the code is a little complicated. Maybe it would help to just share the code

The original SQLAutoVectorQueryEngine is defined here
https://github.com/jerryjliu/llama_index/blob/502a4d66f8d2beff20e481396ec23949cfc03aa2/llama_index/query_engine/sql_vector_query_engine.py#L50

But after it came out, we realized that you should be able to join any index, not just a vector, so we have the base class as the SQLJoinQueryEngine here
https://github.com/jerryjliu/llama_index/blob/502a4d66f8d2beff20e481396ec23949cfc03aa2/llama_index/query_engine/sql_join_query_engine.py#L105
For example, querying a vector index uses a text_qa_template and a refine_template

Then the text2sql has a text2sql prompt for generating queries, and an optional second stage that transforms the sql result into natural language

Then there's some final prompt for aggregating these answers
@Logan M
Thanks a lot, I'll take a look at them.
you should be able to join any index, not just a vector
Sorry what do you mean by any index.

The last thing I would like to confirm is, it is possible to make SQL Auto Query Engine retain memory / chat history. Asking the docs Mendable, it says it can but I'm not sure how you pass in chat history.

Is it in the service_context?
https://github.com/jerryjliu/llama_index/blob/502a4d66f8d2beff20e481396ec23949cfc03aa2/llama_index/query_engine/sql_vector_query_engine.py#L50
Like using the SQLJointQueryEngine, you could join any query engine with a sql database

To maintain chat history, you need to setup an agent most likely
https://gpt-index.readthedocs.io/en/stable/examples/agent/openai_agent_with_query_engine.html

I would start with getting the initial engine working, then passing that engine to an agent πŸ™‚
@Logan M Thanks a lot for the guidance, that was super helpful. I think I have enough to work with now πŸ™πŸ»
Thanks dude let us know when the vector store index filtering is done and thank you again !!
just to check @Logan M , this doesn't yet work?
Not yet for the base vector store
Just merged it -- it will be included in the next release (probably tomorrow?)

If you want it now, you can install from source

Plain Text
pip uninstall llama-index
pip install --upgrade git+https://github.com/jerryjliu/llama_index
omg so cool! I just do those 2 lines in my shell & I'm good to go?
Should be! There's an example of using the filters at the bottom of this page if you need it too https://github.com/jerryjliu/llama_index/blob/main/docs/examples/vector_stores/SimpleIndexDemo.ipynb
hmm - think something wrong with doing that on replit
hmm yea no idea on replit lol
Thanks - think I've made it work.

I added this & now I get no response.

Do I also need to add the source.nodes = retriever.retrieve?
bits I added @Logan M

index = VectorStoreIndex(nodes)

filters = MetadataFilters(filters=[ExactMatchFilter(key="person", value="Sam Altman")])

retriever = VectorIndexRetriever(
index=index,
similarity_top_k=5,
filters=filters,
)

type of response

query_engine = RetrieverQueryEngine.from_args(
retriever,
response_mode="compact",
text_qa_template=QA_PROMPT,
)
PS thank you so much for merging it - so cool
Yea you'll have to re-build the index so that the index is built with the proper info needed for filtering
Hm ok Will have a go and revert if issues - thanks again @Logan M
Hi @Logan M - me again... spent a bit of time on this last night/ this evening.. wonder if you might have some direction. Some context...

So I load data from airtable so documents = reader.load_data etc.

The airtable data loads several different columns in a string. So I then use re to construct nodes that make sense for my use case. See code here -

pattern1 = r'["\']Quotes["\']: [\'"](.*?)[\'"]' pattern2 = r'["\']People["\']: [\'"](.*?)[\'"]' pattern3 = r'["\']Source["\']: [\'"](.*?)[\'"]' pattern4 = r'["\']Tool Type["\']: [\'"](.*?)[\'"]' # create nodes list based on patterns nodes = [] for document in documents: text = document.text matches1 = re.findall(pattern1, text) matches2 = re.findall(pattern2, text) matches3 = re.findall(pattern3, text) matches4 = re.findall(pattern4, text) # takes quotes & people from matches & puts them into list of nodes for quote, person, url, tool in zip(matches1, matches2, matches3, matches4): node_name = f"node{len(nodes) + 1}" node_text = f"{quote}" extra_info = {"Person": person, "url": url, "tool": tool} exec(f"{node_name} = Node(text=node_text, extra_info=extra_info)") nodes.append(eval(node_name))

Then I use the QA prompt & a vector store index with k=5.

& the result is a system that works well and gives me response.source_nodes that look as follows...
To continue @Logan M , the response.sourcenodes look like this: ='465eb334-f87b-4495-8742-d54e8a209462', embedding=None, metadata={'Person': 'Sam Altman', 'url': 'https://blog.samaltman.com/', 'tool': 'Technique'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, hash='6aafb54126146181b8761b895302437473c9581fa4413e825705bb028811744f', text='Sleep seems to be the most important physical factor in productivity for me. Some sort of sleep tracker to figure out how to sleep best is helpful. I’ve found the only thing I’m consistent with are in the set-it-and-forget-it category, and I really like the Emfit QS+Active… I like a cold, dark, quiet room, and a great mattress (I resisted spending a bunch of money on a great mattress for years, which was stupidβ€”it makes a huge difference to my sleep quality. I love this one). I use a Chili Pad to be cold while I sleep if I can’t get the room cold enough, which is great but loud (I set it up to have the cooler unit outside my room). When traveling, I use an eye mask and ear plugs.\n', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n'), score=0.8342998507234975),

Now I'm trying to filter on the metadata.

I assumed I could do something like the following but realizing actually something more is needed..?

QA_PROMPT = QuestionAnswerPrompt(QA_PROMPT_TMPL) # indexing index = VectorStoreIndex(nodes) index.insert(nodes) filters = MetadataFilters(filters=[ExactMatchFilter(key="person", value="Napoleon")]) retriever = index.as_retriever( similarity_top_k=5, filters=filters )

Wdyt..?
Hold on! Now seems to be working!

I removed an index line and capitalized the people...

index = VectorStoreIndex(nodes) filters = MetadataFilters(filters=[ExactMatchFilter(key="Person", value="Sam Altman")]) retriever = index.as_retriever( similarity_top_k=5, filters=filters )
yes it is working... sorry! sometimes you have to ask the question to figure out yourself...

thanks!
haha awesome, glad you got it!
Add a reply
Sign up and join the conversation on Discord