Using AI Agents for Reranking in RavenDB

time to read 7 min | 1278 words

Since version 7.0, RavenDB has native support for vector search. One of my favorite queries ever since has been this one:


$query = 'Italian food'
from "Products" 
where vector.search(embedding.text(Name), $query)
limit 5

If you run that on the sample database for RavenDB (Northwind), you’ll get the following results:

  • Mozzarella di Giovanni
  • Ravioli Angelo
  • Chef Anton's Gumbo Mix
  • Mascarpone Fabioli
  • Chef Anton's Cajun Seasoning

I think we can safely state that the first two are closely related to Italian food, but the last three? What is that about?

The query above is using a pretty simple embedding model (bge-micro-v2 with 384 dimensions), so there is a limit to how sophisticated it can get.

I defined an index using OpenAI’s text-embedding-3-small model with 1,536 dimensions. Here is the index in question:


from p in docs.Products
select new
{
    NameVector = LoadVector("Name", "products-names")
}

And here is the query:


$query = 'Italian food'


from index 'Products/SemanticSearch'
where vector.search(NameVector, $query)
limit 5

The results we got are much better, indeed:

  • Ravioli Angelo
  • Mozzarella di Giovanni
  • Gnocchi di nonna Alice
  • Gorgonzola Telino
  • Original Frankfurter grüne Soße

However… that last result looks very much like a German sausage, not really a hallmark of the Italian kitchen. What is going on?

Vector search is also known as semantic search, and it gets you the closest items in vector space to what you were looking for. Leaving aside the quality of the embeddings model we use, we’ll find anything that is close. But what if we don’t have anything close enough?

For example, what will happen if I search for something that is completely unrelated to the data I have?


$query = 'Giant leap for man'

Remember how vector search finds the nearest matching elements. In this case, here are the results:

  • Sasquatch Ale
  • Vegie-spread
  • Chang
  • Maxilaku
  • Laughing Lumberjack Lager

I think we can safely agree that this isn’t really that great a result. It isn’t the fault of the vector search, by the way. You can define a minimum similarity threshold, but… those are usually fairly arbitrary.

I want to find “Ravioli” when I search for “Italian food”, but that has a score of 0.464, while the score of “Sasquatch Ale” from “Giant leap for man” is 0.267.

We need to add some intelligence into the mix, and luckily we can do that in RavenDB with the help of AI Agents. In this case, we aren’t going to build a traditional chatbot, but rely on the model to give us good results.

Here is the full agent definition, in C#:


var agent = new AiAgentConfiguration
{
    Name = "Search Agent",
    Identifier = "search-agent",
    ConnectionStringName = "OpenAI-Orders-ConStr",
    Parameters = [],
    SystemPrompt = @"
Your task is to act as a **product re-ranking agent** for a product
catalog. Your goal is to provide the user with the most relevant and
accurate product results based on their search query.


### Instructions


1.  **Analyze the User Query:** Carefully evaluate the user's
    request, identifying key product attributes, types, and intent.
2.  **Execute Search:** Use the `Search` query tool to perform a
    semantic search on the product catalog. Formulate effective and
    concise search terms derived from the user's query to maximize the
    initial retrieval of relevant products.
3.  **Re-rank Results:** For each product returned by the `Search`
    function, analyze its features (e.g., title, description,
    specifications) and compare them against the user's original
    query. Re-order the list of products from most to least
    relevant. **Skip any products that are not a good match for
    the user's request, regardless of their initial search score.**
4.  **Finalize & Present:** Output the re-ranked list of products,
    ensuring the top results are the most likely to satisfy the
    user's request.
",
    SampleObject = JsonConvert.SerializeObject(new
    {
        Products = new[]
        {
            new { Id = "The Product ID", Name = "The Product Name"}
        }
    }),
    Queries = [new AiAgentToolQuery
    {
        Name = "Search",
        Description = "Searches the product catalog for matches to the terms",
        ParametersSampleObject = JsonConvert.SerializeObject(new
        {
            query = "The terms to search for"
        }),
        Query = @"from index 'Products/SemanticSearch'
where vector.search(NameVector, $query)
select Name
limit 10"
    }],
};

Assuming that you are not familiar with AI Agent definitions in RavenDB, here is what is going on:

  • We configure the agent to use the OpenAI-Orders-ConStr (which uses the gpt-4.1-mini model) and specify no intrinsic parameters,  since we only perform searches in the public product catalog.
  • We tell the agent what it is tasked with doing. You’ll note that the system prompt is the most complex aspect here. (In this case, I asked the model to generate a good prompt for me from the initial idea).
  • Then we define (using a sample object) how the results should be formatted.
  • Finally, we define the query that the model can call to get results from the product catalog.

With all of that in hand, we can now perform the actual search. Here is how it looks when we run it from the RavenDB Studio:

You can see that it invoked the Search tool to run the query, and then it evaluated the results to return the most relevant ones.

Here is what happened behind the scenes:

And here is what happens when we try to mess around with the agent and search for “Giant leap for man” in the product catalog:

Note that its search tool also returned Ale and Vegie-Spread, but the agent was smart enough to discard them.

This is a small example of how you can use AI Agents in a non-stereotypical role. You aren’t limited to just chatbots, you can do a lot more. In this case, you have the foundation for a very powerful querying agent, written in only a few minutes.

I’m leveraging both RavenDB’s capabilities and the model’s to do all the hard work for you. The end result is smarter applications and more time to focus on business value.