<commit_before>:desc: Leverage information from knowledge bases inside conversations using ActionQueryKnowledgeBase
       in open source bot framework Rasa.

.. _knowledge_bases:

Knowledge Base Actions
======================

.. edit-link::

.. warning::
   This feature is experimental.
   We introduce experimental features to get feedback on implementations, so do please try it out.
   However, the functionality might be changed or removed in the future.
   If you have feedback (positive or negative) please comment in the `community forum <https://forum.rasa.com>`_.

.. contents::
   :local:


Using knowledge base actions you are able to handle the following kind of dialogues:

.. image:: ../_static/images/knowledge-base-example.png

As you can see users might want to obtain detailed information about certain objects, such as restaurants or hotels,
during a conversation.
They want to obtain details, such as, if an restaurant has outside seating or how expensive the restaurant is.
In order to answer those user requests domain knowledge is needed.
Hard-coding the information would not help as the information are subject to change.
Additionally, users do not only refer to objects by their names, but also use terms, such as "the first one" or "it",
to refer to a specific restaurant.
Restaurants need to be recognised and reused at a later point in the conversation.

To handle the above challenges, we recommend that you create a custom action that inherits from ``ActionQueryKnowledgeBase``.
This is a single actions which contains the logic to query a knowledge base for objects and their attributes.
The action is also able to resolve certain mention of objects, such as ``the first one`` or ``that restaurant``.
You can find a complete example in ``examples/knowledge_base_bot``.

.. note::
   There is a tutorial `here <https://blog.rasa.com/integrating-rasa-with-knowledge-bases/>`_ about how to use
   knowledge bases in custom actions. The tutorial will explain in detail the implementation behind
   ``ActionQueryKnowledgeBase``.


Create a Knowledge Base
-----------------------

The data you will use to answer the user's request comes from a knowledge base.
A knowledge base can be used to store complex data structures.
If you just have some data points that fit into memory, you can use our ``InMemoryKnowledgeBase`` implementation.
To initialize an ``InMemoryKnowledgeBase`` you need to provide the data in a json file.

Let's take a look at an example:

.. code-block:: json

    {
        "restaurant": [
            {
                "id": 0,
                "name": "Donath",
                "cuisine": "Italian",
                "outside-seating": true,
                "price-range": "mid-range"
            },
            {
                "id": 1,
                "name": "Berlin Burrito Company",
                "cuisine": "Mexican",
                "outside-seating": false,
                "price-range": "cheap"
            },
            {
                "id": 2,
                "name": "<NAME>",
                "cuisine": "Italian",
                "outside-seating": true,
                "price-range": "mid-range"
            }
        ],
        "hotel": [
            {
                "id": 0,
                "name": "Hilton",
                "price-range": "expensive",
                "breakfast-included": true,
                "city": "Berlin",
                "free-wifi": true,
                "star-rating": 5,
                "swimming-pool": true
            },
            {
                "id": 1,
                "name": "Hilton",
                "price-range": "expensive",
                "breakfast-included": true,
                "city": "Frankfurt am Main",
                "free-wifi": true,
                "star-rating": 4,
                "swimming-pool": false
            },
            {
                "id": 2,
                "name": "B&B",
                "price-range": "mid-range",
                "breakfast-included": false,
                "city": "Berlin",
                "free-wifi": false,
                "star-rating": 1,
                "swimming-pool": false
            },
        ]
    }

The above json file contains data about restaurants and hotels.
The json structure should contain a key for every object type, i.e. "restaurant" and "hotel".
Every object type maps to a list of objects.

Once the data are defined in a json file, called, for example, ``data.json``, you can create your
``InMemoryKnowledgeBase``:

.. code-block:: python

    knowledge_base = InMemoryKnowledgeBase.load("data.json")

Customize your InMemoryKnowledgeBase
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The class ``InMemoryKnowledgeBase`` inherits ``KnowledgeBase``.
You can customize your ``InMemoryKnowledgeBase`` by overwriting the following functions:

