# How to Seamlessly Use MongoDB Atlas and IBM watsonx.ai LLMs in Your GenAI Applications

One of the challenges of e-commerce applications is to provide relevant and personalized product recommendations to customers. Traditional keyword-based search methods often fail to capture the semantic meaning and intent of the user search queries, and return results that do not meet the user’s needs. In turn, they fail to convert into a successful sale. To address this problem, RAG (retrieval-augmented generation) is used as a framework powered by MongoDB Atlas Vector Search, LangChain, and IBM watsonx.ai. 

RAG is a natural language generation (NLG) technique that leverages a retriever module to fetch relevant documents from a large corpus and a generator module to produce text conditioned on the retrieved documents. Here, the RAG framework is used to power product recommendations as an extension to existing semantic search techniques.

- RAG use cases can be easily built using the vector search capabilities of MongoDB Atlas to store and query large-scale product embeddings that represent the features and attributes of each product. Because of MongoDB’s flexible schema, these are stored right alongside the product embeddings, eliminating the complexity and latency of having to retrieve the data from separate tables or databases.
- RAG then retrieves the most similar products to the user query based on the cosine similarity of their embeddings, and generates natural language reasons that highlight why these products are relevant and appealing to the user. 
- RAG can also enhance the user experience (UX) by handling complex and diverse search queries, such as "a cozy sweater for winter" or "a gift for my daughter who is interested in science", and provides accurate and engaging product recommendations that increase customer satisfaction and loyalty.

 is IBM’s next-generation enterprise studio for AI builders, bringing together new generative AI capabilities with traditional machine learning (ML) that span the entire AI lifecycle. With watsonx.ai, you can train, validate, tune, and deploy foundation and traditional ML models.

watsonx.ai brings forth a curated library of foundation models, including IBM-developed models, open-source models, and models sourced from third-party providers. Not all models are created equal, and the curated library provides enterprises with the optionality to select the model best suited to a particular use case, industry, domain, or even price performance. Further, IBM-developed models, such as the Granite model series, offer another level of enterprise-readiness, transparency, and indemnification for production use cases. We’ll be using Granite models in our demonstration. For the interested reader, IBM has published information about its data and training methodology for its Granite foundation models.

## How to build a custom RAG-powered product discovery pipeline

For this tutorial, we will be using an e-commerce products dataset containing over 10,000 product details. We will be using the sentence-transformers/all-mpnet-base-v2 model from Hugging Face to generate the vector embeddings to store and retrieve product information. You will need a Python notebook or an IDE, a MongoDB Atlas account, and a wastonx.ai account for hands-on experience.

For convenience, the notebook to follow along and execute in your environment is available on GitHub.

### Python dependencies

* `langchain`: Orchestration framework

* `ibm-watson-machine-learning`: For IBM LLMs

* `wget`: To download knowledge base data

* `sentence-transformers`: For embedding model

* `pymongo`: For the MongoDB Atlas vector store

### watsonx.ai dependencies

We’ll be using the watsonx.ai foundation models and Python SDK to implement our RAG pipeline in LangChain.

1. **Sign up for a free watsonx.ai trial on IBM cloud**. Register and get set up.
2. **Create a watsonx.ai Project**. During onboarding, a sandbox project can be quickly created for you. You can either use the sandbox project or create one; the link will work once you have registered and set up watsonx.ai. If more help is needed, you can read the documentation.
3. **Create an API key to access watsonx.ai foundation models**. Follow the steps to create your API key.
4. **Install and use watsonx.ai**. Also known as the IBM Watson Machine Learning SDK, watsonx.ai SDK information is available on GitHub. Like any other Python module, you can install it with a pip install. Our example notebook takes care of this for you.

We will be running all the code snippets below in a Jupyter notebook. You can choose to run these on VS Code or any other IDE of your choice.

**Initialize the LLM**

Initialize the watsonx URL to connect by running the below code blocks in your Jupyter notebook:

```python
# watsonx URL

try:
    wxa_url = os.environ"WXA_URL"]

except KeyError:
    wxa_url = getpass.getpass("Please enter your watsonx.ai URL domain (hit enter): ")
```

Enter the URL for accessing the watsonx URL domain. For example: https://us-south.ml.cloud.ibm.com.

To be able to access the LLM models and other AI services on watsonx, you need to initialize the API key. You init the API key by running the following code block in you Jupyter notebook:

