Skip to content

Commit

Permalink
Add a new low-confidence response logic adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
gunthercox committed Nov 17, 2016
1 parent 8fea7ba commit f52f363
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 7 deletions.
7 changes: 4 additions & 3 deletions chatterbot/adapters/logic/__init__.py
@@ -1,9 +1,10 @@
from .logic_adapter import LogicAdapter
from .approximate_sentence_match import ApproximateSentenceMatchAdapter
from .closest_match import ClosestMatchAdapter
from .closest_meaning import ClosestMeaningAdapter
from .time_adapter import TimeLogicAdapter
from .low_confidence import LowConfidenceAdapter
from .mathematical_evaluation import MathematicalEvaluation
from .multi_adapter import MultiLogicAdapter
from .no_knowledge_adapter import NoKnowledgeAdapter
from .mathematical_evaluation import MathematicalEvaluation
from .approximate_sentence_match import ApproximateSentenceMatchAdapter
from .sentiment_adapter import SentimentAdapter
from .time_adapter import TimeLogicAdapter
35 changes: 35 additions & 0 deletions chatterbot/adapters/logic/low_confidence.py
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from chatterbot.conversation import Statement
from .base_match import BaseMatchAdapter


class LowConfidenceAdapter(BaseMatchAdapter):
"""
Returns a default response with a high confidence
when a high confidence response is not known.
"""

def __init__(self, **kwargs):
super(LowConfidenceAdapter, self).__init__(**kwargs)

self.confidence_threshold = kwargs.get('threshold', 0.65)
self.default_response = kwargs.get(
'default_response',
"I'm sorry, I do not understand."
)

def process(self, input_statement):
"""
Return a default response with a high confidence if
a high confidence response is not known.
"""
# Select the closest match to the input statement
confidence, closest_match = self.get(input_statement)

# Confidence should be high only if it is less than the threshold
if confidence < self.confidence_threshold:
confidence = 1
else:
confidence = 0

return confidence, Statement(self.default_response)
25 changes: 23 additions & 2 deletions docs/adapters/logic.rst
Expand Up @@ -26,6 +26,7 @@ take priority.
]
)
Closest Match Adapter
=====================

Expand All @@ -38,6 +39,7 @@ How it works

The closest match algorithm determines the similarity between the input statement and a set of known statements. For example, there is a 65% similarity between the statements *"where is the post office?"* and *"looking for the post office"*. The closest match algorithm selects the highest matching known statements and returns a response based on that selection.


Closest Meaning Adapter
=======================

Expand All @@ -50,15 +52,16 @@ How it works

The closest meaning algorithm uses the `wordnet`_ functionality of `NLTK`_ to determine the similarity of two statements based on the path similarity between each token of each statement. This is essentially an evaluation of the closeness of synonyms. The statement that has the closest path similarity of synsets to the input statement is returned.


Approximate Sentence Match Adapter
----------------------------------
==================================

.. autofunction:: chatterbot.adapters.logic.ApproximateSentenceMatchAdapter

The `ApproximateSentenceMatchAdapter` calculates a Jaccard index and give result to a given statement.

How it works
++++++++++++
------------

The Jaccard index is composed of a numerator and denominator.
In the numerator, we count the number of items that are shared between the sets.
Expand All @@ -77,6 +80,7 @@ The Jaccard index is composed of a numerator and denominator.
The union of the sets is {young, cat, very, hungry}, which has a count of four.
Therefore, our `Jaccard similarity index`_ is two divided by four, or 50%.


Time Logic Adapter
==================

Expand All @@ -91,6 +95,7 @@ Example
| User: What time is it?
| Bot: The current time is 4:45PM.

Mathematical Evaluation Adapter
===============================

Expand All @@ -107,6 +112,7 @@ Example
| User: What is four plus four?
| Bot: (4 + 4) = 8

SentimentAdapter
================

Expand All @@ -115,6 +121,21 @@ SentimentAdapter
This is a logic adapter that selects a response that has the closest matching
sentiment value to the input.


Low Confidence Response Adapter
===============================

This adapter returns a specified default response if a response can not be
determined with a high amount of confidence.

