En el contexto del bombo universal sobre el coronavirus, decidí hacer al menos algo útil (pero no menos bombo). En este artículo, hablaré sobre cómo crear e implementar el Telegram Bot usando métodos de PNL basados en reglas en 2.5 horas (eso es lo que me llevó) para responder preguntas frecuentes usando el caso COVID-19 como ejemplo.En el curso del trabajo, usaremos el viejo Python, la API de Telegram, un par de bibliotecas NLP estándar, así como Docker.

Minuto de atención ovni
La pandemia COVID-19, una infección respiratoria aguda potencialmente grave causada por el coronavirus SARS-CoV-2 (2019-nCoV), se ha anunciado oficialmente en el mundo. Hay mucha información sobre Habré sobre este tema; recuerde siempre que puede ser confiable / útil, y viceversa.
Le instamos a que sea crítico con cualquier información publicada.
Lávese las manos, cuide a sus seres queridos, quédese en casa siempre que sea posible y trabaje de forma remota.
Leer publicaciones sobre: coronavirus | trabajo remoto
Breve Prefacio
Este artículo describe el proceso de creación de un Bot de Telegram simple que responde preguntas frecuentes sobre COVID-19. La tecnología de desarrollo es extremadamente simple y versátil, y se puede utilizar para cualquier otro caso. Una vez más, enfatizo que no pretendo ser el Estado del Arte, sino que solo ofrezco una solución simple y efectiva que pueda ser reutilizada.Como creo que el lector de este artículo ya tiene cierta experiencia con Python, asumiremos que ya tiene instalado Python 3.X y las herramientas de desarrollo necesarias (PyCharm, VS Code), puede crear un Bot en Telegram a través de BotFather, y por lo tanto, me saltearé estas cosas.1. Configurar API
Lo primero que debe instalar es la biblioteca de contenedor para la API de Telegram " python-telegram-bot ". El comando estándar para esto es:pip install python-telegram-bot --upgrade
A continuación, crearemos el marco de nuestro pequeño programa definiendo "controladores" para los siguientes eventos de Bot:- start: comando de lanzamiento de Bot;
- ayuda - comando de ayuda (ayuda);
- mensaje - procesamiento de mensajes de texto;
- error: un error.
La firma de los manejadores se verá así:def start(update, context):
pass
def help(update, context):
pass
def message(update, context):
pass
def error(update, context):
pass
A continuación, por analogía con el ejemplo de la documentación de la biblioteca, definimos la función principal en la que asignamos todos estos controladores e iniciamos el bot:def get_answer():
"""Start the bot."""
updater = Updater("Token", use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
dp.add_handler(MessageHandler(Filters.text, message))
dp.add_error_handler(error)
updater.start_polling()
updater.idle()
if __name__ == "__main__":
get_answer()
Le llamo la atención sobre el hecho de que hay 2 mecanismos para lanzar un bot:- Sondeo estándar: sondeo periódico de Bot utilizando herramientas API estándar de Telegram para nuevos eventos (
updater.start_polling()
); - Webhook: iniciamos nuestro servidor con un punto final, al que llegan los eventos del bot, requiere HTTPS.
Como ya notó, para simplificar, usamos el sondeo estándar.2. Completamos los manejadores estándar con lógica
Comencemos con una simple, complete el inicio y los controladores de ayuda con respuestas estándar, obtenemos algo como esto: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)
Ahora, cuando el usuario envía los comandos / start o / help, recibirá la respuesta prescrita por nosotros. Le llamo la atención sobre el hecho de que el texto está formateado en Markdownparse_mode=telegram.ParseMode.MARKDOWN
A continuación, agregue el registro de errores al controlador de errores:def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
Ahora, verifiquemos si nuestro Bot funciona. Copiar todo el código escrito en un solo archivo, por ejemplo app.py . Añadir las importaciones necesarias .Ejecute el archivo y vaya a Telegram ( no olvide insertar su Token en el código ). Escribimos los comandos / start y / help y nos regocijamos:
3. Procesamos el mensaje y generamos una respuesta.
Lo primero que necesitamos para responder la pregunta es la Base de conocimiento. Lo más simple que puede hacer es crear un archivo json simple en forma de valores Key-Value, donde Key es el texto de la pregunta propuesta y Value es la respuesta a la pregunta. Ejemplo de base de conocimiento:{
" ?": " — . - , , , . , , . , .",
" ?": " :\n \n \n \n \n\n , .",
" ?": " :\n- ( , , )\n- ( )",
}
El algoritmo para responder la pregunta será el siguiente:- Recibimos el texto de la pregunta del usuario;
- Lematizar todas las palabras en el texto del usuario;
- No comparamos claramente el texto resultante con todas las preguntas lematizadas de la base de conocimiento ( distancia de Levenshtein );
- Seleccionamos la pregunta más "similar" de la base de conocimiento;
- Enviamos la respuesta a la pregunta seleccionada al usuario.
Para implementar nuestros planes, necesitamos bibliotecas: fuzzywuzzy (para comparaciones difusas) y pymorphy2 (para lematización).Cree un nuevo archivo e implemente el algoritmo sonado: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
Antes de escribir un controlador de mensajes, escribiremos una función que guarde el historial de correspondencia en un archivo 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)
Ahora, utilizamos el método que escribimos en el mensaje del mensaje de texto del controlador de mensajes: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, ahora ve a Telegram y disfruta de la escritura:
4. Configure Docker e implemente la aplicación.
Como decía el clásico: "Si ejecutas, entonces es hermoso ejecutarlo". Para que tengamos todo como personas, configuraremos la contenedorización con Docker Compose.Para esto necesitamos:- Crear archivo Docker: define la imagen del contenedor y el punto de entrada;
- Crear docker-compose.yml: inicia muchos contenedores con un solo Dockerfile (en nuestro caso no es necesario, pero en caso de que tenga muchos servicios, será útil).
- Cree boot.sh (el script es responsable directamente del lanzamiento).
Entonces, el contenido del 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"]
El contenido de 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
El contenido de 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
Entonces, estamos listos, para comenzar todo esto necesita ejecutar los siguientes comandos en la carpeta del proyecto:sudo docker build -t covid19_rus_bot:latest .
sudo docker-compose up
Eso es todo, nuestro bot está listo.En lugar de una conclusión
Como se esperaba, todo el código está disponible en el repositorio .Este enfoque, mostrado por mí, se puede aplicar en cualquier caso para responder preguntas frecuentes, ¡solo personalice la base de conocimiento! Con respecto a la base de conocimientos, también se puede mejorar cambiando la estructura de Clave y Valor a matrices, por lo que cada par será una serie de preguntas potenciales sobre un tema y una serie de respuestas potenciales (para una variedad de respuestas, puede elegir al azar). Naturalmente, el enfoque basado en reglas no es demasiado flexible para escalar, pero estoy seguro de que este enfoque resistirá una base de conocimiento con aproximadamente 500 preguntas.Los que han leído hasta el final los invito a probar mi Bot aquí .