COVID-19 Telegram-bot //我们自动回答常见问题

考虑到对冠状病毒的普遍炒作,我决定至少要做一些有用的事情(但不要少做些炒作)。在本文中,我将讨论如何使用基于规则的NLP方法在2.5小时(这花了我很多时间)来创建和部署Telegram Bot,以COVID-19案例为例来回答常见问题。

在工作过程中,我们将使用良好的旧Python,Telegram API,几个标准的NLP库以及Docker。



不明飞行物护理分钟


大流行的COVID-19是由SARS-CoV-2冠状病毒(2019-nCoV)引起的潜在严重的急性呼吸道感染,已在全球正式宣布。关于Habré的很多信息都涉及此主题-始终记住,它既可靠又有用,反之亦然。

我们敦促您不要批评任何已发布的信息。



洗手,照顾亲人,尽可能呆在家里并远程工作。

阅读有关以下内容的出版物:冠状病毒 | 远程工作

简要序言


本文介绍了在COVID-19上创建一个简单的Telegram Bot回答FAQ问题的过程。开发技术极其简单和通用,可用于任何其他情况。我再次强调,我并不假装自己是最先进的,而只是提供了一种可以重复使用的简单有效的解决方案。

由于我相信本文的读者已经对Python有一定的经验,因此我们假定您已经安装了Python 3.X和必需的开发工具(PyCharm,VS Code),因此可以通过BotFather在Telegram中创建Bot,并且因此,我将跳过这些事情。

1.配置API


您需要安装的第一件事是Telegram API“ python-telegram-bot的包装库标准命令是:

pip install python-telegram-bot --upgrade

接下来,我们将通过为以下Bot事件定义“处理程序”来构建小型程序的框架:

  • start-Bot的启动命令;
  • help-帮助命令(帮助);
  • 消息-文本消息处理;
  • 错误-错误。

处理程序的签名如下所示:

def start(update, context):
    #   
    pass


def help(update, context):
    #  
    pass


def message(update, context):
    #  
    pass


def error(update, context):
    # 
    pass

接下来,通过类似于库文档中的示例,我们定义主要功能,在其中分配所有这些处理程序并启动机器人:

def get_answer():
    """Start the bot."""
    # Create the Updater and pass it your bot's token.
    # Make sure to set use_context=True to use the new context based callbacks
    # Post version 12 this will no longer be necessary
    updater = Updater("Token", use_context=True)

    # Get the dispatcher to register handlers
    dp = updater.dispatcher

    # on different commands - answer in Telegram
    dp.add_handler(CommandHandler("start", start))
    dp.add_handler(CommandHandler("help", help))

    # on noncommand i.e message - echo the message on Telegram
    dp.add_handler(MessageHandler(Filters.text, message))

    # log all errors
    dp.add_error_handler(error)

    # Start the Bot
    updater.start_polling()

    # Run the bot until you press Ctrl-C or the process receives SIGINT,
    # SIGTERM or SIGABRT. This should be used most of the time, since
    # start_polling() is non-blocking and will stop the bot gracefully.
    updater.idle()


if __name__ == "__main__":
    get_answer()

我提醒您注意以下事实:启动机器人有两种机制:

  • 标准轮询-使用标准电报API工具针对新事件定期对Bot进行轮询(updater.start_polling());
  • Webhook-我们从一个端点启动我们的服务器,该机器人的事件到达该端点,它需要HTTPS。

正如您已经注意到的,为简单起见,我们使用标准轮询。

2.我们用逻辑填充标准处理程序


让我们从一个简单的例子开始,用标准答案填写开始和帮助处理程序,我们得到如下内容:

def start(update, context):
    """Send a message when the command /start is issued."""
    update.message.reply_text("""
!
        COVID-19.
:
- *  ?*
- *  ?*
- *   ?*
 ..
 !
    """, parse_mode=telegram.ParseMode.MARKDOWN)


def help(update, context):
    """Send a message when the command /help is issued."""
    update.message.reply_text("""
     (  COVID-19).
:
- *  ?*
- *  ?*
- *   ?*
 ..
 !
    """, parse_mode=telegram.ParseMode.MARKDOWN)

现在,当用户发送/ start或/ help命令时,他们将收到我们指定的答案。我提请您注意以下事实:文本使用Markdown格式化

parse_mode=telegram.ParseMode.MARKDOWN

接下来,将错误日志记录添加到错误处理程序中:

def error(update, context):
    """Log Errors caused by Updates."""
    logger.warning('Update "%s" caused error "%s"', update, context.error)

现在,让我们检查一下Bot是否有效。复制写在一个单一的文件全代码,例如app.py添加必要的导入

运行该文件并转到Telegram(不要忘记将Token插入代码中)。我们编写命令/ start和/ help并欣喜:



3.我们处理消息并生成响应


