Open draggett opened 4 years ago
The idea above on sub-tasks needs extending to allow you to specify which task is the parent task, as the module could have a number of tasks active at any given time. One idea would be to use @task
to name a currently active task, and @subtask
to enter a subtask for that task. If @task
is missing, or doesn't name a currently active task, then @subtask
would have the same semantics as @enter
. We need to try this out to see how useful sub-tasks are in practice.
Further work is needed to better understand tasks:
In the robot demo, rules can initiate actions that have continuations which are pushed as goals when the action completes. This triggers further behaviour. These actions involve such things as the movement of the robot arm which run in real-time concurrently with the rule engine. This allows the cognitive engine to concurrently control multiple devices.
Concurrent tasks are possible if the actions for each task includes the information needed to continue to the next step. This could be provided directly via properties of the action, or indirectly in task models held in the cortex. The latter would enable introspection over current tasks. Each rule would need to identify which task it applies to. This involves the name of the task and the specific instance.
In regular programming languages, functions have names and the specific instance is represented by the stack frame with the variables declared by the function and implicitly for the case of the return address and caller stack frame. For chunks, we could use a chunk in lieu of the stack frame. The chunk type would correspond to the function name. The chunk's properties would correspond to the function's local variables.
A task module would be one way to expose the "stack frame" to rules via the module chunk buffer. Actions could then support switching to a new task, invoking sub-tasks, completing a task, and abandoning a task along with its subtasks. When invoking tasks, a new instance is created along with continuation parameters.
The rule for completing a task pushes a goal for the continuation, and sets the task buffer to the parent task. The latter is implicit in the corresponding action, e.g. @do
return. The goal properties can be updated to pass back any results.
Rules that invoke sub-tasks set the task buffer to the new task and saves a reference to the parent task as a property. The task chunk is saved to the task module's graph to obtain its chunk ID.
Long running actions are responsible for pushing a goal and updating the task chunk when the action completes. This allows the engine to continue with other rules whilst the long running action is still executing. The engine can likewise be concurrently executing multiple tasks. Each rule for a given task is triggered by a matching goal and task, and is responsible for updating the goal and task as needed. For asynchronous operations, this will ensure that the engine can continue to the next step for that task.
Can we use the goal module for tasks?
At present the goal module is only used for the goal chunk buffer. It would seem reasonable to use the goal module graph to hold tasks - can that be made to work?
There is no problem with using @do put
with the goal module, followed by an @do update
to set the next goal. If the goal is specific to a given task, we can test for the task as a property. Several tasks could be active concurrently, using task specific goals to trigger asynchronous processing that sets the next goal when complete.
My current support for @task
, @enter
and @leave
avoids the need to preserve the task when updating the goal. I need to check whether the perceived problem is significant or not. One challenge is where you want to exploit a rule set for a common use case where that rule set is not specific to this task. The question is how to resume this task when that rule set completes. In principle, such rule sets could support a standard means for continuation on success or failure.
I need more experience on a wider set of use cases, starting with natural language processing.
Tasks allow you to write rules that are only applicable to specific tasks. Tasks are associated with modules, and a given module can have multiple active tasks at the same time. You can use
@task
to name a task in a rule condition. This will succeed if the named task is currently active for the module for that condition. The set of active tasks are held independently of the module's buffer. Clearing the buffer doesn't clear the tasks. In rule actions you can use@enter
with the name of a task to enter, and@leave
with the name of a task to leave. You can enter or leave multiple tasks by using comma separated lists of task names.Tasks and contexts are complementary. You use
@context
to name a particular event/situation, e.g. having dinner at a restaurant, and@task
to segregate rules for different tasks within the overall plan for having dinner (finding a table, reviewing the menu, ordering, paying the bill).Note: it might be convenient to automatically leave sub-tasks when leaving a task. For this we would need a way to enter a sub-task, e.g.
@subtask task1.1
. This is part of the general challenge for reasoning about plans at multiple levels of abstraction, and being able to cope with the variety of different ways that natural language allows you to say more or less the same thing.See https://github.com/w3c/cogai/blob/master/chunks-and-rules.md#tasks