```python
# watsonx API Key

try:
    wxa_api_key = os.environ["WXA_API_KEY"]
except KeyError:
    wxa_api_key = getpass.getpass("Please enter your watsonx.ai API key (hit enter): ")
```

You will be prompted when you run the above code to add the IAM API key you fetched earlier.

Each experiment can tagged or executed under specific projects. To fetch the relevant project, we can initialize the project ID by running the below code block in the Jupyter notebook:

```python
# watsonx Project ID

try:
    wxa_project_id = os.environ["WXA_PROJECT_ID"]
except KeyError:
    wxa_project_id = getpass.getpass("Please enter your watsonx.ai Project ID (hit enter): ")
```

You can find the project ID alongside your IAM API key in the settings panel in the watsonx.ai portal.

**Language model**

In the code example below, we will initialize Granite LLM from IBM and then demonstrate how to use the initialized LLM with the LangChain framework before we build our RAG.

We will use the query: "I want to introduce my daughter to science and spark her enthusiasm. What kind of gifts should I get her?" 

This will help us demonstrate how the LLM and vector search work in an RAG framework at each step.

Firstly, let us initialize the LLM hosted on the watsonx cloud. To access the relevant Granite model from watsonx, you need to run the following code block to initialize and test the model with our sample query in the Jupyter notebook: 

```python
from ibm_watson_machine_learning.foundation_models.utils.enums import ModelTypes
from ibm_watson_machine_learning.foundation_models import Model
from ibm_watson_machine_learning.metanames import GenTextParamsMetaNames as GenParams
from ibm_watson_machine_learning.foundation_models.utils.enums import DecodingMethods

parameters = {
    GenParams.DECODING_METHOD: DecodingMethods.GREEDY,
    GenParams.MIN_NEW_TOKENS: 1,
    GenParams.MAX_NEW_TOKENS: 100
}

model = Model(
    model_id=ModelTypes.GRANITE_13B_INSTRUCT,
    params=parameters,
    credentials={
        "url": wxa_url,
        "apikey": wxa_api_key
    },
    project_id=wxa_project_id
)

from ibm_watson_machine_learning.foundation_models.extensions.langchain import WatsonxLLM

granite_llm_ibm = WatsonxLLM(model=model)

# Sample query chosen in the example to evaluate the RAG use case
query = "I want to introduce my daughter to science and spark her enthusiasm. What kind of gifts should I get her?"

# Sample LLM query without RAG framework
result = granite_llm_ibm(query)
```

Output:

![Jupyter Notebook Output][3]

### Initialize MongoDB Atlas for vector search

