├── actions ├── __init__.py └── actions.py ├── .gitignore ├── .DS_Store ├── config.yml ├── data ├── rules.yml ├── stories.yml └── nlu.yml ├── restaurants.csv ├── domain.yml ├── endpoints.yml └── README.md /actions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rasa 2 | .env 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/kb-demo-chatgpt/HEAD/.DS_Store -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | recipe: default.v1 2 | assistant_id: 20230303-132941-slow-interest 3 | language: en 4 | pipeline: 5 | - name: WhitespaceTokenizer 6 | - name: CountVectorsFeaturizer 7 | - name: CountVectorsFeaturizer 8 | analyzer: char_wb 9 | min_ngram: 1 10 | max_ngram: 4 11 | - name: LogisticRegressionClassifier 12 | epochs: 100 13 | - name: FallbackClassifier 14 | threshold: 0.3 15 | ambiguity_threshold: 0.1 16 | policies: 17 | - name: MemoizationPolicy 18 | - name: RulePolicy 19 | -------------------------------------------------------------------------------- /data/rules.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | rules: 4 | 5 | - rule: Say goodbye anytime the user says goodbye 6 | steps: 7 | - intent: goodbye 8 | - action: utter_goodbye 9 | 10 | - rule: Say 'I am a bot' anytime the user challenges 11 | steps: 12 | - intent: bot_challenge 13 | - action: utter_iamabot 14 | 15 | - rule: show restaurants 16 | steps: 17 | - intent: restaurants 18 | - action: action_show_restaurants 19 | 20 | - rule: show detail 21 | steps: 22 | - intent: detail 23 | - action: action_restaurants_detail -------------------------------------------------------------------------------- /restaurants.csv: -------------------------------------------------------------------------------- 1 | Restaurants,Rating,Has WiFi,cuisine 2 | Mama's Pizza,4.5,False,italian 3 | Tacos el Cabron,4.6,True,mexican 4 | China Palace,3.2,False,chinese 5 | The Cheesecake Factory,4.2,True,american 6 | Pizza Hut,3.8,True,italian 7 | Biryani Bowl,4.9,False,indian 8 | Burger King,2.6,True,american 9 | Taco Bell,3.5,True,mexican 10 | Freshii,4.7,True,healthy 11 | Panda Express,3.9,False,chinese 12 | McDonald's,2.3,True,american 13 | Sushi Roku,4.8,True,japanese 14 | Olive Garden,4.0,True,italian 15 | Chipotle,4.4,True,mexican 16 | Little Caesars,3.1,False,italian -------------------------------------------------------------------------------- /data/stories.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | stories: 4 | 5 | - story: happy path 6 | steps: 7 | - intent: greet 8 | - action: utter_greet 9 | - intent: mood_great 10 | - action: utter_happy 11 | 12 | - story: sad path 1 13 | steps: 14 | - intent: greet 15 | - action: utter_greet 16 | - intent: mood_unhappy 17 | - action: utter_cheer_up 18 | - action: utter_did_that_help 19 | - intent: affirm 20 | - action: utter_happy 21 | 22 | - story: sad path 2 23 | steps: 24 | - intent: greet 25 | - action: utter_greet 26 | - intent: mood_unhappy 27 | - action: utter_cheer_up 28 | - action: utter_did_that_help 29 | - intent: deny 30 | - action: utter_goodbye 31 | -------------------------------------------------------------------------------- /domain.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | slots: 4 | results: 5 | type: text 6 | mappings: 7 | - type: custom 8 | action: action_show_restaurants 9 | 10 | intents: 11 | - greet 12 | - goodbye 13 | - affirm 14 | - deny 15 | - mood_great 16 | - mood_unhappy 17 | - bot_challenge 18 | - restaurants 19 | - detail 20 | 21 | responses: 22 | utter_greet: 23 | - text: "Hey! How are you?" 24 | 25 | utter_cheer_up: 26 | - text: "Here is something to cheer you up:" 27 | image: "https://i.imgur.com/nGF1K8f.jpg" 28 | 29 | utter_did_that_help: 30 | - text: "Did that help you?" 31 | 32 | utter_happy: 33 | - text: "Great, carry on!" 34 | 35 | utter_goodbye: 36 | - text: "Bye" 37 | 38 | utter_iamabot: 39 | - text: "I am a bot, powered by Rasa." 40 | 41 | actions: 42 | - action_show_restaurants 43 | - action_restaurants_detail 44 | 45 | session_config: 46 | session_expiration_time: 60 47 | carry_over_slots_to_new_session: true 48 | -------------------------------------------------------------------------------- /endpoints.yml: -------------------------------------------------------------------------------- 1 | # This file contains the different endpoints your bot can use. 2 | 3 | # Server where the models are pulled from. 4 | # https://rasa.com/docs/rasa/model-storage#fetching-models-from-a-server 5 | 6 | #models: 7 | # url: http://my-server.com/models/default_core@latest 8 | # wait_time_between_pulls: 10 # [optional](default: 100) 9 | 10 | # Server which runs your custom actions. 11 | # https://rasa.com/docs/rasa/custom-actions 12 | 13 | action_endpoint: 14 | url: "http://localhost:5055/webhook" 15 | 16 | # Tracker store which is used to store the conversations. 17 | # By default the conversations are stored in memory. 18 | # https://rasa.com/docs/rasa/tracker-stores 19 | 20 | #tracker_store: 21 | # type: redis 22 | # url: 23 | # port: 24 | # db: 25 | # password: 26 | # use_ssl: 27 | 28 | #tracker_store: 29 | # type: mongod 30 | # url: 31 | # db: 32 | # username: 33 | # password: 34 | 35 | # Event broker which all conversation events should be streamed to. 36 | # https://rasa.com/docs/rasa/event-brokers 37 | 38 | #event_broker: 39 | # url: localhost 40 | # username: username 41 | # password: password 42 | # queue: queue 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using ChatGPT in a custom action to answer questions about data 2 | 3 | 4 | Most Rasa chatbots fetch data from an API. E.g. in the financial-demo, a user can see their latest transactions. 5 | Here, we have a dummy API returning restaurant results. 6 | 7 | Using the ChatGPT API, we then allow users to ask questions *about* the data they've just been shown. 8 | We have an existing feature called [Knowledge Base Actions](https://rasa.com/blog/integrating-rasa-with-knowledge-bases/) which does the same thing. In comparison, using ChatGPT is much simpler, but also occasionally makes up wrong answers! 9 | 10 | 11 | image 12 | 13 | 14 | Here's the data which the API has access to: 15 | 16 | | | Restaurants | Rating | Has WiFi | cuisine | 17 | |---:|:-----------------------|---------:|:-----------|:----------| 18 | | 0 | Mama's Pizza | 4.5 | False | italian | 19 | | 1 | Tacos el Cabron | 4.6 | True | mexican | 20 | | 2 | China Palace | 3.2 | False | chinese | 21 | | 3 | The Cheesecake Factory | 4.2 | True | american | 22 | | 4 | Pizza Hut | 3.8 | True | italian | 23 | | 5 | Biryani Bowl | 4.9 | False | indian | 24 | | 6 | Burger King | 2.6 | True | american | 25 | | 7 | Taco Bell | 3.5 | True | mexican | 26 | | 8 | Freshii | 4.7 | True | healthy | 27 | | 9 | Panda Express | 3.9 | False | chinese | 28 | | 10 | McDonald's | 2.3 | True | american | 29 | | 11 | Sushi Roku | 4.8 | True | japanese | 30 | | 12 | Olive Garden | 4 | True | italian | 31 | | 13 | Chipotle | 4.4 | True | mexican | 32 | | 14 | Little Caesars | 3.1 | False | italian | 33 | -------------------------------------------------------------------------------- /data/nlu.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | nlu: 4 | - intent: greet 5 | examples: | 6 | - hey 7 | - hello 8 | - hi 9 | - hello there 10 | - good morning 11 | - good evening 12 | - moin 13 | - hey there 14 | - let's go 15 | - hey dude 16 | - goodmorning 17 | - goodevening 18 | - good afternoon 19 | 20 | - intent: restaurants 21 | examples: | 22 | - show me places to eat 23 | - I'm looking for a great restaurant 24 | - show me food 25 | 26 | - intent: detail 27 | examples: | 28 | - which of those have wifi? 29 | - what's the highest rated? 30 | - does the chinese place do takeaway? 31 | - does the mexican one have wifi 32 | 33 | - intent: goodbye 34 | examples: | 35 | - cu 36 | - good by 37 | - cee you later 38 | - good night 39 | - bye 40 | - goodbye 41 | - have a nice day 42 | - see you around 43 | - bye bye 44 | - see you later 45 | 46 | - intent: affirm 47 | examples: | 48 | - yes 49 | - y 50 | - indeed 51 | - of course 52 | - that sounds good 53 | - correct 54 | 55 | - intent: deny 56 | examples: | 57 | - no 58 | - n 59 | - never 60 | - I don't think so 61 | - don't like that 62 | - no way 63 | - not really 64 | 65 | - intent: mood_great 66 | examples: | 67 | - perfect 68 | - great 69 | - amazing 70 | - feeling like a king 71 | - wonderful 72 | - I am feeling very good 73 | - I am great 74 | - I am amazing 75 | - I am going to save the world 76 | - super stoked 77 | - extremely good 78 | - so so perfect 79 | - so good 80 | - so perfect 81 | 82 | - intent: mood_unhappy 83 | examples: | 84 | - my day was horrible 85 | - I am sad 86 | - I don't feel very well 87 | - I am disappointed 88 | - super sad 89 | - I'm so sad 90 | - sad 91 | - very sad 92 | - unhappy 93 | - not good 94 | - not very good 95 | - extremly sad 96 | - so saad 97 | - so sad 98 | 99 | - intent: bot_challenge 100 | examples: | 101 | - are you a bot? 102 | - are you a human? 103 | - am I talking to a bot? 104 | - am I talking to a human? 105 | -------------------------------------------------------------------------------- /actions/actions.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Any, Text, Dict, List 3 | import pandas as pd 4 | import requests 5 | from rasa_sdk import Action, Tracker 6 | from rasa_sdk.executor import CollectingDispatcher 7 | from rasa_sdk.events import SlotSet 8 | 9 | 10 | class RestaurantAPI(object): 11 | 12 | def __init__(self): 13 | self.db = pd.read_csv("restaurants.csv") 14 | 15 | def fetch_restaurants(self): 16 | return self.db.head() 17 | 18 | def format_restaurants(self, df, header=True) -> Text: 19 | return df.to_csv(index=False, header=header) 20 | 21 | 22 | class ChatGPT(object): 23 | 24 | def __init__(self): 25 | self.url = "https://api.openai.com/v1/chat/completions" 26 | self.model = "gpt-3.5-turbo" 27 | self.headers={ 28 | "Content-Type": "application/json", 29 | "Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}" 30 | } 31 | self.prompt = "Answer the following question, based on the data shown. " \ 32 | "Answer in a complete sentence and don't say anything else." 33 | 34 | def ask(self, restaurants, question): 35 | content = self.prompt + "\n\n" + restaurants + "\n\n" + question 36 | body = { 37 | "model":self.model, 38 | "messages":[{"role": "user", "content": content}] 39 | } 40 | result = requests.post( 41 | url=self.url, 42 | headers=self.headers, 43 | json=body, 44 | ) 45 | return result.json()["choices"][0]["message"]["content"] 46 | 47 | restaurant_api = RestaurantAPI() 48 | chatGPT = ChatGPT() 49 | 50 | class ActionShowRestaurants(Action): 51 | 52 | def name(self) -> Text: 53 | return "action_show_restaurants" 54 | 55 | def run(self, dispatcher: CollectingDispatcher, 56 | tracker: Tracker, 57 | domain: Dict[Text, Any]) -> List[Dict[Text, Any]]: 58 | 59 | restaurants = restaurant_api.fetch_restaurants() 60 | results = restaurant_api.format_restaurants(restaurants) 61 | readable = restaurant_api.format_restaurants(restaurants[['Restaurants', 'Rating']], header=False) 62 | dispatcher.utter_message(text=f"Here are some restaurants:\n\n{readable}") 63 | 64 | return [SlotSet("results", results)] 65 | 66 | 67 | class ActionRestaurantsDetail(Action): 68 | def name(self) -> Text: 69 | return "action_restaurants_detail" 70 | 71 | def run(self, dispatcher: CollectingDispatcher, 72 | tracker: Tracker, 73 | domain: Dict[Text, Any]) -> List[Dict[Text, Any]]: 74 | 75 | previous_results = tracker.get_slot("results") 76 | question = tracker.latest_message["text"] 77 | answer = chatGPT.ask(previous_results, question) 78 | dispatcher.utter_message(text = answer) 79 | --------------------------------------------------------------------------------