COVID-19 Telegram-bot // Kami menjawab pertanyaan FAQ secara otomatis

Dalam konteks hype universal tentang Coronavirus, saya memutuskan untuk melakukan setidaknya sesuatu yang bermanfaat (tapi tidak kurang hype). Dalam artikel ini saya akan berbicara tentang cara membuat dan menggunakan Telegram Bot menggunakan metode NLP Berbasis Aturan dalam 2,5 jam (itulah yang membuat saya perlu) untuk menjawab pertanyaan FAQ menggunakan kasus COVID-19 sebagai contoh.

Dalam pekerjaan, kita akan menggunakan Python tua yang baik, API Telegram, beberapa pustaka NLP standar, serta Docker.



Menit Perawatan UFO


Pandemi COVID-19, infeksi pernafasan akut yang berpotensi parah yang disebabkan oleh coronavirus SARS-CoV-2 (2019-nCoV), telah secara resmi diumumkan di dunia. Ada banyak informasi tentang Habré tentang topik ini - selalu ingat bahwa Habré dapat diandalkan / bermanfaat, dan sebaliknya.

Kami mendesak Anda untuk kritis terhadap informasi apa pun yang dipublikasikan.


Sumber resmi

, .

Cuci tangan, rawat orang yang Anda cintai, tinggal di rumah kapan saja memungkinkan dan bekerja dari jarak jauh.

Baca publikasi tentang: coronavirus | kerja jarak jauh

Kata Pengantar Singkat


Artikel ini menjelaskan proses membuat Bot Telegram sederhana menjawab pertanyaan FAQ pada COVID-19. Teknologi pengembangan sangat sederhana dan serbaguna, dan dapat digunakan untuk kasus lain. Saya menekankan sekali lagi bahwa saya tidak berpura-pura sebagai Negara Seni, tetapi hanya menawarkan solusi sederhana dan efektif yang dapat digunakan kembali.

Karena saya percaya bahwa pembaca artikel ini sudah memiliki pengalaman dengan Python, kami akan menganggap bahwa Anda sudah menginstal Python 3.X dan alat pengembangan yang diperlukan (PyCharm, VS Code), Anda dapat membuat Bot di Telegram melalui BotFather, dan oleh karena itu, saya akan melewatkan hal-hal ini.

1. Konfigurasi API


Hal pertama yang perlu Anda instal adalah library wrapper untuk API Telegram " python-telegram-bot ". Perintah standar untuk ini adalah:

pip install python-telegram-bot --upgrade

Selanjutnya, kami akan membangun kerangka kerja program kecil kami dengan mendefinisikan "penangan" untuk acara Bot berikut:

  • mulai - perintah peluncuran Bot;
  • help - help command (bantuan);
  • pesan - pemrosesan pesan teks;
  • kesalahan - kesalahan.

Tanda tangan dari penangan akan terlihat seperti ini:

def start(update, context):
    #   
    pass


def help(update, context):
    #  
    pass


def message(update, context):
    #  
    pass


def error(update, context):
    # 
    pass

Selanjutnya, dengan analogi dengan contoh dari dokumentasi perpustakaan, kami mendefinisikan fungsi utama di mana kami menetapkan semua penangan ini dan memulai bot:

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

Saya menarik perhatian Anda pada kenyataan bahwa ada 2 mekanisme cara meluncurkan bot:

  • Polling Standar - polling berkala Bot menggunakan alat API Telegram standar untuk acara baru ( updater.start_polling());
  • Webhook - kami memulai server kami dengan titik akhir, tempat peristiwa dari bot tiba, memerlukan HTTPS.

Seperti yang sudah Anda perhatikan, untuk kesederhanaan kami menggunakan Polling standar.

2. Kami mengisi penangan standar dengan logika


Mari kita mulai dengan yang sederhana, mengisi awal dan membantu penangan dengan jawaban standar, kita mendapatkan sesuatu seperti ini:

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)

Sekarang, ketika pengguna mengirim / memulai atau / membantu perintah, mereka akan menerima jawaban yang ditentukan oleh kami. Saya menarik perhatian Anda pada fakta bahwa teks diformat dalam Markdown

