{"id":20235102,"url":"https://github.com/mivallion/reactive_deliberative","last_synced_at":"2026-06-09T10:31:34.207Z","repository":{"id":62592447,"uuid":"467050683","full_name":"mivallion/reactive_deliberative","owner":"mivallion","description":"The reactive_deliberative project allows creating applications with a reactive-deliberative architecture.","archived":false,"fork":false,"pushed_at":"2022-06-19T11:38:42.000Z","size":313,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-14T12:00:33.453Z","etag":null,"topics":["asyncio","deliberative-agent","intelligent-agent","intelligent-systems","python","python3","reactive","rete"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/reactive-deliberative/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mivallion.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-07T10:41:29.000Z","updated_at":"2022-06-19T12:10:00.000Z","dependencies_parsed_at":"2022-11-04T07:20:46.912Z","dependency_job_id":null,"html_url":"https://github.com/mivallion/reactive_deliberative","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mivallion/reactive_deliberative","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivallion%2Freactive_deliberative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivallion%2Freactive_deliberative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivallion%2Freactive_deliberative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivallion%2Freactive_deliberative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mivallion","download_url":"https://codeload.github.com/mivallion/reactive_deliberative/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivallion%2Freactive_deliberative/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34103355,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","deliberative-agent","intelligent-agent","intelligent-systems","python","python3","reactive","rete"],"created_at":"2024-11-14T08:14:40.068Z","updated_at":"2026-06-09T10:31:34.189Z","avatar_url":"https://github.com/mivallion.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# reactive_deliberative\n\n## Introduction\n\nThe reactive_deliberative project allows creating applications\nwith a reactive-deliberative architecture.\n\nThe hybrid deliberative-reactive architecture arose as a result of the recognition\nthat there is a use of symbolic knowledge in describing the behavior of an intelligent agent.\n\nSuch an architecture allows an agent with reactive behavior to flexibly adjust \nits behavior due to a high-level scheduler in accordance with the current task, \nwhile at the same time taking into account the state of the environment. For \nthe agent, which is a kind of intelligent system, it will provide an opportunity \nto respond to unplanned environmental changes.\n\nIn simple words, the agent follows the behavior described in the rules, interrupting for reactive actions.\n\n![architecture](./img/1.png)\n\nThis system is built using one the implementation of the Rete algorithms - [py_rete][py_rete].\n\n## Installation\n\nThis package is installable via pip with the following command:\n`pip install -U reactive_deliberative`.\n\n## The Basics\n\nThe two high-level structures to support reasoning with py_rete are **facts**\nand **productions**. \n\n### Facts\n\nFacts represent the basic units of knowledge that the productions match over.\nHere are a few examples of facts and how they work.\n\n1. *Facts* are a subclass of dict, so you can treat them similar to dictionaries.\n\n```python\n\u003e\u003e\u003e f = Fact(a=1, b=2)\n\u003e\u003e\u003e f['a']\n1\n```\n\n2. *Facts* extend dictionaries, so they also support positional values without\n   keys. These values are assigned numerical indices based on their position.\n\n```python\n\u003e\u003e\u003e f = Fact('a', 'b', 'c')\n\u003e\u003e\u003e f[0]\n'a'\n```\n\n3. *Facts* can support mixed positional and named arguments, but positional\n   must come before named and named arguments do not get positional references.\n\n```python\n\u003e\u003e\u003e f = Fact('a', 'b', c=3, d=4)\n\u003e\u003e\u003e f[0]\n'a'\n\u003e\u003e\u003e f['c']\n3\n```\n\n5. *Facts* support nesting with other facts. \n\n```python\n\u003e\u003e\u003e f = Fact(subfact=Fact())\nFact(subfact=Fact())\n```\n\nNote that there will be issues if facts contain other data structures that\ncontain facts (they will not be properly added to the rete network or to\nproductions).\n\n### Productions\n\n*Productions* have two components:\n* Conditions, which are essentially facts that can contain pattern matching\n  variables.\n* A Function, which is executed for each rule match, with the arguments to the\n  function being passed the bindings from pattern matching variables.\n\nHere is an example of a simple *Productions* that binds with all *Facts* that\nhave the color red and prints 'I found something red' for each one:\n\n```python\n@Production(Fact(color='red'))\ndef alert_something_red():\n    print(\"I found something red\")\n```\n\nProductions also support logical operators to express more complex conditions.\n\n```python\n@Production(AND(OR(Fact(color='red'),\n                   Fact(color='blue')),\n\t        NOT(Fact(color='green'))))\ndef alert_something_complex():\n    print(\"I found something red or blue without any green present\")\n```\n\nBitwise logical operators can be used as shorthand to make composing complex conditions easier.\n```python\n@Production((Fact(color='red') | Fact(color='blue')) \u0026 ~Fact(color='green'))\ndef alert_something_complex2():\n    print(\"I found something red or blue without any green present\")\n```\n\nIn addition to matching simple facts, pattern matching variables can be used to\nmatch values from Facts. Matching ensures that variable bindings are consistent\nacross conditions. Additionally, variables are passed to arguments in the function\nwith the same name during matching. For example, the following production finds\na Fact with a lastname attribute.  For each Fact it finds, it prints \"I found a\nfact with a lastname attribute: `\u003clastname\u003e`\".  Note, the `V('lastname')`\ncorresponds to a variable named lastname that can bind with values from Facts\nduring matching.  Additionally the variable (`V('lastname')`) and the function\nargument `lastname` match have the same name, which enables the matcher to the\nvariable bindings into the function.\n```python\n@Production(Fact(lastname=V('lastname')))\ndef found_relatives(lastname):\n    print(\"I found a fact with a lastname: {}\".format(lastname))\n```\n\nIt is also possible to employ functional tests (lambdas or functions) using\n`Filter` conditions. Like the function that is being decorated, Filter\nconditions pass variable bindings to their equivelently named function\narguments. It is important to note that positive facts that bind with these\nvariables need to be listed in the production before the tests that use them.\n```python\n@Production(Fact(value=V('a')) \u0026\n            Fact(value=V('b')) \u0026\n            Filter(lambda a, b: a \u003e b) \u0026\n            Fact(value=V('c')) \u0026\n            Filter(lambda b, c: b \u003e c))\ndef three_values(a, b, c):\n    print(\"{} is greater than {} is greater than {}\".format(a, b, c))\n```\n\nIt is also possible to bind *facts* to variables as well, using the bitshift\noperator.\n```python\n@Production(V('name_fact') \u003c\u003c Fact(name=V('name')))\ndef found_name(name_fact):\n    print(\"I found a name fact {}\".format(name_fact))\n```\n\nProductions can have priority and execution timeout:\n\n```python\n@Production(V('fact') \u003c\u003c Fact(state=\"transform\"), priority=2, timeout=1)\nasync def transform(net, fact):\n...\n```\n\nProductions can be asynchronous:\n\n```python\n@Production(V('name_fact') \u003c\u003c Fact(name=V('name')))\nasync def found_name(name_fact):\n    print(\"I found a name fact {} and going to sleep\".format(name_fact))\n    await asyncio.sleep(10)\n```\n\n### ReactiveDeliberative\n\nHere is how you create an engine:\n\n```python\nrd = ReactiveDeliberative()\n```\n\nOnce an engine has been created, then facts can be added to it.\n```python\nf1 = Fact(light_color=\"red\")\nrd.add_fact(f1)\n```\n\nNote, facts added to the network cannot contain any variables or they will\ntrigger an exception when added. Additionally, once a fact has been added to\nnetwork it is assigned a unique internal identifier.\n\nThis makes it possible to update the fact.\n```python\nf1['light_color'] = \"green\"\nnet.update_fact(f1)\n```\n\nIt also make it possible to remove the fact.\n```python\nrd.remove_fact(f1)\n```\n\nWhen updating a fact, note that it is not updated in the network until\nthe `update_fact` method is called on it. An update essentially equates to\nremoving and re-adding the fact.\n\nProductions can also be added to the network. Productions also can make use of\nthe `net` variable, which is automatically bound to the Rete network the\nproduction has been added to. This makes it possible for productions to update\nthe contents of the network when they are fired. For example, the following functions\nhave an argument called `net` that is bound to the rete network even though there is\nno variable by that name in the production conditions.\n```python\n\u003e\u003e\u003e f1 = Fact(light_color=\"red\")\n\u003e\u003e\u003e \n\u003e\u003e\u003e @Production(V('fact') \u003c\u003c Fact(light_color=\"red\"))\n\u003e\u003e\u003e def make_green(net, fact):\n\u003e\u003e\u003e\tprint('making green')\n\u003e\u003e\u003e     fact['light_color'] = 'green'\n\u003e\u003e\u003e     net.update_fact(fact)\n\u003e\u003e\u003e \n\u003e\u003e\u003e @Production(V('fact') \u003c\u003c Fact(light_color=\"green\"))\n\u003e\u003e\u003e def make_red(net, fact):\n\u003e\u003e\u003e\tprint('making red')\n\u003e\u003e\u003e     fact['light_color'] = 'red'\n\u003e\u003e\u003e     net.update_fact(fact)\n\u003e\u003e\u003e \n\u003e\u003e\u003e rd = ReactiveDeliberative()\n\u003e\u003e\u003e rd.add_fact(f1)\n\u003e\u003e\u003e rd.add_production(make_green)\n\u003e\u003e\u003e rd.add_production(make_red)\n```\n\nOnce the above fact and productions have been added the network can be run in the infinite loop. \n```python\n\u003e\u003e\u003e rd.run()\nmaking green\nmaking red\nmaking green\nmaking red\nmaking green\n...\n```\n\n### Reactive predicates and actions\n\nPredicate functions and action functions must be asynchronous.\n\nThe predicate function can contain any calls and calculations,\nbut it must return a Boolean value. If the predicate is executed,\na reactive action will be performed. For example:\n\n```python\nasync def external_upload_predicate():\n    return await dumper.external_upload_predicate()\n\n\nasync def external_upload_action():\n    return await dumper.external_upload_action()\n```\n\nAfter declaring the function, you can add them to the engine:\n\n```python\nrd.add_reactive_action(external_upload_predicate, external_upload_action)\n```\n\nBy default, predicate trigger stops the deliberative loop for the duration of the reactive action.\nTo disable this future pass `force=False` argument in `add_reactive_action`:\n```python\nrd.add_reactive_action(external_upload_predicate, external_upload_action, force=False)\n```\n\n\n[py_rete]: https://github.com/cmaclell/py_rete","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmivallion%2Freactive_deliberative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmivallion%2Freactive_deliberative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmivallion%2Freactive_deliberative/lists"}