Implemented a first and basic version of a Rete rule engine

This commit is contained in:
2021-02-09 16:06:32 +01:00
parent 821dbed189
commit a2a8d5c5e5
110 changed files with 7301 additions and 1654 deletions
+34
View File
@@ -1093,3 +1093,37 @@ Blog
""""""
Hi, I have the feeling that I am almost there with the parsers part. I have
2021-01-30
**********
Blog
""""""
It's been a very long time since I wrote in the blog. I guess I was too busy to spend some time on it.
2020 was the year of the COVID 19. I guess that being contained with my son and my wife gave me other priorities.
Nevertheless, I kept on working on Sheerka.
One major achivement that I made was a demonstration of Sheerka to other people. I happened in mid September.
I demonstrated how to declare numbers using the ExactParser and the BnfNodeParser. Even thought I did not explicitly
mentionned theses names, my demo was far to technique. The impression I gave was that Sheerka was too complicated (in
the sens of too technical too use). Unfortunately, that was the case.
Did the demo come too soon ? I guess so, in a certain way, but It really helped me to have a first feedback, even if it did
not reward the months of hard works.
Where am I today ? It's impressive the admit that since that demon (in September) I did not implemented any major capability.
Sheerka does not know much bettet that at this time
I did though work on :
* performances
* debugger
* simple version of the rule engine (that goes with the debuggger)
Actually almost FOUR months of work for technical benefit. There were some parts of the code that were rewritten.
I am a little bit sad, time flies so fast.
+1
View File
@@ -26,6 +26,7 @@ There will be two types of documentation
tech/tech
tech/debugger
tech/rules
+116
View File
@@ -17,3 +17,119 @@ in an 'imperative' way.
First, you need to enable the debugger. As it consumes resource, it is deactivated by default ::
set_debug()
There are three types of objects that can be debugged :
* variables
* concepts
* rules
Debug Settings
--------------
Path to object
**************
Whatever the type of object to be displayed, it lies within a method name, which is itself inside a service.
For example, the out_tree (the ast tree that contains what to print once an input is evaluated) is under the service
'Out' and within the method 'create_out_tree'
The information regarding the evaluation of the rule #1 is under the service 'EvaluateRules', and within the method 'evaluate_rule'
Context id
**********
The same object may be requested several times. To distinguish different execution, the ExecutionContext id is used.
When using the context id, you can precise if you want to debug a specific context, or a context and its children
Debug id
********
The debug id is supposed to regroup information of the same unit of processing together. For example, within the same context,
if a specific piece of processing is called several times (because of a loop for example), they will share the same context id,
but they will have different debug id.
Note that as of now, this does not
work very well. This information need to be tuned.
Debug Variables
---------------
Debugging variables let you see the content of variables, but also the execution flow of a piece of program.
This execution flow gives a context to the variables
To activate variable debugging :
debug_var(<path_to_variable>, <context_id>, <debug_id>)
With:
* path_to_variable
* 'service_name' : Activates the debug logs for all methods of the service. The variable won't be shown
* 'service_name.method_name' : Activates the debug logs for the specific method of the service, the variables won't be shown
* 'service_name.method_name.variable_name' : Activates the display of a variable, within a specific method, for a specific service
* 'service_name.*'
* context_id
* context_id
* 'context_id+' (context_id followed by the sign '+')
* debug_id
* debug_id
Debug Rule
---------------
Debugging rules let you see how the rules are evaluated, but also the execution flow of the evaluation.
To activate rule debugging :
debug_var(<path_to_rule>, <context_id>, <debug_id>)
With:
* path_to_rule (when a string is given)
* 'service_name' : Activates the debug logs for all methods of the service. The variable won't be shown
* 'service_name.method_name' : Activates the debug logs for the specific method of the service, the variables won't be shown
* 'service_name.method_name.rule_id' : Activates the debug of a specific rule, within a specific method, for a specific service
* 'service_name.*'
* path_to_rule (when an integer is given)
* rule_id
* context_id
* context_id
* 'context_id+' (context_id followed by the sign '+')
* debug_id
* debug_id
Debug Concept
---------------
Debugging concept let you see how the concepts are evaluated, but also the execution flow of the evaluation.
To activate concept debugging :
debug_var(<path_to_concept>, <context_id>, <debug_id>)
With:
* path_to_concept when a string is given
* 'service_name' : Activates the debug logs for all methods of the service. The variable won't be shown
* 'service_name.method_name' : Activates the debug logs for the specific method of the service, the variables won't be shown
* 'service_name.method_name.concept_id' : Activates the debug of a specific concept, within a specific method, for a specific service
* 'service_name.*'
* path_to_concept when an integer is given
* concept_id
* context_id
* context_id
* 'context_id+' (context_id followed by the sign '+')
* debug_id
* debug_id
@@ -1,6 +1,16 @@
Rules
========
Abstract
****************
As I previously explain, there are two main categories of object to make Sheerka come to life :
* the concepts
* the rules
When the purpose of the concepts is to connect Sheerka with the outside world (our world),
The purpose of the rules is to define how to react in different situation.
The combination of the two, the concepts and the rules, brings the intelligence.
Basic definition
@@ -10,15 +20,18 @@ To define a new rule
::
> when <predicate> then <action>
> when <predicate> then|print <action>
Rules can have name, so you can also use the syntax
::
> def rule <name> as when <predicate> then <action>
> def rule <name> as when <predicate> then|print <action>
You can define action rule (using then) or display rule (using print). As of today, the two
sets of rules are different, but they may be merged if no notable difference is found.
Existing rule engines
*********************
@@ -53,7 +66,7 @@ I am not an expert in rule engine. So I guess that the best way to figure out wh
Use cases
*********
I see the rules engine like the caching service or the logging service. It can be used anywhere in the code.
I see the rules engine like the caching service or the logging service, in the way as it can be used anywhere in the code.
It's not just a global feature of Sheerka. It's another way of achieving common task.
For example, in the print service, I want to print all the failed ``ReturnValueConcept`` in red.
@@ -92,9 +105,70 @@ In the predicate part, I need to control how expression are evaluated.
In the predicate part, as well as in the action part, I must be able to used other concepts
::
For example, if I have the following concept already defined ::
> def concept status is not ok as <whatever suits>
> def concept paint in red as <whatever suits>
then I must be able to use ::
> when status is not ok then paint in red
Execution rules
***************
The first rule that I would like to define is the 'hello sheerka' rule. The principle is simple, if I enter 'hello sheerka', Sheerka should respond 'hello kodjo'
First proposal ::
> def greetings as hello x where x
> when greetings and greetings.x == Sheerka then 'hello kodjo'
Note that in this example, greetings is used name of concept when it is recognized, but also
as a name of variable in the test.
Why not ? ::
> def greetings as hello x where x
> when hello Sheerka then 'hello kodjo'
which is different from ::
> def greetings as hello x where x
> when 'hello Sheerka' then 'hello kodjo'
In the first version, I supposed that the concept 'greetings' is recognized
A more advanced version for the action part will be ::
> when greetings and greetings.x == Sheerka then answer(call_concept('greetings', __user))
I said earlier that I would like the rule engine to be called whenever I want. So now,
there is the important question of when calling the rule.
A rule like ``when greetings and greetings.x == Sheerka then 'hello kodjo'`` must be called after than
the concept ``greetings`` is recognized. But Should I call it before or after the 'Print' ?
And should I consider the response as a new event, this time triggerd by Sheerka, rather than by a user ?
Implementation
***************
The 'implementation' deals with how the rules are evaluated. I have two engines.
The first one, within the class ``SheerkaEvaluateRule`` is a simple and naive implementation.
It sorts the rule by priorities and evaluate them one after the other. Once rule matches the available data (the bag)
the algorithm stops.
The other implementation is more sophisticated. It's an implementation of the 'Rete'
algorithm (https://en.wikipedia.org/wiki/Rete_algorithm).
.. _phD: https://fr.wikipedia.org/wiki/Ulysse_31
.. _PyRete: https://github.com/cmaclell/py_rete
My work is based on PyRete_ wich it is itself a Python implementation of
the phD_ of Doorenbos (1995)