{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction to RAG with MLflow and LangChain" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "Download this Notebook
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tutorial Overview\n", "Welcome to this tutorial, where we explore the integration of Retrieval Augmented Generation (RAG) with MLflow and LangChain. Our focus is on demonstrating how to create advanced RAG systems and showcasing the unique capabilities enabled by MLflow in these applications.\n", "\n", "#### Understanding RAG and how to develop one with MLflow\n", "Retrieval Augmented Generation (RAG) combines the power of language model generation with information retrieval, allowing language models to access and incorporate external data. This approach significantly enriches the model's responses with detailed and context-specific information.\n", "\n", "MLflow is instrumental in this process. As an open-source platform, it facilitates the logging, tracking, and deployment of complex models, including RAG chains. With MLflow, integrating LangChain becomes more streamlined, enhancing the development, evaluation, and deployment processes of RAG models.\n", "\n", "> NOTE: In this tutorial, we'll be using GPT-3.5 as our base language model. It's important to note that the results obtained from a RAG system will differ from those obtained by interfacing directly with GPT models. RAG's unique approach of combining external data retrieval with language model generation creates more nuanced and contextually rich responses.\n", "\n", "
\n", " \n", "
\n", "\n", "### Learning Outcomes\n", "By the end of this tutorial, you will learn:\n", "- How to establish a RAG chain using LangChain and MLflow.\n", "- Techniques for scraping and processing documents to feed into a RAG system.\n", "- Best practices for deploying and using RAG models to answer complex queries.\n", "- Understanding the practical implications and differences in responses when using RAG in comparison to direct language model interactions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up our Retriever Dependencies\n", "In order to have a place to store our vetted data (the information that we're going to be retrieving), we're going to use a Vector Database. The framework that we're choosing to use (due to its simplicity, capabilities, and free-to-use characteristics) is FAISS, from **Meta**.\n", "\n", "### FAISS Installation for the Tutorial\n", "\n", "#### Understanding FAISS\n", "For this tutorial, we will be utilizing [FAISS](https://github.com/facebookresearch/faiss/wiki) (Facebook AI Similarity Search, developed and maintained by the [Meta AI research group](https://ai.meta.com/tools/faiss/)), an efficient similarity search and clustering library. It's a highly useful library that easily handles large datasets and is capable of performing operations such as nearest neighbor search, which are critical in Retrieval Augmented Generation (RAG) systems. There are numerous other vector database solutions that can perform similar functionality; we are using FAISS in this tutorial due to its simplicity, ease of use, and fantastic performance.\n", "\n", "### Notebook compatibility\n", "\n", "With rapidly changing libraries such as `langchain`, examples can become outdated rather quickly and will no longer work. For the purposes of demonstration, here are the critical dependencies that are recommended to use to effectively run this notebook:\n", "\n", "| Package | Version |\n", "|:--------------------|:------------|\n", "| langchain | **0.1.16** |\n", "| lanchain-community | **0.0.33** |\n", "| langchain-openai | **0.0.8** |\n", "| openai | **1.12.0** |\n", "| tiktoken | **0.6.0** |\n", "| mlflow | **2.12.1** |\n", "| faiss-cpu | **1.7.4** |\n", "\n", "If you attempt to execute this notebook with different versions, it may function correctly, but it is recommended to use the precise versions above to ensure that your code executes properly.\n", "\n", "#### Installing Requirements\n", "Before proceeding with the tutorial, ensure that you have FAISS and [Beautiful Soup](https://pypi.org/project/beautifulsoup4/) installed via `pip`. The version specifiers for other packages are guaranteed to work with this notebook. Other versions of these packages may not function correctly due to breaking changes their APIs.\n", "\n", "```bash\n", " pip install beautifulsoup4 faiss-cpu==1.7.4 langchain==0.1.16 langchain-community==0.0.33 langchain-openai==0.0.8 openai==1.12.0 tiktoken==0.6.0\n", "```\n", "\n", "> NOTE: If you'd like to run this using your GPU, you can install `faiss-gpu` instead." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "import shutil\n", "import tempfile\n", "\n", "import requests\n", "from bs4 import BeautifulSoup\n", "from langchain.chains import RetrievalQA\n", "from langchain.document_loaders import TextLoader\n", "from langchain.text_splitter import CharacterTextSplitter\n", "from langchain.vectorstores import FAISS\n", "from langchain_openai import OpenAI, OpenAIEmbeddings\n", "\n", "import mlflow\n", "\n", "assert \"OPENAI_API_KEY\" in os.environ, \"Please set the OPENAI_API_KEY environment variable.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **NOTE: If you'd like to use Azure OpenAI with LangChain, you need to install `openai>=1.10.0` and `langchain-openai>=0.0.6`, as well as to specify the following credentials and parameters:**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_openai import AzureOpenAI, AzureOpenAIEmbeddings\n", "\n", "# Set this to `azure`\n", "os.environ[\"OPENAI_API_TYPE\"] = \"azure\"\n", "# The API version you want to use: set this to `2023-05-15` for the released version.\n", "os.environ[\"OPENAI_API_VERSION\"] = \"2023-05-15\"\n", "assert (\n", " \"AZURE_OPENAI_ENDPOINT\" in os.environ\n", "), \"Please set the AZURE_OPENAI_ENDPOINT environment variable. It is the base URL for your Azure OpenAI resource. You can find this in the Azure portal under your Azure OpenAI resource.\"\n", "assert (\n", " \"OPENAI_API_KEY\" in os.environ\n", "), \"Please set the OPENAI_API_KEY environment variable. It is the API key for your Azure OpenAI resource. You can find this in the Azure portal under your Azure OpenAI resource.\"\n", "\n", "azure_openai_llm = AzureOpenAI(\n", " deployment_name=\"\",\n", " model_name=\"gpt-4o-mini\",\n", ")\n", "azure_openai_embeddings = AzureOpenAIEmbeddings(\n", " azure_deployment=\"\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scraping Federal Documents for RAG Processing\n", "\n", "In this section of the tutorial, we will demonstrate how to scrape content from federal document webpages for use in our RAG system. We'll be focusing on extracting transcripts from specific sections of webpages, which will then be used to feed our Retrieval Augmented Generation (RAG) model. This process is crucial for providing the RAG system with relevant external data.\n", "\n", "#### Function Overview\n", "- The function `fetch_federal_document` is designed to scrape and return the transcript of specific federal documents.\n", "- It takes two arguments: `url` (the webpage URL) and `div_class` (the class of the div element containing the transcript).\n", "- The function handles web requests, parses HTML content, and extracts the desired transcript text.\n", "\n", "This step is integral to building a RAG system that relies on external, context-specific data. By effectively fetching and processing this data, we can enrich our model's responses with accurate information directly sourced from authoritative documents.\n", "\n", "> **NOTE**: In a real-world scenario, you would have your specific text data located on disk somewhere (either locally or on your cloud provider) and the process of loading the embedded data into a vector search database would be entirely external to this active fetching displayed below. We're simply showing the entire process here for demonstration purposes to show the entire end-to-end workflow for interfacing with a RAG model." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def fetch_federal_document(url, div_class): # noqa: D417\n", " \"\"\"\n", " Scrapes the transcript of the Act Establishing Yellowstone National Park from the given URL.\n", "\n", " Args:\n", " url (str): URL of the webpage to scrape.\n", "\n", " Returns:\n", " str: The transcript text of the Act.\n", " \"\"\"\n", " # Sending a request to the URL\n", " response = requests.get(url)\n", " if response.status_code == 200:\n", " # Parsing the HTML content of the page\n", " soup = BeautifulSoup(response.text, \"html.parser\")\n", "\n", " # Finding the transcript section by its HTML structure\n", " transcript_section = soup.find(\"div\", class_=div_class)\n", " if transcript_section:\n", " transcript_text = transcript_section.get_text(separator=\"\\n\", strip=True)\n", " return transcript_text\n", " else:\n", " return \"Transcript section not found.\"\n", " else:\n", " return f\"Failed to retrieve the webpage. Status code: {response.status_code}\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Document Fetching and FAISS Database Creation\n", "\n", "In this next part, we focus on two key processes:\n", "\n", "1. **Document Fetching**:\n", " - We use `fetch_and_save_documents` to retrieve documents from specified URLs. \n", " - This function takes a list of URLs and a file path as inputs. \n", " - Each document fetched from the URLs is appended to a single file at the given path.\n", "\n", "2. **FAISS Database Creation**:\n", " - `create_faiss_database` is responsible for creating a FAISS database from the documents saved in the previous step.\n", " - The function leverages `TextLoader` to load the text, `CharacterTextSplitter` for document splitting, and `OpenAIEmbeddings` for generating embeddings.\n", " - The resulting FAISS database, which facilitates efficient similarity searches, is saved to a specified directory and returned for further use.\n", "\n", "These functions streamline the process of gathering relevant documents and setting up a FAISS database, essential for implementing advanced Retrieval-Augmented Generation (RAG) applications in MLflow. By modularizing these steps, we ensure code reusability and maintainability." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def fetch_and_save_documents(url_list, doc_path):\n", " \"\"\"\n", " Fetches documents from given URLs and saves them to a specified file path.\n", "\n", " Args:\n", " url_list (list): List of URLs to fetch documents from.\n", " doc_path (str): Path to the file where documents will be saved.\n", " \"\"\"\n", " for url in url_list:\n", " document = fetch_federal_document(url, \"col-sm-9\")\n", " with open(doc_path, \"a\") as file:\n", " file.write(document)\n", "\n", "\n", "def create_faiss_database(document_path, database_save_directory, chunk_size=500, chunk_overlap=10):\n", " \"\"\"\n", " Creates and saves a FAISS database using documents from the specified file.\n", "\n", " Args:\n", " document_path (str): Path to the file containing documents.\n", " database_save_directory (str): Directory where the FAISS database will be saved.\n", " chunk_size (int, optional): Size of each document chunk. Default is 500.\n", " chunk_overlap (int, optional): Overlap between consecutive chunks. Default is 10.\n", "\n", " Returns:\n", " FAISS database instance.\n", " \"\"\"\n", " # Load documents from the specified file\n", " document_loader = TextLoader(document_path)\n", " raw_documents = document_loader.load()\n", "\n", " # Split documents into smaller chunks with specified size and overlap\n", " document_splitter = CharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)\n", " document_chunks = document_splitter.split_documents(raw_documents)\n", "\n", " # Generate embeddings for each document chunk\n", " embedding_generator = OpenAIEmbeddings()\n", " faiss_database = FAISS.from_documents(document_chunks, embedding_generator)\n", "\n", " # Save the FAISS database to the specified directory\n", " faiss_database.save_local(database_save_directory)\n", "\n", " return faiss_database" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting Up the Working Environment and FAISS Database\n", "\n", "This section of the tutorial deals with the setup for our Retrieval-Augmented Generation (RAG) application. We'll establish the working environment and create the necessary FAISS database:\n", "\n", "1. **Temporary Directory Creation**:\n", " - A temporary directory is created using `tempfile.mkdtemp()`. This directory serves as a workspace for storing our documents and the FAISS database.\n", "\n", "2. **Document Path and FAISS Index Directory**:\n", " - Paths for storing the fetched documents and FAISS database are defined within this temporary directory.\n", "\n", "3. **Document Fetching**:\n", " - We have a list of URLs (`url_listings`) containing the documents we need to fetch.\n", " - `fetch_and_save_documents` function is used to retrieve and save the documents from these URLs into a single file located at `doc_path`.\n", "\n", "4. **FAISS Database Creation**:\n", " - The `create_faiss_database` function is then called to create a FAISS database from the saved documents, using the default `chunk_size` and `chunk_overlap` values.\n", " - This database (`vector_db`) is crucial for the RAG process, as it enables efficient similarity searches on the loaded documents.\n", "\n", "By the end of this process, we have all documents consolidated in a single location and a FAISS database ready to be used for retrieval purposes in our MLflow-enabled RAG application.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "temporary_directory = tempfile.mkdtemp()\n", "\n", "doc_path = os.path.join(temporary_directory, \"docs.txt\")\n", "persist_dir = os.path.join(temporary_directory, \"faiss_index\")\n", "\n", "url_listings = [\n", " \"https://www.archives.gov/milestone-documents/act-establishing-yellowstone-national-park#transcript\",\n", " \"https://www.archives.gov/milestone-documents/sherman-anti-trust-act#transcript\",\n", "]\n", "\n", "fetch_and_save_documents(url_listings, doc_path)\n", "\n", "vector_db = create_faiss_database(doc_path, persist_dir)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Establishing RetrievalQA Chain and Logging with MLflow\n", "\n", "In this final setup phase, we focus on creating the RetrievalQA chain and integrating it with MLflow:\n", "\n", "1. **Initializing the RetrievalQA Chain**:\n", " - The `RetrievalQA` chain is initialized using the OpenAI language model (`llm`) and the retriever from our previously created FAISS database (`vector_db.as_retriever()`).\n", " - This chain will use the OpenAI model for generating responses and the FAISS retriever for document-based information retrieval.\n", "\n", "2. **Loader Function for Retrieval**:\n", " - A `load_retriever` function is defined to load the retriever from the FAISS database saved in the specified directory.\n", " - This function is crucial for reloading the retriever when the model is used later.\n", "\n", "3. **Logging the Model with MLflow**:\n", " - The RetrievalQA chain is logged using `mlflow.langchain.log_model`.\n", " - This process includes specifying the `artifact_path`, the `loader_fn` for the retriever, and the `persist_dir` where the FAISS database is stored.\n", " - Logging the model with MLflow ensures it is tracked and can be easily retrieved for future use.\n", "\n", "Through these steps, we successfully integrate a complex RAG application with MLflow, showcasing its capability to handle advanced NLP tasks." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "mlflow.set_experiment(\"Legal RAG\")\n", "\n", "retrievalQA = RetrievalQA.from_llm(llm=OpenAI(), retriever=vector_db.as_retriever())\n", "\n", "\n", "# Log the retrievalQA chain\n", "def load_retriever(persist_directory):\n", " embeddings = OpenAIEmbeddings()\n", " vectorstore = FAISS.load_local(\n", " persist_directory,\n", " embeddings,\n", " allow_dangerous_deserialization=True, # This is required to load the index from MLflow\n", " )\n", " return vectorstore.as_retriever()\n", "\n", "\n", "with mlflow.start_run() as run:\n", " model_info = mlflow.langchain.log_model(\n", " retrievalQA,\n", " artifact_path=\"retrieval_qa\",\n", " loader_fn=load_retriever,\n", " persist_dir=persist_dir,\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **IMPORTANT**: In order to load a stored vectorstore instance such as our FAISS instance above, we need to specify within the load function the argument `allow_dangeous_deserialization` to `True` in order for the load to succeed. This is due to a safety warning that was introduced in `langchain` for loading objects that have been serialized using `pickle` or `cloudpickle`. While this issue of remote code execution is not a risk with using MLflow, as the serialization and deserialization happens entirely via API and within your environment, the argument must be set in order to prevent an Exception from being thrown at load time. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Our RAG application in the MLflow UI\n", "\n", "![Our Model in the UI](https://i.imgur.com/u9zdkmM.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing our RAG Model\n", "\n", "Now that we have the model stored in MLflow, we can load it back as a `pyfunc` and see how well it answers a few critically important questions about these acts of Congress in America." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "loaded_model = mlflow.pyfunc.load_model(model_info.model_uri)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def print_formatted_response(response_list, max_line_length=80):\n", " \"\"\"\n", " Formats and prints responses with a maximum line length for better readability.\n", "\n", " Args:\n", " response_list (list): A list of strings representing responses.\n", " max_line_length (int): Maximum number of characters in a line. Defaults to 80.\n", " \"\"\"\n", " for response in response_list:\n", " words = response.split()\n", " line = \"\"\n", " for word in words:\n", " if len(line) + len(word) + 1 <= max_line_length:\n", " line += word + \" \"\n", " else:\n", " print(line)\n", " line = word + \" \"\n", " print(line)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's make sure that this thing works\n", "Let's try out our Retriever model by sending it a fairly simple but purposefully vague question." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The document states that all persons who shall locate or settle upon or occupy \n", "the land reserved for the public park, except as provided, shall be considered \n", "trespassers and removed from the park. \n" ] } ], "source": [ "answer1 = loaded_model.predict([{\"query\": \"What does the document say about trespassers?\"}])\n", "\n", "print_formatted_response(answer1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Understanding the RetrievalQA Response\n", "\n", "With this model, our approach combines text retrieval from a database with language model generation to answer specific queries.\n", "\n", "#### How It Works:\n", "\n", "- **RetrievalQA Model**: This model, a part of the LangChain suite, is designed to first retrieve relevant information from a predefined database and then use a language model to generate a response based on this information.\n", "- **Database Integration**: In this example, we've created a FAISS database from historical documents, such as the Act Establishing Yellowstone National Park. This database is used by the RetrievalQA model to find relevant sections of text.\n", "- **Query Processing**: When we execute `loaded_model.predict([{\"query\": \"What does the document say about trespassers?\"}])`, the model first searches the database for parts of the document that are most relevant to the query about trespassers.\n", "\n", "#### Why Is This Response Different?\n", "\n", "- **Context-Specific Answers**: Unlike a direct query to GPT-3.5, which might generate an answer based on its training data without specific context, the RetrievalQA model provides a response directly derived from the specific documents in the database.\n", "- **Accurate and Relevant**: The response is more accurate and contextually relevant because it's based on the actual content of the specific document being queried.\n", "- **No Generalization**: There's less generalization or assumption in the response. The RetrievalQA model is not \"guessing\" based on its training; it's providing information directly sourced from the document.\n", "\n", "#### Key Takeaway:\n", "\n", "- This methodology demonstrates how MLflow and LangChain facilitate complex RAG use cases, where direct interaction with historical or specific texts yields more precise answers than generic language model predictions.\n", "- The tutorial highlights how leveraging RAG can be particularly useful in scenarios where responses need to be grounded in specific texts or documents, showcasing a powerful blend of retrieval and generation capabilities." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Analyzing the Bridle-Path Query Response\n", "\n", "This section of the tutorial showcases an interesting aspect of the RetrievalQA model's capabilities, particularly in handling queries that involve both specific information retrieval and additional context generation.\n", "\n", "#### Query and Response Breakdown:\n", "\n", "- **Query**: The user asks, \"What is a bridle-path and can I use one at Yellowstone?\"\n", "- **Response**: The model responds by explaining what a bridle-path is and confirms that bridle-paths can be used at Yellowstone based on the act.\n", "\n", "#### Understanding the Response Dynamics:\n", "\n", "1. **Combining Document Data with LLM Context**: \n", " - The query about bridle-paths isn't directly answered in the act establishing Yellowstone. \n", " - The model uses its language understanding capabilities to provide a definition of a bridle-path.\n", " - It then merges this information with the context it retrieves from the FAISS database about the act, particularly regarding the construction of roads and bridle-paths in the park.\n", "\n", "2. **Enhanced Contextual Understanding**: \n", " - The RetrievalQA model demonstrates an ability to supplement direct information from the database with additional context through its language model.\n", " - This approach provides a more comprehensive answer that aligns with the user’s query, showing a blend of document-specific data and general knowledge.\n", "\n", "3. **Why This Is Notable**:\n", " - Unlike a standard LLM response, the RetrievalQA model doesn't solely rely on its training data for general responses.\n", " - It effectively integrates specific document information with broader contextual understanding, offering a more nuanced answer.\n", "\n", "#### Key Takeaway:\n", "\n", "- This example highlights how MLflow and LangChain, through the RetrievalQA model, facilitate a sophisticated response mechanism. The model not only retrieves relevant document information but also intelligently fills in gaps with its own language understanding capabilities.\n", "- Such a response mechanism is particularly useful when dealing with queries that require both specific document references and additional contextual information, showcasing the advanced capabilities of RAG in practical applications." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A bridle-path is a narrow path or trail designed for horseback riding. Yes, you \n", "can use a bridle-path at Yellowstone as it is designated for the enjoyment and \n", "benefit of the people visiting the park. However, it may be subject to certain \n", "regulations and restrictions set by the Secretary of the Interior. \n" ] } ], "source": [ "answer2 = loaded_model.predict(\n", " [{\"query\": \"What is a bridle-path and can I use one at Yellowstone?\"}]\n", ")\n", "\n", "print_formatted_response(answer2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A most serious question\n", "\n", "In this section of our tutorial, we delve into a whimsically ridiculous query and how our model tackles it with a blend of accuracy and a hint of humor.\n", "\n", "#### Query Overview:\n", "\n", "- **The Query**: \"Can I buy Yellowstone from the Federal Government to set up a buffalo-themed day spa?\"\n", "- **The Response**: The model, with a straight face, responds, \"No, you cannot buy Yellowstone from the Federal Government to set up a buffalo-themed day spa.\"\n", "\n", "#### A Peek into the Model's Thought Process:\n", "\n", "1. **Direct and No-Nonsense Response**: \n", " - Despite the query's comedic undertone, the model gives a direct and clear-cut response. It's like the model is saying, \"Nice try, but no, you can't do that.\"\n", " - This highlights the model's ability to remain factual, even when faced with a question that's clearly more humorous than serious.\n", "\n", "2. **Understanding Legal Boundaries**: \n", " - The response respects the legal and regulatory sanctity of national parks. It seems our model takes the protection of national treasures like Yellowstone pretty seriously!\n", " - The model's training on legal and general knowledge assists in delivering a response that's accurate, albeit the question being a facetious one.\n", "\n", "3. **Contrast with Traditional LLM Responses**: \n", " - A traditional LLM might have given a more generic answer. In contrast, our model, equipped with context-specific data, promptly debunks the whimsical idea of buying a national park for a spa.\n", "\n", "#### A Dash of Humor in Learning:\n", "\n", "- The query, while absurd, serves as an amusing example of the model's capability to provide contextually relevant answers to even the most far-fetched questions.\n", "- It's a reminder that learning can be both informative and entertaining. In this case, the model plays the role of a straight-faced comedian, addressing a wildly imaginative business proposal with a firm yet comical \"No.\"\n", "\n", "So, while you can't buy Yellowstone for your buffalo-themed spa dreams, you can certainly enjoy the park's natural beauty... just as a visitor, not as a spa owner!\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No, you cannot buy Yellowstone from the Federal Government to set up a \n", "buffalo-themed day spa. The land near the headwaters of the Yellowstone River \n", "has been reserved and withdrawn from settlement, occupancy, or sale under the \n", "laws of the United States and dedicated and set apart as a public park for the \n", "benefit and enjoyment of the people. The Secretary of the Interior has control \n", "over the park and is responsible for making and publishing rules and \n", "regulations for its management. Leases for building purposes are only granted \n", "for small parcels of ground for the accommodation of visitors, and the proceeds \n", "are used for the management of the park and the construction of roads and \n", "bridle-paths. Additionally, the wanton destruction of fish and game within the \n", "park is prohibited. Furthermore, the Act to protect trade and commerce against \n", "unlawful restraints and monopolies (approved July 2, 1890) states that any \n", "contract, combination, or conspiracy in restraint of trade or commerce in any \n", "Territory or the District of Columbia is illegal. Thus, buying land to set up a \n", "buffalo-themed day spa would likely be considered a violation of this act. \n" ] } ], "source": [ "answer3 = loaded_model.predict(\n", " [\n", " {\n", " \"query\": \"Can I buy Yellowstone from the Federal Government to set up a buffalo-themed day spa?\"\n", " }\n", " ]\n", ")\n", "\n", "print_formatted_response(answer3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Maintaining Composure: Answering Another Whimsical Query\n", "\n", "In this part of our tutorial, we explore another amusing question about leasing land in Yellowstone for a buffalo-themed day spa. Let's see how our model, with unflappable composure, responds to this quirky inquiry.\n", "\n", "#### Query and Response:\n", "\n", "- **The Query**: \"Can I lease a small parcel of land from the Federal Government for a small buffalo-themed day spa for visitors to the park?\"\n", "- **The Response**: \"No, you cannot lease a small parcel of land from the Federal Government for a small buffalo-themed day spa for visitors to the park...\"\n", "\n", "#### Insights into the Model's Response:\n", "\n", "1. **Factual and Unwavering**: \n", " - Despite the continued outlandish line of questioning, our model remains as cool as a cucumber. It patiently explains the limitations and actual purposes of leasing land in Yellowstone.\n", " - The response cites Section 2 of the act, adding legal precision to its rebuttal.\n", "\n", "2. **A Lawyer's Patience Tested?**:\n", " - Imagine if this question was posed to an actual lawyer. By now, they might be rubbing their temples! But our model is unfazed and continues to provide factual answers.\n", " - This showcases the model's ability to handle repetitive and unusual queries without losing its 'cool'.\n", "\n", "In conclusion, while our model firmly closes the door on the buffalo-themed day spa dreams, it does so with informative grace, demonstrating its ability to stay on course no matter how imaginative the queries get." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No, according to the context provided, the Secretary of the Interior may grant \n", "leases for building purposes for terms not exceeding ten years, but only for \n", "the accommodation of visitors. It does not specifically mention a \n", "buffalo-themed day spa as a possible use for the leased land. \n" ] } ], "source": [ "answer4 = loaded_model.predict(\n", " [\n", " {\n", " \"query\": \"Can I lease a small parcel of land from the Federal Government for a small \"\n", " \"buffalo-themed day spa for visitors to the park?\"\n", " }\n", " ]\n", ")\n", "\n", "print_formatted_response(answer4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another Attempt at the Buffalo-Themed Day Spa Dream\n", "\n", "Once more, we pose an imaginative question to our model, this time adding a hotel to the buffalo-themed day spa scenario. The reason for this modification is to evaluate whether the RAG application can discern a nuanced element of the intentionally vague wording of the two acts that we've loaded. Let's see the response to determine if it can resolve both bits of information!\n", "\n", "#### Quick Takeaway:\n", "\n", "- The model, sticking to its informative nature, clarifies the leasing aspects based on the Act's provisions.\n", "- It interestingly connects the query to another act related to trade and commerce, showing its ability to cross-reference related legal documents.\n", "- This response demonstrates the model's capacity to provide detailed, relevant information, even when faced with quirky and hypothetical scenarios." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No, it is not possible to lease land from the Federal Government for a \n", "commercial purpose within the designated park area. The Secretary of the \n", "Interior may grant leases for building purposes for terms not exceeding ten \n", "years, but only for small parcels of ground for the accommodation of visitors. \n", "Additionally, all proceeds from leases and other revenues must be used for the \n", "management of the park and the construction of roads and bridle-paths. \n" ] } ], "source": [ "answer5 = loaded_model.predict(\n", " [\n", " {\n", " \"query\": \"Can I lease a small parcel of land from the Federal Government for a small \"\n", " \"buffalo-themed day spa and hotel for visitors to stay in and relax at while visiting the park?\"\n", " }\n", " ]\n", ")\n", "print_formatted_response(answer5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Well, what can I do then?\n", "\n", "#### Takeaway:\n", "\n", "- The response reassuringly confirms that one can enjoy Yellowstone's natural beauty, provided park rules and regulations are respected.\n", "- This illustrates the model's ability to provide straightforward, practical advice in response to simple, real-world questions. It clearly has the context of the original act and is able to infer what is permissible (enjoying the reserved land).\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Yes, according to the context, the park was set apart as a public park or \n", "pleasuring-ground for the benefit and enjoyment of the people. However, the \n", "park is under the exclusive control of the Secretary of the Interior who has \n", "the authority to make and publish rules and regulations for the care and \n", "management of the park. Therefore, it is important to follow any rules or \n", "regulations set by the Secretary to ensure the preservation and protection of \n", "the park for future enjoyment. \n" ] } ], "source": [ "answer6 = loaded_model.predict(\n", " [{\"query\": \"Can I just go to the park and peacefully enjoy the natural splendor?\"}]\n", ")\n", "\n", "print_formatted_response(answer6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Evaluating the RetrievalQA Model's Legal Context Integration\n", "\n", "This section of the tutorial showcases the RetrievalQA model's sophisticated ability to integrate and interpret context from multiple legal documents. The model is challenged with a query that requires synthesizing information from distinct sources. This test is particularly interesting for its demonstration of the model's proficiency in:\n", "\n", "1. **Contextual Integration**: The model adeptly pulls in relevant legal details from different documents, illustrating its capacity to navigate through multiple sources of information.\n", "\n", "2. **Legal Interpretation**: It interprets the legal implications related to the query, highlighting the model's understanding of complex legal language and concepts.\n", "\n", "3. **Cross-Document Inference**: The model’s ability to discern and extract the most pertinent information from a pool of multiple documents is a testament to its advanced capabilities in multi-document scenarios.\n", "\n", "This evaluation provides a clear example of the model's potential in handling intricate queries that necessitate a deep and nuanced understanding of diverse data sources.\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No, according to the context, any attempt to monopolize trade or commerce in \n", "the Yellowstone area or in any territory of the United States is considered \n", "illegal. Additionally, the park is under the exclusive control of the Secretary \n", "of the Interior, who may grant leases for building purposes but is expected to \n", "prevent the destruction of natural resources and the exploitation of fish and \n", "game for profit. Therefore, it would not be possible to start a buffalo themed \n", "day spa outside of Yellowstone National Park and stifle competition without \n", "violating the law and risking consequences from the Secretary of the Interior. \n" ] } ], "source": [ "answer7 = loaded_model.predict(\n", " [\n", " {\n", " \"query\": \"Can I start a buffalo themed day spa outside of Yellowstone National Park and stifle any competition?\"\n", " }\n", " ]\n", ")\n", "\n", "print_formatted_response(answer7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Cleanup: Removing Temporary Directory**:\n", " - After we're done asking our Retriever Model a bunch of silly questions, the temporary directory created earlier is cleaned up using `shutil.rmtree`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# Clean up our temporary directory that we created with our FAISS instance\n", "shutil.rmtree(temporary_directory)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conclusion: Mastering RAG with MLflow\n", "\n", "In this tutorial, we explored the depths of Retrieval Augmented Generation (RAG) applications, enabled and simplified by MLflow. Here's a recap of our journey and the key takeaways:\n", "\n", "1. **Ease of Developing RAG Applications**: We learned how MLflow facilitates the development of RAG applications by streamlining the process of integrating large language models with external data sources. Our hands-on experience demonstrated the process of fetching, processing, and embedding documents into a FAISS database, all managed within the MLflow framework.\n", "\n", "2. **Advanced Query Handling**: Through our tests, we observed how the MLflow-wrapped LangChain RAG model adeptly handled complex queries, drawing from multiple documents to provide context-rich and accurate responses. This showcased the potential of RAG models in processing and understanding queries that require multi-source data integration.\n", "\n", "3. **MLflow's Role in Deployment and Management**: MLflow's robustness was evident in how it simplifies the logging, deployment, and management of complex models. Its ability to track experiments, manage artifacts, and ease the deployment process highlights its indispensability in the machine learning lifecycle.\n", "\n", "4. **Practical Application Insights**: Our queries, while humorous at times, served to illustrate the practical capabilities of RAG models. From legal interpretations to hypothetical scenarios, we saw how these models could be applied in real-world situations, providing insightful and contextually relevant responses.\n", "\n", "5. **The Future of RAG and MLflow**: This tutorial underscored the potential of combining RAG with MLflow's streamlined management capabilities. As RAG continues to evolve, MLflow stands out as a crucial tool for harnessing its power, making advanced NLP applications more accessible and efficient.\n", "\n", "In summary, our journey through this tutorial has not only equipped us with the knowledge to develop and deploy RAG applications effectively but also opened our eyes to the vast possibilities that lie ahead in the realm of advanced NLP, all made more attainable through MLflow.\n", "\n", "### What's next?\n", "\n", "If you'd like to learn more about how MLflow and LangChain integrate, see the other [advanced tutorials for MLflow's LangChain flavor](https://www.mlflow.org/docs/latest/llms/langchain/index.html#advanced-tutorials)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.13" } }, "nbformat": 4, "nbformat_minor": 4 }