Agent class is big and hard to maintainAgent:Agent / ProcessorTrackerStore: Separate it from the model specific domain (the TrackerStore should not know of the model domain)Processor class to the Model interface but could use this then as before
This UML diagram how the workflow is when running rasa train
It shows how
TrainingDataImporterModelFactory is selected based on a default value / config 
It shows how
TrackerStore (returns a list of events instead of a DialogueStateTrackerModel interface
This shows how:
ModelServer (that's the most vague part I'd say)
This looks very interesting and I want to understand it better - would you mind adding a very short description of what each UML diagram describes? (just to understand their context better)
@federicotdn Done. Do you have any specific questions which I should clarify?
How does this simplify e.g. the implementation of the response selector?
A couple of questions:
Agent and Processor into a new class, Assistant, right? Asking because it's not explicitly mentioned.ModelFactory. It would separate the logic of creating a model (which is relatively complex, if training is involved) with actually using the model. However, I don't understand why ModelFactory is in charge of saving the models to files. That sounds like it could go either in Model itself (like it is now), or maybe ModelFactory should be renamed to represent the fact that it has more responsibilities than just loading models.TrackerStore.get_or_create_tracker should return a list of events, rather than a DialogueStateTracker itself? Isn't the latter a better way of representing a series of conversation events?ModelRepository class. In the last UML diagram, it looks like it is delegating the model loading to ModelFactory.DialogueStateTracker to Tracker? (and then add DialogueStateTracker = Tracker below the class declaration 馃 )How does this simplify e.g. the implementation of the response selector?
Good question. If we would have a structure as proposed then you would approach implementing the feature less from a "how can I fit this in" but rather a "where does this belong" perspective. To be honest I know too less about the difficulties of the response selector implementation to propose how I would have done it given the proposed changes.
An easier example is maybe implementing an end-to-end model. You could simply create a new Model subclass and start coding (while re-using what you need from the existing modules). Both models could live side by side until we decide that we are ready too switch completely.
This means that you would like to merge Agent and Processor into a new class, Assistant, right? Asking because it's not explicitly mentioned.
I wouldn't say merging. Actually it would be more separating them since they are quite entangled at the moment. I guess that a lot of Processor logic would go into RasaModel while the Assistant would be more focused on delegating between channels / tracker stores / lock stores / models .
However, I don't understand why ModelFactory is in charge of saving the models to files.
True, I think both is possible. Might be that the ModelFactory is too much abstraction. From my view the ModelFactory is responsible for everything around (de)-serialization of the model. You could e.g. implement a different ModelFactory to store models on different sources (e.g. S3, disk, ...) or in different formats.
I couldn't understand the objective of having the ModelRepository class. In the last UML diagram, it looks like it is delegating the model loading to ModelFactory.
The ModelRepository is responsible for selecting the appropriate model, e.g. selecting the suited model from a directory on disk or from bucket on AWS. The ModelFactory would then deal with converting the selected model to the right Python objet.
Why do you think TrackerStore.get_or_create_tracker should return a list of events, rather than a DialogueStateTracker itself? Isn't the latter a better way of representing a series of conversation events?
The TrackerStore currently requires the Domain to return the DialogueStateTracker. The Domain is part of the model and shouldn't be exposed to the TrackerStore in my opinion (e.g. different models might handle the events differently). I completely agree that the Model should then apply a suited data structure to this raw lists to make the handling easier.
Can we rename DialogueStateTracker to Tracker? (and then add DialogueStateTracker = Tracker below the class declaration 馃
I'd prefer ConversationHistory actually since it's more intuitive 馃槃
In general this is just a first draft / proposal. A lot details about the interactions (e.g. between ModelRepository / ModelFactory still need revisiting.
Thanks for your detailed feedback @federicotdn !
The way I see our code is as that it is organized as a bunch of lego blocks which interact with each other through clearly defined interfaces. This makes the single blocks replaceable, the code concise and hence more flexible.
Currently, the agent / processor logic leaks into some of our existing "lego blocks" (e.g. training data, tracker store, policies) (sorry, that I forgot the lockstore).

(I believe) Introducing a Model interface + the other changes would reinforce the re-usable blocks and remove the leaks.

That looks great, I like the idea of reworking the Agent and Processor classes into Assistant, while at the same time making the boundaries between all classes better defined. Currently Agent and Processor are very tightly related and sometimes it's difficult to see what each of them individually is meant to do. I think these changes also give us the opportunity to improve our public Python API.
An issue regarding process safe handling of models. Not necessary part of this issue, but worth thinking about before implementing this https://github.com/RasaHQ/rasa/issues/5376
processor._cancel_reminders and _schedule_remindersModel interfaceDomain and TrackerStoreList[Event] for get_or_create_trackerinit_tracker somewhere else (probably to DialogueStateTracker)TrackerStore.save to accept a List[Event]processor.get_trackerexporter._fetch_events_within_time_rangesave returns a DialogueStateTrackerDialogueStateTrackerModel interfaceModel Interface Processsor implement model interfaceAgent only speak to processor via interfaceModel.parse_messageAgent.parse_message_using_nlu_interpreterModel.predictAgent.handle_messageAgent.trigger_intentModel.predict_nextAgent.predict_nextModel.executeAgent.execute_actionAgent.log_messageparse_message directly to the tracker + adding slotsTrackerStore from Processorserver module to processorAgent 馃Events (not DialogueStateTracker!) to every function of the processor which needs ittracker_store from Processor constructor get_trackerAgent still knows to much about the insides of ProcessorModelFactory : Removing Rasa Core training from AgentModelFactory interfaceModelFactory with a from_training_data_importer functionAgent.train steps in here PolicyEnsemble at this stepModelFactory which accepts the model and can save itrasa.core.train to implementation of ModelFactory Agent Ensemble to Agent as usually restaurant bot examplerasa.core.trainload_dataAgent taking to the EnsembleDialogueStateTrackers using Domain Agent still knows to much about the insides of Processornlu.train.train to factory (same part where core is trained)from_training_data_importer an implementation of the Model interface (aka Processor)save function in the ModelFactory to save everything as a Rasa Modelno need to retrain logic to ModelFactory delete toggle_memoization or think about it
How does our "no need to retrain" fit in there
ModelFactory to load Model from pathrasa.core.run.load_agent_on_start just pass the path instead of the interpreterload_agent to use the ModelFactory when loading model from pathload_from_serverload_from_remote_storageload_local_model Agent does not now how a model looks inside anymore Requirements
ModelFactory Model within the Agentthere are at least two ways to handle this: we need to think about pros / cons
Agent model loading one simple / function callback and move other related code to a new / different moduleAgent.visualizemax_historyinterpreter is None which means probably RegexInterpreterTrainingDataImporter to load required dataimporter to rasa.core.visualize and just skip agent.visualizemax_history based on config + default value + CLI variableAgent.visualize
@tmbo I split the proposal in single steps. The comment looks a bit daunting due to its size but you can focus on the step headlines and the rough plan sections. The image describes the dependencies between the steps (e.g. we can can't do Step 4 without doing Step 0 first). In my opinion every step (besides step 0, 2 & 5) makes sense to do, even if we don't continue with the subsequent steps. Some things are not figured out to the last detail, but imo that doesn't matter at this stage (e.g. how to move the details of model loading out of Agent)
Most helpful comment
Refactoring steps
Simplifications
Todos
processor._cancel_remindersand_schedule_remindersSteps
Part 0: Agree on a
ModelinterfacePart 1: The divorce of
DomainandTrackerStoreRough plan
List[Event]forget_or_create_trackerinit_trackersomewhere else (probably toDialogueStateTracker)TrackerStore.saveto accept aList[Event]Current Dependencies (just for plan ideation)
processor.get_trackerexporter._fetch_events_within_time_rangesavereturns a DialogueStateTrackerDialogueStateTrackerPart 2: Hello
ModelinterfaceRough plan
Model InterfaceProcesssorimplement model interfaceAgentonly speak to processor via interfaceCurrent Dependencies (just for plan ideation)
Model.parse_messageAgent.parse_message_using_nlu_interpreterModel.predictAgent.handle_messageAgent.trigger_intentModel.predict_nextAgent.predict_nextModel.executeAgent.execute_actionAgent.log_messageparse_messagedirectly to the tracker + adding slotsPart 3: Remove
TrackerStorefromProcessorRough plan
servermodule toprocessorAgent馃Events(notDialogueStateTracker!) to every function of the processor which needs ittracker_storefromProcessorconstructorCurrent Dependencies (just for plan ideation)
get_trackerSituation after Part 3
Agentstill knows to much about the insides ofProcessorPart 4: Hello
ModelFactory: Removing Rasa Core training fromAgentRough plan
ModelFactoryinterfaceModelFactorywith afrom_training_data_importerfunctionAgent.trainsteps in herePolicyEnsembleat this stepModelFactorywhich accepts the model and can save itrasa.core.trainto implementation ofModelFactoryAgentEnsembleto Agent as usuallyCurrent Dependencies for Rasa Core training (just for plan ideation)
restaurant bot examplerasa.core.trainload_dataAgenttaking to theEnsembleDialogueStateTrackers using DomainSituation after Part 4
Agentstill knows to much about the insides ofProcessorPart 5: ModelFactory, let me introduce you to NLU
Rough Steps
nlu.train.trainto factory (same part where core is trained)from_training_data_importeran implementation of theModelinterface (akaProcessor)savefunction in theModelFactoryto save everything as a Rasa Modelno need to retrainlogic toModelFactorydelete
toggle_memoizationor think about itOpen Question
How does our "no need to retrain" fit in there
Part 6: Agent, forget about the model internals
Rough Steps
ModelFactoryto loadModelfrom pathrasa.core.run.load_agent_on_startjust pass thepathinstead of theinterpreterload_agentto use theModelFactorywhen loading model from pathload_from_serverload_from_remote_storageload_local_modelSituation after Part 6
Agentdoes not now how a model looks inside anymorePart 7: Specialized Model loader
Requirements
ModelFactoryModelwithin theAgentRough Steps (needs further thoughts 鈥硷笍)
there are at least two ways to handle this: we need to think about pros / cons
Agentmodel loading one simple / function callback and move other related code to a new / different moduleThoughts
Step 8: Remove
Agent.visualizeCurrent Dependencies (just for plan ideation)
max_historyinterpreteris None which means probablyRegexInterpreterRough steps
TrainingDataImporterto load required dataimportertorasa.core.visualizeand just skipagent.visualizemax_historybased on config + default value + CLI variableAgent.visualizeStep dependencies