- ``get_key_attribute_of_object``: To keep track of what object the user was talking about last, we store the value
  of the key attribute in a specific slot. Every object should have a key attribute that is unique, i.e.
  similar to the primary key in a relation database. By default the name of the key attribute for every object type
  is set to "id". You can overwrite the name of the key attribute for a specific object type by calling
  ``set_key_attribute_of_object()``.
- ``get_representation_function_of_object``: This methods returns a lambda function that maps an object from the
  knowledge base to a string representation. This function is used whenever an object is outputted to the user.
  By default the lambda function is set to ``lambda obj: obj["name"]``. So, it returns the value of the attribute
  "name" of the object. If your object does not have an attribute "name", or the "name" of an object might be
  ambiguous, you should set a new lambda function for that object type by calling
  ``set_representation_function_of_object()``.
- ``set_ordinal_mention_mapping``: The ordinal mention mapping is needed to resolve an ordinal mention to an object
  in a list. For example, if the bot listed a few restaurants in Berlin, and the user then asked "Does the second one
  have outside seating?", you need to resolve "second one" to the correct object the bot listed before. Per
  default the ordinal mention mapping looks like this:

  .. code-block:: python

      {
          "1": lambda l: l[0],
          "2": lambda l: l[1],
          "3": lambda l: l[2],
          "4": lambda l: l[3],
          "5": lambda l: l[4],
          "6": lambda l: l[5],
          "7": lambda l: l[6],
          "8": lambda l: l[7],
          "9": lambda l: l[8],
          "10": lambda l: l[9],
          "ANY": lambda l: random.choice(list),
          "LAST": lambda l: l[-1],
      }

  You can overwrite it by calling the function ``set_ordinal_mention_mapping``.<commit_msg>improve explanation of representation function<commit_after>:desc: Leverage information from knowledge bases inside conversations using ActionQueryKnowledgeBase
       in open source bot framework Rasa.

.. _knowledge_bases:

Knowledge Base Actions
======================

.. edit-link::

.. warning::
   This feature is experimental.
   We introduce experimental features to get feedback on implementations, so do please try it out.
   However, the functionality might be changed or removed in the future.
   If you have feedback (positive or negative) please comment in the `community forum <https://forum.rasa.com>`_.

.. contents::
   :local:


Using knowledge base actions you are able to handle the following kind of dialogues:

.. image:: ../_static/images/knowledge-base-example.png

As you can see users might want to obtain detailed information about certain objects, such as restaurants or hotels,
during a conversation.
They want to obtain details, such as, if an restaurant has outside seating or how expensive the restaurant is.
In order to answer those user requests domain knowledge is needed.
Hard-coding the information would not help as the information are subject to change.
Additionally, users do not only refer to objects by their names, but also use terms, such as "the first one" or "it",
to refer to a specific restaurant.
Restaurants need to be recognised and reused at a later point in the conversation.

To handle the above challenges, we recommend that you create a custom action that inherits from ``ActionQueryKnowledgeBase``.
This is a single actions which contains the logic to query a knowledge base for objects and their attributes.
The action is also able to resolve certain mention of objects, such as ``the first one`` or ``that restaurant``.
You can find a complete example in ``examples/knowledge_base_bot``.

.. note::
   There is a tutorial `here <https://blog.rasa.com/integrating-rasa-with-knowledge-bases/>`_ about how to use
   knowledge bases in custom actions. The tutorial will explain in detail the implementation behind
   ``ActionQueryKnowledgeBase``.


Create a Knowledge Base
-----------------------

The data you will use to answer the user's request comes from a knowledge base.
A knowledge base can be used to store complex data structures.
If you just have some data points that fit into memory, you can use our ``InMemoryKnowledgeBase`` implementation.
To initialize an ``InMemoryKnowledgeBase`` you need to provide the data in a json file.

Let's take a look at an example:

