def __init__(self, configuration): """ Initialize the internal structures of this agent. :param configuration: a dictionary representing the configuration file """ super(ConversationalSingleAgent, self).__init__() self.configuration = configuration # There is only one agent in this setting self.agent_id = 0 # dialogue statistics self.dialogue_episode = 0 self.dialogue_turn = 0 self.num_successful_dialogues = 0 self.num_task_success = 0 self.cumulative_rewards = 0 self.total_dialogue_turns = 0 # Default meta-parameter values self.minibatch_length = 500 self.train_interval = 50 self.train_epochs = 3 # True values here would imply some default modules self.USE_USR_SIMULATOR = False self.USER_SIMULATOR_NLU = False self.USER_SIMULATOR_NLG = False self.USE_NLG = False self.USE_SPEECH = False self.USER_HAS_INITIATIVE = True self.SAVE_LOG = True self.SAVE_INTERVAL = 10000 # The dialogue will terminate after MAX_TURNS (this agent will issue # a bye() dialogue act. self.MAX_TURNS = 15 self.dialogue_turn = -1 self.ontology = None self.database = None self.domain = None self.global_args = {} self.dialogue_manager = None self.user_model = None self.user_simulator = None self.user_simulator_args = {} self.nlu = None self.nlg = None self.agent_role = None self.agent_goal = None self.goal_generator = None self.curr_state = None self.prev_state = None self.curr_state = None self.prev_usr_utterance = None self.prev_sys_utterance = None self.prev_action = None self.prev_reward = None self.prev_success = None self.prev_task_success = None self.user_model = UserModel() self.recorder = DialogueEpisodeRecorder() # TODO: Handle this properly - get reward function type from config self.reward_func = SlotFillingReward() # self.reward_func = SlotFillingGoalAdvancementReward() if self.configuration: # Error checks for options the config must have if not self.configuration['GENERAL']: raise ValueError('Cannot run Plato without GENERAL settings!') elif not self.configuration['GENERAL']['interaction_mode']: raise ValueError('Cannot run Plato without an ' 'interaction mode!') elif not self.configuration['DIALOGUE']: raise ValueError('Cannot run Plato without DIALOGUE settings!') elif not self.configuration['AGENT_0']: raise ValueError('Cannot run Plato without at least ' 'one agent!') # dialogue domain self.settings if 'DIALOGUE' in self.configuration and \ self.configuration['DIALOGUE']: if 'initiative' in self.configuration['DIALOGUE']: self.USER_HAS_INITIATIVE = bool( self.configuration['DIALOGUE']['initiative'] == 'user' ) self.user_simulator_args['us_has_initiative'] = \ self.USER_HAS_INITIATIVE if self.configuration['DIALOGUE']['domain']: self.domain = self.configuration['DIALOGUE']['domain'] if 'ontology_path' in self.configuration['DIALOGUE']: if os.path.isfile( self.configuration['DIALOGUE']['ontology_path'] ): self.ontology = ontology.Ontology( self.configuration['DIALOGUE']['ontology_path'] ) else: raise FileNotFoundError( 'domain file %s not found' % self.configuration['DIALOGUE']['ontology_path']) # Alternatively, look at global_arguments for ontology path elif 'global_arguments' in self.configuration['GENERAL'] \ and 'ontology' in \ self.configuration['GENERAL']['global_arguments']: if os.path.isfile( self.configuration['GENERAL'][ 'global_arguments']['ontology'] ): self.ontology = ontology.Ontology( self.configuration['GENERAL'][ 'global_arguments']['ontology'] ) else: raise FileNotFoundError( 'domain file %s not found' % self.configuration['GENERAL'][ 'global_arguments']['ontology']) if 'db_path' in self.configuration['DIALOGUE']: if os.path.isfile( self.configuration['DIALOGUE']['db_path'] ): if 'db_type' in self.configuration['DIALOGUE']: if self.configuration['DIALOGUE']['db_type'] == \ 'sql': self.database = database.SQLDataBase( self.configuration['DIALOGUE']['db_path'] ) else: self.database = database.DataBase( self.configuration['DIALOGUE']['db_path'] ) else: # Default to SQL self.database = database.SQLDataBase( self.configuration['DIALOGUE']['db_path'] ) else: raise FileNotFoundError( 'Database file %s not found' % self.configuration['DIALOGUE']['db_path'] ) # Alternatively, look at global arguments for db path elif 'global_arguments' in self.configuration['GENERAL'] \ and 'database' in \ self.configuration['GENERAL']['global_arguments']: if os.path.isfile( self.configuration['GENERAL'][ 'global_arguments']['database'] ): self.database = database.DataBase( self.configuration['GENERAL'][ 'global_arguments']['database'] ) else: raise FileNotFoundError( 'domain file %s not found' % self.configuration['GENERAL'][ 'global_arguments']['ontology']) if 'goals_path' in self.configuration['DIALOGUE']: if os.path.isfile( self.configuration['DIALOGUE']['goals_path'] ): self.goals_path = \ self.configuration['DIALOGUE']['goals_path'] else: raise FileNotFoundError( 'Goals file %s not found' % self.configuration['DIALOGUE']['goals_path'] ) # General settings if 'GENERAL' in self.configuration and \ self.configuration['GENERAL']: if 'global_arguments' in self.configuration['GENERAL']: self.global_args = \ self.configuration['GENERAL']['global_arguments'] if 'experience_logs' in self.configuration['GENERAL']: dialogues_path = None if 'path' in \ self.configuration['GENERAL']['experience_logs']: dialogues_path = \ self.configuration['GENERAL'][ 'experience_logs']['path'] if 'load' in \ self.configuration['GENERAL']['experience_logs'] \ and bool(self.configuration['GENERAL'][ 'experience_logs']['load']): if dialogues_path and os.path.isfile(dialogues_path): self.recorder.load(dialogues_path) else: raise FileNotFoundError( 'dialogue Log file %s not found (did you ' 'provide one?)' % dialogues_path) if 'save' in \ self.configuration['GENERAL']['experience_logs']: self.recorder.set_path(dialogues_path) self.SAVE_LOG = bool( self.configuration['GENERAL'][ 'experience_logs']['save'] ) if self.configuration['GENERAL']['interaction_mode'] == \ 'simulation': self.USE_USR_SIMULATOR = True elif self.configuration['GENERAL']['interaction_mode'] == \ 'speech': self.USE_SPEECH = True self.asr = speech_rec.Recognizer() # Agent Settings # Retrieve agent role if 'role' in self.configuration['AGENT_0']: self.agent_role = self.configuration['AGENT_0']['role'] else: raise ValueError( 'agent: No role assigned for agent {0} in ' 'config!'.format(self.agent_id) ) if self.agent_role == 'user': if self.ontology and self.database: self.goal_generator = GoalGenerator({ 'ontology': self.ontology, 'database': self.database }) else: raise ValueError( 'Conversational Single Agent (user): Cannot generate ' 'goal without ontology and database.' ) # Retrieve agent parameters if 'max_turns' in self.configuration['AGENT_0']: self.MAX_TURNS = self.configuration['AGENT_0']['max_turns'] if 'train_interval' in self.configuration['AGENT_0']: self.train_interval = \ self.configuration['AGENT_0']['train_interval'] if 'train_minibatch' in self.configuration['AGENT_0']: self.minibatch_length = \ self.configuration['AGENT_0']['train_minibatch'] if 'train_epochs' in self.configuration['AGENT_0']: self.train_epochs = \ self.configuration['AGENT_0']['train_epochs'] if 'save_interval' in self.configuration['AGENT_0']: self.SAVE_INTERVAL = \ self.configuration['AGENT_0']['save_interval'] # usr Simulator # Check for specific simulator self.settings, otherwise # default to agenda if 'USER_SIMULATOR' in self.configuration['AGENT_0']: # Agent 0 simulator configuration if 'package' in \ self.configuration['AGENT_0']['USER_SIMULATOR'] and \ 'class' in \ self.configuration['AGENT_0']['USER_SIMULATOR']: if 'arguments' in \ self.configuration['AGENT_0']['USER_SIMULATOR']: self.user_simulator_args =\ self.configuration[ 'AGENT_0']['USER_SIMULATOR']['arguments'] self.user_simulator_args.update(self.global_args) self.user_simulator = \ ConversationalGenericAgent.load_module( self.configuration['AGENT_0']['USER_SIMULATOR'][ 'package'], self.configuration['AGENT_0']['USER_SIMULATOR'][ 'class'], self.user_simulator_args ) if hasattr(self.user_simulator, 'nlu'): self.USER_SIMULATOR_NLU = self.user_simulator.nlu if hasattr(self.user_simulator, 'nlg'): self.USER_SIMULATOR_NLG = self.user_simulator.nlg else: # Fallback to agenda based simulator with default settings self.user_simulator = AgendaBasedUS( self.user_simulator_args ) # NLU Settings if 'NLU' in self.configuration['AGENT_0']: nlu_args = {} if 'package' in self.configuration['AGENT_0']['NLU'] and \ 'class' in self.configuration['AGENT_0']['NLU']: if 'arguments' in \ self.configuration['AGENT_0']['NLU']: nlu_args = \ self.configuration['AGENT_0']['NLU']['arguments'] nlu_args.update(self.global_args) self.nlu = \ ConversationalGenericAgent.load_module( self.configuration['AGENT_0']['NLU'][ 'package'], self.configuration['AGENT_0']['NLU'][ 'class'], nlu_args ) # DM Settings if 'DM' in self.configuration['AGENT_0']: dm_args = dict( zip( ['settings', 'ontology', 'database', 'domain', 'agent_id', 'agent_role'], [self.configuration, self.ontology, self.database, self.domain, self.agent_id, self.agent_role ] ) ) if 'package' in self.configuration['AGENT_0']['DM'] and \ 'class' in self.configuration['AGENT_0']['DM']: if 'arguments' in \ self.configuration['AGENT_0']['DM']: dm_args.update( self.configuration['AGENT_0']['DM']['arguments'] ) dm_args.update(self.global_args) self.dialogue_manager = \ ConversationalGenericAgent.load_module( self.configuration['AGENT_0']['DM'][ 'package'], self.configuration['AGENT_0']['DM'][ 'class'], dm_args ) # NLG Settings if 'NLG' in self.configuration['AGENT_0']: nlg_args = {} if 'package' in self.configuration['AGENT_0']['NLG'] and \ 'class' in self.configuration['AGENT_0']['NLG']: if 'arguments' in \ self.configuration['AGENT_0']['NLG']: nlg_args = \ self.configuration['AGENT_0']['NLG']['arguments'] nlg_args.update(self.global_args) self.nlg = \ ConversationalGenericAgent.load_module( self.configuration['AGENT_0']['NLG'][ 'package'], self.configuration['AGENT_0']['NLG'][ 'class'], nlg_args ) if self.nlg: self.USE_NLG = True # True if at least one module is training self.IS_TRAINING = self.nlu and self.nlu.training or \ self.dialogue_manager and self.dialogue_manager.training or \ self.nlg and self.nlg.training
def __init__(self, configuration, agent_id): """ Initialize the internal structures of this agent. :param configuration: a dictionary representing the configuration file :param agent_id: an integer, this agent's id """ super(ConversationalMultiAgent, self).__init__() self.agent_id = agent_id # Flag to alternate training between roles self.train_system = True # dialogue statistics self.dialogue_episode = 1 # Note that since this is a multi-agent setting, dialogue_turn refers # to this agent's turns only self.dialogue_turn = 0 self.num_successful_dialogues = 0 self.num_task_success = 0 self.cumulative_rewards = 0 self.total_dialogue_turns = 0 self.minibatch_length = 200 self.train_interval = 50 self.train_epochs = 3 self.global_args = {} # Alternate training between the agents self.train_alternate_training = True self.train_switch_trainable_agents_every = self.train_interval self.configuration = configuration # True values here would imply some default modules self.USE_NLU = False self.USE_NLG = False self.USE_SPEECH = False self.USER_HAS_INITIATIVE = True self.SAVE_LOG = True self.SAVE_INTERVAL = 10000 # The dialogue will terminate after MAX_TURNS (this agent will issue # a bye() dialogue act. self.MAX_TURNS = 10 self.ontology = None self.database = None self.domain = None self.dialogue_manager = None self.user_model = None self.nlu = None self.nlg = None self.agent_role = None self.agent_goal = None self.goal_generator = None self.goals_path = None self.prev_state = None self.curr_state = None self.prev_usr_utterance = None self.prev_sys_utterance = None self.prev_action = None self.prev_reward = None self.prev_success = None self.prev_task_success = None self.user_model = UserModel() # The size of the experience pool is a hyperparameter. # Do not have an experience window larger than the current batch, # as past experience may not be relevant since both agents learn. self.recorder = DialogueEpisodeRecorder(size=20000) # TODO: Get reward type from the config self.reward_func = SlotFillingReward() if self.configuration: ag_id_str = 'AGENT_' + str(self.agent_id) # Error checks for options the config must have if not self.configuration['GENERAL']: raise ValueError('Cannot run Plato without GENERAL settings!') elif not self.configuration['GENERAL']['interaction_mode']: raise ValueError('Cannot run Plato without an interaction ' 'mode!') elif not self.configuration['DIALOGUE']: raise ValueError('Cannot run Plato without DIALOGUE settings!') elif not self.configuration[ag_id_str]: raise ValueError('Cannot run Plato without at least ' 'one agent!') # General settings if 'GENERAL' in self.configuration and \ self.configuration['GENERAL']: if 'global_arguments' in self.configuration['GENERAL']: self.global_args = \ self.configuration['GENERAL']['global_arguments'] if 'experience_logs' in self.configuration['GENERAL']: dialogues_path = None if 'path' in \ self.configuration['GENERAL']['experience_logs']: dialogues_path = \ self.configuration['GENERAL'][ 'experience_logs']['path'] if 'load' in \ self.configuration['GENERAL'][ 'experience_logs'] and \ bool( self.configuration[ 'GENERAL' ]['experience_logs']['load'] ): if dialogues_path and os.path.isfile(dialogues_path): self.recorder.load(dialogues_path) else: raise FileNotFoundError( 'dialogue Log file %s not found (did you ' 'provide one?)' % dialogues_path ) if 'save' in \ self.configuration['GENERAL']['experience_logs']: self.recorder.set_path(dialogues_path) self.SAVE_LOG = bool( self.configuration['GENERAL'][ 'experience_logs']['save'] ) # Retrieve agent role if 'role' in self.configuration[ag_id_str]: self.agent_role = self.configuration[ag_id_str][ 'role'] else: raise ValueError( 'agent: No role assigned for ' 'agent {0} in ' 'config!'.format(self.agent_id) ) # Dialogue settings if 'DIALOGUE' in self.configuration and \ self.configuration['DIALOGUE']: if 'initiative' in self.configuration['DIALOGUE']: self.USER_HAS_INITIATIVE = bool( self.configuration['DIALOGUE']['initiative'] == 'user' ) if 'domain' in self.configuration['DIALOGUE']: self.domain = self.configuration['DIALOGUE']['domain'] elif 'domain' in self.global_args: self.domain = self.global_args['domain'] ontology_path = None if 'ontology_path' in self.configuration['DIALOGUE']: ontology_path = \ self.configuration['DIALOGUE']['ontology_path'] elif 'ontology' in self.global_args: ontology_path = self.global_args['ontology'] if ontology_path and os.path.isfile(ontology_path): self.ontology = ontology.Ontology(ontology_path) else: raise FileNotFoundError( 'domain file %s not found' % ontology_path ) db_path = None if 'db_path' in self.configuration['DIALOGUE']: db_path = self.configuration['DIALOGUE']['db_path'] elif 'database' in self.global_args: db_path = self.global_args['database'] if db_path and os.path.isfile(db_path): if db_path[-3:] == '.db': self.database = database.SQLDataBase(db_path) else: self.database = database.DataBase(db_path) else: raise FileNotFoundError( 'Database file %s not found' % db_path ) if 'goals_path' in self.configuration['DIALOGUE']: if os.path.isfile( self.configuration['DIALOGUE']['goals_path'] ): self.goals_path = \ self.configuration['DIALOGUE']['goals_path'] else: raise FileNotFoundError( 'Goals file %s not ' 'found' % self.configuration[ 'DIALOGUE' ]['goals_path'] ) # Agent Settings # Retrieve agent role if 'role' in self.configuration[ag_id_str]: self.agent_role = self.configuration[ag_id_str][ 'role'] else: raise ValueError( 'agent: No role assigned for agent {0} in ' 'config!'.format(self.agent_id) ) if self.agent_role == 'user': if self.ontology and self.database: self.goal_generator = GoalGenerator({ 'ontology': self.ontology, 'database': self.database }) else: raise ValueError( 'Conversational Multi Agent (user): Cannot generate ' 'goal without ontology and database.' ) # Retrieve agent parameters if 'max_turns' in self.configuration[ag_id_str]: self.MAX_TURNS = self.configuration[ag_id_str][ 'max_turns'] if 'train_interval' in self.configuration[ag_id_str]: self.train_interval = \ self.configuration[ag_id_str]['train_interval'] if 'train_minibatch' in self.configuration[ag_id_str]: self.minibatch_length = \ self.configuration[ag_id_str]['train_minibatch'] if 'train_epochs' in self.configuration[ag_id_str]: self.train_epochs = \ self.configuration[ag_id_str]['train_epochs'] if 'save_interval' in self.configuration[ag_id_str]: self.SAVE_INTERVAL = \ self.configuration[ag_id_str]['save_interval'] # NLU Settings if 'NLU' in self.configuration[ag_id_str]: nlu_args = {} if 'package' in self.configuration[ag_id_str]['NLU'] and \ 'class' in self.configuration[ag_id_str]['NLU']: if 'arguments' in \ self.configuration[ag_id_str]['NLU']: nlu_args = \ self.configuration[ag_id_str]['NLU'][ 'arguments'] nlu_args.update(self.global_args) self.nlu = \ ConversationalGenericAgent.load_module( self.configuration[ag_id_str]['NLU'][ 'package'], self.configuration[ag_id_str]['NLU'][ 'class'], nlu_args ) # DM Settings if 'DM' in self.configuration[ag_id_str]: dm_args = dict( zip( ['settings', 'ontology', 'database', 'domain', 'agent_id', 'agent_role'], [self.configuration, self.ontology, self.database, self.domain, self.agent_id, self.agent_role ] ) ) if 'package' in self.configuration[ag_id_str]['DM'] and \ 'class' in self.configuration[ag_id_str]['DM']: if 'arguments' in \ self.configuration[ag_id_str]['DM']: dm_args.update( self.configuration[ag_id_str]['DM'][ 'arguments'] ) dm_args.update(self.global_args) self.dialogue_manager = \ ConversationalGenericAgent.load_module( self.configuration[ag_id_str]['DM'][ 'package'], self.configuration[ag_id_str]['DM'][ 'class'], dm_args ) # NLG Settings if 'NLG' in self.configuration[ag_id_str]: nlg_args = {} if 'package' in self.configuration[ag_id_str]['NLG'] and \ 'class' in self.configuration[ag_id_str]['NLG']: if 'arguments' in \ self.configuration[ag_id_str]['NLG']: nlg_args = \ self.configuration[ag_id_str]['NLG'][ 'arguments'] nlg_args.update(self.global_args) self.nlg = \ ConversationalGenericAgent.load_module( self.configuration[ag_id_str]['NLG'][ 'package'], self.configuration[ag_id_str]['NLG'][ 'class'], nlg_args ) if self.nlg: self.USE_NLG = True # True if at least one module is training self.IS_TRAINING = self.nlu and self.nlu.training or \ self.dialogue_manager and self.dialogue_manager.training or \ self.nlg and self.nlg.training