├── streamlit_frontend ├── __init__ ├── app.py └── backend.py ├── image.png ├── img ├── image.png ├── image-1.png ├── image-2.png ├── image-3.png └── stores_mall_event.png ├── stores.csv ├── LICENSE.md ├── .gitignore ├── requirements.txt ├── README.md └── autonomous-mall-assistant.ipynb /streamlit_frontend/__init__: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/image.png -------------------------------------------------------------------------------- /img/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/img/image.png -------------------------------------------------------------------------------- /img/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/img/image-1.png -------------------------------------------------------------------------------- /img/image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/img/image-2.png -------------------------------------------------------------------------------- /img/image-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/img/image-3.png -------------------------------------------------------------------------------- /img/stores_mall_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepanogil/autonomous-mall-assistant/HEAD/img/stores_mall_event.png -------------------------------------------------------------------------------- /stores.csv: -------------------------------------------------------------------------------- 1 | store_name,retail_category,subcategory,location,running_promos 2 | ace hardware,home,,3rd floor,10% off on selected items. 3 | adidas,sports,,2nd floor,Buy 1 get 1 on selected shoes. 4 | bench,clothing,,ground floor,End of season sale - up to 50% off! 5 | daiso,home,,4th floor,New arrivals starting from Php100! 6 | h & m,clothing,,ground floor,20% off on selected denim! 7 | fat fook,food,taiwanese,ground floor,Free dimsum for meal worth Php500! 8 | jollibee,food,fast food,ground floor,Free drink with any combo meal! 9 | mcdonalds,food,fast food,ground floor,Free apple pie for orders over Php500! 10 | nike,sports,,2nd floor,Clearance sale - up to 40% off! 11 | penshoppe,clothing,,ground floor,Buy 2 shirts get 1 free. 12 | toys r us,kids,,5th floor,15% off on board games. 13 | eras tour,mall event,,,The Taylor Swift Eras tour is showing in our cinemas. Buy your tickets now! 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Stephen Bonifacio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *.pyo 5 | *.pyd 6 | .Python 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | .env 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Jupyter Notebook 58 | .ipynb_checkpoints 59 | 60 | # IDEs and editors 61 | .idea/ 62 | .vscode/ 63 | *.swp 64 | *.swo 65 | *.swn 66 | *.suo 67 | *.bak 68 | 69 | # OS-specific 70 | .DS_Store 71 | Thumbs.db 72 | 73 | # drafts 74 | drafts/ 75 | -------------------------------------------------------------------------------- /streamlit_frontend/app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import random 3 | from backend import chat 4 | from streamlit_chat import message 5 | #from st_chat_message import message 6 | 7 | 8 | # Streamlit app 9 | st.header("Mall Assistant Chatbot") 10 | st.markdown("Ask questions about store locations, promotions, and more.") 11 | 12 | if "past" not in st.session_state: 13 | st.session_state["past"] = [] 14 | if "generated" not in st.session_state: 15 | st.session_state["generated"] = [] 16 | 17 | if "input_message_key" not in st.session_state: 18 | st.session_state["input_message_key"] = str(random.random()) 19 | 20 | chat_container = st.container() 21 | 22 | user_input = st.text_input("Type your message and press Enter to send.", key=st.session_state["input_message_key"]) 23 | 24 | if st.button("Send"): 25 | response = chat(user_input) 26 | 27 | st.session_state["past"].append(user_input) 28 | st.session_state["generated"].append(response) 29 | 30 | st.session_state["input_message_key"] = str(random.random()) 31 | 32 | st.experimental_rerun() 33 | 34 | if st.session_state["generated"]: 35 | with chat_container: 36 | for i in range(len(st.session_state["generated"])): 37 | message(st.session_state["past"][i], is_user=True, key=str(i) + "_user") 38 | message(st.session_state["generated"][i], key=str(i)) 39 | 40 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.6 2 | aiosignal==1.3.1 3 | altair==5.1.2 4 | annotated-types==0.6.0 5 | anyio==3.7.1 6 | asttokens==2.4.1 7 | async-timeout==4.0.3 8 | attrs==23.1.0 9 | backcall==0.2.0 10 | blinker==1.6.3 11 | cachetools==5.3.2 12 | certifi==2023.7.22 13 | charset-normalizer==3.3.1 14 | click==8.1.7 15 | comm==0.1.4 16 | dataclasses-json==0.6.1 17 | debugpy==1.8.0 18 | decorator==5.1.1 19 | exceptiongroup==1.1.3 20 | executing==2.0.0 21 | frozenlist==1.4.0 22 | gitdb==4.0.11 23 | GitPython==3.1.40 24 | greenlet==3.0.1 25 | idna==3.4 26 | importlib-metadata==6.8.0 27 | ipykernel==6.26.0 28 | ipython==8.16.1 29 | jedi==0.19.1 30 | Jinja2==3.1.2 31 | jsonpatch==1.33 32 | jsonpointer==2.4 33 | jsonschema==4.19.1 34 | jsonschema-specifications==2023.7.1 35 | jupyter_client==8.5.0 36 | jupyter_core==5.4.0 37 | langchain==0.0.325 38 | langsmith==0.0.53 39 | markdown-it-py==3.0.0 40 | MarkupSafe==2.1.3 41 | marshmallow==3.20.1 42 | matplotlib-inline==0.1.6 43 | mdurl==0.1.2 44 | multidict==6.0.4 45 | mypy-extensions==1.0.0 46 | nest-asyncio==1.5.8 47 | numpy==1.26.1 48 | openai==0.28.1 49 | packaging==23.2 50 | pandas==2.1.2 51 | parso==0.8.3 52 | pexpect==4.8.0 53 | pickleshare==0.7.5 54 | Pillow==9.5.0 55 | platformdirs==3.11.0 56 | prompt-toolkit==3.0.39 57 | protobuf==4.24.4 58 | psutil==5.9.6 59 | ptyprocess==0.7.0 60 | pure-eval==0.2.2 61 | pyarrow==13.0.0 62 | pydantic==2.4.2 63 | pydantic_core==2.10.1 64 | pydeck==0.8.1b0 65 | Pygments==2.16.1 66 | Pympler==1.0.1 67 | python-dateutil==2.8.2 68 | python-dotenv==1.0.0 69 | pytz==2023.3.post1 70 | pytz-deprecation-shim==0.1.0.post0 71 | PyYAML==6.0.1 72 | pyzmq==25.1.1 73 | referencing==0.30.2 74 | requests==2.31.0 75 | rich==13.6.0 76 | rpds-py==0.10.6 77 | six==1.16.0 78 | smmap==5.0.1 79 | sniffio==1.3.0 80 | SQLAlchemy==2.0.22 81 | st-chat-message==0.3.8 82 | stack-data==0.6.3 83 | streamlit==1.24.0 84 | streamlit-chat==0.1.1 85 | tenacity==8.2.3 86 | toml==0.10.2 87 | toolz==0.12.0 88 | tornado==6.3.3 89 | tqdm==4.66.1 90 | traitlets==5.12.0 91 | typing-inspect==0.9.0 92 | typing_extensions==4.8.0 93 | tzdata==2023.3 94 | tzlocal==4.3.1 95 | urllib3==2.0.7 96 | validators==0.22.0 97 | watchdog==3.0.0 98 | wcwidth==0.2.8 99 | yarl==1.9.2 100 | zipp==3.17.0 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autonomous Mall Assistant 2 | 3 | 4 | ## Description 5 | 6 | `autonomous-mall-assistant` is an AI-powered mall assistant designed to help shoppers easily locate stores within a shopping mall. The system utilizes a Large Language Model (LLM) to understand user queries and provide precise information or alternative suggestions. 7 | 8 | 9 | ## How it Works 10 | 11 | 12 | 1. User interacts with the chat interface to ask about a specific store or retail category. 13 | 2. The LLM identifies the `store name` and `retail category` from the user's query. 14 | 3. The system searches the database (pandas df) for the store information. 15 | - If found, returns relevant details to the LLM as context data. 16 | - If not found, suggests alternative stores from the same retail category and return it to the LLM to propose to the users as alternatives. 17 | 18 | 19 | ## Demo 20 | Stores Database - stored as a pandas dataframe but you should be able to use any other DBs (e.g. SQL/NoSQL) as long as you can wrap it into a function. 21 | 22 |  23 | 24 | ### Q and A with the Assistant 25 | Note: more examples in autonomous-mall-assistant.ipynb 26 | 27 | 28 | ](img/image-2.png) 29 | 30 | Mentions mall wide events, if any. 31 | 32 |  33 | 34 | ## Roadmap 35 | 36 | - ~~Add mall wide events/promotions to the output~~ - Completed on [2023-11-05] 37 | - Add mall general info like opening hours; exceptions to the scheds because of special holidays, etc. 38 | - Define handling of multiple stores/categories in user input. 39 | - Incorporate some guardrail mechanism e.g., nemo guardrails. 40 | - (longish-term) Breakdown the single `search_store` function into their respective retail category functions to handle targeted queries (e.g., sports - "Does Nike sell football boots?", food - "Which restaurants offer halal food?") 41 | - (longish-term) Utilize a better database that can scale better (e.g., managed DB offerings from Azure, AWS, etc.) 42 | - (longish-term) Add GPT-4 tool/function for complex queries requiring reasoning ability. 43 | 44 | 45 | ## Tech Stack 46 | 47 | 48 | - GPT-4 49 | - LangChain 50 | - pandas 51 | - streamlit 52 | - streamlit-chat 53 | 54 | 55 | ## Installation 56 | 57 | 58 | ```bash 59 | 1. clone this repo 60 | 2. pip install requirements.txt 61 | 3.1 run autonomous-mall-assistant.ipynb OR 62 | 3.2 cd to streamlit_frontend and run `streamlit run app.py` in terminal 63 | ``` 64 | 65 | ## Contributing 66 | 67 | ...is welcome! 🤗 68 | 69 | ## Developer Notes 70 | 71 | - **[2023-11-05]**: Implemented `Chat History` feature to maintain conversation context. Since LLM API interactions are stateless, this enhancement captures and sends the entire history of user-AI exchanges alongside the current user query, enabling the LLM to generate contextually relevant responses. 72 | 73 | ## Author 74 | 75 | 76 | #### Stephen Bonifacio 77 | 78 | Feel free to connect with me on: 79 | 80 | Linkedin: https://www.linkedin.com/in/stephenbonifacio/ 81 | Twitter: https://twitter.com/Stepanogil 82 | 83 | Did you find this useful? If you'd like to show some love, I won't say no to a cup of coffee! 🤗 84 | 85 | [](https://ko-fi.com/Q5Q6QPABZ) 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /streamlit_frontend/backend.py: -------------------------------------------------------------------------------- 1 | # NOTE: I'm building using the jupyter notebook primarily (autonommous-mall-assistant.ipynb). 2 | # This file is just a copy of that and is meant to be used with streamlit. This file might not be updated as often. 3 | # It has some modifications to account for streamlit's rendering capabilities: 4 | # - LLM not asked to use html or bullet points. 5 | # - Few shot prompt removed. 6 | # - Removed Ipython.display HTML in chat output 7 | 8 | import os 9 | import openai 10 | import pandas as pd 11 | import json 12 | from typing import List 13 | from pydantic import BaseModel, Field 14 | from langchain.utils.openai_functions import convert_pydantic_to_openai_function 15 | from typing import Optional 16 | from dotenv import load_dotenv 17 | 18 | load_dotenv() 19 | 20 | openai.api_key = os.getenv("OPENAI_API_KEY") 21 | 22 | # load data frame 23 | df = pd.read_csv("/home/stepanogil/ChatBotDev/autonomous-mall-assistant/stores.csv") 24 | 25 | # function 26 | def search_store(store_name: str, retail_category: str) -> str: 27 | """ 28 | Searches for a store by its name and retail category in the dataframe and returns relevant information. 29 | 30 | The function first tries to find a store with both the provided name and retail category. 31 | If the store is not found but the category exists, it suggests alternative stores within that category. 32 | If neither the store nor the category are found, it returns "No results found". 33 | 34 | Args: 35 | store_name (str): The name of the store to search for. 36 | retail_category (str): The category of retail to which the store belongs. 37 | 38 | Returns: 39 | str: A string containing either the found store's information or alternative suggestions. 40 | 41 | Examples: 42 | >>> search_store("Adidas", "Sports") 43 | "Store found: Name - adidas Location - ground floor, Promos - Buy 1 get 1 on selected shoes" 44 | 45 | >>> search_store("Unknown Store", "Kids") 46 | "We don't have this store, but here are alternatives: Name - toy r us, Location - 5th floor, Promos - 15% off on board games" 47 | 48 | >>> search_store("Unknown Store", "Unknown Category") 49 | "No results found" 50 | """ 51 | # Convert arguments to lowercase 52 | store_name = store_name.lower() 53 | retail_category = retail_category.lower() 54 | 55 | search_result = "" 56 | 57 | filtered_stores = df[(df['store_name'] == store_name) & (df['retail_category'] == retail_category)] 58 | 59 | # if store name and retail category are found 60 | if not filtered_stores.empty: 61 | store_info = filtered_stores[['store_name','location', 'running_promos']].to_dict(orient='records')[0] 62 | search_result = f"Store found: Name - {store_info['store_name']}, Location - {store_info['location']}, Promos - {store_info['running_promos']}" 63 | # if not found, find alternatives 64 | else: 65 | # find stores with same category as retail_category arg 66 | category_filtered_stores = df[df['retail_category'] == retail_category] 67 | if not category_filtered_stores.empty: 68 | alternatives = category_filtered_stores[['store_name', 'location', 'running_promos']].to_dict(orient='records') 69 | alternative_stores_str = ", ".join([f"{store['store_name']}: Location - {store['location']}, Promos - {store['running_promos']}" for store in alternatives]) 70 | search_result = f"We don't have this store, but here are alternatives: {alternative_stores_str}" 71 | else: 72 | search_result = "No results found" 73 | 74 | return search_result 75 | 76 | # function schema 77 | class RetrieveStoreAndCategory(BaseModel): 78 | """Retrieves information about the store name and retail category from the user content.""" 79 | store_name: str = Field(description="name of the store") 80 | retail_category: str = Field(description="the category of the store. can be only ONE of the following: home, sports, clothing, food, kids, other") 81 | 82 | 83 | def chat(query: str): 84 | """ 85 | Chat with the autonomous mall assistant to help users find stores or suggest alternatives. 86 | 87 | It first attempts to determine the store and category the user is interested in and then generates an 88 | appropriate chat response. 89 | 90 | Args: 91 | query (str): The user's query asking for assistance, typically asking for the location of a particular store. 92 | 93 | Returns: 94 | None: Outputs the assistant's formatted response to the front-end for display. 95 | 96 | Examples: 97 | >>> chat("Is there a Zara store here?") 98 | "CHATBOT: I'm sorry but we currently do not have a Zara store at GO Shopping Mall. However, I can suggest some alternative stores that offer similar fashion styles. ..." 99 | 100 | """ 101 | # system messages 102 | system_message_routing = """\ 103 | Don't make assumptions about what values to plug into functions. 104 | Ask for clarification if the user content is ambiguous. 105 | """ 106 | 107 | system_message_assistant = """\ 108 | You are a mall assistant for GO Shopping Mall. You are tasked to help customers find stores in the mall. 109 | If the store is not available, you should recommend alternatives. Use emojis to make your responses more friendly. 110 | """ 111 | 112 | messages = [{"role": "system", "content": system_message_routing}, 113 | {"role": "user", "content": query}] 114 | 115 | # routing 116 | response_routing = openai.ChatCompletion.create( 117 | model="gpt-4-0613", 118 | messages=messages, 119 | functions=[convert_pydantic_to_openai_function(RetrieveStoreAndCategory)], 120 | function_call={'name': 'RetrieveStoreAndCategory'}, 121 | temperature=0, 122 | max_tokens=256 123 | ) 124 | args = json.loads(response_routing["choices"][0]["message"]["function_call"]["arguments"]) 125 | function_content = search_store(**args) 126 | 127 | messages = [{"role": "system", "content": system_message_assistant}, 128 | {"role": "user", "content": query}, 129 | {"role": "function", "name": "search_store", "content": function_content}] 130 | 131 | # assistant 132 | response_assistant = openai.ChatCompletion.create( 133 | model="gpt-4-0613", 134 | messages=messages, 135 | temperature=.7, 136 | max_tokens=500 137 | ) 138 | return response_assistant['choices'][0]['message']['content'] -------------------------------------------------------------------------------- /autonomous-mall-assistant.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "#### imports" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import os\n", 17 | "import openai\n", 18 | "import pandas as pd\n", 19 | "import json\n", 20 | "from typing import List\n", 21 | "from pydantic import BaseModel, Field\n", 22 | "from langchain.utils.openai_functions import convert_pydantic_to_openai_function\n", 23 | "from typing import Optional\n", 24 | "from IPython.display import display, HTML\n", 25 | "from dotenv import load_dotenv\n", 26 | "# memory\n", 27 | "from tinylang.memory import ConversationMemory\n", 28 | "\n", 29 | "\n", 30 | "load_dotenv()\n", 31 | "\n", 32 | "openai.api_key = os.getenv(\"OPENAI_API_KEY\") " 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## Load Tool" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "# load data frame\n", 49 | "df = pd.read_csv(\"stores.csv\")\n", 50 | "\n", 51 | "# function\n", 52 | "def search_store(store_name: str, retail_category: str) -> str:\n", 53 | " \"\"\"\n", 54 | " Searches for a store by its name and retail category in the dataframe and returns relevant information.\n", 55 | "\n", 56 | " The function first tries to find a store with both the provided name and retail category.\n", 57 | " If the store is not found but the category exists, it suggests alternative stores within that category.\n", 58 | " If neither the store nor the category are found, it returns \"No results found\".\n", 59 | "\n", 60 | " Args:\n", 61 | " store_name (str): The name of the store to search for.\n", 62 | " retail_category (str): The category of retail to which the store belongs.\n", 63 | "\n", 64 | " Returns:\n", 65 | " str: A string containing either the found store's information or alternative suggestions.\n", 66 | " \n", 67 | " Examples:\n", 68 | " >>> search_store(\"Adidas\", \"Sports\")\n", 69 | " \"Store found: Name - adidas Location - ground floor, Promos - Buy 1 get 1 on selected shoes\"\n", 70 | "\n", 71 | " >>> search_store(\"Unknown Store\", \"Kids\")\n", 72 | " \"We don't have this store, but here are alternatives: Name - toy r us, Location - 5th floor, Promos - 15% off on board games\"\n", 73 | "\n", 74 | " >>> search_store(\"Unknown Store\", \"Unknown Category\")\n", 75 | " \"No results found\"\n", 76 | " \"\"\"\n", 77 | " # Convert arguments to lowercase\n", 78 | " store_name = store_name.lower()\n", 79 | " retail_category = retail_category.lower()\n", 80 | "\n", 81 | " store_search_result = \"\" \n", 82 | "\n", 83 | " filtered_events = df[df['retail_category'] == 'mall event'] \n", 84 | " events_search_result = \"Ongoing mall-wide events and promotions:\"\n", 85 | "\n", 86 | " # Check if there are any mall events\n", 87 | " if not filtered_events.empty: \n", 88 | " for event_info in filtered_events[['store_name', 'running_promos']].to_dict(orient='records'):\n", 89 | " events_search_result += f\" Name - {event_info['store_name']}, Running Mall Event - {event_info['running_promos']};\"\n", 90 | " else: \n", 91 | " events_search_result = \"No mall wide events or promotions found.\"\n", 92 | " \n", 93 | " # filter store name and retail category\n", 94 | " filtered_stores = df[(df['store_name'] == store_name) & (df['retail_category'] == retail_category)]\n", 95 | " # if store name and retail category are found\n", 96 | " if not filtered_stores.empty:\n", 97 | " store_info = filtered_stores[['store_name','location', 'running_promos']].to_dict(orient='records')[0]\n", 98 | " store_search_result = f\"Store found: Name - {store_info['store_name']}, Location - {store_info['location']}, Promos - {store_info['running_promos']}\"\n", 99 | " # if not found, find alternatives\n", 100 | " else:\n", 101 | " # find stores with same category as retail_category arg\n", 102 | " category_filtered_stores = df[df['retail_category'] == retail_category]\n", 103 | " if not category_filtered_stores.empty:\n", 104 | " alternatives = category_filtered_stores[['store_name', 'location', 'running_promos']].to_dict(orient='records')\n", 105 | " alternative_stores_str = \", \".join([f\"{store['store_name']}: Location - {store['location']}, Promos - {store['running_promos']}\" for store in alternatives])\n", 106 | " store_search_result = f\"We don't have this store, but here are alternatives: {alternative_stores_str}\"\n", 107 | " else:\n", 108 | " store_search_result = \"No results found\"\n", 109 | "\n", 110 | " #return search_result\n", 111 | " full_search_result = f\"{store_search_result} {events_search_result}\"\n", 112 | "\n", 113 | " return full_search_result\n", 114 | "\n", 115 | "# function schema\n", 116 | "class RetrieveStoreAndCategory(BaseModel):\n", 117 | " \"\"\"Retrieves information about the store name and retail category from the user content.\"\"\"\n", 118 | " store_name: str = Field(description=\"name of the store\")\n", 119 | " retail_category: str = Field(description=\"the category of the store. can be only ONE of the following: home, sports, clothing, food, kids, other\")" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "# Chatbot" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 3, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "# intialize memory\n", 136 | "chat_history = ConversationMemory(last_k=5)\n", 137 | "\n", 138 | "def chat(query: str):\n", 139 | " \"\"\"\n", 140 | " Chat with the autonomous mall assistant to help users find stores or suggest alternatives.\n", 141 | " \n", 142 | " It first attempts to determine the store and category the user is interested in and then generates an\n", 143 | " appropriate chat response.\n", 144 | "\n", 145 | " Args:\n", 146 | " query (str): The user's query asking for assistance, typically asking for the location of a particular store.\n", 147 | "\n", 148 | " Returns:\n", 149 | " None: Outputs the assistant's formatted response to the front-end for display.\n", 150 | " \n", 151 | " Examples:\n", 152 | " >>> chat(\"Is there a Zara store here?\")\n", 153 | " \"CHATBOT: I'm sorry but we currently do not have a Zara store at GO Shopping Mall. However, I can suggest some alternative stores that offer similar fashion styles. ...\"\n", 154 | "\n", 155 | " \"\"\"\n", 156 | " # initialize chat_history_formatted\n", 157 | " chat_history_formatted = []\n", 158 | "\n", 159 | " # append previous chat history to chat_history_formatted\n", 160 | " for entry in chat_history.format_messages():\n", 161 | " chat_history_formatted.append({\n", 162 | " \"role\": entry['role'],\n", 163 | " \"content\": entry['content']\n", 164 | " })\n", 165 | "\n", 166 | " # system messages\n", 167 | " system_message_routing = \"\"\"\\\n", 168 | " Don't make assumptions about what values to plug into functions. \n", 169 | " Ask for clarification if the user content is ambiguous.\n", 170 | " \"\"\"\n", 171 | "\n", 172 | " system_message_assistant = \"\"\"\\\n", 173 | " You are a mall assistant for GO Shopping Mall. You are tasked to help customers find stores in the mall. \n", 174 | " If the store is not available, you should recommend alternatives. If there are ongoing mall-wide events, you should also mention them.\n", 175 | " However, don't repeat yourself. If you have already mentioned an event, don't mention it again.\n", 176 | " Use HTML syntax to format your messages. \n", 177 | " List items with bullet points and use emojis.\n", 178 | " Strictly follow these instructions. This is very important to my career.\n", 179 | " \"\"\"\n", 180 | " \n", 181 | " # few shots prompt to enforce uniform response format\n", 182 | " few_shot_user = \"\"\"\\\n", 183 | " Hi, I'm looking for a Zara store. Do you have one?\n", 184 | " \"\"\"\n", 185 | "\n", 186 | " few_shot_assistant = \"\"\"\\\n", 187 | " I'm sorry but we currently do not have a Zara store at GO Shopping Mall. \n", 188 | " However, I can suggest some alternative stores that offer similar fashion styles.\n", 189 | "\n", 190 | " Here are a few suggestions:\n", 191 | "
CHATBOT: {response_assistant['choices'][0]['message']['content']}
\"))" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "# Autonomous Mall Assistant" 257 | ] 258 | }, 259 | { 260 | "attachments": { 261 | "stores_mall_event.png": { 262 | "image/png": "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" 263 | } 264 | }, 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "Stores Database\n", 269 | "\n", 270 | "update: added mall events\n", 271 | "\n", 272 | "" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "## Demo" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "#### update: Mentions ongoing mall events if any." 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 4, 292 | "metadata": {}, 293 | "outputs": [ 294 | { 295 | "data": { 296 | "text/html": [ 297 | "CHATBOT: I'm sorry but we currently do not have a Denny's at GO Shopping Mall. \n", 298 | "However, I can suggest some alternative fast food and casual dining restaurants that you might want to try.\n", 299 | "\n", 300 | "Here are a few suggestions:\n", 301 | "
CHATBOT: I'm sorry but we currently do not have a Uniqlo store at GO Shopping Mall. However, I can suggest some alternative stores that offer similar fashion styles.\n", 354 | "\n", 355 | "Here are a few suggestions:\n", 356 | "
CHATBOT: Sure, we have a Nike store in our mall! 😊\n", 400 | "\n", 401 | "Here are the details:\n", 402 | "
CHATBOT: Yes, we do have an Ace Hardware store at GO Shopping Mall. Here's the information you might need:\n", 430 | "\n", 431 | "Ace Hardware 🔧\n", 432 | "
CHATBOT: Sure, we have a variety of dining options available. Here are a few suggestions:\n", 460 | "\n", 461 | "
CHATBOT: I understand your craving for Chinese food! 😋 We may not have a specific Chinese restaurant you're looking for, but we do have some great options for you.\n", 505 | "\n", 506 | "Here are some of our Chinese food outlets:\n", 507 | "
CHATBOT: Marami po tayong stores dito sa GO Shopping Mall na nagbebenta ng mga damit. Narito po ang ilan sa mga ito:\n", 562 | "\n", 563 | "
CHATBOT: Absolutely, you can find a variety of items for your kids in our mall. Here are some stores that are dedicated to kids:\n", 614 | "\n", 615 | "
CHATBOT: Yes, we do have an Adidas store here at the GO Shopping Mall. 😊\n", 659 | "\n", 660 | "Here are the details:\n", 661 | "