.. autofunction:: chatterbot.adapters.logic.LowConfidenceAdapter

Example usage
-------------

.. literalinclude:: ../../examples/default_response_example.py
:language: python

.. _wordnet: http://www.nltk.org/howto/wordnet.html
.. _NLTK: http://www.nltk.org/
.. _`Jaccard similarity index`: https://en.wikipedia.org/wiki/Jaccard_index
4 changes: 2 additions & 2 deletions docs/adapters/output.rst
Expand Up @@ -78,14 +78,14 @@ Be sure to also see the documentation for the :ref:`HipChat input adapter <hipch
)
Microsoft Adapter
===============
=================

.. autofunction:: chatterbot.adapters.output.Microsoft

This is an output adapter that allows a ChatterBot instance to send responses
to a `Microsoft`_ using *Direct Line protocol*.

Be sure to also see the documentation for the :ref:`Microsoft input adapter <microsoft-input-adapter>`.
Be sure to also see the documentation for the `Microsoft input adapter <microsoft-input-adapter>`_.

.. code-block:: python
Expand Down
32 changes: 32 additions & 0 deletions examples/default_response_example.py
@@ -0,0 +1,32 @@
from chatterbot import ChatBot


# Create a new instance of a ChatBot
bot = ChatBot(
'Default Response Example Bot',
storage_adapter='chatterbot.adapters.storage.JsonFileStorageAdapter',
logic_adapters=[
{
'import_path': 'chatterbot.adapters.logic.ClosestMatchAdapter'
},
{
'import_path': 'chatterbot.adapters.logic.LowConfidenceAdapter',
'threshold': 0.65,
'default_response': 'I am sorry, but I do not understand.'
}
],
trainer='chatterbot.trainers.ListTrainer'
)

# Train the chat bot with a few responses
bot.train([
'How can I help you?',
'I want to create a chat bot',
'Have you read the documentation?',
'No, I have not',
'This should help get you started: http://chatterbot.rtfd.org/en/latest/quickstart.html'
])

# Get a response for some unexpected input
response = bot.get_response('How do I make an omelette?')
print(response)
60 changes: 60 additions & 0 deletions tests/logic_adapter_tests/test_low_confidence_adapter.py
@@ -0,0 +1,60 @@
from unittest import TestCase
from mock import MagicMock
from chatterbot.adapters.logic import LowConfidenceAdapter
from chatterbot.conversation import Statement, Response
from .test_closest_match import MockContext


class LowConfidenceAdapterTestCase(TestCase):
"""
Test cases for the LowConfidenceAdapter
"""

def setUp(self):
super(LowConfidenceAdapterTestCase, self).setUp()
self.adapter = LowConfidenceAdapter()

# Add a mock storage adapter to the context
self.adapter.set_context(MockContext())

possible_choices = [
Statement('Who do you love?', in_response_to=[
Response('I hear you are going on a quest?')
]),
Statement('What is the meaning of life?', in_response_to=[
Response('Yuck, black licorice jelly beans.')
]),
Statement('I am Iron Man.', in_response_to=[
Response('What... is your quest?')
]),
Statement('What... is your quest?', in_response_to=[
Response('I am Iron Man.')
]),
Statement('Yuck, black licorice jelly beans.', in_response_to=[
Response('What is the meaning of life?')
]),
Statement('I hear you are going on a quest?', in_response_to=[
Response('Who do you love?')
]),
]
self.adapter.context.storage.filter = MagicMock(return_value=possible_choices)

def test_high_confidence(self):
"""
Test the case that I high confidence response is known.
"""
statement = Statement('What is your quest?')
confidence, match = self.adapter.process(statement)

self.assertEqual(confidence, 0)
self.assertEqual(match, self.adapter.default_response)

def test_low_confidence(self):
"""
Test the case that a high confidence response is not known.
"""
statement = Statement('Is this a tomato?')
confidence, match = self.adapter.process(statement)

self.assertEqual(confidence, 1)
self.assertEqual(match, self.adapter.default_response)

0 comments on commit f52f363

Please sign in to comment.