parse_mode=telegram.ParseMode.MARKDOWN

Selanjutnya, tambahkan logging kesalahan ke penangan kesalahan:

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

Sekarang, mari kita periksa apakah Bot kita berfungsi. Menyalin seluruh kode yang ditulis dalam satu file, misalnya app.py . Tambahkan impor yang diperlukan .

Jalankan file dan pergi ke Telegram ( jangan lupa untuk memasukkan Token Anda ke dalam kode ). Kami menulis perintah / mulai dan / membantu dan bersukacita:



3. Kami memproses pesan dan menghasilkan respons


Hal pertama yang perlu kita jawab adalah Basis Pengetahuan. Hal paling sederhana yang dapat Anda lakukan adalah membuat file json sederhana dalam bentuk nilai Key-Value, di mana Key adalah teks dari pertanyaan yang diajukan, dan Value adalah jawaban untuk pertanyaan itu. Contoh Basis Pengetahuan:

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

Algoritma untuk menjawab pertanyaan adalah sebagai berikut:

  1. Kami mendapatkan teks pertanyaan dari pengguna;
  2. Lemasi semua kata dalam teks pengguna;
  3. Kami tidak dengan jelas membandingkan teks yang dihasilkan dengan semua pertanyaan lemmatized dari basis pengetahuan ( jarak Levenshtein );
  4. Kami memilih pertanyaan yang paling "mirip" dari basis pengetahuan;
  5. Kami mengirim jawaban untuk pertanyaan yang dipilih kepada pengguna.

Untuk mengimplementasikan rencana kami, kami membutuhkan perpustakaan: fuzzywuzzy (untuk perbandingan fuzzy) dan pymorphy2 (untuk lemmatization).

Buat file baru dan terapkan algoritma yang terdengar:

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

Sebelum menulis penangan pesan, kami akan menulis fungsi yang menyimpan riwayat korespondensi dalam file 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)

Sekarang, kami menggunakan metode yang kami tulis dalam pesan teks pesan penangan pesan:

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, sekarang pergi ke Telegram dan nikmati tulisannya:



4. Konfigurasikan Docker dan gunakan aplikasi


Seperti yang dikatakan klasik: "Jika Anda mengeksekusi, maka itu indah untuk dieksekusi.", Sehingga kami memiliki segalanya sebagai orang, kami akan mengonfigurasikan containerization menggunakan Docker Compose.

Untuk ini kita perlu:

  1. Buat Dockerfile - mendefinisikan gambar wadah dan titik masuk;
  2. Buat docker-compose.yml - meluncurkan banyak wadah menggunakan Dockerfile tunggal (dalam kasus kami itu tidak perlu, tetapi jika Anda memiliki banyak layanan, itu akan berguna.)
  3. Buat boot.sh (skrip bertanggung jawab langsung untuk meluncurkan).

Jadi, isi 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"]

Isi dari 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

Isi dari 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

Jadi, kami siap, untuk memulai semua ini, Anda perlu menjalankan perintah berikut di folder proyek:

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

Itu dia, bot kami sudah siap.

Alih-alih sebuah kesimpulan


Seperti yang diharapkan, semua kode tersedia di repositori .

Pendekatan ini, yang ditunjukkan oleh saya, dapat diterapkan dalam hal apa pun untuk menjawab pertanyaan FAQ, sesuaikan basis pengetahuan! Mengenai basis pengetahuan, itu juga dapat ditingkatkan dengan mengubah struktur Kunci dan Nilai menjadi array, sehingga setiap pasangan akan menjadi array pertanyaan potensial pada satu topik dan array jawaban potensial untuk mereka (untuk berbagai jawaban, Anda dapat memilih secara acak). Secara alami, pendekatan Berbasis Aturan tidak terlalu fleksibel untuk penskalaan, tetapi saya yakin bahwa pendekatan ini akan tahan terhadap basis pengetahuan dengan sekitar 500 pertanyaan.

Mereka yang telah membaca sampai akhir saya mengundang Anda untuk mencoba Bot saya di sini .

All Articles