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
        """
        self.agent_id = agent_id

        # 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

        self.minibatch_length = 250
        self.train_interval = 50
        self.train_epochs = 10

        self.configuration = configuration

        self.recorder = DialogueEpisodeRecorder()

        self.SAVE_LOG = True
        self.SAVE_INTERVAL = 1000
        self.MAX_TURNS = 15
        self.INTERACTION_MODE = 'simulation'
        self.USE_GUI = False

        # This indicates which module controls the state so that we can query
        # it for dialogue termination (e.g. at end_dialogue)
        self.STATEFUL_MODULE = -1

        self.reward_func = SlotFillingGoalAdvancementReward()

        self.ConversationalModules = []
        self.prev_m_out = ConversationalFrame({})

        self.goal_generator = None
        self.agent_goal = None
        self.agent_role = ''

        ag_id_str = 'AGENT_' + str(agent_id)

        if self.configuration:
            if 'GENERAL' not in self.configuration:
                raise ValueError('No GENERAL section in config!')
            if 'AGENT_'+str(agent_id) not in self.configuration:
                raise ValueError(f'NO AGENT_{agent_id} section in config!')

            if 'role' in self.configuration[ag_id_str]:
                self.agent_role = self.configuration[ag_id_str]['role']

            # 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']

            if 'interaction_mode' in self.configuration['GENERAL']:
                self.INTERACTION_MODE = \
                    self.configuration['GENERAL']['interaction_mode']

            if 'use_gui' in self.configuration['GENERAL']:
                self.USE_GUI = self.configuration['GENERAL']['use_gui']

            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']
                    )

            self.NModules = 0
            if 'modules' in self.configuration[ag_id_str]:
                self.NModules = int(
                    self.configuration[ag_id_str]['modules']
                )

            if 'stateful_module' in self.configuration[ag_id_str]:
                self.STATEFUL_MODULE = int(
                    self.configuration[ag_id_str]['stateful_module']
                )

            # Note: Since we pass settings as a default argument, any
            #       module can access the global args. However, we
            #       add it here too for ease of use.
            self.global_arguments = {'settings': deepcopy(self.configuration)}
            if 'global_arguments' in self.configuration['GENERAL']:
                self.global_arguments.update(
                    self.configuration['GENERAL']['global_arguments']
                )

            # Load the goal generator, if any
            if 'GOAL_GENERATOR' in self.configuration[ag_id_str]:
                if 'package' not in \
                        self.configuration[ag_id_str]['GOAL_GENERATOR']:
                    raise ValueError(f'No package path provided for '
                                     f'goal generator!')
                elif 'class' not in \
                        self.configuration[ag_id_str]['GOAL_GENERATOR']:
                    raise ValueError(f'No class name provided for '
                                     f'goal generator!')
                else:
                    self.goal_generator = self.load_module(
                        self.configuration[ag_id_str][
                            'GOAL_GENERATOR']['package'],
                        self.configuration[ag_id_str][
                            'GOAL_GENERATOR']['class'],
                        self.global_arguments
                    )

            # Load the modules
            for m in range(self.NModules):
                if 'MODULE_'+str(m) not in \
                        self.configuration[ag_id_str]:
                    raise ValueError(f'No MODULE_{m} section in config!')

                if 'parallel_modules' in self.configuration[
                    ag_id_str
                ]['MODULE_' + str(m)]:

                    n_parallel_modules = self.configuration[
                        ag_id_str][
                        'MODULE_' + str(m)]['parallel_modules']

                    parallel_modules = []

                    for pm in range(n_parallel_modules):
                        if 'package' not in self.configuration[
                            ag_id_str
                        ]['MODULE_' + str(m)]['PARALLEL_MODULE_' + str(pm)]:
                            raise ValueError(
                                f'No arguments provided for parallel module '
                                f'{pm} of module {m}!')

                        package = self.configuration[
                            ag_id_str
                            ]['MODULE_' + str(m)][
                            'PARALLEL_MODULE_' + str(pm)]['package']

                        if 'class' not in self.configuration[
                            ag_id_str
                        ]['MODULE_' + str(m)]['PARALLEL_MODULE_' + str(pm)]:
                            raise ValueError(
                                f'No arguments provided for parallel module '
                                f'{pm} of module {m}!')

                        klass = self.configuration[
                            ag_id_str
                        ]['MODULE_' + str(m)][
                            'PARALLEL_MODULE_' + str(pm)]['class']

                        # Append global arguments
                        # (add configuration by default)
                        args = deepcopy(self.global_arguments)
                        if 'arguments' in \
                                self.configuration[
                                    ag_id_str
                                ]['MODULE_' + str(m)][
                                    'PARALLEL_MODULE_' + str(pm)]:
                            args.update(
                                self.configuration[
                                    ag_id_str
                                ]['MODULE_' + str(m)][
                                    'PARALLEL_MODULE_' + str(pm)]['arguments'])

                        parallel_modules.append(
                            self.load_module(package, klass, args))

                    self.ConversationalModules.append(
                        parallel_modules
                    )

                else:
                    if 'package' not in self.configuration[
                        ag_id_str
                    ]['MODULE_' + str(m)]:
                        raise ValueError(f'No arguments provided for module '
                                         f'{m}!')

                    package = self.configuration[
                        ag_id_str
                    ]['MODULE_' + str(m)]['package']

                    if 'class' not in self.configuration[
                        ag_id_str
                    ]['MODULE_' + str(m)]:
                        raise ValueError(f'No arguments provided for module '
                                         f'{m}!')

                    klass = self.configuration[
                        ag_id_str
                    ]['MODULE_' + str(m)]['class']

                    # Append global arguments (add configuration by default)
                    args = deepcopy(self.global_arguments)
                    if 'arguments' in \
                            self.configuration[
                                ag_id_str
                            ]['MODULE_' + str(m)]:
                        args.update(
                            self.configuration[
                                'AGENT_'+str(agent_id)
                            ]['MODULE_'+str(m)]['arguments'])

                    self.ConversationalModules.append(
                        self.load_module(package, klass, args)
                    )

        else:
            raise AttributeError('ConversationalGenericAgent: '
                                 'No settings (config) provided!')

        # TODO: Parse config modules I/O and raise error if
        #       any inconsistencies found

        # Initialize automatic speech recognizer, if necessary
        self.asr = None
        if self.INTERACTION_MODE == 'speech' and not self.USE_GUI:
            self.asr = speech_rec.Recognizer()

        Initialize the internal structures of this agent.

        :param configuration: a dictionary representing the configuration file
        :param agent_id: an integer, this agent's id
        
plato/agent/conversational_agent/conversational_generic_agent.py