About Abbot
Abbot is a programmable bot that turns your team chat into a shared command center. We handle all the boilerplate of building and running these conmmands so that you can focus on making tools that help you ship faster.
We built Abbot because we saw the power of this style of work (called ChatOps), when we worked at GitHub. ChatOps made it possible for GitHub to work productively without meetings, while globally distributed. We think it’s a pretty great way to work, so we made it easy to use in Slack, Discord, and Microsoft Teams.
You can read more about Abbot here, check out our blog, or take a look at some of the other cool packages available as a one-click install from Abbot’s Package Directory.
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)