COVID-19 Telegramm-Bot // Wir beantworten FAQ-Fragen automatisch

Im Zusammenhang mit dem allgemeinen Hype um Coronavirus habe ich beschlossen, zumindest etwas Nützliches zu tun (aber nicht weniger Hype). In diesem Artikel werde ich darüber sprechen, wie der Telegramm-Bot mithilfe regelbasierter NLP-Methoden in 2,5 Stunden erstellt und bereitgestellt wird (so lange habe ich gebraucht), um häufig gestellte Fragen am Beispiel COVID-19 zu beantworten.

Im Laufe der Arbeit werden wir das gute alte Python, die Telegramm-API, einige Standard-NLP-Bibliotheken sowie Docker verwenden.



UFO Care Minute


Die Pandemie COVID-19, eine potenziell schwere akute Atemwegsinfektion, die durch das SARS-CoV-2-Coronavirus (2019-nCoV) verursacht wird, wurde weltweit offiziell angekündigt. Zu diesem Thema gibt es viele Informationen zu Habré - denken Sie immer daran, dass es sowohl zuverlässig / nützlich sein kann als auch umgekehrt.

Wir bitten Sie dringend, veröffentlichte Informationen zu kritisieren.


Offizielle Quellen

, .

Waschen Sie Ihre Hände, kümmern Sie sich um Ihre Lieben, bleiben Sie wann immer möglich zu Hause und arbeiten Sie aus der Ferne.

Lesen Sie Veröffentlichungen über: coronavirus | Heimarbeit

Kurzes Vorwort


Dieser Artikel beschreibt den Prozess der Erstellung eines einfachen Telegramm-Bots, der häufig gestellte Fragen zu COVID-19 beantwortet. Die Entwicklungstechnologie ist äußerst einfach und vielseitig und kann für alle anderen Fälle verwendet werden. Ich betone noch einmal, dass ich nicht vorgebe, auf dem neuesten Stand der Technik zu sein, sondern nur eine einfache und effektive Lösung anbiete, die wiederverwendet werden kann.

Da ich glaube, dass der Leser dieses Artikels bereits Erfahrung mit Python hat, gehen wir davon aus, dass Sie Python 3.X bereits installiert haben und die erforderlichen Entwicklungstools (PyCharm, VS Code) haben. Sie können einen Bot in Telegram über BotFather erstellen und Deshalb werde ich diese Dinge überspringen.

1. Konfigurieren Sie die API


Als erstes müssen Sie die Wrapper-Bibliothek für die Telegramm-API " python-telegram-bot " installieren . Der Standardbefehl hierfür lautet:

pip install python-telegram-bot --upgrade

Als Nächstes erstellen wir den Rahmen für unser kleines Programm, indem wir "Handler" für die folgenden Bot-Ereignisse definieren:

  • start - Bot's Startbefehl;
  • help - Hilfebefehl (Hilfe);
  • Nachricht - Textnachrichtenverarbeitung;
  • Fehler - ein Fehler.

Die Signatur der Handler sieht folgendermaßen aus:

def start(update, context):
    #   
    pass


def help(update, context):
    #  
    pass


def message(update, context):
    #  
    pass


def error(update, context):
    # 
    pass

Als nächstes definieren wir in Analogie zum Beispiel aus der Bibliotheksdokumentation die Hauptfunktion, in der wir alle diese Handler zuweisen und den Bot starten:

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()

Ich mache Sie darauf aufmerksam, dass es zwei Mechanismen gibt, um einen Bot zu starten:

  • Standard Polling - Periodisches Polling von Bot unter Verwendung von Standard-Telegramm-API-Tools für neue Ereignisse ( updater.start_polling());
  • Webhook - Wir starten unseren Server mit einem Endpunkt, zu dem Ereignisse vom Bot kommen, für den HTTPS erforderlich ist.

Wie Sie bereits bemerkt haben, verwenden wir der Einfachheit halber das Standard-Polling.

2. Wir füllen Standardhandler mit Logik aus


Beginnen wir mit einem einfachen, füllen Sie den Anfang aus und helfen Sie den Handlern mit Standardantworten. Wir erhalten so etwas:

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)

Wenn der Benutzer nun die Befehle / start oder / help sendet, erhält er die von uns vorgeschriebene Antwort. Ich mache Sie darauf aufmerksam, dass der Text in Markdown formatiert ist