.. code-block:: json

    {
        "restaurant": [
            {
                "id": 0,
                "name": "Donath",
                "cuisine": "Italian",
                "outside-seating": true,
                "price-range": "mid-range"
            },
            {
                "id": 1,
                "name": "Berlin Burrito Company",
                "cuisine": "Mexican",
                "outside-seating": false,
                "price-range": "cheap"
            },
            {
                "id": 2,
                "name": "<NAME> forni",
                "cuisine": "Italian",
                "outside-seating": true,
                "price-range": "mid-range"
            }
        ],
        "hotel": [
            {
                "id": 0,
                "name": "Hilton",
                "price-range": "expensive",
                "breakfast-included": true,
                "city": "Berlin",
                "free-wifi": true,
                "star-rating": 5,
                "swimming-pool": true
            },
            {
                "id": 1,
                "name": "Hilton",
                "price-range": "expensive",
                "breakfast-included": true,
                "city": "Frankfurt am Main",
                "free-wifi": true,
                "star-rating": 4,
                "swimming-pool": false
            },
            {
                "id": 2,
                "name": "B&B",
                "price-range": "mid-range",
                "breakfast-included": false,
                "city": "Berlin",
                "free-wifi": false,
                "star-rating": 1,
                "swimming-pool": false
            },
        ]
    }

The above json file contains data about restaurants and hotels.
The json structure should contain a key for every object type, i.e. "restaurant" and "hotel".
Every object type maps to a list of objects.

Once the data are defined in a json file, called, for example, ``data.json``, you can create your
``InMemoryKnowledgeBase``:

.. code-block:: python

    knowledge_base = InMemoryKnowledgeBase.load("data.json")

Customize your InMemoryKnowledgeBase
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The class ``InMemoryKnowledgeBase`` inherits ``KnowledgeBase``.
You can customize your ``InMemoryKnowledgeBase`` by overwriting the following functions:

- ``get_key_attribute_of_object``: To keep track of what object the user was talking about last, we store the value
  of the key attribute in a specific slot. Every object should have a key attribute that is unique, i.e.
  similar to the primary key in a relation database. By default the name of the key attribute for every object type
  is set to "id". You can overwrite the name of the key attribute for a specific object type by calling
  ``set_key_attribute_of_object()``.
- ``get_representation_function_of_object``: Let's focus on the following restaurant:

  .. code-block:: json

      {
          "id": 0,
          "name": "Donath",
          "cuisine": "Italian",
          "outside-seating": true,
          "price-range": "mid-range"
      }

  When the user is asking to list any Italian restaurant, you don't want to confront the user with all details of that
  restaurant. You want to provide a meaningful name that identifies the restaurant. Most likely you would use
  just the name of the restaurant to speak about it.
  Thus, the function ``get_representation_function_of_object`` returns a lambda function that maps, for example, the
  above restaurant object to its name.

  .. code-block:: python

      lambda obj: obj["name"]

  This function is used whenever the bot is talking about a specific object, so that the user is given a meaningful
  name and knows what exactly the bot is talking about.
  By default the lambda function is set to ``lambda obj: obj["name"]``. So, it returns the value of the attribute
  "name" of the object. If your object does not have an attribute "name", or the "name" of an object might be
  ambiguous, you should set a new lambda function for that object type by calling
  ``set_representation_function_of_object()``.
- ``set_ordinal_mention_mapping``: The ordinal mention mapping is needed to resolve an ordinal mention to an object
  in a list. For example, if the bot listed a few restaurants in Berlin, and the user then asked "Does the second one
  have outside seating?", you need to resolve "second one" to the correct object the bot listed before. Per
  default the ordinal mention mapping looks like this:

  .. code-block:: python

      {
          "1": lambda l: l[0],
          "2": lambda l: l[1],
          "3": lambda l: l[2],
          "4": lambda l: l[3],
          "5": lambda l: l[4],
          "6": lambda l: l[5],
          "7": lambda l: l[6],
          "8": lambda l: l[7],
          "9": lambda l: l[8],
          "10": lambda l: l[9],
          "ANY": lambda l: random.choice(list),
          "LAST": lambda l: l[-1],
      }

  You can overwrite it by calling the function ``set_ordinal_mention_mapping``.
{'fraction_non_alphanumeric': 0.09970145108657918, 'fraction_numerical': 0.0043740887315142675, 'mean_word_length': 2.423003802281369, 'pattern_counts': {'":': 111, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2241776', 'n_tokens_mistral': 3879, 'n_tokens_neox': 3624, 'n_words': 1686}