我们需要回答的第一件事是知识库。您可以做的最简单的事情是以Key-Value值的形式创建一个简单的json文件,其中Key是所提出问题的文本,而Value是问题的答案。知识库示例:

{
  "      ?": "  —  .     -     ,     ,     ,        .    ,      ,   .        ,     .",
  "   ?": "  :\n     \n    \n    \n     \n\n         ,    .",
  "  ?": " :\n- (    , , )\n- (  )",
  }

回答问题的算法如下:

  1. 我们从用户那里得到问题的文本;
  2. 将用户文本中的所有单词合法化;
  3. 我们没有清楚地将结果文本与知识库中所有词条化问题进行比较(Levenshtein距离);
  4. 我们从知识库中选择最“相似”的问题;
  5. 我们将所选问题的答案发送给用户。

要实施我们的计划,我们需要以下库:Fuzzywuzzy(用于模糊比较)和pymorphy2(用于lemmatization)。

创建一个新文件并实现完善的算法:

import json
from fuzzywuzzy import fuzz
import pymorphy2

#   
morph = pymorphy2.MorphAnalyzer()
#  
with open("faq.json") as json_file:
    faq = json.load(json_file)


def classify_question(text):
    #  
    text = ' '.join(morph.parse(word)[0].normal_form for word in text.split())
    questions = list(faq.keys())
    scores = list()
    #      
    for question in questions:
        #    
        norm_question = ' '.join(morph.parse(word)[0].normal_form for word in question.split())
        #       
        scores.append(fuzz.token_sort_ratio(norm_question.lower(), text.lower()))
    # 
    answer = faq[questions[scores.index(max(scores))]]

    return answer

在编写消息处理程序之前,我们将编写一个将通信历史保存在tsv文件中的函数:

def dump_data(user, question, answer):
    username = user.username
    full_name = user.full_name
    id = user.id

    str = """{username}\t{full_name}\t{id}\t{question}\t{answer}\n""".format(username=username,
                                                                 full_name=full_name,
                                                                 id=id,
                                                                 question=question,
                                                                 answer=answer)

    with open("/data/dump.tsv", "a") as myfile:
        myfile.write(str)

现在,我们使用在消息文本消息处理程序消息中编写的方法:

def message(update, context):
    """Answer the user message."""
    # 
    answer = classify_question(update.message.text)
    #  
    dump_data(update.message.from_user, update.message.text, answer)
    # 
    update.message.reply_text(answer)

瞧,现在去电报,享受写作:



4.配置Docker并部署应用程序


正如经典所说:“如果执行,那么执行就很漂亮。”,为了使所有人像人一样,我们将使用Docker Compose配置容器化。

为此,我们需要:

  1. 创建Dockerfile-定义容器的映像和入口点;
  2. 创建docker-compose.yml-使用单个Dockerfile启动多个容器(在我们的情况下不是必须的,但是如果您有很多服务,它将很有用。)
  3. 创建boot.sh(脚本直接负责启动)。

因此,Dockerfile的内容为:

#
FROM python:3.6.6-slim

#  
WORKDIR /home/alex/covid-bot

#  requirements.txt
COPY requirements.txt ./

# Install required libs
RUN pip install --upgrade pip -r requirements.txt; exit 0

#      
COPY data data

#   
COPY app.py faq.json reply_generator.py boot.sh  ./

#   
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# 
RUN chmod +x boot.sh

#  
ENTRYPOINT ["./boot.sh"]

docker-compose.yml的内容:

# docker-compose
version: '2'
#  
services:
  bot:
    restart: unless-stopped
    image: covid19_rus_bot:latest
    container_name: covid19_rus_bot
    #    boot.sh
    environment:
      - SERVICE_TYPE=covid19_rus_bot
    # volume      
    volumes: 
        - ./data:/data

boot.sh的内容:

#!/bin/bash
if [ -n $SERVICE_TYPE ]
then
  if [ $SERVICE_TYPE == "covid19_rus_bot" ]
  then
    exec python app.py
    exit
  fi
else
  echo -e "SERVICE_TYPE not set\n"
fi

因此,我们准备就绪,为了开始所有这些,您需要在项目文件夹中执行以下命令:

sudo docker build -t covid19_rus_bot:latest .
sudo docker-compose up

就是这样,我们的机器人已经准备就绪。

而不是结论


如预期的那样,所有代码都在存储库中可用

我展示的这种方法可以在任何情况下用于回答FAQ问题,只需自定义知识库即可!关于知识库,也可以通过将键和值的结构更改为数组来进行改进,因此每对将是一个主题上的一系列潜在问题和它们的一系列潜在答案(对于各种答案,您可以随机选择)。自然,基于规则的方法在扩展方面不太灵活,但是我确信该方法可以承受约500个问题的知识库。

那些读到最后的人,我邀请您在这里尝试我的机器人

All Articles