parse_mode=telegram.ParseMode.MARKDOWN

Fügen Sie als Nächstes dem Fehlerbehandler die Fehlerprotokollierung hinzu:

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

Lassen Sie uns nun überprüfen, ob unser Bot funktioniert. Kopieren Sie den gesamten Code geschrieben in einer einzigen Datei, zum Beispiel app.py . Fügen Sie die erforderlichen Importe hinzu .

Führen Sie die Datei aus und gehen Sie zu Telegramm ( vergessen Sie nicht, Ihr Token in den Code einzufügen ). Wir schreiben die Befehle / start und / help und freuen uns:



3. Wir verarbeiten die Nachricht und generieren eine Antwort


Das erste, was wir zur Beantwortung der Frage benötigen, ist die Knowledge Base. Am einfachsten ist es, eine einfache JSON-Datei in Form von Schlüsselwertwerten zu erstellen, wobei Schlüssel der Text der vorgeschlagenen Frage und Wert die Antwort auf die Frage ist. Beispiel für eine Wissensdatenbank:

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

Der Algorithmus zur Beantwortung der Frage lautet wie folgt:

  1. Wir erhalten den Text der Frage vom Benutzer;
  2. Lemmatisieren Sie alle Wörter im Text des Benutzers.
  3. Wir vergleichen den resultierenden Text nicht klar mit allen lemmatisierten Fragen aus der Wissensbasis ( Levenshtein-Abstand );
  4. Wir wählen die „ähnlichste“ Frage aus der Wissensbasis aus.
  5. Wir senden die Antwort auf die ausgewählte Frage an den Benutzer.

Zur Umsetzung unserer Pläne benötigen wir Bibliotheken: fuzzywuzzy (für Fuzzy-Vergleiche) und pymorphy2 (für die Lemmatisierung).

Erstellen Sie eine neue Datei und implementieren Sie den Sounded-Algorithmus:

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

Bevor wir einen Nachrichtenhandler schreiben, schreiben wir eine Funktion, die den Korrespondenzverlauf in einer tsv-Datei speichert:

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)

Jetzt verwenden wir die Methode, die wir in der Nachrichtentext-Handler-Nachricht geschrieben haben:

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)

Voila, jetzt geh zum Telegramm und genieße das Schreiben:



4. Konfigurieren Sie Docker und stellen Sie die Anwendung bereit


Wie der Klassiker sagte: "Wenn Sie ausführen, ist die Ausführung wunderschön." Damit wir alles als Personen haben, konfigurieren wir die Containerisierung mithilfe von Docker Compose.

Dafür brauchen wir:

  1. Docker-Datei erstellen - Definiert das Image des Containers und den Einstiegspunkt.
  2. Docker-compose.yml erstellen - Startet viele Container mit einer einzigen Docker-Datei (in unserem Fall ist dies nicht erforderlich, aber wenn Sie über viele Dienste verfügen, ist dies hilfreich.)
  3. Erstellen Sie boot.sh (das Skript ist direkt für den Start verantwortlich).

Also, der Inhalt der Docker-Datei:

#
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"]

Der Inhalt von 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

Der Inhalt von 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

Wir sind also bereit, um all dies zu starten, müssen Sie die folgenden Befehle im Projektordner ausführen:

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

Das war's, unser Bot ist bereit.

Anstelle einer Schlussfolgerung


Wie erwartet ist der gesamte Code im Repository verfügbar .

Dieser von mir gezeigte Ansatz kann auf jeden Fall zur Beantwortung von FAQ-Fragen angewendet werden. Passen Sie einfach die Wissensdatenbank an! Die Wissensbasis kann auch verbessert werden, indem die Struktur von Schlüssel und Wert in Arrays geändert wird, sodass jedes Paar eine Reihe potenzieller Fragen zu einem Thema und eine Reihe potenzieller Antworten auf diese Fragen enthält (zur Abwechslung können die Antworten zufällig ausgewählt werden). Natürlich ist der regelbasierte Ansatz für die Skalierung nicht zu flexibel, aber ich bin sicher, dass dieser Ansatz einer Wissensbasis mit etwa 500 Fragen standhalten wird.

Diejenigen, die bis zum Ende gelesen haben, lade ich Sie ein, meinen Bot hier auszuprobieren .

All Articles