{ "cells": [ { "cell_type": "markdown", "id": "e06c733a-1124-44b5-a634-37d0887fdfe6", "metadata": {}, "source": [ "# The Price is Right\n", "\n", "## Week 8 Order of Play\n", "\n", "Day 1: Modal.com and SpecialistAgent \n", "Day 2: RAG, FrontierAgent, Ensemble Agent \n", "Day 3: ScannerAgent, MessengerAgent \n", "Day 4: AutonomousPlannerAgent and DealAgentFramework \n", "Day 5: The Price Is Right Finale\n", "\n", "## RAG (Retrieval Augmented Generation) based on a dataset of 800,000 scraped Amazon products\n", "\n", "#### For our 2nd agent, we will be asking OpenAI to estimate the price of one of our deals - and we will give it a hand.\n", "\n", "We discovered that LLMs are really good at this, out of the box.\n", "\n", "And we discovered that we can beat a frontier LLM by fine-tuning an open-source LLM.\n", "\n", "Now we are going to try **inference time** techniques instead of training -- by using RAG!" ] }, { "cell_type": "code", "execution_count": null, "id": "2db71ba5-55a8-48b7-97d5-9db8dc872837", "metadata": {}, "outputs": [], "source": [ "# imports\n", "\n", "import os\n", "import logging\n", "from dotenv import load_dotenv\n", "from huggingface_hub import login\n", "import numpy as np\n", "import re\n", "from sentence_transformers import SentenceTransformer\n", "import chromadb\n", "from sklearn.manifold import TSNE\n", "import plotly.graph_objects as go\n", "from litellm import completion\n", "from tqdm.notebook import tqdm\n", "from agents.evaluator import evaluate\n", "from agents.items import Item" ] }, { "cell_type": "code", "execution_count": null, "id": "b044d040-e467-4463-a3a5-119939ca8199", "metadata": {}, "outputs": [], "source": [ "# environment\n", "\n", "load_dotenv(override=True)\n", "DB = \"products_vectorstore\"" ] }, { "cell_type": "code", "execution_count": null, "id": "5c1cb7f1-41f7-4df8-95fa-f3143b4ce312", "metadata": {}, "outputs": [], "source": [ "# Log in to HuggingFace\n", "# If you don't have a HuggingFace account, you can set one up for free at www.huggingface.co\n", "# And then add the HF_TOKEN to your .env file as explained in the project README\n", "\n", "hf_token = os.environ['HF_TOKEN']\n", "login(token=hf_token, add_to_git_credential=False)" ] }, { "cell_type": "code", "execution_count": null, "id": "15ec0e43", "metadata": {}, "outputs": [], "source": [ "LITE_MODE = True" ] }, { "cell_type": "code", "execution_count": null, "id": "a5b8f790", "metadata": {}, "outputs": [], "source": [ "username = \"ed-donner\"\n", "dataset = f\"{username}/items_lite\" if LITE_MODE else f\"{username}/items_full\"\n", "\n", "train, val, test = Item.from_hub(dataset)\n", "\n", "print(f\"Loaded {len(train):,} training items, {len(val):,} validation items, {len(test):,} test items\")" ] }, { "cell_type": "markdown", "id": "cf43f181-8b51-43c8-9763-599220cf6e66", "metadata": {}, "source": [ "# Now create a Chroma Datastore\n", "\n", "Now we will use the free, open-source Vector database Chroma. \n", "We will create a Chroma datastore with 400,000 products from our training dataset." ] }, { "cell_type": "code", "execution_count": null, "id": "6ba77914-ea9a-4b92-9280-863ee07ca8d2", "metadata": {}, "outputs": [], "source": [ "client = chromadb.PersistentClient(path=DB)" ] }, { "cell_type": "markdown", "id": "1744c683-847a-4151-b6e0-56066f1fe4b0", "metadata": {}, "source": [ "# Introducing the SentenceTransformer Encoding LLM\n", "\n", "The all-MiniLM is a very useful model from HuggingFace that maps sentences & paragraphs to 384 dimensional vectors and is ideal for tasks like semantic search.\n", "\n", "https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2\n", "\n", "It can run pretty quickly locally.\n", "\n", "As an alternative, OpenAI provides a closed-source Embeddings model. Benefits compared to OpenAI embeddings:\n", "1. It's free and fast!\n", "3. We can run it locally, so the data never leaves our box - might be useful if you're building a personal RAG" ] }, { "cell_type": "code", "execution_count": null, "id": "af2545a0-e160-41db-8914-f77b1c7eff26", "metadata": {}, "outputs": [], "source": [ "encoder = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')" ] }, { "cell_type": "code", "execution_count": null, "id": "32abb023-64b5-40a4-bfc1-e22c3ec31221", "metadata": {}, "outputs": [], "source": [ "# Pass in a list of texts, get back a numpy array of vectors\n", "\n", "vector = encoder.encode([\"A proficient AI engineer who has almost reached the finale of AI Engineering Core Track!\"])[0]\n", "print(vector.shape)\n", "vector" ] }, { "cell_type": "markdown", "id": "fa837b98-17ff-486e-ac30-a4b4f794af7b", "metadata": {}, "source": [ "## With that background, let's populate our Chroma database\n", "\n", "### By calculating vectors for 800,000 scraped products\n", "\n", "This takes 30 minutes on my machine on my GPU - it might take longer for you - feel free to use the Lite dataset!" ] }, { "cell_type": "code", "execution_count": null, "id": "b1c8f101-9c81-462d-be2e-9b479831857f", "metadata": {}, "outputs": [], "source": [ "# Check if the collection exists; if not, create it\n", "\n", "collection_name = \"products\"\n", "existing_collection_names = [collection.name for collection in client.list_collections()]\n", "\n", "if collection_name not in existing_collection_names:\n", " collection = client.create_collection(collection_name)\n", " for i in tqdm(range(0, len(train), 1000)):\n", " documents = [item.summary for item in train[i: i+1000]]\n", " vectors = encoder.encode(documents).astype(float).tolist()\n", " metadatas = [{\"category\": item.category, \"price\": item.price} for item in train[i: i+1000]]\n", " ids = [f\"doc_{j}\" for j in range(i, i+1000)]\n", " ids = ids[:len(documents)]\n", " collection.add(ids=ids, documents=documents, embeddings=vectors, metadatas=metadatas)\n", "\n", "collection = client.get_or_create_collection(collection_name)" ] }, { "cell_type": "markdown", "id": "c65375e1-a8eb-4203-b8f1-dfff69a693cc", "metadata": {}, "source": [ "# Let's visualize the vectorized data" ] }, { "cell_type": "code", "execution_count": null, "id": "202c3b08-dc89-4995-a25c-041417ec9b9b", "metadata": {}, "outputs": [], "source": [ "# It is very fun turning this up to 800_000 and seeing the full dataset visualized,\n", "# but it almost crashes my box every time so do that at your own risk!! 10_000 is safe!\n", "\n", "MAXIMUM_DATAPOINTS = 10_000" ] }, { "cell_type": "code", "execution_count": null, "id": "c653565a-6405-4c5a-b925-7e14a17bf2da", "metadata": {}, "outputs": [], "source": [ "CATEGORIES = ['Appliances', 'Automotive', 'Cell_Phones_and_Accessories', 'Electronics','Musical_Instruments', 'Office_Products', 'Tools_and_Home_Improvement', 'Toys_and_Games']\n", "COLORS = ['cyan', 'blue', 'brown', 'orange', 'yellow', 'green' , 'purple', 'red']" ] }, { "cell_type": "code", "execution_count": null, "id": "6a754334-69ef-4b4f-92c7-d7da89457f7d", "metadata": {}, "outputs": [], "source": [ "# Prework\n", "result = collection.get(include=['embeddings', 'documents', 'metadatas'], limit=MAXIMUM_DATAPOINTS)\n", "vectors = np.array(result['embeddings'])\n", "documents = result['documents']\n", "categories = [metadata['category'] for metadata in result['metadatas']]\n", "colors = [COLORS[CATEGORIES.index(c)] for c in categories]" ] }, { "cell_type": "code", "execution_count": null, "id": "9a30b5e9-7dd9-45c1-a9a7-74cb22cdef2f", "metadata": {}, "outputs": [], "source": [ "# Let's try a 2D chart\n", "# TSNE stands for t-distributed Stochastic Neighbor Embedding - it's a common technique for reducing dimensionality of data\n", "\n", "tsne = TSNE(n_components=2, random_state=42)\n", "reduced_vectors = tsne.fit_transform(vectors)" ] }, { "cell_type": "code", "execution_count": null, "id": "d7a97fc5-9f44-4f1d-a253-8c8f0bcd9ec9", "metadata": {}, "outputs": [], "source": [ "# Create the 2D scatter plot\n", "fig = go.Figure(data=[go.Scatter(\n", " x=reduced_vectors[:, 0],\n", " y=reduced_vectors[:, 1],\n", " mode='markers',\n", " marker=dict(size=4, color=colors, opacity=0.7),\n", " text=[f\"Category: {c}
Text: {d[:50]}...\" for c, d in zip(categories, documents)],\n", " hoverinfo='text'\n", ")])\n", "\n", "fig.update_layout(\n", " title='2D Chroma Vectorstore Visualization',\n", " scene=dict(xaxis_title='x', yaxis_title='y'),\n", " width=1200,\n", " height=800,\n", " margin=dict(r=20, b=10, l=10, t=40)\n", ")\n", "\n", "fig.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "0bdb35e3-bd0d-4569-872b-34bea8316675", "metadata": {}, "outputs": [], "source": [ "# Let's try 3D!\n", "\n", "tsne = TSNE(n_components=3, random_state=42)\n", "reduced_vectors = tsne.fit_transform(vectors)" ] }, { "cell_type": "code", "execution_count": null, "id": "4361c151-9f1b-4652-9204-695baf3860d5", "metadata": {}, "outputs": [], "source": [ "# Create the 3D scatter plot\n", "fig = go.Figure(data=[go.Scatter3d(\n", " x=reduced_vectors[:, 0],\n", " y=reduced_vectors[:, 1],\n", " z=reduced_vectors[:, 2],\n", " mode='markers',\n", " marker=dict(size=2, color=colors, opacity=0.7),\n", " text=[f\"Category: {c}
Text: {d[:50]}...\" for c, d in zip(categories, documents)],\n", " hoverinfo='text'\n", ")])\n", "\n", "fig.update_layout(\n", " title='3D Chroma Vector Store Visualization',\n", " scene=dict(xaxis_title='x', yaxis_title='y', zaxis_title='z'),\n", " width=1200,\n", " height=800,\n", " margin=dict(r=20, b=10, l=10, t=40)\n", ")\n", "\n", "fig.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "caa8e99f", "metadata": {}, "outputs": [], "source": [ "test[0]" ] }, { "cell_type": "code", "execution_count": null, "id": "4b72f1b9", "metadata": {}, "outputs": [], "source": [ "def vector(item):\n", " return encoder.encode(item.summary)" ] }, { "cell_type": "code", "execution_count": null, "id": "b5dc3419", "metadata": {}, "outputs": [], "source": [ "def find_similars(item):\n", " vec = vector(item)\n", " results = collection.query(query_embeddings=vec.astype(float).tolist(), n_results=5)\n", " documents = results['documents'][0][:]\n", " prices = [m['price'] for m in results['metadatas'][0][:]]\n", " return documents, prices" ] }, { "cell_type": "code", "execution_count": null, "id": "98de579a", "metadata": {}, "outputs": [], "source": [ "find_similars(test[0])" ] }, { "cell_type": "code", "execution_count": null, "id": "c74a25b1-f93c-4a75-9999-09e262f9abc9", "metadata": {}, "outputs": [], "source": [ "# We need to give some context to GPT-5.1 by selecting 5 products with similar descriptions\n", "\n", "def make_context(similars, prices):\n", " message = \"For context, here are some other items that might be similar to the item you need to estimate.\\n\\n\"\n", " for similar, price in zip(similars, prices):\n", " message += f\"Potentially related product:\\n{similar}\\nPrice is ${price:.2f}\\n\\n\"\n", " return message" ] }, { "cell_type": "code", "execution_count": null, "id": "8b10dd77", "metadata": {}, "outputs": [], "source": [ "documents, prices = find_similars(test[0])\n", "print(make_context(documents, prices))" ] }, { "cell_type": "code", "execution_count": null, "id": "05b57490-060d-47ff-9cf0-2b61b455bcd8", "metadata": {}, "outputs": [], "source": [ "def messages_for(item, similars, prices):\n", " message = f\"Estimate the price of this product. Respond with the price, no explanation\\n\\n{item.summary}\\n\\n\"\n", " message += make_context(similars, prices)\n", " return [{\"role\": \"user\", \"content\": message}]" ] }, { "cell_type": "code", "execution_count": null, "id": "a81ff141", "metadata": {}, "outputs": [], "source": [ "documents, prices = find_similars(test[0])\n", "print(messages_for(test[0], documents, prices)[0]['content'])" ] }, { "cell_type": "code", "execution_count": null, "id": "721ab130-b6d8-4356-9704-687c9bc2636f", "metadata": {}, "outputs": [], "source": [ "# The function for gpt-5-mini\n", "\n", "def gpt_5__1_rag(item):\n", " documents, prices = find_similars(item)\n", " response = completion(model=\"gpt-5.1\", messages=messages_for(item, documents, prices), reasoning_effort=\"none\", seed=42)\n", " return response.choices[0].message.content" ] }, { "cell_type": "code", "execution_count": null, "id": "613a6278-2da5-4bf8-b733-2947736feb63", "metadata": {}, "outputs": [], "source": [ "# How much does our favorite distortion pedal cost?\n", "\n", "test[0].price" ] }, { "cell_type": "code", "execution_count": null, "id": "d30586f2-6b84-4750-acf5-a113ac9ccb48", "metadata": {}, "outputs": [], "source": [ "# Let's do this!!\n", "\n", "gpt_5__1_rag(test[0])" ] }, { "cell_type": "code", "execution_count": null, "id": "846067a3", "metadata": {}, "outputs": [], "source": [ "evaluate(gpt_5__1_rag, test)" ] }, { "cell_type": "code", "execution_count": null, "id": "cb035c28", "metadata": {}, "outputs": [], "source": [ "import modal\n", "Pricer = modal.Cls.from_name(\"pricer-service\", \"Pricer\")\n", "pricer = Pricer()" ] }, { "cell_type": "code", "execution_count": null, "id": "715300c0", "metadata": {}, "outputs": [], "source": [ "def specialist(item):\n", " return pricer.price.remote(item.summary)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "3acd1a24", "metadata": {}, "outputs": [], "source": [ "def get_price(reply):\n", " reply = reply.replace(\"$\", \"\").replace(\",\", \"\")\n", " match = re.search(r\"[-+]?\\d*\\.\\d+|\\d+\", reply)\n", " return float(match.group()) if match else 0" ] }, { "cell_type": "markdown", "id": "944a9b15", "metadata": {}, "source": [ "## Download the Neural Network weights from Week 6 into this directory\n", "\n", "The file `deep_neural_network.pth` here:\n", "\n", "https://drive.google.com/drive/folders/1uq5C9edPIZ1973dArZiEO-VE13F7m8MK?usp=drive_link" ] }, { "cell_type": "code", "execution_count": null, "id": "b45acd2c", "metadata": {}, "outputs": [], "source": [ "\n", "from agents.deep_neural_network import DeepNeuralNetworkInference\n", "\n", "runner = DeepNeuralNetworkInference()\n", "runner.setup()\n", "runner.load(\"deep_neural_network.pth\")\n", "\n", "def deep_neural_network(item):\n", " return runner.inference(item.summary)" ] }, { "cell_type": "code", "execution_count": null, "id": "6327b3bd", "metadata": {}, "outputs": [], "source": [ "def ensemble(item):\n", " price1 = get_price(gpt_5__1_rag(item))\n", " price2 = specialist(item)\n", " price3 = deep_neural_network(item)\n", " return price1 * 0.8 + price2 * 0.1 + price3 * 0.1\n" ] }, { "cell_type": "code", "execution_count": null, "id": "9624bce1", "metadata": {}, "outputs": [], "source": [ "evaluate(ensemble, test)" ] }, { "cell_type": "code", "execution_count": null, "id": "b2684714-e6d0-47d6-bf31-87fe349fc15c", "metadata": {}, "outputs": [], "source": [ "root = logging.getLogger()\n", "root.setLevel(logging.INFO)" ] }, { "cell_type": "code", "execution_count": null, "id": "c293f0a8-7097-4744-a2e9-7da5268406a8", "metadata": {}, "outputs": [], "source": [ "from agents.frontier_agent import FrontierAgent\n", "\n", "agent = FrontierAgent(collection)\n", "agent.price(\"Quadcast HyperX condenser mic, connects via usb-c to your computer for crystal clear audio\")" ] }, { "cell_type": "code", "execution_count": null, "id": "8043d5e4-ec42-4fa0-9344-28a856d4f6d2", "metadata": {}, "outputs": [], "source": [ "agent.price(\"Shure MV7+ professional podcaster microphone with usb-c and XLR outputs\")" ] }, { "cell_type": "code", "execution_count": null, "id": "39799a17-20ad-45ef-8c04-4712f189c9d7", "metadata": {}, "outputs": [], "source": [ "from agents.neural_network_agent import NeuralNetworkAgent\n", "agent = NeuralNetworkAgent()\n" ] }, { "cell_type": "code", "execution_count": null, "id": "029d5dd2", "metadata": {}, "outputs": [], "source": [ "agent.price(\"Shure MV7+ professional podcaster microphone with usb-c and XLR outputs\")" ] }, { "cell_type": "code", "execution_count": null, "id": "de6ace7d", "metadata": {}, "outputs": [], "source": [ "from agents.ensemble_agent import EnsembleAgent\n", "agent = EnsembleAgent(collection)" ] }, { "cell_type": "code", "execution_count": null, "id": "72533425", "metadata": {}, "outputs": [], "source": [ "agent.price(\"Shure MV7+ professional podcaster microphone with usb-c and XLR outputs\")" ] }, { "cell_type": "code", "execution_count": null, "id": "c22c03a4", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": ".venv", "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.12.12" } }, "nbformat": 4, "nbformat_minor": 5 }