AI Sommelier Built with PaLM API and LangChain

Project walkthrough to build an LLM-powered application using PaLM API, Pinecone as the vector database, and Streamlit as the interface.

Gabriel Cassimiro
5 min readSep 26, 2023

In this article, I aim to demonstrate how to use the PaLM API to build solutions using LangChain. I will build a solution using LangChain chains, Google Embeddings, Pinecone as the VectorDB, and Streamlit as the interface to interact with users.

The problem we want to tackle is personalized wine recommendations. I want to build an AI capable of recommending wines based on user preference and also using a database containing 130K wines.

The full code can be found here.

The data

The data used in this project is the dataset wine reviews, license. This dataset contains 130K reviews of wines. It also contains the country, region, variety, and winery of each wine. This is a sample of the data:

Image by Author

The Problem

So, to tackle our problem we want to first understand the taste of the user, then search the database for the wines that may be a good recommendation, and decide which one is the final recommendation.

To get the user input we will use a simple questionnaire inside of Streamlit with the following questions:

  1. Preferred Taste Profile;
  2. Level of experience;
  3. Red or White wine preference;
  4. Favorite Flavours;
  5. Pairing intent;
  6. Open field to add any information about your taste.

All of these (except the last) have pre-selected categories. However, since we are using LLMs this isn't strictly necessary. We could leave all questions with free text input because the LLM is able to use it without the categories. However, to guide the user I chose to use categories.

The Architecture

The Architecture of the solution is shown below:

Image by Author

For the solution, we will have two calls to the LLM API. In the first one, we will pass the taste form information and ask for the LLM to generate a string query that summarizes the taste and will be used for similarity search on the vector DB. After that, we can find the most similar descriptions of the wines to the person's taste.

However, this is not enough for a final decision, because some characteristics like red vs. white are not taken into account by this simple similarity search. That is why we call the LLM again passing the original taste form and the top 3 most similar wines to the LLM and ask it to select the best one for it and explain the reasoning.

The Code

Let's start by looking at the chains used for the calls to the API.

First, if you do not know already what is LangChain, this is a brief definition:

LangChain is a framework for developing applications powered by language models.

That is the official definition of LangChain. This framework was created recently and is already used as the industry standard for building tools powered by LLMs.

Chains are a core feature of LangChain and enable the integration of various components to form a unified application. They can format user input using a PromptTemplate and then forward it to an LLM. By linking multiple chains or combining them with other elements, we can create more complex chains.

This is the code for both of the chains:

Here we have a couple of elements I would like to point out. First, we have the Response Schema and Output Parser. These are used to create a prompt in which the LLM will always return the output with the same structure and the parser will process that output to be able to work back in the code.

Then we have the prompt built using the Chat Prompt Template. This allows us to pass variables into the prompt like the answers of the taste form. Lastly, we put everything back together with the Sequential Chain, defining the inputs and outputs.

Now we have to initialize the DB.

The Database

For the Vector Database, we are using Pinecone however you can easily change the one you want to use because we are using LangChain to interact with the DB.

To add the wines to the Database we need to first create an Embedding of each one and then upload them. We can easily perform this with the code below:

We create an instance of Document from LangChain containing the text and the Metadata. Then we use LangChain and Google PaLM Embeddings to upload to Pinecone. The embeddings have 768 dimensions.

For this implementation I am using the free tier from Pinecone, which allows for 1 free index of the standard resource.

Image by Author

Now we just need to put everything together passing the input from the application to the chains and connect to the vector database.

The App

The App will be the main control of the flow. It is also where we will create all the resources we need: the form, the PaLM API Connection, the Pinecone connection and the final visualization.

This code can be divided in three main parts:

  1. The form to get the user input;
  2. The interaction of the chains and the vector database;
  3. The display of the final recommendation.

These parts are executed inside the main script but for a larger application should be modularized.

This is an example of using the tool:

Image by Author

So this is the final product. All we have to do now is deploy this application. With this in mind, the first thing we need is to allow the user to choose which LLM to use and have an input for the API key.

The final version can be seen here.

Conclusion

Using LangChain allows us to build quick and in a modular manner. For the problem of recommending wines based on a database we were able to use Chains with two prompts that helped to understand the users taste, find some options to suggest and make the final decision with an explanation.

An easy way to interact with the user is through an interface such as Streamlit, making it fast to develop and deploy.

--

--

Gabriel Cassimiro

Solving and creating problems with AI. Google Developer Expert in ML