README
QQ can learn to answer questions.
Learn something:
qq fact: Bob's birthday is in January.
qq fact: See Jamie about new account setups.
Ask something:
qq when is Bob's birthday?
`qq who do I see about creating a new account?
This skill requires the following secrets to be configured:
nludbapikey
The API Key for NLUDB
Usage
QQ can learn to answer questions.
Learn something:
qq fact: Bob's birthday is in January.
qq fact: See Jamie about new account setups.
Ask something:
qq when is Bob's birthday?
`qq who do I see about creating a new account?
Code
class MODELS:
QA = «st_msmarco_distilbert_base_v3»
SIMILARITY = «st_paraphrase_distilroberta_base_v1»
class NLUDB:
pass
class NLUDBEmbeddingIndex:
«»»
An Embedding Index is a persistent index over embeddings.
«»»
def __init__(self, nludb: NLUDB, id: str, name: str):
self.nludb = nludb
self.name = name
self.id = id
def insert(self, value: str):
return self.nludb.post(‘embedding-index/insert’, {
«indexId»: self.id,
«value»: value
})
def embed(self):
return self.nludb.post(‘embedding-index/embed’, {
«indexId»: self.id
})
def search(self, query: str):
return self.nludb.post(‘embedding-index/search’, {
«indexId»: self.id,
«query»: query
})
class NLUDB:
def __init__(self, api_key: str=None, version: int=1):
self.api_key = api_key
self.endpoint = «https://api.nludb.com/api/v{}».format(version)
def post(self, operation: str, payload):
url = «{}/{}».format(self.endpoint, operation)
resp = requests.post(
url,
json=payload,
headers = {«Authorization»: «Bearer {}».format(self.api_key)})
j = resp.json()
if ‘data’ in j:
return j[‘data’]
else:
raise Exception(j[‘reason’])
def create_index(self, name: str, model: str, upsert: bool = False):
res = self.post(‘embedding-index/create’, {
«name»: name,
«model»: model,
«upsert»: upsert
})
return NLUDBEmbeddingIndex(nludb=self, name=name, id=res[«id»])
class NludbChatbotBase:
def __init__(self, api_key: str, server_name: str, min_confidence: float = 0.4):
self.nludb = NLUDB(api_key)
self.server_name = server_name
self.min_confidence = min_confidence
self.index = self.nludb.create_index(
server_name,
MODELS.QA,
upsert=True
)
def get_fact(self, message: str) -> str:
words = re.sub(«\s\s+» , » «, message).split(‘ ‘)
if len(words) == 0:
return None
first = words[0].lower()
if first in [‘fact’, ‘fact:’, ‘learn’, ‘learn:’]:
return » «.join(words[1:])
return None
def dispatch(self, message: str, username: str = None, roomname: str = None) -> str:
«»»
Todo: add a «forget» feature
«»»
maybe_fact = self.get_fact(message)
if maybe_fact is not None:
return self.add_fact(maybe_fact, username, roomname)
return self.answer_question(message, username, roomname)
def add_fact(self, fact: str, username: str = None, roomname: str = None) -> str:
self.index.insert(fact)
self.index.embed()
return None
def answer_question(self, question: str, username: str = None, roomname: str = None) -> Tuple[float, str]:
resp = self.index.search(question)
if ‘hits’ not in resp or len(resp[‘hits’]) == 0:
return None
confidence = resp[‘hits’][0][‘score’]
text = resp[‘hits’][0][‘text’]
if confidence < self.min_confidence:
return None
return (confidence, text)
class NludbChatbot(NludbChatbotBase):
«»»
This class is intended to wrap the core functionality with
any extra personality, error messages, etc.
Also can provide a threshold here below which either no fact is
returned or the bot humorously tries to hedge its answer..
«»»
def __init__(self, api_key: str, server_name: str):
super().__init__(api_key, server_name)
def dispatch(self, message: str, username: str = None, roomname: str = None) -> str:
message = message.strip()
words = re.split(‘\s+’, message)
if len(message) == 0 or len(words) == 0:
return «»»QQ can learn to answer questions.
Learn something:
`qq fact: Bob’s birthday is in January.`
`qq fact: See Jamie about new account setups.`
Ask something:
`qq when is Bob’s birthday?`
`qq who do I see about creating a new account?»»»
elif bot.arguments == ‘forget everything you know’:
return «I don’t yet know how to forget»
maybe_fact = self.get_fact(message)
if maybe_fact is not None:
if len(maybe_fact.strip()) == 0:
return «Say `qq fact Some fact: goes here` to learn something»
super().dispatch(message, username, roomname)
return «{}… got it!».format(maybe_fact)
# It’s a question
resp = super().dispatch(message, username, roomname)
if resp is None:
return «I’m not really sure how to answer that..\n\nAdd new facts with `qq fact: Some fact goes here`.»
score, text = resp
return text
secret = bot.secrets.read(«nludbapikey»)
if secret is None:
bot.reply(«Please set the `nludbapikey` secret in https://Ab.Bot»)
else:
# if docs is None:
# bot.reply(«I haven’t learned anything yet! Say `qq fact Some fact goes here` to teach me»)
user = bot.from_user[‘UserName’]
#bot.reply(«{}».format(bot.conversation_reference))
chatbot = NludbChatbot(secret, «AbbotDemo1»)
reply = chatbot.dispatch(bot.arguments.strip())
bot.reply(reply)