Prior to starting this section, you should have already set up a cluster in MongoDB Atlas. If you have not created one for yourself, then you can follow the steps in the [MongoDB Atlas tutorial to create an account in Atlas (the developer data platform) and a cluster with which we can store and retrieve data. It is also advised that the users spin an Atlas dedicated cluster with size M10 or higher for this tutorial.

Now, let us see how we can set up MongoDB Atlas to provide relevant information to augment our RAG framework. 

**Init Mongo client**

We can connect to the MongoDB Atlas cluster using the connection string as detailed in the tutorial link above. To initialize the connection string, run the below code block in your Jupyter notebook:

```python
from pymongo import MongoClient

try:
    MONGO_CONN = os.environ"MONGO_CONN"]
except KeyError:
    MONGO_CONN = getpass.getpass("Please enter your MongoDB connection String (hit enter): ")
```

When prompted, you can enter your MongoDB Atlas connection string.

**Download and load data to MongoDB Atlas**

In the steps below, we demonstrate how to download the products dataset from the provided URL link and add the documents to the respective collection in MongoDB Atlas. We will also be embedding the raw product texts as vectors before adding them in MongoDB. You can do this by running the following lines of code your Jupyter notebook:

```python
import wget

filename = './amazon-products.jsonl'
url = "https://github.com/ashwin-gangadhar-mdb/mbd-watson-rag/raw/main/amazon-products.jsonl"

if not os.path.isfile(filename):
    wget.download(url, out=filename)

# Load the documents using Langchain Document Loader
from langchain.document_loaders import JSONLoader

loader = JSONLoader(file_path=filename, jq_schema=".text",text_content=False,json_lines=True)
docs = loader.load()

# Initialize Embedding for transforming raw documents to vectors**
from langchain.embeddings import HuggingFaceEmbeddings
from tqdm import tqdm as notebook_tqdm

embeddings = HuggingFaceEmbeddings()

# Initialize MongoDB client along with Langchain connector module
from langchain.vectorstores import MongoDBAtlasVectorSearch

client = MongoClient(MONGO_CONN)
vcol = client["amazon"]["products"]
vectorstore = MongoDBAtlasVectorSearch(vcol, embeddings)

# Load documents to collection in MongoDB**
vectorstore.add_documents(docs)
```

You will be able to see the documents have been created in `amazon` database under the collection `products`.

![MongoDB Atlas Products Collection][4]

Now all the product information is added to the respective collection, we can go ahead and create a vector index by following the steps given in the [Atlas Search index tutorial. You can create the search index using both the Atlas UI as well as programmatically. Let us look at the steps if we are doing this using the Atlas UI.

.

**Sample query to vector search**

We can test the vector similarity search by running the sample query with the LangChain MongoDB Atlas Vector Search connector. Run the following code in your Jupyter notebook:

```python
texts_sim = vectorstore.similarity_search(query, k=3)

print("Number of relevant texts: " + str(len(texts_sim)))
print("First 100 characters of relevant texts.")

for i in range(len(texts_sim)):
    print("Text " + str(i) + ": " + str(texts_simi].page_content[0:100]))
```

![Sample Vector Search Query Output][7]

In the above example code, we are able to use our sample text query to retrieve three relevant products. Further in the tutorial, let’s see how we can combine the capabilities of LLMs and vector search to build a RAG framework. For further information on various operations you can perform with the `MongoDBAtlasVectorSearch` module in LangChain, you can visit the [Atlas Vector Search documentation.

### RAG chain

In the code snippets below, we demonstrate how to initialize and query the RAG chain. We also introduce methods to improve the output from RAG so you can customize your output to cater to specific needs, such as the reason behind the product recommendation, language translation, summarization, etc.

So, you can set up the RAG chain and execute to get the response for our sample query by running the following lines of code in your Jupyter notebook:

```python
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

prompt = PromptTemplate(template="""

Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.

{context}

##Question:{question} \n\

##Top 3 recommnedations from Products and reason:\n""",input_variables="context","question"])

chain_type_kwargs = {"prompt": prompt}
retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={'k': 6, 'lambda_mult': 0.25})

qa = RetrievalQA.from_chain_type(llm=granite_llm_ibm, chain_type="stuff",
                                 retriever=retriever,
                                 chain_type_kwargs=chain_type_kwargs)

res = qa.run(query)

print(f"{'-'*50}")
print("Query: " + query)
print(f"Response:\n{res}\n{'-'*50}\n")
```
The output will look like this:

![Sample RAG Chain Output][8]

You can see from the example output where the RAG is able to recommend products based on the query as well as provide a reasoning or explanation as to how this product suggestion is relevant to the query, thereby enhancing the user experience.

## Conclusion

In this tutorial, we demonstrated how to use watsonx LLMs along with Atlas Vector Search to build a RAG framework. We also demonstrated how to efficiently use the RAG framework to customize your application needs, such as the reasoning for product suggestions. By following the steps in the article, we were also able to bring the power of machine learning models to a private knowledge base that is stored in the Atlas Developer Data Platform.

In summary, RAG is a powerful NLG technique that can generate product recommendations as an extension to semantic search using vector search capabilities provided by MongoDB Atlas. RAG can also improve the UX of product recommendations by providing more personalized, diverse, informative, and engaging descriptions.

## Next steps

Explore more details on how you can [build generative AI applications using various assisted technologies and MongoDB Atlas Vector Search.

To learn more about Atlas Vector Search, visit the product page or the documentation for creating a vector search index or running vector search queries.

To learn more about watsonx, visit the IBM watsonx page.

  [1]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltabec5b11a292b3d6/6553a958c787a446c12ab071/image1.png
  [2]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blte019110c36fc59f5/6553a916c787a4d4282ab069/image3.png
  [3]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltabec5b11a292b3d6/6553a958c787a446c12ab071/image1.png
  [4]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blta69be6d193654a53/6553a9ad4d2859f3c8afae47/image5.png
  [5]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt70003655ac1919b7/6553a9d99f2b9963f6bc99de/image6.png
  [6]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltcbdae931b43cc17a/6553a9f788cbda51858566f6/image2.png
  [7]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt0b43cc03bf7bb27f/6553aa124452cc3ed9f9523d/image7.png
  [8]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltf6c21ef667b8470b/6553aa339f2b993db7bc99e3/image4.png