├── bot ├── actions │ ├── __init__.py │ ├── data_validator.py │ ├── forms.py │ └── actions.py ├── tests │ └── conversation_tests.md ├── credentials.yml ├── endpoints.yml ├── config.yml ├── Makefile ├── data │ ├── stories.md │ └── nlu.md └── domain.yml ├── .gitbook.yaml ├── requirements.txt ├── x-requirements.txt ├── env ├── elasticsearch.env ├── bot-telegram.env ├── notebooks.env ├── rabbitmq.env ├── kibana.env └── rabbitmq-consumer.env ├── modules ├── webchat │ ├── assets │ │ ├── bot.png │ │ └── launcher_button.svg │ └── index.html ├── elasticsearch │ └── elasticsearch.yml ├── analytics │ ├── import_dashboards.py │ └── setup_elastic.py ├── rabbitmq │ └── consumer │ │ ├── consume_bot_messages.py │ │ └── elastic_connector.py ├── rocketchat │ └── bot_config.py └── notebooks │ └── stories │ └── stories-analysis.ipynb ├── .github ├── PULL_REQUEST_TEMPLATE.MD ├── ISSUE_TEMPLATE │ ├── funcionalidade.md │ └── bug_report.md └── workflows │ └── python.yml ├── docker ├── elasticsearch.Dockerfile ├── bot.Dockerfile ├── consumer.Dockerfile ├── actions.Dockerfile ├── notebooks.Dockerfile ├── kibana.Dockerfile └── requirements.Dockerfile ├── docs ├── add_bot_rocketchat.md ├── setup_telegram.md ├── Setup │ ├── setup_telegram.md │ └── setup_user_elasticsearch.md ├── Tutoriais │ ├── setup_user_elasticsearch.md │ ├── tutorial-como-fazer-uma-utter.md │ ├── tutorial-testes-automatizados.md │ ├── tutorial-primeira-conversa.md │ ├── tutorial-como-treinar-o-modelo.md │ ├── tutorial-pipeline-de-bot.md │ └── tutorial-criar-BI.md ├── setup_analytics.md ├── pipeline-de-qualidade.md ├── README-en.md └── README.md ├── Makefile ├── .gitignore ├── docker-compose.yml ├── CONTRIBUTING.md └── README.md /bot/actions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | 3 | structure: 4 | readme: README.md 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | rasa==1.10.10 2 | nltk==3.4.5 3 | elasticsearch==7.0.4 4 | -------------------------------------------------------------------------------- /x-requirements.txt: -------------------------------------------------------------------------------- 1 | --extra-index-url https://pypi.rasa.com/simple 2 | rasa-x==0.31.2 3 | -------------------------------------------------------------------------------- /env/elasticsearch.env: -------------------------------------------------------------------------------- 1 | discovery.type=single-node 2 | -------------------------------------------------------------------------------- /modules/webchat/assets/bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOSS-BigOpenSourceSibling/bot-da-boss/HEAD/modules/webchat/assets/bot.png -------------------------------------------------------------------------------- /env/bot-telegram.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_BOT_USERNAME=lappisbot 2 | TELEGRAM_TOKEN=token 3 | TELEGRAM_WEBHOOK=your_webhook_server/webhooks/telegram/webhook -------------------------------------------------------------------------------- /env/notebooks.env: -------------------------------------------------------------------------------- 1 | DIR_PATH=/bot/ 2 | DOMAIN_PATH=/bot/domain.yml 3 | CONFIG_PATH=/bot/config.yml 4 | DATA_PATH=/bot/data/ 5 | MODELS_PATH=/bot/models/ 6 | -------------------------------------------------------------------------------- /bot/tests/conversation_tests.md: -------------------------------------------------------------------------------- 1 | ## me_ajuda 2 | * cumprimentar: oi 3 | - utter_cumprimentar 4 | * o_que_sei_falar: #meajuda 5 | - utter_o_que_sei_falar 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.MD: -------------------------------------------------------------------------------- 1 | ## Descrição 2 | 3 | Descrição completa do que foi feito 4 | 5 | ## Resolve (Issues) 6 | 7 | Issues que foram resolvidas com o PR 8 | -------------------------------------------------------------------------------- /docker/elasticsearch.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.elastic.co/elasticsearch/elasticsearch:7.8.1 2 | 3 | RUN mkdir backup 4 | 5 | RUN chown -R elasticsearch:elasticsearch backup 6 | -------------------------------------------------------------------------------- /docker/bot.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM botrequirements 2 | 3 | WORKDIR /bot 4 | COPY ./bot /bot 5 | COPY ./modules /modules 6 | 7 | RUN find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf 8 | -------------------------------------------------------------------------------- /env/rabbitmq.env: -------------------------------------------------------------------------------- 1 | RABBITMQ_DEFAULT_USER=admin 2 | RABBITMQ_DEFAULT_PASS=admin 3 | -------------------------------------------------------------------------------- /env/kibana.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT=5601 2 | ELASTICSEARCH_URL=http://elasticsearch:9200 3 | KIBANA_URL=http://kibana:5601 4 | -------------------------------------------------------------------------------- /docker/consumer.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-slim 2 | 3 | RUN python -m pip install --upgrade pip 4 | 5 | RUN pip install --no-cache-dir -I pika==1.1.0 elasticsearch==6.3.1 nltk==3.3 certifi==2019.3.9 6 | RUN python -c "import nltk; nltk.download('stopwords');" 7 | RUN find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf 8 | -------------------------------------------------------------------------------- /docker/actions.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM botrequirements 2 | 3 | COPY ./bot/actions/actions.py /bot/actions/actions.py 4 | COPY ./bot/Makefile /bot/Makefile 5 | 6 | WORKDIR /bot 7 | 8 | EXPOSE 5055 9 | HEALTHCHECK --interval=300s --timeout=60s --retries=5 \ 10 | CMD curl -f http://0.0.0.0:5055/health || exit 1 11 | 12 | CMD make run-actions 13 | -------------------------------------------------------------------------------- /modules/elasticsearch/elasticsearch.yml: -------------------------------------------------------------------------------- 1 | cluster.name: "docker-cluster" 2 | network.host: 0.0.0.0 3 | 4 | # minimum_master_nodes need to be explicitly set when bound on a public IP 5 | # # set to 1 to allow single node clusters 6 | # # Details: https://github.com/elastic/elasticsearch/pull/17288 7 | # discovery.zen.minimum_master_nodes: 1 8 | path.repo: ["/usr/share/elasticsearch/backup"] 9 | -------------------------------------------------------------------------------- /bot/credentials.yml: -------------------------------------------------------------------------------- 1 | #telegram: 2 | # access_token: ${TELEGRAM_TOKEN} 3 | # verify: ${TELEGRAM_BOT_USERNAME} 4 | # webhook_url: ${TELEGRAM_WEBHOOK} 5 | 6 | #rocketchat: 7 | # user: ${ROCKETCHAT_BOT_USERNAME} 8 | # password: ${ROCKETCHAT_BOT_PASSWORD} 9 | # server_url: ${ROCKETCHAT_URL} 10 | 11 | socketio: 12 | user_message_evt: user_uttered 13 | bot_message_evt: bot_uttered 14 | session_persistence: true 15 | 16 | rest: 17 | -------------------------------------------------------------------------------- /bot/endpoints.yml: -------------------------------------------------------------------------------- 1 | # Configuração para ambiente docker 2 | action_endpoint: 3 | url: "http://actions:5055/webhook" 4 | 5 | # Configuração para ambiente local 6 | #action_endpoint: 7 | # url: "http://localhost:5055/webhook" 8 | 9 | # Descomente estas linhas para integrar o bot com o Analytics 10 | #event_broker: 11 | # type: pika 12 | # url: rabbitmq 13 | # username: admin 14 | # password: admin 15 | # queues: 16 | # - bot_messages 17 | -------------------------------------------------------------------------------- /docker/notebooks.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM botrequirements 2 | 3 | RUN apt-get update && apt-get install -y graphviz libgraphviz-dev pkg-config 4 | 5 | # Pygraphviz depends on package graphviz wich needs to be configurated 6 | # acording to each OS. because of it it's not added to bot.requirements 7 | RUN pip install jupyter pygraphviz==1.5 8 | 9 | COPY ./bot /bot 10 | 11 | WORKDIR /work/ 12 | 13 | CMD jupyter-notebook --allow-root --NotebookApp.token='' --ip=0.0.0.0 --NotebookApp.password='' 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/funcionalidade.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Funcionalidade 3 | about: Sugerir uma ideia para o projeto 4 | title: '' 5 | labels: evolucao 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Problema 11 | Descreva o problema ou melhoria 12 | 13 | # Sugestão de solução 14 | Fala um pouco o que seria uma boa solução (não necessariamente tecnicamente) 15 | 16 | ## Tarefas 17 | - [ ] Alguns passos para a solução 18 | - [ ] Critério de aceitação etc 19 | 20 | 21 | > Usem o espaço da issue para discutir sobre as soluções 22 | -------------------------------------------------------------------------------- /docker/kibana.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.elastic.co/kibana/kibana:7.8.1 2 | 3 | COPY modules/analytics/import_dashboards.py analytics/import_dashboards.py 4 | COPY modules/analytics/dashboards.json analytics/dashboards.json 5 | 6 | USER root 7 | 8 | RUN yum -y update 9 | RUN yum -y install yum-utils 10 | RUN yum -y groupinstall development 11 | RUN yum install -y https://repo.ius.io/ius-release-el7.rpm 12 | RUN yum -y install python36u 13 | RUN yum -y install python36u-pip 14 | 15 | RUN pip3.6 install requests 16 | 17 | USER kibana 18 | 19 | WORKDIR /usr/share/kibana/analytics 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Crie um aviso para nos ajudar a melhorar 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Descreva o bug** 11 | Uma descrição clara e consisa sobre o que o bug é. 12 | 13 | **Para reproduzir o bug** 14 | Passo-a-passo para reproduzir o comportamento do bug: 15 | 1. Vá para '...' 16 | 2. Clique em '....' 17 | 4. Veja o erro 18 | 19 | **Comportamento esperado** 20 | Uma descrição clara e consisa sobre o que deveria acontecer (ao invés do bug). 21 | 22 | **Screenshots** 23 | Se necessário. 24 | 25 | **Ambiente:** 26 | - SO: [e.g. Ubuntu] 27 | - Versão [e.g. 22] 28 | 29 | **Contexto Adicional** 30 | Adicione o contexto relacionado ao problema. 31 | -------------------------------------------------------------------------------- /docker/requirements.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | 3 | COPY ./requirements.txt /tmp 4 | COPY ./x-requirements.txt /tmp 5 | 6 | RUN apt-get update && \ 7 | apt-get install -y gcc make build-essential && \ 8 | python -m pip install --upgrade pip && \ 9 | pip install --no-cache-dir -r /tmp/requirements.txt && \ 10 | pip install --no-cache-dir -r /tmp/x-requirements.txt && \ 11 | python -c "import nltk; nltk.download('stopwords');" && \ 12 | find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf && \ 13 | apt-get clean && \ 14 | apt-get remove -y build-essential 15 | -------------------------------------------------------------------------------- /bot/config.yml: -------------------------------------------------------------------------------- 1 | language : "pt" 2 | 3 | pipeline: 4 | - name: WhitespaceTokenizer 5 | - name: RegexFeaturizer 6 | - name: LexicalSyntacticFeaturizer 7 | - name: CountVectorsFeaturizer 8 | analyzer: "char_wb" 9 | min_ngram: 1 10 | max_ngram: 4 11 | - name: DIETClassifier 12 | epochs: 55 13 | - name: EntitySynonymMapper 14 | - name: ResponseSelector 15 | 16 | policies: 17 | - name: TEDPolicy 18 | epochs: 10 19 | featurizer: 20 | - name: MaxHistoryTrackerFeaturizer 21 | max_history: 5 22 | state_featurizer: 23 | - name: BinarySingleStateFeaturizer 24 | - name: "FormPolicy" 25 | - name: MemoizationPolicy 26 | max_history: 5 27 | - name: MappingPolicy 28 | - name: FallbackPolicy 29 | nlu_threshold: 0.38 30 | core_threshold: 0.3 31 | fallback_action_name: "utter_fallback" 32 | 33 | -------------------------------------------------------------------------------- /env/rabbitmq-consumer.env: -------------------------------------------------------------------------------- 1 | RABBITMQ_DEFAULT_USER=admin 2 | RABBITMQ_DEFAULT_PASS=admin 3 | ELASTICSEARCH_URL=elasticsearch:9200 4 | ENVIRONMENT_NAME=localhost 5 | BOT_VERSION=last-bot-commit-hash 6 | #ELASTICSEARCH_USER=admin 7 | #ELASTICSEARCH_PASSWORD=admin 8 | #ELASTICSEARCH_HTTP_SCHEME=https 9 | #ELASTICSEARCH_PORT=443 10 | -------------------------------------------------------------------------------- /bot/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean actions 2 | 3 | clean: 4 | rm -rf graph.html results/ models/* *.db* 5 | 6 | install: 7 | pip install --upgrade pip && \ 8 | pip install -r ../requirements.txt && \ 9 | pip install -r ../x-requirements.txt 10 | 11 | # RASA X 12 | x: 13 | rasa x 14 | 15 | # NLU 16 | train-nlu: 17 | rasa train nlu -vv 18 | 19 | # CORE 20 | train: 21 | rasa train -vv 22 | 23 | # TESTS 24 | test: 25 | rasa test --out results/ 26 | 27 | test-nlu: 28 | rasa test nlu --out results/results-nlu-test 29 | 30 | test-core: 31 | rasa test core --fail-on-prediction-errors --out results/results-core-test 32 | 33 | # VALIDACAO 34 | validate: 35 | rasa data validate -vv 36 | 37 | # MENSAGEIROS 38 | shell: 39 | rasa shell -m models/ -vv --endpoints endpoints.yml --port 5004 40 | 41 | telegram: 42 | rasa run -m models/ -vv --port 5001 --credentials credentials.yml \ 43 | --endpoints endpoints.yml 44 | 45 | webchat: 46 | rasa run -m models/ -vv --endpoints endpoints.yml --credentials credentials.yml --port 5005 --cors '*' 47 | 48 | api: 49 | rasa run -m models/ -vv --endpoints endpoints.yml --enable-api 50 | 51 | # ACTIONS 52 | actions: 53 | rasa run actions --actions actions -vv 54 | -------------------------------------------------------------------------------- /modules/webchat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |

Seja bem vindo ao BOILERPLATE integrado com webchat

6 | Você deve ver uma bola azul no canto inferior direito do seu navegador. 7 |
8 |
9 | 10 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /bot/actions/data_validator.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def isCpfValid(cpf): 5 | """ If cpf in the Brazilian format is valid, it returns True, otherwise, it returns False. """ 6 | 7 | # Check if type is str 8 | if not isinstance(cpf, str): 9 | return False 10 | 11 | # Remove some unwanted characters 12 | cpf = re.sub("[^0-9]", "", cpf) 13 | 14 | # Checks if string has 11 characters 15 | if len(cpf) != 11: 16 | return False 17 | 18 | sum = 0 19 | weight = 10 20 | 21 | """ Calculating the first cpf check digit. """ 22 | for n in range(9): 23 | sum = sum + int(cpf[n]) * weight 24 | 25 | # Decrement weight 26 | weight = weight - 1 27 | 28 | verifyingDigit = 11 - sum % 11 29 | 30 | if verifyingDigit > 9: 31 | firstVerifyingDigit = 0 32 | else: 33 | firstVerifyingDigit = verifyingDigit 34 | 35 | """ Calculating the second check digit of cpf. """ 36 | sum = 0 37 | weight = 11 38 | for n in range(10): 39 | sum = sum + int(cpf[n]) * weight 40 | 41 | # Decrement weight 42 | weight = weight - 1 43 | 44 | verifyingDigit = 11 - sum % 11 45 | 46 | if verifyingDigit > 9: 47 | secondVerifyingDigit = 0 48 | else: 49 | secondVerifyingDigit = verifyingDigit 50 | 51 | if cpf[-2:] == "%s%s" % (firstVerifyingDigit, secondVerifyingDigit): 52 | return True 53 | return False 54 | -------------------------------------------------------------------------------- /docs/add_bot_rocketchat.md: -------------------------------------------------------------------------------- 1 | ### Adicionar bot ao RocketChat 2 | 3 | Para configurar o bot no rocketchat, devem ser seguidos os passos a seguir. 4 | 5 | - Primeiro você deve clicar no ícone Directory localizado na parte superior esquerda da tela, conforme mostra a imagem a seguir. 6 |
7 | 8 | - A seguir será aberta uma nova tela com os canais existentes. Clique no canal General 9 |
10 | 11 | - Após isso clique na opção Join 12 |
13 | 14 | 15 | - O canal do bot será exibido nessa tela, clique no nome do bot como indicado na imagem abaixo 16 |
17 | 18 | - Por último clique na opção conversation, que irá redirecionar para o chat do bot 19 |
20 | 21 | - Agora basta conversar com @ FAMOOS@! 22 | -------------------------------------------------------------------------------- /.github/workflows/python.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: build_bot 5 | 6 | on: 7 | push: 8 | branches: [ main, devel ] 9 | pull_request: 10 | branches: [ main, devel ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.6, 3.7] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | # Added "gast=0.2.2" as a workarround because of a Tensorflow issue, should be removed soon 29 | python -m pip install --upgrade pip gast==0.2.2 30 | pip install flake8 pytest 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | - name: Test Rasa version 39 | run: | 40 | rasa --version 41 | - name: Rasa train 42 | run: | 43 | cd bot/ 44 | make train 45 | - name: Test Rasa bot 46 | run: | 47 | cd bot/ 48 | make test 49 | -------------------------------------------------------------------------------- /modules/analytics/import_dashboards.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import os 4 | import logging 5 | 6 | logging.basicConfig(level=logging.DEBUG) 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | def getRequestDatas(finalUrl): 11 | fullUrl = [] 12 | 13 | url = os.getenv("KIBANA_URL", "http://kibana:5601") 14 | url = url + finalUrl 15 | 16 | header = {"kbn-xsrf": "true", "Content-Type": "application/json"} 17 | 18 | fullUrl.append(url) 19 | fullUrl.append(header) 20 | 21 | return fullUrl 22 | 23 | 24 | def getIdDashboards(pathToFile): 25 | dashboardsIds = {} 26 | 27 | # Path that contains the data of the export.json to search dashboard id's 28 | # export.json from version 7.3 of kibana 29 | with open(pathToFile) as file: 30 | data = json.load(file) 31 | 32 | for savedObjects in data["saved_objects"]: 33 | attributes = savedObjects["attributes"]["title"] 34 | dashboardsIds[attributes] = savedObjects["id"] 35 | 36 | return dashboardsIds 37 | 38 | 39 | def importDashboards(pathToFile): 40 | finalUrl = "/api/kibana/dashboards/import?exclude=index-pattern" 41 | requestData = getRequestDatas(finalUrl) 42 | 43 | datas = open(pathToFile, "rb").read() 44 | datas = datas.decode("utf-8") 45 | 46 | requests.post(url=requestData[0], headers=requestData[1], data=json.dumps(datas)) 47 | 48 | createIndexPattern() 49 | 50 | 51 | def createIndexPattern(): 52 | # TODO: Import value of the variable from the environment 53 | idIndex = "194404f0-e6b4-11e8-bb67-918dc5752875" 54 | finalUrl = "/api/saved_objects/index-pattern/" + idIndex 55 | 56 | requestData = getRequestDatas(finalUrl) 57 | 58 | datas = {"attributes": {"title": "messages*"}} 59 | 60 | requests.post(url=requestData[0], headers=requestData[1], data=json.dumps(datas)) 61 | 62 | 63 | if __name__ == "__main__": 64 | try: 65 | importDashboards("./dashboards.json") 66 | except Exception as ex: 67 | logger.error(str(ex)) 68 | -------------------------------------------------------------------------------- /docs/setup_telegram.md: -------------------------------------------------------------------------------- 1 | # Setup do bot no Telegram 2 | 3 | ## Crie um bot no Telegram 4 | 5 | Converse com o [@BotFather do Telegram](https://t.me/BotFather) e crie um bot de teste unicamente seu seguindo as instruções dele. 6 | 7 | ## Exporte as variáveis do seu bot 8 | 9 | Após escolher um nome para seu bot, o @BotFather lhe dará um token para utilizar para acessar a API do Telegram. Adicione ambos no [arquivo de configurações do bot](../env/bot-telegram.env), como a seguir. Substitua o TELEGRAM_TOKEN pelo token lhe enviado pelo @BotFather e TELEGRAM_BOT_USERNAME pelo nome do seu bot. 10 | 11 | ```sh 12 | TELEGRAM_BOT_USERNAME=username_do_bot 13 | TELEGRAM_TOKEN=token_fornecido_pelo_BotFather 14 | ``` 15 | 16 | ## Execute o ngrok 17 | 18 | Após a etapa anterior, é necessário utilizar o [ngrok](https://ngrok.com/download) para expor determinada porta para ser utilizado 19 | pelo Telegram. 20 | 21 | Conforme a seguir, execute o ngrok na porta 5001. 22 | 23 | ```sh 24 | ./ngrok http 5001 25 | ``` 26 | 27 | **Atenção:** O conector do Telegram está utilizando a porta 5001 como padrão. Caso queira mudar, somente altere 28 | a porta utilizada pelo no Makefile. 29 | 30 | 31 | ## Exporte a URL do Webhook 32 | 33 | Enquanto o ngrok estiver em execução, ele apresentará uma série de informações da sessão atual. Copie a url do campo Forwarding com o protocolo HTTPS e cole no [arquivo de configurações do bot](../env/bot-telegram.env). ela será similar à seguinte. 34 | 35 | ```sh 36 | TELEGRAM_WEBHOOK=link_do_ngrok/webhooks/telegram/webhook 37 | ``` 38 | 39 | ::Lembre-se::: sempre que executar o ngrok essa url deve ser exportada. 40 | 41 | 42 | ## Execução do bot no telegram 43 | 44 | Ao final de realizar essas configurações, seu [arquivo de configurações do bot](../env/bot-telegram.env) deve estar de acordo com o exibido logo abaixo: 45 | 46 | ```sh 47 | TELEGRAM_BOT_USERNAME=lappisbot 48 | TELEGRAM_TOKEN=token 49 | TELEGRAM_WEBHOOK=your_webhook_server/webhooks/telegram/webhook 50 | ``` 51 | 52 | Com isso, é possível realizar a execução do bot seguindo os passos do [README](../README.md) 53 | 54 | -------------------------------------------------------------------------------- /docs/Setup/setup_telegram.md: -------------------------------------------------------------------------------- 1 | ## Setup do bot no Telegram 2 | 3 | ##### Crie um bot no Telegram 4 | 5 | Converse com o [@BotFather do Telegram](https://t.me/BotFather) e crie um bot de teste unicamente seu seguindo as instruções dele. 6 | 7 | 8 | 9 | 10 | ##### Exporte as variáveis do seu bot 11 | Após escolher um nome para seu bot, o @BotFather lhe dará um token para utilizar para acessar a API do Telegram. Adicione ambos no [arquivo de configurações do bot](../env/bot-telegram.env), como a seguir. Substitua o TELEGRAM_TOKEN pelo token lhe enviado pelo @BotFather e TELEGRAM_BOT_USERNAME pelo nome do seu bot. 12 | 13 | ```sh 14 | TELEGRAM_BOT_USERNAME=token_fornecido_pelo_BotFather 15 | TELEGRAM_TOKEN=username_do_bot 16 | ``` 17 | 18 | ##### Execute o ngrok 19 | Após a etapa anterior, é necessário utilizar o [ngrok](https://ngrok.com/download) para expor determinada porta para ser utilizado 20 | pelo Telegram. 21 | 22 | Conforme a seguir, execute o ngrok na porta 5001. 23 | 24 | ```sh 25 | ./ngrok http 5001 26 | ``` 27 | 28 | **Atenção:** O conector do Telegram está utilizando a porta 5001 como padrão. Caso queira mudar, somente altere 29 | a porta utilizada pelo no Makefile. 30 | 31 | 32 | ##### Exporte a URL do Webhook 33 | 34 | Enquanto o ngrok estiver em execução, ele apresentará uma série de informações da sessão atual. Copie a url do campo Forwarding com o protocolo HTTPS e cole no [arquivo de configurações do bot](../env/bot-telegram.env). ela será similar à seguinte. 35 | 36 | ```sh 37 | TELEGRAM_WEBHOOK=link_do_ngrok/webhooks/telegram/webhook 38 | ``` 39 | 40 | ::Lembre-se::: sempre que executar o ngrok essa url deve ser exportada. 41 | 42 | 43 | ##### Execução do bot no telegram 44 | 45 | Ao final de realizar essas configurações, seu [arquivo de configurações do bot](../env/bot-telegram.env) deve estar de acordo com o exibido logo abaixo: 46 | 47 | ```sh 48 | TELEGRAM_BOT_USERNAME=lappisbot 49 | TELEGRAM_TOKEN=token 50 | TELEGRAM_WEBHOOK=your_webhook_server/webhooks/telegram/webhook 51 | ``` 52 | 53 | Com isso, é possível realizar a execução do bot seguindo os passos do [README](../README.md) 54 | 55 | -------------------------------------------------------------------------------- /modules/analytics/setup_elastic.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import argparse 4 | 5 | from elasticsearch import Elasticsearch 6 | 7 | parser = argparse.ArgumentParser(description="configures elastic") 8 | parser.add_argument("--task", "-t", default="setup", choices=["setup", "delete"]) 9 | args = parser.parse_args() 10 | 11 | logging.basicConfig(level=logging.DEBUG) 12 | logger = logging.getLogger(__name__) 13 | 14 | es = Elasticsearch([os.getenv("ELASTICSEARCH_URL", "elasticsearch:9200")]) 15 | 16 | settings = { 17 | "settings": {"number_of_shards": 1, "number_of_replicas": 0}, 18 | "mappings": { 19 | "message": { 20 | "properties": { 21 | "environment": {"type": "keyword"}, 22 | "version": {"type": "keyword"}, 23 | "user_id": {"type": "keyword"}, 24 | "is_bot": {"type": "boolean"}, 25 | "text": {"type": "text"}, 26 | "tags": {"type": "keyword"}, 27 | "timestamp": {"type": "date", "format": "yyyy/MM/dd HH:mm:ss"}, 28 | "intent_name": {"type": "keyword"}, 29 | "intent_confidence": {"type": "double"}, 30 | "entities": {"type": "keyword"}, 31 | "utter_name": {"type": "keyword"}, 32 | "is_fallback": {"type": "boolean"}, 33 | } 34 | } 35 | }, 36 | } 37 | 38 | param = {"include_type_name": "true"} 39 | 40 | index_name = "messages" 41 | 42 | if __name__ == "__main__": 43 | if args.task == "setup": 44 | try: 45 | if not es.indices.exists(index_name): 46 | logger.debug( 47 | es.indices.create( 48 | index=index_name, ignore=400, params=param, body=settings, 49 | ) 50 | ) 51 | logger.info("Created Index") 52 | else: 53 | logger.info("Index {} already exists".format(index_name)) 54 | except Exception as ex: 55 | logger.error(str(ex)) 56 | 57 | elif args.task == "delete": 58 | logger.debug(es.indices.delete(index=index_name, ignore=[400, 404])) 59 | logger.info("Index deleted") 60 | -------------------------------------------------------------------------------- /modules/rabbitmq/consumer/consume_bot_messages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import pika 3 | import os 4 | import json 5 | import logging 6 | from elastic_connector import ElasticConnector 7 | 8 | username = os.getenv("RABBITMQ_DEFAULT_USER") 9 | password = os.getenv("RABBITMQ_DEFAULT_USER") 10 | credentials = pika.PlainCredentials(username, password) 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | _elastic_connector = None 15 | 16 | elastic_user = os.getenv("ELASTICSEARCH_USER") 17 | if elastic_user is None: 18 | _elastic_connector = ElasticConnector( 19 | domain=os.getenv("ELASTICSEARCH_URL", "elasticsearch:9200") 20 | ) 21 | else: 22 | _elastic_connector = ElasticConnector( 23 | domain=os.getenv("ELASTICSEARCH_URL", "elasticsearch:9200"), 24 | user=os.getenv("ELASTICSEARCH_USER", "user"), 25 | password=os.getenv("ELASTICSEARCH_PASSWORD", "password"), 26 | scheme=os.getenv("ELASTICSEARCH_HTTP_SCHEME", "http"), 27 | scheme_port=os.getenv("ELASTICSEARCH_PORT", "80"), 28 | ) 29 | 30 | connection = pika.BlockingConnection( 31 | pika.ConnectionParameters( 32 | host="rabbitmq", credentials=credentials, connection_attempts=20, retry_delay=5, 33 | ) 34 | ) 35 | 36 | channel = connection.channel() 37 | channel.queue_declare(queue="bot_messages", durable=True) 38 | 39 | 40 | def callback(ch, method, properties, body): 41 | logger.warning(" [x] Received %r" % body) 42 | 43 | message = json.loads(body.decode("utf-8")) 44 | 45 | if message["event"] == "user": 46 | _elastic_connector.save_user_message(message) 47 | _elastic_connector.previous_user_message = message 48 | 49 | elif message["event"] == "action": 50 | if message["name"] == "action_listen": 51 | _elastic_connector.previous_action = None 52 | return 53 | 54 | _elastic_connector.previous_action = message 55 | 56 | elif message["event"] == "bot": 57 | if _elastic_connector.previous_action is None: 58 | _elastic_connector.previous_user_message = None 59 | return 60 | 61 | bot_message = message 62 | action_message = _elastic_connector.previous_action 63 | previous_user_message = _elastic_connector.previous_user_message 64 | 65 | _elastic_connector.save_bot_message( 66 | bot_message, action_message, previous_user_message 67 | ) 68 | 69 | 70 | if __name__ == "__main__": 71 | channel.basic_consume( 72 | queue="bot_messages", on_message_callback=callback, auto_ack=True 73 | ) 74 | 75 | logger.warning("[*] Waiting for messages.") 76 | channel.start_consuming() 77 | -------------------------------------------------------------------------------- /docs/Setup/setup_user_elasticsearch.md: -------------------------------------------------------------------------------- 1 | ### Configurando os usuários 2 | 3 | Caso seja desejada a função de se ter usuários com diferentes permissões dentro do kibana, deve-se seguir os seguintes passos (Caso não seja esse seu interesse, pode passar para a próxima sessão): 4 | 5 | OBS: Os seguintes passos podem ser encontrados, com mais detalhes, na seguinte paǵina: https://www.elastic.co/guide/en/elastic-stack-overview/current/get-started-enable-security.html 6 | 7 | **1. Habilitar função de segurança do elastic search:** 8 | 9 | Depois de se subir o container do elastic de acordo com a seção [Setup](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master#setup-elasticsearch), 10 | adicione a seguinte linha ao arquivo 'rasa-ptbr-boilerplate/elasticsearch/elasticsearch.yml': 11 | 12 | ``` 13 | xpack.security.enabled: true 14 | ``` 15 | 16 | Após adicionar a linha, reinicie o container do Elastic Search 17 | 18 | ``` 19 | sudo docker-compose restart elasticsearch 20 | ``` 21 | 22 | 23 | **2. Definir senhas para usuários internos do elasticsearch:** 24 | 25 | Entre no container com o comando: 26 | 27 | 28 | ``` 29 | sudo docker-compose exec elasticsearch bash 30 | ``` 31 | 32 | Dentro do container execute o seguinte comando, preenchendo as senhas que deseja para cada um dos usuários. 33 | 34 | 35 | ``` 36 | ./bin/elasticsearch-setup-passwords interactive 37 | ``` 38 | 39 | **3. Adicionar usuário kibana ao container do kibana** 40 | 41 | Depois de subir o container do kibana de acordo com a seção [Visualização](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master#setup-kibana-visualização), entre no mesmo com o comando: 42 | 43 | ``` 44 | sudo docker-compose exec kibana bash 45 | ``` 46 | 47 | Dentro do container, execute os seguintes comandos, digitando, quando necessário, o usuário kibana, e a senha criada no passo anterior. 48 | 49 | ``` 50 | ../bin/kibana-keystore create 51 | ../bin/kibana-keystore add elasticsearch.username 52 | ../bin/kibana-keystore add elasticsearch.password 53 | ``` 54 | 55 | Após executar os comandos acima, reinicie o container do kibana. 56 | 57 | ``` 58 | sudo docker-compose restart kibana 59 | ``` 60 | 61 | 62 | **4. Crie usuários além do administrador** 63 | 64 | Após os três passos anteriores, será possível entrar no kibana utilizando a conta com usuário 'elastic'. 65 | 66 | Na interface, é possível criar outros usuários, com diversas outras permissões, entrando em ***Management / Security / Users***, e após isso clicando no botão Create New User. 67 | 68 | **5. Definir permissões para usuários.** 69 | 70 | Na criação de usuários é possível se definir permissões para cada um deles, utilizando os ***roles*** definidos pelo elastic. 71 | 72 | Por exemplo, um usuário que deva ter somente aceesso a leitura dos dashboards criados, deverá ter o role ***kibana_dashboard_only_user*** e ***apm_user*** 73 | 74 | É possível criar novos ***roles*** em ***Management / Security / Roles*** 75 | -------------------------------------------------------------------------------- /docs/Tutoriais/setup_user_elasticsearch.md: -------------------------------------------------------------------------------- 1 | # Configurando os usuários 2 | 3 | Caso seja desejada a função de se ter usuários com diferentes permissões dentro do kibana, deve-se seguir os seguintes passos (Caso não seja esse seu interesse, pode passar para a próxima sessão): 4 | 5 | OBS: Os seguintes passos podem ser encontrados, com mais detalhes, na seguinte paǵina: https://www.elastic.co/guide/en/elastic-stack-overview/current/get-started-enable-security.html 6 | 7 | **1. Habilitar função de segurança do elastic search:** 8 | 9 | Depois de se subir o container do elastic de acordo com a seção [Setup](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master#setup-elasticsearch), 10 | adicione a seguinte linha ao arquivo 'rasa-ptbr-boilerplate/elasticsearch/elasticsearch.yml': 11 | 12 | ``` 13 | xpack.security.enabled: true 14 | ``` 15 | 16 | Após adicionar a linha, reinicie o container do Elastic Search 17 | 18 | ``` 19 | sudo docker-compose restart elasticsearch 20 | ``` 21 | 22 | 23 | **2. Definir senhas para usuários internos do elasticsearch:** 24 | 25 | Entre no container com o comando: 26 | 27 | 28 | ``` 29 | sudo docker-compose exec elasticsearch bash 30 | ``` 31 | 32 | Dentro do container execute o seguinte comando, preenchendo as senhas que deseja para cada um dos usuários. 33 | 34 | 35 | ``` 36 | ./bin/elasticsearch-setup-passwords interactive 37 | ``` 38 | 39 | **3. Adicionar usuário kibana ao container do kibana** 40 | 41 | Depois de subir o container do kibana de acordo com a seção [Visualização](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master#setup-kibana-visualização), entre no mesmo com o comando: 42 | 43 | ``` 44 | sudo docker-compose exec kibana bash 45 | ``` 46 | 47 | Dentro do container, execute os seguintes comandos, digitando, quando necessário, o usuário kibana, e a senha criada no passo anterior. 48 | 49 | ``` 50 | ../bin/kibana-keystore create 51 | ../bin/kibana-keystore add elasticsearch.username 52 | ../bin/kibana-keystore add elasticsearch.password 53 | ``` 54 | 55 | Após executar os comandos acima, reinicie o container do kibana. 56 | 57 | ``` 58 | sudo docker-compose restart kibana 59 | ``` 60 | 61 | 62 | **4. Crie usuários além do administrador** 63 | 64 | Após os três passos anteriores, será possível entrar no kibana utilizando a conta com usuário 'elastic'. 65 | 66 | Na interface, é possível criar outros usuários, com diversas outras permissões, entrando em ***Management / Security / Users***, e após isso clicando no botão Create New User. 67 | 68 | **5. Definir permissões para usuários.** 69 | 70 | Na criação de usuários é possível se definir permissões para cada um deles, utilizando os ***roles*** definidos pelo elastic. 71 | 72 | Por exemplo, um usuário que deva ter somente aceesso a leitura dos dashboards criados, deverá ter o role ***kibana_dashboard_only_user*** e ***apm_user*** 73 | 74 | É possível criar novos ***roles*** em ***Management / Security / Roles*** 75 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | current_dir := $(shell pwd) 2 | user := $(shell whoami) 3 | 4 | clean: 5 | docker-compose down 6 | cd bot/ && make clean 7 | 8 | stop: 9 | docker-compose stop 10 | 11 | ############################## BOILERPLATE ############################## 12 | first-run: 13 | make build 14 | make train 15 | make run-shell 16 | 17 | build: 18 | make build-requirements 19 | make build-coach 20 | make build-bot 21 | 22 | build-requirements: 23 | docker build . --no-cache -f docker/requirements.Dockerfile -t botrequirements 24 | 25 | build-bot: 26 | docker-compose build --no-cache bot 27 | 28 | build-coach: 29 | docker-compose build --no-cache coach 30 | 31 | build-analytics: 32 | docker-compose up -d elasticsearch 33 | docker-compose up -d rabbitmq 34 | docker-compose up -d rabbitmq-consumer 35 | docker-compose up -d kibana 36 | make config-elastic 37 | make config-kibana 38 | 39 | config-elastic: 40 | docker-compose run --rm -v $(current_dir)/modules/analytics/setup_elastic.py:/analytics/setup_elastic.py bot python /analytics/setup_elastic.py 41 | 42 | config-kibana: 43 | docker-compose run --rm -v $(current_dir)/modules/analytics/:/analytics/ kibana python3 /analytics/import_dashboards.py 44 | $(info ) 45 | $(info Acesse o KIBANA em: http://localhost:5601) 46 | $(info ) 47 | 48 | run-shell: 49 | docker-compose run --rm --service-ports bot make shell 50 | 51 | run-api: 52 | docker-compose run --rm --service-ports bot make api 53 | 54 | run-actions: 55 | docker-compose run --rm --service-ports bot make actions 56 | 57 | run-x: 58 | docker-compose run --rm --service-ports bot make x 59 | 60 | run-webchat: 61 | $(info ) 62 | $(info Executando Bot com Webchat.) 63 | $(info ) 64 | docker-compose run -d --rm --service-ports bot-webchat 65 | $(info ) 66 | $(info Caso o FIREFOX não seja iniciado automáticamente, abra o seguinte arquivo com seu navegador:) 67 | $(info modules/webchat/index.html) 68 | $(info ) 69 | firefox modules/webchat/index.html 70 | 71 | run-telegram: 72 | docker-compose run -d --rm --service-ports bot_telegram make telegram 73 | 74 | run-notebooks: 75 | docker-compose up -d notebooks 76 | $(info ) 77 | $(info Acesse o KIBANA em: http://localhost:8888) 78 | $(info ) 79 | 80 | train: 81 | mkdir -p bot/models 82 | docker-compose up --build coach 83 | 84 | ############################## TESTS ############################## 85 | test: 86 | docker-compose run --rm bot make test 87 | 88 | run-test-nlu: 89 | docker-compose run --rm bot make test-nlu 90 | 91 | run-test-core: 92 | docker-compose run --rm bot make test-core 93 | 94 | validate: 95 | docker-compose run --rm bot rasa data validate --domain domain.yml --data data/ -vv 96 | 97 | visualize: 98 | docker-compose run --rm -v $(current_dir)/bot:/coach coach rasa visualize --domain domain.yml --stories data/stories.md --config config.yml --nlu data/nlu.md --out ./graph.html -vv 99 | $(info ) 100 | $(info Caso o FIREFOX não seja iniciado automáticamente, abra o seguinte arquivo com seu navegador:) 101 | $(info bot/graph.html) 102 | firefox bot/graph.html 103 | -------------------------------------------------------------------------------- /docs/setup_analytics.md: -------------------------------------------------------------------------------- 1 | ## Configuração da stack de analytics 2 | Este documento tem por objetivo detalhar o processo de configuração da *stack* de *analytics* utilizada no boilerplate. 3 | 4 | Aqui os passos de configuração são descritos separadamente, para facilitar o entendimento e a adaptação. Caso você já tenha executado os comandos `make build-analytics`, `make config-elastic` e `make config-kibana`, não é necessário executar os passos abaixo, e o *analytics* já deve estar funcionando para o seu *bot*. 5 | 6 | #### RabbitMQ 7 | 8 | ###### Configuração do *server* e dos *consumers* 9 | Em primeiro lugar para fazer o setup do analytics é necessário subir o RabbitMQ e definir suas configurações. 10 | 11 | Inicie o serviço do servidor do RabbitMQ: 12 | 13 | ```sh 14 | sudo docker-compose up -d rabbitmq 15 | ``` 16 | 17 | Inicie o serviço do consumidor do RabbitMQ, que ficará responsável por enviar as mensagens para o ElasticSearch: 18 | 19 | ```sh 20 | sudo docker-compose up -d rabbitmq-consumer 21 | ``` 22 | 23 | Lembre-se de configurar as credenciais de acesso do *server* e dos *consumers* do `rabbitmq`, nos arquivos `env/rabbitmq.env` e `env/rabbitmq-consumer.env`. Estas credenciais devem ser as mesmas, para que estes serviços consigam se integrar corretamente: 24 | 25 | ```sh 26 | RABBITMQ_DEFAULT_USER=admin 27 | RABBITMQ_DEFAULT_PASS=admin 28 | ``` 29 | 30 | ###### Integração com Rasa 31 | 32 | Cada mensagem trocada com o *bot* será enviada à uma fila no servidor do *RabbitMQ*, para isso, é precisso integrar esses dois serviços. 33 | Isso é feito definindo as configurações de *broker* do Rasa no arquivo `bot/endpoints.yml`: 34 | 35 | ```yml 36 | event_broker: 37 | type: pika 38 | url: rabbitmq 39 | username: admin 40 | password: admin 41 | queues: 42 | - bot_messages 43 | ``` 44 | 45 | #### Configuração ElasticSearch 46 | 47 | O ElasticSearch é o serviço responsável por armazenar os dados provenientes da interação entre o usuário e o chatbot. 48 | 49 | As mensagens são inseridas no índice do ElasticSearch utilizando o *broker* RabbitMQ. 50 | 51 | Para subir o ambiente do ElasticSearch rode os seguintes comandos: 52 | 53 | ``` 54 | sudo docker-compose up -d elasticsearch 55 | sudo docker-compose run --rm -v $(pwd)/modules/analytics:/analytics bot python /analytics/setup_elastic.py 56 | ``` 57 | 58 | Lembre-se de setar as seguintes variaveis de ambiente no `docker-compose`. 59 | 60 | ``` 61 | ENVIRONMENT_NAME=localhost 62 | BOT_VERSION=last-commit-hash 63 | ``` 64 | 65 | #### Configuração Kibana (Visualização) 66 | 67 | Para a análise dos dados das conversas com o usuário, utilize o kibana, e veja como os usuários estão interagindo com o bot, os principais assuntos, média de usuários e outras informações da análise de dados. 68 | 69 | O Kibana nos auxilia com uma interface para criação de visualização para os dados armazenados nos índices do ElasticSearch. 70 | 71 | ```sh 72 | sudo docker-compose up -d kibana 73 | ``` 74 | 75 | **Atenção:** Caso queira configurar permissões diferentes de usuários (Login) no ElasticSearch/Kibana, siga esse tutorial ([link](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master/docs/setup_user_elasticsearch.md)). 76 | 77 | ###### Importação de dashboards 78 | 79 | Caso queira subir com os dashboards que criamos para fazer o monitoramento de bots: 80 | 81 | ``` 82 | sudo docker-compose run --rm -v $(pwd)/modules/analytics/:/analytics/ kibana python3 /analytics/import_dashboards.py 83 | ``` 84 | 85 | Você pode acessar o kibana na url `localhost:5601` 86 | -------------------------------------------------------------------------------- /bot/actions/forms.py: -------------------------------------------------------------------------------- 1 | # This files contains your custom actions which can be used to run 2 | # custom Python code. 3 | # 4 | # See this guide on how to implement these action: 5 | # https://rasa.com/docs/rasa/core/actions/#custom-actions/ 6 | 7 | from typing import Any, Text, Dict, List, Union 8 | import re 9 | 10 | from rasa_sdk import Action, Tracker 11 | from rasa_sdk.executor import CollectingDispatcher 12 | from rasa_sdk.events import SlotSet 13 | from rasa_sdk.forms import FormAction 14 | 15 | from .data_validator import isCpfValid 16 | 17 | 18 | class LoginForm(FormAction): 19 | """Form used to handle login information""" 20 | 21 | def name(self) -> Text: 22 | """Unique identifier of the form""" 23 | return "login_form" 24 | 25 | @staticmethod 26 | def required_slots(tracker: Tracker) -> List[Text]: 27 | """A list of required slots that will be needed to login""" 28 | return ["cpf", "data_nascimento"] 29 | 30 | def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]: 31 | """A dictionary to map required slots to 32 | - an extracted entity 33 | - intent: value pairs 34 | - a whole message 35 | or a list of them, where a first match will be picked""" 36 | return { 37 | "cpf": self.from_text(not_intent="cancelar"), 38 | "data_nascimento": self.from_text(not_intent="cancelar"), 39 | } 40 | 41 | def submit( 42 | self, 43 | dispatcher: CollectingDispatcher, 44 | tracker: Tracker, 45 | domain: Dict[Text, Any], 46 | ) -> List[Dict]: 47 | """Define what the login form will do after 48 | all required slots are filled""" 49 | 50 | cpf = tracker.get_slot("cpf") 51 | data_nascimento = tracker.get_slot("data_nascimento") 52 | dispatcher.utter_message( 53 | "Então seu CPF é: {} e sua data de nascimento é: {}?".format( 54 | cpf, data_nascimento 55 | ) 56 | ) 57 | return [] 58 | 59 | def validate_cpf( 60 | self, 61 | value: Text, 62 | dispatcher: CollectingDispatcher, 63 | tracker: Tracker, 64 | domain: Dict[Text, Any], 65 | ) -> Dict[Text, Any]: 66 | """Validate cpf value.""" 67 | 68 | regex = re.compile("[0-9]{3}[\.]?[0-9]{3}[\.]?[0-9]{3}[-]?[0-9]{2}$") 69 | if re.match(regex, value) is None: 70 | dispatcher.utter_template("utter_errado_cpf_formato", tracker) 71 | return {"cpf": None} 72 | elif not isCpfValid(value): 73 | dispatcher.utter_template("utter_errado_cpf_invalido", tracker) 74 | return {"cpf": None} 75 | else: 76 | return {"cpf": value} 77 | 78 | def validate_data_nascimento( 79 | self, 80 | value: Text, 81 | dispatcher: CollectingDispatcher, 82 | tracker: Tracker, 83 | domain: Dict[Text, Any], 84 | ) -> Dict[Text, Any]: 85 | """Validate data_nascimento value.""" 86 | 87 | regex = re.compile( 88 | "(([0-2][0-9]|(3)[0-1])(\/)(((0)[0-9])|((1)[0-2]))(\/)\d{4})" 89 | ) 90 | if re.match(regex, value) is not None: 91 | return {"data_nascimento": value} 92 | else: 93 | dispatcher.utter_template("utter_errado_data_nascimento", tracker) 94 | # validation failed, set this slot to None, meaning the 95 | # user will be asked for the slot again 96 | return {"data_nascimento": None} 97 | 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Rasa ### 2 | bot/models 3 | bot/results* 4 | 5 | ### Rasa X ### 6 | events.db 7 | events.db-shm 8 | events.db-wal 9 | rasa.db 10 | *.db* 11 | 12 | ### Rasa visualize ### 13 | graph.html 14 | 15 | ### Rasa evaluation ### 16 | .ipynb_checkpoints/ 17 | modules/notebooks/intents/models/ 18 | modules/notebooks/intents/results/ 19 | modules/notebooks/stories/models/ 20 | modules/notebooks/stories/results/ 21 | bot/.ipython/ 22 | bot/.keras/ 23 | bot/.local/ 24 | bot/results/ 25 | 26 | ### database ### 27 | db/ 28 | 29 | ### PyCharm ### 30 | .idea/ 31 | 32 | # Created by https://www.gitignore.io/api/vim,linux,macos,python 33 | ### Linux ### 34 | *~ 35 | 36 | # temporary files which can be created if a process still has a handle open of a deleted file 37 | .fuse_hidden* 38 | 39 | # KDE directory preferences 40 | .directory 41 | 42 | # Linux trash folder which might appear on any partition or disk 43 | .Trash-* 44 | 45 | # .nfs files are created when an open file is removed but is still being accessed 46 | .nfs* 47 | 48 | ### macOS ### 49 | *.DS_Store 50 | .AppleDouble 51 | .LSOverride 52 | 53 | # Icon must end with two \r 54 | Icon 55 | 56 | # Thumbnails 57 | ._* 58 | 59 | # Files that might appear in the root of a volume 60 | .DocumentRevisions-V100 61 | .fseventsd 62 | .Spotlight-V100 63 | .TemporaryItems 64 | .Trashes 65 | .VolumeIcon.icns 66 | .com.apple.timemachine.donotpresent 67 | 68 | # Directories potentially created on remote AFP share 69 | .AppleDB 70 | .AppleDesktop 71 | Network Trash Folder 72 | Temporary Items 73 | .apdisk 74 | 75 | ### Python ### 76 | # Byte-compiled / optimized / DLL files 77 | __pycache__/ 78 | *.py[cod] 79 | *$py.class 80 | 81 | # C extensions 82 | *.so 83 | 84 | # Distribution / packaging 85 | .Python 86 | build/ 87 | develop-eggs/ 88 | dist/ 89 | downloads/ 90 | eggs/ 91 | .eggs/ 92 | lib/ 93 | lib64/ 94 | parts/ 95 | sdist/ 96 | var/ 97 | wheels/ 98 | *.egg-info/ 99 | .installed.cfg 100 | *.egg 101 | 102 | # PyInstaller 103 | # Usually these files are written by a python script from a template 104 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 105 | *.manifest 106 | *.spec 107 | 108 | # Installer logs 109 | pip-log.txt 110 | pip-delete-this-directory.txt 111 | 112 | # Unit test / coverage reports 113 | htmlcov/ 114 | .tox/ 115 | .coverage 116 | .coverage.* 117 | .cache 118 | .pytest_cache/ 119 | nosetests.xml 120 | coverage.xml 121 | *.cover 122 | .hypothesis/ 123 | 124 | # Translations 125 | *.mo 126 | *.pot 127 | 128 | # Flask stuff: 129 | instance/ 130 | .webassets-cache 131 | 132 | # Scrapy stuff: 133 | .scrapy 134 | 135 | # Sphinx documentation 136 | docs/_build/ 137 | 138 | # PyBuilder 139 | target/ 140 | 141 | # Jupyter Notebook 142 | *.ipynb_checkpoints* 143 | 144 | # pyenv 145 | .python-version 146 | 147 | # celery beat schedule file 148 | celerybeat-schedule.* 149 | 150 | # SageMath parsed files 151 | *.sage.py 152 | 153 | # Environments 154 | .env 155 | .venv 156 | venv/ 157 | ENV/ 158 | env.bak/ 159 | venv.bak/ 160 | 161 | # Spyder project settings 162 | .spyderproject 163 | .spyproject 164 | 165 | # Rope project settings 166 | .ropeproject 167 | 168 | # mkdocs documentation 169 | /site 170 | 171 | # mypy 172 | .mypy_cache/ 173 | 174 | ### Vim ### 175 | # swap 176 | .sw[a-p] 177 | .*.sw[a-p] 178 | # session 179 | Session.vim 180 | # temporary 181 | .netrwhist 182 | # auto-generated tag files 183 | tags 184 | 185 | # End of https://www.gitignore.io/api/vim,linux,macos,python 186 | -------------------------------------------------------------------------------- /bot/actions/actions.py: -------------------------------------------------------------------------------- 1 | # Este arquivo contém custom actions que utilizão código python 2 | # para executar ações no diálogo. 3 | # 4 | # Veja o guia na documentação do RASA em: 5 | # https://rasa.com/docs/rasa/core/actions/#custom-actions/ 6 | 7 | 8 | from typing import Any, Text, Dict, List 9 | 10 | from rasa_sdk import Action, Tracker 11 | from rasa_sdk.executor import CollectingDispatcher 12 | from rasa_sdk.events import SlotSet 13 | 14 | import requests 15 | 16 | from random import randint 17 | 18 | class ActionTeste(Action): 19 | def name(self) -> Text: 20 | return "action_teste" 21 | 22 | def run( 23 | self, 24 | dispatcher: CollectingDispatcher, 25 | tracker: Tracker, 26 | domain: Dict[Text, Any], 27 | ) -> List[Dict[Text, Any]]: 28 | try: 29 | dispatcher.utter_message("Mensagem enviada por uma custom action.") 30 | except ValueError: 31 | dispatcher.utter_message(ValueError) 32 | return [] 33 | 34 | 35 | class ActionTelefone(Action): 36 | def name(self) -> Text: 37 | return "action_telefone" 38 | 39 | def run( 40 | self, 41 | dispatcher: CollectingDispatcher, 42 | tracker: Tracker, 43 | domain: Dict[Text, Any], 44 | ) -> List[Dict[Text, Any]]: 45 | 46 | telefone = tracker.get_slot('telefone') 47 | 48 | try: 49 | dispatcher.utter_message("O seu telefone é {}?".format(telefone)) 50 | except ValueError: 51 | dispatcher.utter_message(ValueError) 52 | return [SlotSet("telefone", telefone)] 53 | 54 | 55 | class ActionAdvices(Action): 56 | def name(self) -> Text: 57 | return "action_pedir_conselho" 58 | 59 | def run(self, dispatcher, tracker, domain): 60 | 61 | nome = tracker.get_slot('nome') 62 | 63 | req = requests.request('GET', "https://api.adviceslip.com/advice") 64 | conselho = req.json()["slip"]["advice"] 65 | 66 | try: 67 | if nome: 68 | dispatcher.utter_message("{} olha que conselho legal: {}".format(nome, conselho)) 69 | else: 70 | dispatcher.utter_message("Olha que conselho legal: {}".format(conselho)) 71 | except ValueError: 72 | dispatcher.utter_message(ValueError) 73 | 74 | class ActionSortingHat(Action): 75 | def name(self) -> Text: 76 | return "action_sorting_hat" 77 | 78 | def run(self, dispatcher, tracker, domain): 79 | 80 | nome = tracker.get_slot('nome') 81 | 82 | req = requests.get("https://www.potterapi.com/v1/sortingHat") 83 | casa = req.json() 84 | 85 | try: 86 | if nome: 87 | dispatcher.utter_message("{} Sua casa de Hogwarts é: {}".format(nome, casa)) 88 | else: 89 | dispatcher.utter_message("Sua casa de Hogwarts: {}".format(casa)) 90 | except ValueError: 91 | dispatcher.utter_message(ValueError) 92 | 93 | class ActionCatFacts(Action): 94 | def name(self) -> Text: 95 | return "action_cat_facts" 96 | 97 | def run(self, dispatcher, tracker, domain): 98 | if tracker.get_slot("fatos_sobre_gatos") == None: 99 | req = requests.request('GET', "https://cat-fact.herokuapp.com/facts") 100 | lista = [] 101 | for n in range(20): 102 | lista.append(req.json()["all"][n]["text"]) 103 | 104 | fato = lista[randint(0, 19)] 105 | 106 | try: 107 | dispatcher.utter_message("{}".format(fato)) 108 | except ValueError: 109 | dispatcher.utter_message(ValueError) 110 | return [SlotSet("fatos_sobre_gatos", lista)] 111 | else: 112 | fato = tracker.get_slot("fatos_sobre_gatos")[randint(0, 19)] 113 | try: 114 | dispatcher.utter_message("{}".format(fato)) 115 | except ValueError: 116 | dispatcher.utter_message(ValueError) 117 | return [] 118 | 119 | -------------------------------------------------------------------------------- /modules/rabbitmq/consumer/elastic_connector.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | import datetime 5 | import hashlib 6 | import json 7 | 8 | from elasticsearch import Elasticsearch 9 | 10 | try: 11 | from nltk.corpus import stopwords 12 | except Exception: 13 | import nltk 14 | 15 | nltk.download("stopwords") 16 | from nltk.corpus import stopwords 17 | 18 | pass 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | ENVIRONMENT_NAME = os.getenv("ENVIRONMENT_NAME", "locahost") 23 | BOT_VERSION = os.getenv("BOT_VERSION", "notdefined") 24 | HASH_GEN = hashlib.md5() 25 | 26 | 27 | def gen_id(timestamp): 28 | HASH_GEN.update(str(timestamp).encode("utf-8")) 29 | _id = HASH_GEN.hexdigest()[10:] 30 | return _id 31 | 32 | 33 | def get_timestamp(): 34 | ts = time.time() 35 | timestamp = datetime.datetime.strftime( 36 | datetime.datetime.fromtimestamp(ts), "%Y/%m/%d %H:%M:%S" 37 | ) 38 | return timestamp 39 | 40 | 41 | class ElasticConnector: 42 | def __init__(self, domain, user=None, password=None, scheme="http", scheme_port=80): 43 | if user is None: 44 | self.es = Elasticsearch([domain]) 45 | else: 46 | self.es = Elasticsearch( 47 | ["{}://{}:{}@{}:{}".format(scheme, user, password, domain, scheme_port)] 48 | ) 49 | 50 | self.previous_action = None 51 | self.previous_user_message = None 52 | 53 | def insert_on_elastic(self, ts, message): 54 | try: 55 | self.es.index( 56 | index="messages", 57 | doc_type="message", 58 | id="{}_user_{}".format(ENVIRONMENT_NAME, gen_id(ts)), 59 | body=json.dumps(message), 60 | ) 61 | except Exception as ex: 62 | logger.error("Could not send message to Elastic Search") 63 | logger.error(str(ex)) 64 | 65 | def save_user_message(self, user_message): 66 | if not user_message["text"]: 67 | return 68 | 69 | ts = time.time() 70 | timestamp = datetime.datetime.strftime( 71 | datetime.datetime.fromtimestamp(ts), "%Y/%m/%d %H:%M:%S" 72 | ) 73 | 74 | # Bag of words 75 | tags = [] 76 | for word in ( 77 | user_message["text"] 78 | .replace(". ", " ") 79 | .replace(",", " ") 80 | .replace('"', "") 81 | .replace("'", "") 82 | .replace("*", "") 83 | .replace("(", "") 84 | .replace(")", "") 85 | .split(" ") 86 | ): 87 | if word.lower() not in stopwords.words("portuguese") and len(word) > 1: 88 | tags.append(word) 89 | 90 | message = { 91 | "environment": ENVIRONMENT_NAME, 92 | "version": BOT_VERSION, 93 | "user_id": user_message["sender_id"], 94 | "is_bot": False, 95 | "timestamp": timestamp, 96 | "text": user_message["text"], 97 | "tags": tags, 98 | "entities": user_message["parse_data"]["entities"], 99 | "intent_name": user_message["parse_data"]["intent"]["name"], 100 | "intent_confidence": (user_message["parse_data"]["intent"]["confidence"]), 101 | "utter_name": "", 102 | "is_fallback": False, 103 | } 104 | 105 | self.insert_on_elastic(ts, message) 106 | 107 | def save_bot_message(self, bot_message, action_message, user_message): 108 | ts = time.time() 109 | timestamp = datetime.datetime.strftime( 110 | datetime.datetime.fromtimestamp(ts), "%Y/%m/%d %H:%M:%S" 111 | ) 112 | 113 | message = { 114 | "environment": ENVIRONMENT_NAME, 115 | "version": BOT_VERSION, 116 | "user_id": user_message["sender_id"], 117 | "is_bot": True, 118 | "text": user_message["text"], 119 | "tags": [], 120 | "timestamp": timestamp, 121 | "entities": [], 122 | "intent_name": user_message["parse_data"]["intent"]["name"], 123 | "intent_confidence": "", 124 | "utter_name": action_message["name"], 125 | "is_fallback": action_message["name"] == "action_default_fallback", 126 | } 127 | 128 | self.insert_on_elastic(ts, message) 129 | -------------------------------------------------------------------------------- /modules/webchat/assets/launcher_button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_button 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-como-fazer-uma-utter.md: -------------------------------------------------------------------------------- 1 | # Criação de Utter 2 | 3 | **Formato** - A Utter (textos que o Bot envia) não devem ser muito grande, pense em uma postagem do Twitter. 4 | 5 | **Conteúdo** - Deve seguir um Roteiro como se fosse uma conversa natural entre duas pessoas, sendo assim, as perguntas feitas pelo seu Bot devem ter um significado dentro dos objetivos e missões dele. 6 | 7 | **Personalidade** - Por fim, para que a conversa seja interessante e cativante você deve criar uma personalidade para o seu Chatbot que vai aparecer através do modo como ele se comunica, assim como uma pessoa ele deve ter características e comportamentos únicos que aparecem no modo como ele fala e interage com o interlocutor, seja por meio de emojis, ou pela escolha de palavras que usa, você pode ver exemplos disso nas recomendações. 8 | 9 | ## COMO COLOCAR A PERSONALIDADE DA TAIS NAS UTTERS 10 | 11 | A Tais usa norma padrão com um toque de oralidade, ela não conversa de um jeito muito formal, não usa jargões do direito, exceto quando manda algum artigo da Lei Rouanet. Ela gosta muito de usar palavras que enfatizam uma conversa falada: 12 | 13 | “aí” (“E aí, por onde vamos começar?”) 14 | 15 | “daí” (“daí você me manda outra!”) 16 | 17 | “que tal” (“Que tal falar de outra forma?”) 18 | 19 | “Hummmm…” (“Hummmm... Não sei se entendi.”) 20 | 21 | “olha”, (“Olha, alguns familiares não podem incentivar projetos”) 22 | 23 | Ela também usa emojis para demonstrar expressar simpatia: “Para lembrar dos assuntos que eu domino, é só digitar: #MEAJUDA :)” 24 | 25 | Para deixar sua utter mais fácil de ler, use conjunções para ligar os elementos da frase, mas lembre-se que a Tais não é muito formal então nunca use um “destarte” opte pelo mais simples como “logo”. Vou colocar alguns exemplos de conjunções/conectivos que você pode usar, não se esqueça de que você pode usar outros que não estão nessa lista e não tenham um tom tão formal: e, nem, mas também, assim, dessa forma, além disso, assim como, mas, porém, se bem que, se não, ou, já, talvez, da mesma maneira, tal como, da mesma forma, segundo, de acordo com, se, caso, quanto mais, quanto menos, porque, pois, para que, provavelmente, realmente, sem dúvida, com certeza, isto é, ou seja, em primeiro lugar, para começar, então, ainda, na medida em que, já que, por isso. 26 | 27 | ## ROTEIRO: 28 | 29 | 1 - Primeiro você precisa analisar o tema da utter, deste modo, leia o texto que a Secretaria Especial da Cultura enviou como resposta para a pergunta do usuário; 30 | 31 | 2 - É importante observar quais são as informações mais importantes. Se o texto for muito grande e abarcar muitos temas, você pode quebrá-lo em várias utters desde que encontre uma lógica para que as utters se liguem e o usuário possa ser guiado até elas através da conversa; 32 | 33 | 3 - Retire o essencial, desde que esse essencial consiga sanar a dúvida do usuário. Seja simples, às vezes menos é mais! Uma utter pode responder várias dúvidas ou apenas uma, isso vai depender de como você vai conseguir encaixar todos esses assuntos e se é mais viável criar novas utters ou colocar tudo em uma; 34 | 35 | 4 - Agora, você tem que pensar em quais dúvidas as expressões usadas ali podem causar nos usuários, depois disso, escreva a resposta com as suas palavras, implementando a personalidade da Tais no seu texto; 36 | 37 | 5 - O texto deve ser fluído, coerente e fácil de ler. Deve ser o menor possível, deve ser claro e objetivo, vá direto ao ponto! 38 | 39 | ## RECOMENDAÇÕES: 40 | 41 | **Polida** - “Obrigada por conversar comigo!” 42 | A Tais é muito educada, mas não exagere demais para não parecer uma conversa forçada. Seja simples, agradeça, use “por favor”, mas sem extrapolar na polidez. 43 | 44 | **Dedicada** - “quero te ajudar” 45 | A Tais precisa demonstrar ao usuário que está engajada em sua missão, ela demonstra sua dedicação quando oferece ajuda e se mostra sempre disponível. 46 | 47 | **Paciente** - “você pode escrever de novo de outra forma?” 48 | Ela está sempre disposta a tentar de novo, então se ela errou ou não sabe explicar, vai continuar pedindo para o usuário perguntar novamente de outro modo. 49 | 50 | **Gentil** - “espero estar te ajudando” 51 | A gentileza da Assistente Virtual da Secretaria Especial da Cultura é muito mais sutil e harmoniosa: “Foi um prazer te ajudar! Sempre que tiver alguma dúvida, volte aqui!” 52 | 53 | **Passiva** - “Que tal falar de outra forma?” 54 | Quando a Tais é afrontada e o usuário usa um palavrão, o melhor caminho será sempre a passividade: “Hummm… Não gostei muito dessa expressão que você usou. Que tal falar de outra forma?”. 55 | 56 | **Não dá espaço para intimidade** - “Oi, sou assistente virtual da Secretaria Especial da Cultura” 57 | Ela é muito atenciosa, mas é muito reservada. Então, nada de “Oi, bebê!”. 58 | 59 | Caso você queira entender como implementar a personalidade da Tais de forma mais profunda, leia o Guia de Conversação completo, acessando aqui: 60 | https://lappis-unb.github.io/tais/guia-de-conversacao-da-tais/ 61 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | # =============================== Coach ================================= 5 | # All the models are trained by this coach. 6 | coach: 7 | build: 8 | context: . 9 | dockerfile: ./docker/bot.Dockerfile 10 | volumes: 11 | - ./bot/models/:/bot/models/ 12 | command: "make train" 13 | 14 | 15 | # ================================= Bot ===================================== 16 | # Generic Rasa bot, used to run console for example. 17 | bot: 18 | build: 19 | context: . 20 | dockerfile: ./docker/bot.Dockerfile 21 | restart: unless-stopped 22 | volumes: 23 | - ./bot/:/bot/ 24 | ports: 25 | - 5004:5004 26 | depends_on: 27 | - actions 28 | 29 | # ================================= RASA X ================================== 30 | # Rasa X container 31 | x: 32 | build: 33 | context: . 34 | dockerfile: ./docker/bot.Dockerfile 35 | restart: unless-stopped 36 | volumes: 37 | - ./bot/:/bot/ 38 | ports: 39 | - 5002:5002 40 | depends_on: 41 | - actions 42 | command: sh -c "make x" 43 | 44 | # ================================= Actions ================================= 45 | # Rasa middleware used to connect with external APIs. 46 | actions: 47 | build: 48 | context: . 49 | dockerfile: ./docker/actions.Dockerfile 50 | ports: 51 | - 5055:5055 52 | volumes: 53 | - ./bot/actions:/bot/actions 54 | command: sh -c "make actions" 55 | 56 | # ============================ WebChat Bot ================================= 57 | # Specific Rasa bot integrated with WebChat. 58 | bot-webchat: 59 | build: 60 | context: . 61 | dockerfile: ./docker/bot.Dockerfile 62 | volumes: 63 | - ./bot/:/bot/ 64 | ports: 65 | - 5005:5005 66 | depends_on: 67 | - actions 68 | command: sh -c "make webchat" 69 | 70 | # =============================== Analytics ================================= 71 | # Analitics ElasticSearch Stack. 72 | elasticsearch: 73 | build: 74 | context: . 75 | dockerfile: ./docker/elasticsearch.Dockerfile 76 | restart: unless-stopped 77 | ports: 78 | - 9200:9200 79 | - 9300:9300 80 | env_file: 81 | - env/elasticsearch.env 82 | volumes: 83 | - esbackup:/usr/share/elasticsearch/backup 84 | - ./modules/elasticsearch/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml 85 | - esdata:/usr/share/elasticsearch/data 86 | 87 | # Visualization layer of Analitics Stack. 88 | kibana: 89 | build: 90 | context: . 91 | dockerfile: ./docker/kibana.Dockerfile 92 | restart: unless-stopped 93 | ports: 94 | - 5601:5601 95 | env_file: 96 | - env/kibana.env 97 | depends_on: 98 | - elasticsearch 99 | volumes: 100 | - ./modules/analytics:/usr/share/kibana/analytics 101 | 102 | # =============================== Broker ==================================== 103 | # Custom broker to store Rasa tracker data. 104 | rabbitmq: 105 | image: rabbitmq:3-management 106 | restart: unless-stopped 107 | volumes: 108 | - ./db/rabbitmq:/var/lib/rabbitmq/mnesia 109 | ports: 110 | - 15672:15672 111 | env_file: 112 | - env/rabbitmq.env 113 | 114 | # Custom broker consumer responsible to store data into ElasticSearch. 115 | rabbitmq-consumer: 116 | build: 117 | context: . 118 | dockerfile: ./docker/consumer.Dockerfile 119 | restart: unless-stopped 120 | volumes: 121 | - ./modules/rabbitmq/consumer/:/opt/scripts/ 122 | depends_on: 123 | - rabbitmq 124 | env_file: 125 | - env/rabbitmq-consumer.env 126 | command: python3 /opt/scripts/consume_bot_messages.py 127 | 128 | # ============================ Telegram Bot ================================= 129 | # Specific Rasa bot integrated with Telegram. 130 | bot_telegram: 131 | build: 132 | context: . 133 | dockerfile: ./docker/bot.Dockerfile 134 | volumes: 135 | - ./bot/:/bot/ 136 | env_file: 137 | - env/bot-telegram.env 138 | ports: 139 | - 5001:5001 140 | depends_on: 141 | - actions 142 | command: sh -c "make telegram" 143 | 144 | # =============================== Notebooks ================================= 145 | # Rasa lab to enhance hyperparameters. 146 | notebooks: 147 | build: 148 | context: . 149 | dockerfile: ./docker/notebooks.Dockerfile 150 | env_file: 151 | - env/notebooks.env 152 | volumes: 153 | - ./modules/notebooks:/work/notebooks 154 | - ./bot/:/bot/ 155 | ports: 156 | - 8888:8888 157 | 158 | volumes: 159 | notebook_models: 160 | mongo_data: 161 | rabbit_data: 162 | esbackup: 163 | esdata: 164 | driver: local 165 | -------------------------------------------------------------------------------- /docs/pipeline-de-qualidade.md: -------------------------------------------------------------------------------- 1 | # Qualidade do bot 2 | 3 | Além do grande desafio de como gerenciar o conteúdo de maneira viável e organizada, existe o desafio de como crescer o domínio do bot com qualidade. À medida que a base de treinamento do bot muda, muitas vezes é necessário também atualizar os parâmetros de treinamento da rede para garantir que a acurácia do *bot* se mantenha. 4 | 5 | Aliado à isso, no contexto colaborativo de uma equipe com diversas pessoas evoluindo o bot simultaneamente, é preciso alinhar a visão dos membros do time em relação aos parâmetros de qualidade. 6 | 7 | ## Qualidade em ChatBots Rasa 8 | 9 | Se Tratando de Chatbots desenvolvidos com Rasa, os parâmetros de qualidade estão relacionados principalmente à dois aspectos: 10 | 11 | **Identificação de intenções:** Qual o desempenho do *bot* em classificar um texto em uma categoria que define a sua intenção; 12 | 13 | **Desempenho em fluxos de conversa:** O quão bem o *bot* consegue se comportar de acordo com as intenções classificadas e responder corretamente o usuário de acordo com o histórico das interações e a base de treinamento; 14 | 15 | Também há a preocupação em relação à usabilidade do *bot*, uma vez que caso o *bot* tenha uma Experiência de Usuário ruim, ele poderá ter um péssimo desempenho e se comportar de forma inesperada, mesmo com uma boa base de treinamentos e uma rede muito bem configurada. A melhoria desta experiência está relacionada há fatores como desenvolvimento de personalidade, construção de roteiros de conversa, adequação da linguagem do bot ao público-alvo, etc. Porém, aferir de forma automatizada a qualidade desses parâmetros ainda é uma tarefa inviável, sendo assim a qualidade dessas métricas exige uma análise direta e profunda por parte da equipe. 16 | 17 | ## Abordagem de aferição da qualidade 18 | 19 | Tendo em consideração os parâmetros anteriores, a construção desta abordagem é baseada no objetivo de garantir de forma automatizada a acurácia do ChatBot em relação à detecção de intenções e o desempenho nos fluxos de conversas. 20 | 21 | **Estágio 1:** O primeiro estágio consiste em garantir a consistência de sintaxe dos arquivos de treinamento do *bot*. O Rasa utiliza um formato de configuração que depende de um arquivo principal chamado `domain`, onde devem estar definidas todas as *intents* e *utters* definidas e utilizadas. Caso haja alguma inconsistência entre essas informações o desempenho do *bot* poderá ser afetado, uma vez que parâmetros importantes podem não ser corretamente utilizados durante o processo de treinamento e predição do *bot*. 22 | 23 | Objetivo: O foco deste estágio é sanar possíveis erros de digitação, inconsistências durante *merges* e definições incorretas de *intents* e *utters* nos arquivos de configuração. 24 | 25 | Solução proposta: Dentro da TAIS é utilizado um [script de validação](https://github.com/lappis-unb/tais/blob/master/coach/validator.py) automatizada desses parâmetros, que analiza os arquivos de configuração do Rasa e o *dataset* para garantir que não hajam inconsistências. 26 | 27 | **Estágio 2:** O segundo estágio consiste na validação do comportamento do *bot* durante as interações e a execução dos fluxos de conversa. 28 | 29 | O Rasa já oferece um [recurso de validação dos fluxos de conversação](https://rasa.com/docs/core/evaluation/). Através desse *evaluation* é possível testar o comportamento do *bot* dentro do contexto de uma conversa, e entender se ele está fazendo uma predição correta das ações a serem tomadas de acordo com cada intenção identificada. Além disso, esse mecanismo facilita o teste da capacidade de generalização do *bot*. 30 | 31 | Objetivo: O objetivo deste estágio é garantir que o *bot* está se comportando da forma esperada para determinados fluxos de conversa. 32 | 33 | Solução proposta: Pode-se utilizar a funcionalidade de [evaluation](https://rasa.com/docs/core/evaluation/) do Rasa para testar diretamente o funcionamento de um fluxo de conversa. 34 | 35 | Dentro da TAIS foi adicionada mais uma camada de testes com um [script para melhoria da visualização dos testes](https://github.com/lappis-unb/tais/blob/master/bot/test_stories.py). Onde é possível ver no *console* quais são as *intents* e *utters* que falharam. Isso permite que a equipe de desenvolvimento possa identificar diretamente os pontos onde a acurácia do *bot* não está tão boa ou o comportamento não está sendo o esperado. 36 | 37 | ![](../../assets/teste_quebrado.png) 38 | 39 | Ao fim desse *script* é exibido um log das histórias que falharam dentro de cada um dos arquivos. 40 | 41 | ![](../../assets/teste_resultado.png) 42 | 43 | ## Utilização do Pipeline 44 | 45 | Pode-se configurar tasks para a execução destes *scripts* dentro de cada contexto. 46 | Uma das alternativas, que é utilizada dentro da TAIS, é utilizar um serviço de CI para validação automatizada destes passos simultaneamente ao processo de contribuição no repositório. 47 | Na TAIS foram configuradas duas *tasks* simples utilizando um `Makefile`. A primeira *task* executa a validação dos arquivos de configuração do *bot* e recebe como parâmetro o path para o arquivo `domain`, e os diretórios onde estão as `intents` e `stories`. A segunda *task* executa o `evaluation` nos fluxos de teste que estão dentro do diretório `e2e`. 48 | 49 | ```makefile 50 | run-validator: 51 | python3 validator.py --intents data/intents/ --stories data/stories --domain domain.yml 52 | 53 | test-stories: 54 | python3 test_stories.py --stories e2e/ --e2e 55 | ``` 56 | 57 | Após isso foram definidas duas *tasks* simples para validação dos parâmetros definidos. Estes estágios são validados no CI a cada *commit*, mas podem ser configurado segundo regras específicas, por exemplo serem executados apenas em determinadas *branches* do repositório. 58 | 59 | ```yml 60 | run dataset validator: 61 | stage: validate format 62 | image: lappis/coach:latest 63 | script: 64 | - cd coach/ 65 | - make run-validator 66 | 67 | test stories: 68 | stage: test stories 69 | image: lappis/bot:latest 70 | script: 71 | - cd bot/ 72 | - make test-stories 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-testes-automatizados.md: -------------------------------------------------------------------------------- 1 | # Criação de Testes Automatizados 2 | 3 | Ao construir um bot com o framework Rasa, deve se ter em mente como funciona a construção do diálogo do ponto de vista do bot, como ele processa todas as informações que são jogadas. O bot agrega uma concepção de um ser que interage e que se mostra "pensante", mas os desenvolvedores devem ter noção de como um bot age, como ele constrói todas suas previsões de diálogo e as usa a seu favor. 4 | 5 | Imagine que começamos a fazer um bot qualquer, com um escopo definido, ao construirmos todas as estruturas necessárias para que ele responda e entenda, na verdade estaremos construindo a sua rede neural, que na parte humana seria a associação que fazemos da resposta a uma pergunta. Dessa forma, a rede neural é baseada na inserção de conteúdo e a ligação do mesmo a interação com o usuário, por exemplo no Boiler-Plate, ao colocarmos cada vez mais assunto, estamos expandindo nosso escopo e deixando nosso bot mais "inteligente". Em suma, as redes neurais são construídas a partir do treinamento do bot, que possibilita-o prever respostas, sintetizando os fluxos possíveis mediante a intenção do usuário. 6 | 7 | No Rasa isso é feito de uma forma muito simples, ao criarmos novas intents e stories, damos abertura ao nosso bot dialogar sobre mais assuntos. Infelizmente temos um grande problema nesse quesito, ao inserirmos mais conteúdo, estamos abrindo espaço para que nosso bot esteja confundindo algum fluxo com outro. 8 | 9 | Para inserirmos mais conteúdo em nosso bot de forma segura, utilizamos os testes automatizados que identifica se o novo conteúdo foi interferido ou interferiu em outro fluxo, acarretando em uma validação de fluxo e trazendo uma evolução muito mais fácil em estrutura e conteúdo. 10 | 11 | Para construirmos os testes devemos primeiro escolher um fluxo no qual queremos tratar, depois estruturar uma conversa para esse fluxo, que nos permite avaliar todo o escopo que escolhemos. Por exemplo, no BoilerPlate temos a intent esporte e queremos ter a certeza que ela sempre estará funcionando. Nesse contexto, podemos gerar um diálogo que condiz com o que queremos: 12 | 13 | ``` 14 | Usuário 15 | 16 | - oi (intent cumprimentar) 17 | - qual o seu jogo favorito? (intent esporte) 18 | - tchau (intent despedir) 19 | ``` 20 | 21 | Esse é um exemplo bem simples, que conseguimos tirar dele informações que confirma o fluxo que queremos e nos informa se alguma mudança ocorreu. 22 | 23 | Para construirmos o código dos testes, primeiro devemos entender que ao criarmos o fluxo que queremos temos que identificar a utter que corresponde aquela intenção: 24 | 25 | ``` 26 | Usuário Bot 27 | 28 | - oi -> utter_cumprimentar 29 | - qual o seu jogo favorito? -> utter_esporte 30 | - tchau -> utter_despedir 31 | ``` 32 | 33 | Agora fazer o código fica bem mais fácil: 34 | 35 | ``` 36 | ## end-to-end story 1 37 | * cumprimentar: ola 38 | - utter_cumprimentar 39 | * esporte: qual o seu jogo favorito? 40 | - utter_esporte 41 | * despedir: tchau 42 | - utter_despedir 43 | ``` 44 | 45 | O código é um arquivo MarkDown composto por um título que indica que estamos trabalhando com arquivos de ponta a ponta, em seguida dizemos a intent e o texto pré estabelecido para o teste, embaixo dizemos a utter que corresponde ao texto. 46 | 47 | Esse código está localizado no coach/data/e2e, nele concentramos todos os arquivos de teste, como boa prática é comum começarmos o nome do arquivo de "e2e". 48 | 49 | ## Executando os arquivos e2e 50 | 51 | Todas as informações de como executar os arquivos de teste está descrito no README do [BoilerPlate](https://github.com/lappis-unb/rasa-ptbr-boilerplate) 52 | 53 | ## Entendendo os resultados 54 | 55 | Sabe-se que o teste do Rasa nos possibilita avaliar o fluxo de ponta a ponta, com as intenções pré definidas pelo mesmo, desta forma, ele nos disponibiliza uma forma de visualização desse teste no qual ele nos fornece: 56 | 57 | "precision" - quantidade de utters_esperadas divididas pela quantidade de utters_ocorridas 58 | "recall" - porcentagem de respostas que corresponderam a utter esperada. 59 | "f1-score" - a porcentagem de relação da precisão com o recall, sendo (2x precisão x recall) / (precisão + recall) 60 | "support" - o tanto de vezes que teste utiliza cada variável. 61 | 62 | Matriz: 63 | 64 | Desses dados gera uma matriz pelo script que permite visualizar a distribuição de confiança para todas as previsões 65 | 66 | Essa matriz nos permite visualizar a relação entre as utters_esperadas (criadas pelos testes) e as utters_ocorridas (as que realmente foram respondidas mediante ao fluxo), priorizando o fluxo em que a conversa segue e a intenções que as correspondem, como pode ser visto no exemplo acima onde todas as utters_esperadas correlacionam com as utters_ocorridas. Forma-se uma matriz quadrada onde sua coluna 0 e linha 0 são respectivamente, a quantidade de utters_esperadas que correlacionaram com as utters ocorridas + as utters_esperadas que não resultaram em nenhum valor e a action_list (o numero de intenções no teste). 67 | 68 | Há apenas uma exceção nesse quesito, quando houver um texto que retorne o fallback, ele criará uma matriz onde a coluna 0 e linha 0 são respectivamente, fallback e NONE. Isso pode ser visto na imagem abaixo no qual o texto retornou um fallback e a relação com a utter_esperada na ultima linha. 69 | 70 | Pode-se dizer que as linhas são a utters_esperadas e as colunas são as utters_ocorridas, e os números são as quantidades de vezes que uma utter corresponde a outra ou deixa de corresponder naquele fluxo da conversa. Quando as utters_esperadas são relacionadas com as corretas utters_ocorridas este número irá ficar na diagonal principal sinalizando a quantidade de vezes de correspondência. 71 | 72 | Na matriz pode-se perceber que existem dois números fora da diagonal principal, eles correspondem a utters que não corresponderam a sua respectiva resposta esperada. No número fora da diagonal na linha 0, conclui-se que é uma ação que ocorreu de algum utter que não foi esperada, já no segundo número fora da diagonal é uma utter esperada que não correspondeu a utter_ocorrida. -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-primeira-conversa.md: -------------------------------------------------------------------------------- 1 | # Primeira Conversa 2 | 3 | O Boilerplate vem com a prosposta de facilitar a criação de um chat-bot para diferentes contextos, desenvolvendo uma estrutura um tanto complexa, mas com passos simples para seu bot conseguir dar os primeiros avanços do jeito que desejar. 4 | 5 | As ferramentas utilizadas nessa tutorial foi estudada e utilizada pelo LAPPIS (Laboratório de produção, pesquisa e inovação em software), para criação da Tais, um chat-bot que visa explicar os conceitos e novidades sobre a lei de incentivo à cultura do Brasil. No cume do nosso projeto, foi utilizado o Rasa NLU, uma ferramenta open source para processamento de linguagem natural, sendo focada em classificação de intenções e extração de identidades, e o Rasa Core, uma ferramenta livre para construção de sistemas de conversação. 6 | 7 | Na utilização dessas ferramentas, há a necessidade de ter um arquivo domain.yml que define o contexto em que o bot está inserido. Dentro desse arquivo são especificadas as intenções e ações a serem utilizadas durante a execução do bot. 8 | 9 | A primeira coisa a se fazer é clonar o repositório do BoilerPlate localmente. 10 | 11 | Dentro do diretório `coach/data` há uma pasta chamada `intents`, que contém vários arquivos mark-down, que definem os textos relacionados à cada intenção. E outra pasta chamada `stories`, que contém vários arquivos mark-down, que descrevem os contextos de conversação esperados a partir das intenções. 12 | 13 | Então vamos fazer passo a passo de como criar o seu primeiro chatbot. 14 | 15 | ## Intent 16 | 17 | Se imagine conversando com uma pessoa, vocês estão tentando decidir qual será os sabores de pizza que vocês irão comprar para jantar. A cada frase formulada pelos dois, deve-se observar que tem um intuito naquilo que é falado. Para construção de um bot, tiramos esse conceito e damos o nome de intents, dessa forma conseguimos estruturar muito bem o que o nosso bot irá dizer, definindo o assunto que corresponderá todo o escopo de conversação, ou seja, a partir do que o usuário interage, conseguimos esteriotipar a sua fala, dando uma intenção para aquilo. 18 | 19 | As intents são criadas em um arquivo Mark-Down, onde ele se baseia na estrutura do arquivo para determinar qual tipo de exemplo de frase corresponde a intent. A estrutura para as construções de intents é bem simples, primeiramente precisamos especificar que é uma intent `#intent:`, ao colocar assim estamos dizendo que existe uma nova intent naquele arquivo (Você pode acompanhar acessando um dos arquivos na pasta coach/data/intents). Em frente, você colocará o nome da intent `#intent: nome_intent`, por exemplo: 20 | 21 | ``` MarkDown 22 | #intent: sabores_pizza 23 | ``` 24 | 25 | Para a finalização das intents você precisa dar exemplos de assunto, ou seja, como no nosso exemplo da `intent: sabores_pizza`, temos vários exemplos no qual podemos identificar como sabores de pizza que o usuário poderá falar, como calabreza, mussarela, a moda, etc: 26 | 27 | ``` MarkDown 28 | #intent: sabores_pizza 29 | - Quais sabores vocês tem? 30 | - Quais são os sabores? 31 | - Você pode me dizer os sabores 32 | ``` 33 | 34 | Assim você pode dar vários exemplos para que o bot seja treinado e consiga entender vários sabores de pizza :) 35 | 36 | ## Utter 37 | 38 | Em um bot, devemos esperar que ele não consiga fazer a formulação das frases sozinho. Na construção do bot a partir do Boiler-Plaite, há um arquivo chamado domain, encontrado dentro da pasta `coach/`, onde a primeira vista fica muito dificil de se analisar e compreender o que está ocorrendo, mas que ficará tudo claro logo. 39 | 40 | Domain é um arquivo yml, que é composto por uma estrutura bem simples: 41 | 42 | Na primeira parte do domain podemos perceber que está sendo listado todos os nomes das `intents` que compõe o Boiler-Plaite, nessa parte estamos especificamos quais são os assuntos que o nosso bot consiguirá responder. 43 | 44 | Em seguida há o nome `entities`, que é o conteúdo que agrega a corrente principal de fluxo de conversa do seu bot, sendo assim, podemos perceber a partir das entities do que nosso bot irá falar descartando o fluxo natural que imaginamos que um bot deverá seguir (claro que esse fluxo pode ser mudado ao gosto do usufruidor). 45 | 46 | Após entities temos `templates`, que é composto de todas as respostas que o bot pode fazer, damos o nome de utters. Na primeira parte podemos perceber, uma utter muito importante para a construção de qualquer bot, a `utter_default`, essa utter tem o objetivo de sinalizar ao usuário que não está entendendo o que ele está falando ou orienta-lo a falar de uma forma mais compreensivel para o bot. Para construirmos uma utter primeiramente devemos dar o nome como por exemplo `utter_sabores_pizza:`, por convenção colocamos a primeira palavra de utter. Após o nome iremos complementar com `- text: |`, sinalizando que iremos escrever um texto e em seguida escrever a mensagem desejada, exemplo: 47 | 48 | ``` MarkDown 49 | utter_sabores_pizza: 50 | - text: | 51 | temos calabreza, mussarela, a moda e portuguesa 52 | ``` 53 | 54 | Ao final do arquivo temos as `actions`, que é composta de todas as utters que criamos nos templates. 55 | 56 | ## Stories 57 | 58 | Há várias coisas para se analisar em uma conversa, mas há uma coisa muito essencial em qualquer interação verbal: o assunto, ou um tema que corresponde aquele diálogo. Quando extraímos isso para âmbito do chat-bot, conseguimos denomina-los como stories, são eles que descrevem os contextos de conversação esperados a partir das intenções. 59 | 60 | Na construção de um novo storie, você deve ir ao diretório `coach/data/stories` e acessar ou criar um arquivo mark-down com o nome relacionado a storie que será criada. Dentro do arquivo, primeiro você dá o nome do storie precedido de "##" e depois você lista em ordem o fluxo que a pessoa irá seguir, onde primeiramente você diz qual intent com "*" e depois a utter com "-", e é dessa forma que conseguimos correlacionar intents com utters, fazendo nosso bot entender e responder o esperado, exemplo: 61 | 62 | ``` MarkDown 63 | ## Oi Tudo Bem Story 1 64 | * cumprimentar 65 | - utter_cumprimentar 66 | * tudo_bem 67 | - utter_tudo_bem 68 | ``` 69 | 70 | 71 | ## Executando e testando seu bot 72 | 73 | Todas as informações de como executar e treinar seu bot está descrito no README do BoilerPlate. Para desenvolvimento sugerimos a utilização do console, já que o mesmo especifica o comportamento do seu chat-bot. Entrentanto se o desejado for a utilização de alguma plataforma de interação com o usuário, incentivamos a seguir o tutorial do RocketChat ou Telegram (https://github.com/lappis-unb/rasa-ptbr-boilerplate). 74 | 75 | ## Próximos passos 76 | 77 | 78 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Este é um documento para orientação em como contribuir para o repositório do Rasa PT-BR Boilerplate. Antes de começar a contribuir veja as [issues já abertas](http://github.com/lappis-unb/rasa-ptbr-boilerplate/issues), e principalmente o funcionamento de nossa [arquitetura](https://github.com/lappis-unb/rasa-ptbr-boilerplate/blob/master/README.md). 2 | 3 | Veja as orientações abaixo para cada tipo de contribuição: 4 | * [Por onde começar a contribuir?](#comece-a-contribuir) 5 | * [Reportar erros](#encontrou-um-bug) 6 | * [Consertar erros](#concertou-um-bug) 7 | * [Contribuir para a documentação](#quer-contribuir-para-a-nossa-documentação) 8 | * [Adicionar nova feature](#quer-adicionar-uma-feature-nova-ou-ajudar-com-uma-existente) 9 | * [Contribuir para o conteúdo do Boilerplate](#quer-contribuir-para-o-conteúdo-do-boilerplate) 10 | 11 | ### Comece a contribuir 12 | Quer começar a contribuir para o Boilerplate? O processo em geral é bem simples: 13 | 14 | - Crie uma issue descrevendo uma feature que você queira trabalhar ou entre em issues já abertas (caso comece por uma issue já existente comente na issue que você está desenvolvendo). 15 | - Escreva seu código, testes e documentação 16 | - Abra um pull request descrevendo as suas alterações propostas 17 | - Seu pull request será revisado por um dos mantenedores, que pode levantar questões para você sobre eventuais mudanças necessárias ou questões. 18 | 19 | Veja nossa [documentação](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master/docs) para entender um pouco melhor sobre nosso código e [arquitetura](https://github.com/lappis-unb/rasa-ptbr-boilerplate/blob/master/README.md), veja nossas issues, principalmente as com as tags `help-wanted` e `good-first-issue`, que são as ideais para começar a contribuir para o Boilerplate. 20 | 21 | 22 | ### Encontrou um Bug? 23 | Caso tenha encontrado algum erro, nos informe por uma issue, assim poderemos estar sempre melhorando. Pedimos que seja descritivo, dessa forma poderemos identificar e reproduzir o erro para ser possível consertá-lo. 24 | 25 | Antes de reportar o Bug, veja as [issues com a tag `bug`](https://github.com/lappis-unb/rasa-ptbr-boilerplate/labels/bug) e verifique se o erro identificado já não possui uma issue criada. 26 | 27 | Para uma boa documentação: 28 | * Nomeie a issue com um nome claro e descritivo de acordo com o problema; 29 | * Descreva o passo a passo para chegar no erro encontrado; 30 | * Mostre exemplos do erro ocorrido; 31 | * Descreva o comportamento esperado e o comportamento obtido; 32 | * Marque a issue criada com a tag `bug`. 33 | 34 | Veja a seguinte estrutura de issue: 35 | 36 | ``` markdown 37 | 38 | **Descrição do erro encontrado:** 39 | ... 40 | 41 | **Passo a passo para a reprodução do erro:** 42 | 1. 43 | 2. 44 | ... 45 | **Comportamento esperado:** ... 46 | **Comportamento obtido:** ... 47 | ``` 48 | 49 | ### Consertou um Bug? 50 | Para enviar a sua solução e consertar um bug existente, fork nosso repositório e crie um Pull Request descrevendo o problema e como ele foi corrigido. 51 | 52 | Para uma bom Pull Request: 53 | * Nomeie o PR de forma descritiva e clara de acordo com o problema resolvido; 54 | * Descreva o problema e a sua solução; 55 | * Marque a issue que o PR soluciona. 56 | 57 | Veja o exemplo abaixo: 58 | 59 | ``` markdown 60 | **Issue:** #[Número-da-Issue] 61 | **Descrição do Problema:** 62 | ... 63 | **Descrição da Solução:** 64 | ... 65 | ``` 66 | 67 | ### Quer contribuir para a nossa Documentação? 68 | Para contribuir com a documentação, veja a documentação já existente, e as issues pendentes para documentação marcadas com a [tag `documentação`](https://github.com/lappis-unb/rasa-ptbr-boilerplate/labels/documentação). 69 | 70 | Caso queira resolver uma issue já existente, comente na issue que está trabalhando, caso ainda não exista uma issue crie uma nova issue descrevendo o problema encontrado e marque com a tag `documentação`. 71 | 72 | Para solucionar faça um PR com a descrição do que foi feito e a referência a issue que está resolvendo. 73 | 74 | ### Quer adicionar uma feature nova ou ajudar com uma existente? 75 | 76 | Nosso desenvolvimento é dividido em algumas frentes principais, sendo elas: 77 | * **Bot Rasa:** Frente para o desenvolvimento do conteúdo do Boilerplate, com a utilização do Rasa (na pasta `bot`); 78 | * **ElasticSearch:** Frente para o desenvolvimento de dashboards com Kibana para a análise das conversas do bot com os usuários (na pasta `analytics`); 79 | * **Plataforma de Conteúdo:** Desenvolvimento de uma plataforma para adicionar conteúdo no Boilerplate, sem a necessidade de mexer diretamente nos arquivos do código ([no repositório `rasa-nlu-trainer`](https://github.com/lappis-unb/rasa-nlu-trainer)). 80 | 81 | Além de que também temos algumas outras áreas, onde é possível contribuir, como: 82 | * **Notebooks:** Notebooks jupyter para análise da estrutura e funcionamento do Bot (na pasta `notebooks`); 83 | 84 | Aceitamos contribuições em todas as áreas do nosso código, desde que seja uma contribuição válida e traga reais melhorias para o projeto. Para fazer uma contribuição abra uma issue, com nome descritivo, especificando o que será feito e qual frente será afetada. Veja o exemplo abaixo de um bom template a ser feito: 85 | 86 | ``` markdown 87 | **Frente a ser trabalhada:** ... 88 | **Descrição da nova feature:** 89 | ... 90 | **Porque essa feature melhoraria o código:** 91 | ... 92 | ``` 93 | Caso sua contribuição entre um uma das frentes principais, marque com a sua label específica. Temos as labels: 94 | * `elasticSearch`: para issues da frente de análise do Elasticsearch com Kibana; 95 | * `plataforma-conteudo`: para issues da frente do front-end de adicionar conteúdo no Boilerplate; 96 | * `rasa`: Para issues sobre o Rasa, na frente do Bot. 97 | 98 | Após fazer a issue, dê um fork do repositório e faça um Pull Request com sua nova feature. Dê um bom nome para o PR, especifique a sua solução, em qual frente ela se encaixa e a referência aissue relacionada. Veja o exemplo de uma estrutura de PR abaixo: 99 | 100 | ``` markdown 101 | **Frente a ser trabalhada:**... 102 | **Issue:** #[Número-da-Issue] 103 | 104 | **Descrição da nova feature:** 105 | ... 106 | **Descrição de como foi feito:** 107 | 108 | **Descrição de como ela funciona:** 109 | ... 110 | ``` 111 | 112 | ### Quer Contribuir para o conteúdo do Boilerplate? 113 | Além de contribuição de código, o Boilerplate também possui contribuições de conteúdo. Veja as [issues marcadas com a tag `conteudo`](https://github.com/lappis-unb/rasa-ptbr-boilerplate/labels/conteudo). Comente na issue que está trabalhando, faça o fork e submeta seu PR. Lembre-se de definir o que foi feito, linkar com a issue e marcar com a tag `conteudo`. Veja abaixo um exemplo de PR para contribuição de conteúdo. 114 | 115 | ``` markdown 116 | **Issue:**#[Número-da-Issue] 117 | **Conteúdo adicionado:** 118 | * Exemplo de pergunta do usuário: ... 119 | * Resposta: ... 120 | ... 121 | 122 | ``` 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rasa Boilerplate 2 | 3 | 4 | 5 | ![badge_build](https://github.com/lappis-unb/rasa-ptbr-boilerplate/workflows/build_bot/badge.svg) 6 | 7 | 8 | ## For English version, see [README-en](docs/README-en.md) 9 | 10 | ## Tutorial para configurar todo o projeto 11 | 12 | ### Pré requisitos 13 | 14 | Para rodar o projeto em sua máquina é necessário ter instalado: 15 | - Docker 16 | - Docker compose 17 | 18 | ### Primeiros passos 19 | 20 | Primeiramente, clone o repositório para sua máquina local usando o comando: 21 | 22 | ```sh 23 | git clone 24 | ``` 25 | 26 | Para ter seu chatbot Rasa funcionando, certifique-se de estar dentro da pasta do projeto e então execute no terminal o seguinte comando: 27 | 28 | ```sh 29 | make first-run 30 | ``` 31 | 32 | ⚠️ **Atenção**: Caso ocorra algum erro de permissão, executar o comando `sudo make first-run`. 33 | 34 | 35 | Esse comando irá construir a infraestrutura necessária (subir containers com as dependências, treinar o chatbot, etc) para possibilitar a interação com o chatbot. 36 | 37 | Tudo está dockerizado então você não deve ter problemas de instalação do ambiente. 38 | 39 | Depois que tudo for instalado, você verá a seguinte mensagem e pode começar a interagir com o bot 40 | 41 | ```sh 42 | Bot loaded. Type a message and press enter (use '/stop' to exit): 43 | Your input -> 44 | ``` 45 | 46 | Para fechar a interação com o bot é só dar `ctrl+c`. 47 | 48 | 49 | Para conferir se os contêineres foram construídos corretamente, execute o comando: 50 | 51 | ```sh 52 | docker ps 53 | ``` 54 | Se tudo der certo, você conseguirá ver uma tabela com dois contêineres de nomes `rasa-ptbr-boilerplate_bot-webchat` e 55 | `rasa-ptbr-boilerplate_actions` na coluna IMAGE. 56 | 57 | Para iniciar uma conversa com o chatbot, execute o comando `make run-shell`, espere o comando rodar e divirta-se! 58 | 59 | 60 | ## Introdução 61 | 62 | Um projeto feito em Rasa com configurações necessárias para a construção de um projeto grande de chatbot. 63 | 64 | Este projeto teve como base o projeto [Tais](http://github.com/lappis-unb/tais). 65 | 66 | ### Entenda a Arquitetura 67 | 68 | É utilizado no boilerplate diversas tecnologias que interagem entre si para obter um melhor resultado. Veja a arquitetura implementada: 69 | 70 | ![](https://user-images.githubusercontent.com/8556291/57933140-d8d66b80-7892-11e9-8d58-a7eda60b099b.png) 71 | 72 | O usuário interage com a Boilerplate via Telegram, que manda as mensagens para o Rasa NLU através de 73 | conectores, onde ele identifica a *intent*, e responde pelo Rasa Core, de acordo com as *stories* e *actions*. 74 | As *models* utilizadas para a conversação foram geradas pelo módulo *trainer* e depois transferidas para o bot, estes 75 | modelos podem ser versionados e evoluídos entre bots. 76 | Os notebooks avaliam o funcionamento de acordo com o formato das *intents* e *stories*. 77 | O elasticsearch coleta os dados da conversa e armazena para a análise feita pelo kibana, que gera gráficos para 78 | avaliação das conversas dos usuários e do boilerplate. 79 | 80 | ### Bot 81 | 82 | Este script foi configurado para construir as imagens genéricas necessárias para execução deste ambiente. 83 | Caso seu projeto utilize este boilerplate e vá realizar uma integração contínua ou similar, é interessante 84 | criar um repositório para as imagens e substitua os nomes das imagens "lappis/bot", e "lappis/botrequirements" pelas suas respectivas novas imagens, por exemplo "/bot" em repositório público. 85 | 86 | 87 | ### Treinamento 88 | 89 | **Atenção**: o comando de treinamento é usado para criar os modelos necessários na conversação do bot. Para treinar o seu chatbot execute o comando: 90 | 91 | ```sh 92 | make train 93 | ``` 94 | 95 | ### Executando o bot no terminal 96 | Para executar o bot no terminal execute: 97 | 98 | ```sh 99 | make run-shell 100 | ``` 101 | 102 | ### Executando o bot no Telegram 103 | 104 | Após realizar o [tutorial](/docs/setup_telegram.md) de exportação de todas variávies de ambiente necessárias, é possível realizar a execução do bot no telegram corretamente. 105 | 106 | **Antes de seguir adiante. Importante:** As variáveis de ambiente são necessárias para o correto funcionamento do bot, por isso não esqueça de exportá-las. 107 | 108 | Edite o arquivo **credentials.yml** e descomente as linhas referentes ao telegram: 109 | 110 | ```sh 111 | telegram: 112 | access_token: ${TELEGRAM_TOKEN} 113 | verify: ${TELEGRAM_BOT_USERNAME} 114 | webhook_url: ${TELEGRAM_WEBHOOK} 115 | ``` 116 | 117 | Depois execute o bot no telegram: 118 | 119 | ```sh 120 | make run-telegram 121 | ``` 122 | 123 | ### Analytics 124 | 125 | Para a visualização dos dados da interação entre o usuário e o chatbot nós utilizamos uma parte da Stack do Elastic, composta pelo ElasticSearch e o Kibana. Com isso, utilizamos um broker para fazer a gerência de mensagens. Então conseguimos adicionar mensagens ao ElasticSearch independente do tipo de mensageiro que estamos utilizando. 126 | 127 | * Para uma **configuração rápida** execute o seguinte comando: 128 | 129 | ```sh 130 | make build-analytics 131 | ``` 132 | 133 | Espere até os serviço do *ElasticSearch* estar pronto, e execute o comando abaixo para configurar os índices: 134 | 135 | ``` 136 | make config-elastic 137 | ``` 138 | 139 | Espere até os serviço do *Kibana* estar pronto, e execute o comando abaixo para configurar os *dashboards*: 140 | 141 | ``` 142 | make config-kibana 143 | ``` 144 | 145 | O comando acima precisa ser executado apenas 1 vez e já vai deixar toda a infra de `analytics` pronta para o uso. 146 | 147 | Acesse o **kibana** na url `locahost:5601` 148 | 149 | Caso você deseje entender o processo de configuração da *stack* de *analytics*, veja a [explicação completa de analytics](docs/setup_analytics.md). 150 | 151 | ## Notebooks - Análise de dados 152 | 153 | ### Setup 154 | 155 | Levante o container `notebooks` 156 | 157 | ```sh 158 | make run-notebooks 159 | ``` 160 | 161 | Acesse o notebook em `localhost:8888` 162 | 163 | # Como conseguir ajuda 164 | 165 | Parte da documentação técnica do framework da Tais está disponível na 166 | [wiki do repositório](https://github.com/lappis-unb/tais/wiki). Caso não encontre sua resposta, abra uma issue 167 | com a tag `duvida` que tentaremos responder o mais rápido possível. 168 | 169 | Em caso de dúvidas em relação ao Rasa, veja o grupo [Telegram Rasa Stack Brasil](https://t.me/RasaBrasil), 170 | estamos lá também para ajudar. 171 | 172 | Veja mais informações de contato em nosso site: https://lappis.rocks 173 | 174 | # Licença 175 | 176 | Todo o framework do boilerplate é desenvolvido sob a licença 177 | [GPL3](https://github.com/lappis-unb/rasa-ptbr-boilerplate/blob/master/LICENSE) 178 | 179 | Veja a lista de dependências de licenças [aqui](https://libraries.io/github/lappis-unb/rasa-ptbr-boilerplate) 180 | -------------------------------------------------------------------------------- /docs/README-en.md: -------------------------------------------------------------------------------- 1 | # Rasa Boilerplate 2 | 3 | 4 | 5 | 6 | 7 | A project made in Rasa with the necessary configurations for the construction of a large chatbot project. 8 | 9 | This project was based on [Tais](http://github.com/lappis-unb/tais). 10 | 11 | # Understand the Architecture 12 | 13 | Various technologies are used in boilerplate that interact with each other to obtain a better result. See the implemented architecture: 14 | 15 | ![](https://user-images.githubusercontent.com/8556291/57933140-d8d66b80-7892-11e9-8d58-a7eda60b099b.png) 16 | 17 | The user interacts with Boilerplate via RocketChat or Telegram, which sends the messages to the Rasa NLU via 18 | connectors, where it identifies the *intent*, and responds to the Rasa Core, according to *stories* and *actions*. 19 | The *models* used for the conversation were generated by the *trainer* module and then transferred to the bot; these 20 | models can be versioned and evolved between bots. 21 | Notebooks evaluate the operation according to the format of *intents* and *stories*. 22 | Elasticsearch collects data from the conversation and stores it for the analysis done by kibana, which generates charts for 23 | evaluation of user conversations and boilerplate. 24 | 25 | ## Bot 26 | 27 | This script has been configured to build the generic images needed to run this environment. 28 | If your project uses this boilerplate and goes to perform continuous or similar integration, it is interesting 29 | to create a repository for the images and replace the names of the "bot", "coach" and "requirements" their respective new images, for example "/bot" in a public repository. 30 | 31 | ### RocketChat 32 | 33 | ```sh 34 | sudo docker-compose up -d rocketchat 35 | # wait 3 minutes for the rocketchat to finish rising 36 | sudo docker-compose up bot 37 | ``` 38 | 39 | For the virtual assistant to start the conversation, you must create a `trigger`. 40 | To do this, enter the rocketchat as `admin`, and go to the Livechat panel at 41 | In the Triggers section, click `New Trigger`. Fill in the Trigger as follows: 42 | 43 | ```yaml 44 | Enabled: Yes 45 | Name: Start Talk 46 | Description: Start Talk 47 | Condition: Visitor time on site 48 | Value: 3 49 | Action: Send Message 50 | Value: Impersonate next agent from queue 51 | Value: Olá! 52 | ``` 53 | 54 | The `http://localhost:8080/` value should be the Bot access URL. 55 | 56 | #### Installation 57 | 58 | To run the bot on a website you need to enter the following Javascript in your page 59 | 60 | ```js 61 | 62 | 73 | 74 | ``` 75 | 76 | 77 | **Attention**: You need to change the `host` variable within the above code to the url of the site where you will be 78 | your Rocket.Chat. 79 | 80 | ### Telegram 81 | 82 | To carry out this process, it is recommended to create a 83 | [Telegram Bot](https://core.telegram.org/bots#3-how-do-i-create-a-bot) to get all the necessary information. 84 | 85 | To run the _stack_ of the bot by the Telegram together with the attached services, it is necessary to comment the part 86 | related to Rocket.Chat and uncomment the service related to the telegram bot. 87 | 88 | After that, you need to use [ngrok](https://ngrok.com/download) to expose a certain port to be used 89 | by the Telegram. 90 | 91 | When downloading, just run it using the following command: 92 | 93 | ``` 94 | ./ngrok http {used port} 95 | ``` 96 | 97 | **Attention:** The Telegram connector is using port 5001 by default. If you want to change, just change 98 | the port used by the in the Makefile. 99 | 100 | When running, a link will be generated where it will be used to retrieve all information obtained by Bot's webhook 101 | by the Telegram, similar to this link: 102 | 103 | ``` 104 | Example: 105 | https://283e291f.ngrok.io 106 | ``` 107 | 108 | Configure all necessary information in the docker-compose to integrate the created telegram bot: 109 | 110 | ```yml 111 | - TELEGRAM_ACCESS_TOKEN={token from BotFather} 112 | - VERIFY={bot username} 113 | - WEBHOOK_URL={ngrok link}/webhooks/telegram/webhook 114 | ``` 115 | 116 | To execute only the bot service for the telegram, use the following command: 117 | 118 | If you have not yet trained your bot run the following command first: 119 | 120 | ```sh 121 | make train 122 | ``` 123 | 124 | **Attention**: the command "make train" run a container dock, in case I need sudo on your computer 125 | To run the docking window, use the "sudo make train". 126 | 127 | 128 | Then run the bot on the telegram: 129 | 130 | ```sh 131 | sudo docker-compose up telegram_bot 132 | ``` 133 | 134 | ### Console 135 | 136 | ```sh 137 | make train 138 | sudo docker-compose run --rm bot make run-console 139 | ``` 140 | 141 | ### Train Online 142 | 143 | ``` 144 | make train 145 | sudo docker-compose run --rm coach make train-online 146 | ``` 147 | 148 | ## Analytics 149 | 150 | ### Setup 151 | 152 | ``` 153 | sudo docker-compose run --rm -v $PWD/analytics:/analytics bot python /analytics/setup_elastic.py 154 | sudo docker-compose up -d elasticsearch 155 | ``` 156 | 157 | Remember to set the following environment variables in the `docker-compose` file. 158 | 159 | ``` 160 | ENVIRONMENT_NAME=localhost 161 | BOT_VERSION=last-commit-hash 162 | ``` 163 | 164 | ### Preview 165 | 166 | ``` 167 | sudo docker-compose up -d kibana 168 | ``` 169 | 170 | You can access kibana at `locahost:5601` 171 | 172 | 173 | ## Notebooks - Data Analysis 174 | 175 | ### Setup 176 | 177 | Lift the 'notebooks' container 178 | 179 | ```sh 180 | docker-compose up -d notebooks 181 | ``` 182 | 183 | Access the notebook at `localhost:8888` 184 | 185 | 186 | 187 | ## Tutorial to support the entire stack 188 | 189 | ```sh 190 | sudo docker-compose up -d rocketchat 191 | 192 | sudo docker-compose up -d kibana 193 | sudo docker-compose run --rm -v $PWD/analytics:/analytics bot python/analytics/setup_elastic.py 194 | 195 | sudo docker-compose up -d bot 196 | ``` 197 | 198 | 199 | # How to get help 200 | 201 | Part of the technical documentation for the Tais framework is 202 | [repository wiki](https://github.com/lappis-unb/tais/wiki). If you can not find your answer, open an issue 203 | with a `duvida` tag that we will try to respond to as quickly as possible. 204 | 205 | If you have any questions regarding Rasa, please see the Telegram group [Telegram Rasa Stack Brasil](https://t.me/RasaBrasil), 206 | we're there to help too. 207 | 208 | See more contact information on our website: https://lappis.rocks 209 | 210 | # License 211 | 212 | The entire boilerplate framework is developed under license 213 | [GPL3](https://github.com/lappis-unb/rasa-ptbr-boilerplate/blob/master/LICENSE) 214 | 215 | See a list of license dependencies [here](https://libraries.io/github/lappis-unb/rasa-ptbr-boilerplate) -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-como-treinar-o-modelo.md: -------------------------------------------------------------------------------- 1 | # Configuração da Policy do chatbot 2 | 3 | 4 | Este tutorial tem como objetivo mostrar como funciona a configuração do treinamento de um *chatbot* contruido em **_Rasa_**, mostrando como funciona as **Policies**, suas características, e os hiperparâmetros necessários para configurá-las. 5 | 6 | As informações mais detalhadas sobre as policies podem ser encontradas na [documentação](https://rasa.com/docs/rasa/core/policies/) do Rasa, e a configuração usada como referência é a utilizada na [TAIS](https://github.com/lappis-unb/tais/blob/master/coach/policy_config.yml) 7 | 8 | 9 | ## Policies 10 | 11 | Na arquitetura do *Rasa* as policies são aquelas que recebem as intenções do usuários, já identificadas pelo *chatbot*, e a partir dessa informação determina qual ação será toma a seguir. Sem grande rigor, a **Policy** recebe a entrada identificada como por exemplo `ìntent_cumprimentar` e preve qual será a resposta do *bot*, usando como base os exemplos de conversas. 12 | 13 | O *Rasa* possue várias policies implementadas e também suporte para construção de policy customizada. As que serão detalhadas neste documento são as **Keras Policy**, **Memoization Policy**, **Embedding Policy** e **Fallback Policy**. 14 | 15 | No arquivo `policies_config.yml`, ou `config.yml` é definido a sequência de prioridades das policies a ser executada. Normalmente, a **Memoization Policy** é a que tem maior prioridade, pois avalia se existe um storie nos arquivos de treinamento seguindo exatamente a conversa intepretada, e a **Fallback Policy** é última prioridade, e age se todas as outras não atingirem o nível de confiança adequado. Entre a **Memoization Policy** e a **Fallback Policy** normalmente é definido uma das policies detalhadas abaixo. Essas policies (**Keras Policy**, **Embedding Policy**) são redes neurais que inferem o contexto da conversa a partir de um histórico e prediz qual a ação mais adequada, com a sua respectiva probabilidade. Essas redes neurais são treinadas com os exemplos de conversas salvos na pasta "stories" dos dados. 16 | 17 | ### Keras Policy 18 | 19 | Esta policy tem a rede neural implementada usando a biblioteca Python [Keras]https://keras.io). Formada por camadas utilizando o algoritmo **LSTM**. 20 | 21 | Em sua configuração sugerida na documentação, ela vem acompanhada de duas **[Featurization](#featurization)**, __MaxHistoryTrackerFeaturizer__ e a __BinarySingleStateFeaturizer__ 22 | 23 | ### Embedding Policy 24 | 25 | Ou também, Recurrent Embedding Dialogue Policy ([REDP](https://arxiv.org/abs/1811.11707)), tem como foco tratar conversas não cooperativas do usuário com um desempenho maior que a Keras Policiy. 26 | 27 | Tem-se como conversa não cooperativa: 28 | 29 | * Chitchat: "Small-talk" ou perguntas não relacionadas a tarefa 30 | * Correction: Correção de uma resposta anterior 31 | * Broad context: Perguntas referentes ao estado da tarefa (Ex: "Já te informei o local, tem como você me dar a informação agora?") 32 | * Narrow context: Perguntas relacionadas a contextos imediatos (Ex: quando o usuário pergunta o porquê da informação dada pelo bot) 33 | 34 | Para fazer a previsão da ação do bot em uma conversa não comperetativa, essa policy tem o foco em ações tomadas anteriormente, não somente as antigas intents previstas. 35 | 36 | Por padrão,o Rasa Core gera histórias mais longas, colando aleatoriamente a histórias pré-definidas. Pois quando ocorre stories assim: 37 | 38 | ``` 39 | # thanks 40 | * thankyou 41 | - utter_youarewelcome 42 | 43 | # bye 44 | * goodbye 45 | - utter_goodbye 46 | ``` 47 | o objetivo é ignorar contextos anteriores e responder assim como descrito. 48 | 49 | Porém, para o funcionamento da **REDP**, o contexto da intent é relevante, logo esse comportamento do Rasa deve ser evitado, assim `augmentation` é colocado `0`. 50 | 51 | ### Memoization Policy 52 | 53 | A **Memoization Policy** é aquela que memoriza os dados de treinamento e prevê de acordo com as stories descritas. Se a próxima ação predita, dada a intent identificada for igual a um dado de treinamento, esta responderá com confiança de 1.0, caso contrário será 0.0. 54 | 55 | Nesta é importante a geração de bastante stories e também o uso de `augmentation` se as stories não dependerem de contexto. 56 | 57 | ### Fallback Policy 58 | 59 | A policy Fallback é acionada quando nenhuma das outras policies atingem o nivel de confiança esperado. Assim que ela é chamada, esta executa a **Fallback Action** que é a resposta padrão do bot. 60 | 61 | Nela, deve-se estabelecer o nível de confiança mímino que as policies devem atingir para que não execute o Fallback (**threshold**), tanto na parte do processamento de linguagem natural (NLU), que interpreta as intents do usuário, quando na parte da previsão (Core). 62 | 63 | 64 | ## Featurization 65 | 66 | Existem dois tipos de Featurization para construir vetores que representem as conversas, a **State Featurizers** e **Tracker Featurizers**. 67 | 68 | As **States Featurizers** utilizando o **tracker**, que dá informações de intents, entidades e slots prévios (ou seja, as **features**) e converte em um array. 69 | * Em BinarySingleStateFeaturizer, ele cria um vetor x,y que indica a presença de certas intent, entidades, ações anteriores e slots. 70 | * Na **LabelTokenizerSingleStateFeaturizer**, se cria uma vetor baseado nos nomes das features, separado em tokens e representados como **bag-of-words**. Por exemplo `utter_explain_details_hotel` e `utter_explain_details_restaurant` terão 3 features em comum. 71 | 72 | Já as **Trackers Featurizers** itera pelos trackers states e chama o SingleStateFeaturizer para cada estado, sendo que a diferença entre os dois Trackers Featurizers são: 73 | * **FullDialogueTrackerFeaturizer**: cria uma representação numerica das stories para alimentar a rede neural. 74 | * **MaxHistoryTrackerFeaturizer**: cria um array dos estados anteriores para cada utter ou action do bot. 75 | 76 | 77 | ## Configuração 78 | 79 | Algumas configurações utilizadas pela Tais, com base nas policies apresentadas. Lembrando que os valores de **threshold**, **augmentation**, **MaxHistoryTrackerFeaturizer** e entre outros, dependem do contexto que o bot trabalha e sempre é bom analisar a confiança e acurácia, para assim mudar os valores, se necessário. 80 | 81 | Outra variável importante de se olhar para ver se todos os hiperparâmetros estão ajustados corretamente é o **loss**. Para sabe qual é o melhor número de épocas (*epoches*) o ideal é o maior valor de acurácia e menor valor de *loss*, indicando a maior precisão da rede neural e do seu bot. 82 | 83 | Só que deve-se levar em conta quão diferente esse valores ficaram, entre uma época e outra, pois se não acontecer uma redução significativa de loss e um aumento de acurácia, pode chegar ao *overfitting* da sua rede, e o bot ficar bom somente em casos específicos e não ser preciso em casos generalizados. 84 | 85 | Resumindo, observe o loss e a acurácia, ajuste a época com um valor onde o loss é minimo e a acurácia é máxima, mas quando ainda ocorrer diferenças significativas entre uma época e outra. 86 | 87 | ### Keras + Memoization + Fallback 88 | 89 | * Em **policies_config.yml** 90 | 91 | ``` 92 | policies: 93 | - name: KerasPolicy 94 | epochs: 7 95 | batch_size: 10 96 | featurizer: 97 | - name: FullDialogueTrackerFeaturizer 98 | state_featurizer: 99 | - name: LabelTokenizerSingleStateFeaturizer 100 | - name: FallbackPolicy 101 | nlu_threshold: 0.6 102 | core_threshold: 0.6 103 | - name: MemoizationPolicy 104 | max_history: 2 105 | 106 | ``` 107 | 108 | * Em **train.py** 109 | 110 | ``` 111 | 'augmentation_factor': 20, 112 | ``` 113 | 114 | ### Embedding + Memoization + Fallback 115 | 116 | 117 | * Em **policies_config.yml** 118 | ``` 119 | policies: 120 | - name: "EmbeddingPolicy" 121 | epochs: 500 122 | attn_shift_range: 5 123 | featurizer: 124 | - name: FullDialogueTrackerFeaturizer 125 | state_featurizer: 126 | - name: LabelTokenizerSingleStateFeaturizer 127 | - name: FallbackPolicy 128 | nlu_threshold: 0.6 129 | core_threshold: 0.6 130 | - name: MemoizationPolicy 131 | max_history: 2 132 | ``` 133 | * Em **train.py** 134 | 135 | ``` 136 | 'augmentation_factor': 20, 137 | ``` 138 | -------------------------------------------------------------------------------- /modules/rocketchat/bot_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import logging 5 | import requests 6 | import os 7 | 8 | # == Log Config == 9 | 10 | logger = logging.getLogger("Bot Config") 11 | logger.setLevel(logging.DEBUG) 12 | 13 | ch = logging.StreamHandler() 14 | ch.setLevel(logging.DEBUG) 15 | 16 | formatter = logging.Formatter("%(asctime)s :: %(name)s :: %(levelname)s :: %(message)s") 17 | 18 | ch.setFormatter(formatter) 19 | 20 | logger.addHandler(ch) 21 | 22 | 23 | host = os.getenv("ROCKETCHAT_URL", "http://rocketchat:3000") 24 | if host[-1] == "/": 25 | host = host[:-1] 26 | 27 | if not host.startswith("http://"): 28 | host = "http://" + host 29 | 30 | path = "/api/v1/login" 31 | 32 | bot = { 33 | "name": os.getenv("ROCKETCHAT_BOT_NAME", "Bot"), 34 | "username": os.getenv("ROCKETCHAT_BOT_USERNAME", "bot"), 35 | "password": os.getenv("ROCKETCHAT_BOT_PASSWORD", "bot"), 36 | "avatar": os.getenv( 37 | "ROCKETCHAT_BOT_AVATAR_URL", 38 | "https://raw.githubusercontent.com/" 39 | "lappis-unb/rouana/master/images/rouana_avatar.jpeg", 40 | ), 41 | "email": os.getenv("ROCKETCHAT_BOT_USERNAME", "bot") + "@email.com", 42 | } 43 | 44 | admin_name = os.getenv("ROCKETCHAT_ADMIN_USERNAME", "admin") 45 | admin_password = os.getenv("ROCKETCHAT_ADMIN_PASSWORD", "admin") 46 | 47 | rasa_url = os.getenv("RASA_URL", "http://bot:5005/webhooks/rocketchat/webhook") 48 | user_header = None 49 | 50 | 51 | def api(endpoint, values=None, is_post=True): 52 | requests.adapters.DEFAULT_RETRIES = 5 53 | 54 | if endpoint[0] == "/": 55 | endpoint = endpoint[1:] 56 | 57 | url = host + "/api/v1/" + endpoint 58 | 59 | data = None 60 | if values: 61 | data = json.dumps(values) 62 | if is_post: 63 | response = requests.post(url, data=data, headers=user_header) 64 | else: 65 | response = requests.get(url, data=data, headers=user_header) 66 | 67 | if response.json()["success"] is True: 68 | logger.info("Success {} :: {}".format(url, response.json())) 69 | else: 70 | logger.error("ERROR {} :: {}".format(url, response.json())) 71 | raise EnvironmentError 72 | 73 | return response.json() 74 | 75 | 76 | def api_post(endpoint, values=None): 77 | return api(endpoint, values) 78 | 79 | 80 | def api_get(endpoint, values=None): 81 | return api(endpoint, values, False) 82 | 83 | 84 | def get_authentication_token(): 85 | login_data = {"username": admin_name, "password": admin_password} 86 | response = requests.post(host + path, data=json.dumps(login_data)) 87 | 88 | if response.json()["status"] == "success": 89 | logger.info("Login suceeded") 90 | 91 | authToken = response.json()["data"]["authToken"] 92 | userId = response.json()["data"]["userId"] 93 | user_header = { 94 | "X-Auth-Token": authToken, 95 | "X-User-Id": userId, 96 | "Content-Type": "application/json", 97 | } 98 | 99 | return user_header 100 | 101 | 102 | def create_bot_user(): 103 | try: 104 | api_post( 105 | "users.create", 106 | { 107 | "name": bot["name"], 108 | "email": bot["email"], 109 | "password": bot["password"], 110 | "username": bot["username"], 111 | "requirePasswordChange": False, 112 | "sendWelcomeEmail": True, 113 | "roles": ["bot"], 114 | }, 115 | ) 116 | except Exception: 117 | print("User already created.") 118 | 119 | api_post( 120 | "users.setAvatar", {"avatarUrl": bot["avatar"], "username": bot["username"]}, 121 | ) 122 | 123 | 124 | def create_livechat_agent(): 125 | response = api_post("livechat/users/agent", {"username": bot["username"]}) 126 | return response["user"]["_id"] 127 | 128 | 129 | def configure_livechat(): 130 | # Enable Livechat 131 | api_post("settings/Livechat_enabled", {"value": True}) 132 | 133 | # Disable show pre-registration form 134 | api_post("settings/Livechat_registration_form", {"value": False}) 135 | 136 | # Change Livechat Color 137 | api_post("settings/Livechat_title_color", {"value": "#039046", "editor": "color"}) 138 | 139 | # Change Livechat Title 140 | api_post("settings/Livechat_title", {"value": bot["name"]}) 141 | 142 | # Disable Livechat Email display 143 | api_post("settings/Livechat_show_agent_email", {"value": False}) 144 | 145 | # Disable file upload 146 | api_post("settings/Livechat_fileupload_enabled", {"value": False}) 147 | 148 | # Change Livechat Webhook URL 149 | api_post("settings/Livechat_webhookUrl", {"value": rasa_url}) 150 | 151 | # Activate Livechat Webhook Send Request on Visitor Message 152 | api_post("settings/Livechat_webhook_on_visitor_message", {"value": True}) 153 | 154 | # Activate Livechat Webhook Send Request on Agent Messages 155 | api_post("settings/Livechat_webhook_on_agent_message", {"value": True}) 156 | 157 | 158 | def configure_webhooks(): 159 | webooks = api_get("integrations.list") 160 | 161 | name = "Rasa Webhook" 162 | 163 | for integration in webooks["integrations"]: 164 | if integration.get("name") == name: 165 | logger.info("Intergration {} already exists!".format(name)) 166 | return 167 | 168 | api_post( 169 | "integrations.create", 170 | { 171 | "name": name, 172 | "type": "webhook-outgoing", 173 | "enabled": True, 174 | "scriptEnabled": False, 175 | "event": "sendMessage", 176 | "urls": [rasa_url], 177 | "username": bot["username"], 178 | "channel": "@" + bot["username"], 179 | }, 180 | ) 181 | 182 | 183 | def configure_rocketchat(): 184 | api_post("settings/Language", {"value": "pt_BR"}) 185 | 186 | api_post("settings/Accounts_RegistrationForm", {"value": "Disable"}) 187 | 188 | api_post("settings/Iframe_Integration_send_enable", {"value": True}) 189 | 190 | api_post("settings/Iframe_Integration_receive_enable", {"value": True}) 191 | 192 | api_post("settings/API_Enable_CORS", {"value": True}) 193 | 194 | 195 | def create_department(bot_agent_id): 196 | get_departments_url = host + "/api/v1/livechat/department" 197 | 198 | get_departments_response = requests.get(get_departments_url, headers=user_header) 199 | 200 | number_of_departments = len(get_departments_response.json()["departments"]) 201 | 202 | if number_of_departments == 0: 203 | api_post( 204 | "livechat/department", 205 | { 206 | "department": { 207 | "enabled": True, 208 | "showOnRegistration": True, 209 | "name": "department", 210 | "description": "default department", 211 | }, 212 | "agents": [ 213 | { 214 | "agentId": bot_agent_id, 215 | "username": bot["username"], 216 | "count": 0, 217 | "order": 0, 218 | } 219 | ], 220 | }, 221 | ) 222 | 223 | 224 | if __name__ == "__main__": 225 | logger.info("===== Automatic env configuration =====") 226 | 227 | try: 228 | user_header = get_authentication_token() 229 | except Exception: 230 | print("\n\n --------- Rocket Chat Unavailable! --------\n\n") 231 | 232 | if user_header: 233 | logger.info(">> Create user") 234 | create_bot_user() 235 | 236 | logger.info(">> Create livechat agent") 237 | bot_agent_id = create_livechat_agent() 238 | 239 | logger.info(">> Configure livechat") 240 | configure_livechat() 241 | 242 | logger.info(">> Configure Rocketchat") 243 | configure_rocketchat() 244 | 245 | logger.info(">> Configure Webhooks") 246 | configure_webhooks() 247 | 248 | logger.info(">> Create livechat department") 249 | create_department(bot_agent_id) 250 | 251 | else: 252 | logger.error("Login Failed") 253 | -------------------------------------------------------------------------------- /bot/data/stories.md: -------------------------------------------------------------------------------- 1 | ## story_login_form 2 | * request_login 3 | - utter_login_form 4 | - login_form 5 | - form{"name": "login_form"} 6 | - form{"name": null} 7 | * afirmar 8 | - utter_finaliza_forms 9 | 10 | ## story_login_form 11 | * request_login 12 | - utter_login_form 13 | - login_form 14 | - form{"name": "login_form"} 15 | * cancelar 16 | - utter_pergunta_cancelar 17 | * negar 18 | - form{"name": "login_form"} 19 | - form{"name": null} 20 | - utter_finaliza_forms 21 | 22 | ## story_login_form_cancelar 23 | * request_login 24 | - utter_login_form 25 | - login_form 26 | - form{"name": "login_form"} 27 | * cancelar 28 | - utter_pergunta_cancelar 29 | * afirmar 30 | - action_deactivate_form 31 | - form{"name": null} 32 | - utter_forms_cancelado 33 | - utter_continuar_conversa 34 | * negar 35 | - utter_despedir 36 | 37 | ## story_login_form_cancelar 38 | * request_login 39 | - utter_login_form 40 | - login_form 41 | - form{"name": "login_form"} 42 | * cancelar 43 | - utter_pergunta_cancelar 44 | * afirmar 45 | - action_deactivate_form 46 | - form{"name": null} 47 | - utter_forms_cancelado 48 | - utter_continuar_conversa 49 | 50 | ## testa acoes 51 | * cumprimentar 52 | - utter_cumprimentar 53 | * testa_acoes 54 | - action_teste 55 | 56 | ## testa acoes 57 | * testa_acoes 58 | - action_teste 59 | 60 | ## testa slots 61 | * informa_telefone 62 | - action_telefone 63 | 64 | ## path_religiao 1 65 | * religiao 66 | - utter_religiao 67 | - utter_continuar_conversa 68 | 69 | ## path_religiao 2 70 | * cumprimentar 71 | - utter_cumprimentar 72 | * religiao 73 | - utter_religiao 74 | - utter_continuar_conversa 75 | 76 | ## path_time 1 77 | * time 78 | - utter_time 79 | - utter_continuar_conversa 80 | 81 | ## path_time 2 82 | * cumprimentar 83 | - utter_cumprimentar 84 | * time 85 | - utter_time 86 | - utter_continuar_conversa 87 | 88 | ## path_genero 1 89 | * genero 90 | - utter_genero 91 | - utter_continuar_conversa 92 | 93 | ## path_genero 2 94 | * cumprimentar 95 | - utter_cumprimentar 96 | * genero 97 | - utter_genero 98 | - utter_continuar_conversa 99 | 100 | ## path_star_wars 1 101 | * star_wars 102 | - utter_star_wars 103 | - utter_continuar_conversa 104 | 105 | ## path_star_wars 2 106 | * cumprimentar 107 | - utter_cumprimentar 108 | * star_wars 109 | - utter_star_wars 110 | - utter_continuar_conversa 111 | 112 | ## path_piada 1 113 | * piada 114 | - utter_piada 115 | - utter_continuar_conversa 116 | 117 | ## path_piada 2 118 | * cumprimentar 119 | - utter_cumprimentar 120 | * piada 121 | - utter_piada 122 | - utter_continuar_conversa 123 | 124 | ## path_license 1 125 | * license 126 | - utter_license 127 | - utter_continuar_conversa 128 | 129 | ## path_license 2 130 | * cumprimentar 131 | - utter_cumprimentar 132 | * license 133 | - utter_license 134 | - utter_continuar_conversa 135 | 136 | ## path_onde_voce_mora 1 137 | * onde_voce_mora 138 | - utter_onde_voce_mora 139 | - utter_continuar_conversa 140 | 141 | ## path_onde_voce_mora 2 142 | * cumprimentar 143 | - utter_cumprimentar 144 | * onde_voce_mora 145 | - utter_onde_voce_mora 146 | - utter_continuar_conversa 147 | 148 | ## path_como_estou 1 149 | * como_estou 150 | - utter_como_estou 151 | - utter_continuar_conversa 152 | 153 | ## path_como_estou 2 154 | * cumprimentar 155 | - utter_cumprimentar 156 | * como_estou 157 | - utter_como_estou 158 | - utter_continuar_conversa 159 | 160 | ## path_playlist 1 161 | * playlist 162 | - utter_playlist 163 | - utter_continuar_conversa 164 | 165 | ## path_playlist 2 166 | * cumprimentar 167 | - utter_cumprimentar 168 | * playlist 169 | - utter_playlist 170 | - utter_continuar_conversa 171 | 172 | ## path_comida 1 173 | * comida 174 | - utter_comida 175 | - utter_continuar_conversa 176 | 177 | ## path_comida 2 178 | * cumprimentar 179 | - utter_cumprimentar 180 | * comida 181 | - utter_comida 182 | - utter_continuar_conversa 183 | 184 | ## path_cor 1 185 | * cor 186 | - utter_cor 187 | - utter_continuar_conversa 188 | 189 | ## path_cor 2 190 | * cumprimentar 191 | - utter_cumprimentar 192 | * cor 193 | - utter_cor 194 | - utter_continuar_conversa 195 | 196 | ## path_relacionamento 197 | * relacionamento 198 | - utter_relacionamento 199 | - utter_continuar_conversa 200 | 201 | ## path_filhos 1 202 | * filhos 203 | - utter_filhos 204 | - utter_continuar_conversa 205 | 206 | ## path_filhos 2 207 | * cumprimentar 208 | - utter_cumprimentar 209 | * filhos 210 | - utter_filhos 211 | - utter_continuar_conversa 212 | 213 | ## path_signo 1 214 | * signo 215 | - utter_signo 216 | - utter_continuar_conversa 217 | 218 | ## path_signo 2 219 | * cumprimentar 220 | - utter_cumprimentar 221 | * signo 222 | - utter_signo 223 | - utter_continuar_conversa 224 | 225 | ## path_triste 1 226 | * triste 227 | - utter_triste 228 | - utter_continuar_conversa 229 | 230 | ## path_triste 2 231 | * cumprimentar 232 | - utter_cumprimentar 233 | * triste 234 | - utter_triste 235 | - utter_continuar_conversa 236 | 237 | ## path_historia 1 238 | * historia 239 | - utter_historia 240 | - utter_continuar_conversa 241 | 242 | ## path_historia 2 243 | * cumprimentar 244 | - utter_cumprimentar 245 | * historia 246 | - utter_historia 247 | - utter_continuar_conversa 248 | 249 | ## cumprimentar 250 | * cumprimentar 251 | - utter_cumprimentar 252 | 253 | ## Despedir 254 | * despedir 255 | - utter_despedir 256 | 257 | ## Tudo Bem Story 258 | * tudo_bem 259 | - utter_tudo_bem 260 | 261 | ## Oi Tudo Bem Story 262 | * cumprimentar 263 | - utter_cumprimentar 264 | * tudo_bem 265 | - utter_tudo_bem 266 | 267 | ## Nao entendi 268 | * diga_mais 269 | - utter_diga_mais 270 | 271 | ## fallback 272 | * out_of_scope 273 | - utter_fallback 274 | 275 | ## negar sem contexto 276 | * negar 277 | - utter_despedir 278 | 279 | ## elogios 1 280 | * elogios 281 | - utter_elogios 282 | - utter_continuar_conversa 283 | 284 | ## elogios 2 285 | * cumprimentar 286 | - utter_cumprimentar 287 | * elogios 288 | - utter_elogios 289 | - utter_continuar_conversa 290 | 291 | ## meajuda 1 292 | * o_que_sei_falar 293 | - utter_o_que_sei_falar 294 | 295 | ## meajuda 2 296 | * cumprimentar 297 | - utter_cumprimentar 298 | * o_que_sei_falar 299 | - utter_o_que_sei_falar 300 | 301 | ## objetivo 302 | * objetivo 303 | - utter_objetivo 304 | 305 | ## path_daria 306 | * cumprimentar 307 | - utter_cumprimentar 308 | * daria 309 | - utter_daria 310 | - utter_continuar_conversa 311 | 312 | ## falar_de_anime 313 | * anime 314 | - utter_anime 315 | 316 | ## recomendacao_anime 317 | * tudo_bem 318 | - utter_tudo_bem 319 | * anime 320 | - utter_anime 321 | - utter_recomenda_anime 322 | 323 | ## avatar 324 | * avatar 325 | - utter_avatar 326 | - utter_continuar_conversa 327 | 328 | ## harry_potter 329 | * harry_potter 330 | - utter_harry_potter 331 | 332 | ## friends 333 | * friends 334 | - utter_friends 335 | 336 | ## cumprimentar_friends 337 | * cumprimentar 338 | - utter_cumprimentar 339 | * friends 340 | - utter_friends 341 | - utter_temporadas_friends 342 | 343 | ## falar_da_boss 344 | * o_que_e_boss 345 | - utter_boss_apresenta 346 | - utter_talk_like_a_boss 347 | 348 | ## falar_da_boss_com_conversinha 349 | * tudo_bem 350 | - utter_tudo_bem 351 | * o_que_e_boss 352 | - utter_boss_apresenta 353 | 354 | ## story_limpar_slots 355 | * limpar_slots 356 | - action_restart 357 | 358 | ## pedir_conselho 359 | * cumprimentar 360 | - utter_cumprimentar 361 | * pedir_conselho 362 | - action_pedir_conselho 363 | 364 | ## informar_nome 365 | * cumprimentar 366 | - utter_cumprimentar 367 | * informar_nome 368 | - utter_informar_nome 369 | 370 | ## casa_hogwarts 371 | * cumprimentar 372 | - utter_cumprimentar 373 | * casa_hogwarts 374 | - utter_chapeu_seletor 375 | - action_sorting_hat 376 | 377 | ## fato_sobre_gato 378 | * fatos_sobre_gatos 379 | - utter_fato_sobre_gatos 380 | - action_cat_facts 381 | 382 | ## path_bots_brasil_1 383 | * bots_brasil 384 | - utter_bots_brasil 385 | 386 | ## path_bots_brasil_2 387 | * cumprimentar 388 | - utter_cumprimentar 389 | * bots_brasil 390 | - utter_bots_brasil 391 | - utter_continuar_conversa -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Rasa Boilerplate 2 | 3 | 4 | 5 | 6 | ## Tutorial para configurar todo o projeto 7 | 8 | Para ter seu chatbot Rasa no ar e funcionando rápidamente no `shell` execute os seguintes comandos: 9 | 10 | ```sh 11 | sudo make build-bot 12 | sudo make train 13 | sudo make run-console 14 | ``` 15 | 16 | Estes comandos irão construir o seu chatbot e abrir a conversação no terminal. Tudo está dockerizado 17 | então você não terá problemas de instalação do ambiente. 18 | 19 | ## Introdução 20 | 21 | ### For English version, see [README-en](docs/README-en.md) 22 | 23 | Um projeto feito em Rasa com configurações necessárias para a construção de um projeto grande de chatbot. 24 | 25 | Este projeto teve como base o projeto [Tais](http://github.com/lappis-unb/tais). 26 | 27 | ### Entenda a Arquitetura 28 | 29 | É utilizado no boilerplate diversas tecnologias que interagem entre si para obter um melhor resultado. Veja a arquitetura implementada: 30 | 31 | ![](https://user-images.githubusercontent.com/8556291/57933140-d8d66b80-7892-11e9-8d58-a7eda60b099b.png) 32 | 33 | O usuário interage com a Boilerplate via RocketChat ou Telegram, que manda as mensagens para o Rasa NLU através de 34 | conectores, onde ele identifica a *intent*, e responde pelo Rasa Core, de acordo com as *stories* e *actions*. 35 | As *models* utilizadas para a conversação foram geradas pelo módulo *trainer* e depois transferidas para o bot, estes 36 | modelos podem ser versionados e evoluídos entre bots. 37 | Os notebooks avaliam o funcionamento de acordo com o formato das *intents* e *stories*. 38 | O elasticsearch coleta os dados da conversa e armazena para a análise feita pelo kibana, que gera gráficos para 39 | avaliação das conversas dos usuários e do boilerplate. 40 | 41 | ### Bot 42 | 43 | Este script foi configurado para construir as imagens genéricas necessárias para execução deste ambiente. 44 | Caso seu projeto utilize este boilerplate e vá realizar uma integração contínua ou similar, é interessante 45 | criar um repositório para as imagens e substitua os nomes das imagens "lappis/bot", e "lappis/botrequirements" pelas suas respectivas novas imagens, por exemplo "/bot" em repositório público. 46 | 47 | 48 | ### Treinamento 49 | 50 | **Atenção**: o comando de treinamento é usado para criar os modelos necessários na conversação do bot para treinar o seu chatbot execute o comando: 51 | ```sh 52 | sudo make train 53 | ``` 54 | 55 | ### Console 56 | 57 | ```sh 58 | sudo make run-console 59 | ``` 60 | 61 | ### Telegram 62 | 63 | Após realizar o [tutorial](/docs/setup_telegram.md) de exportação de todas variávies de ambiente necessárias, é possível realizar a execução do bot no telegram corretamente. 64 | 65 | **Antes de seguir adiante. Importante:** As variáveis de ambiente são necessárias para o correto funcionamento do bot, por isso não esqueça de exportá-las. 66 | 67 | Se ainda não tiver treinado seu bot execute antes: 68 | 69 | 70 | Depois execute o bot no telegram: 71 | 72 | ```sh 73 | sudo docker-compose up bot_telegram 74 | ``` 75 | ### Analytics 76 | 77 | Para a visualização dos dados da interação entre o usuário e o chatbot nós utilizamos uma parte da Stack do Elastic, composta pelo ElasticSearch e o Kibana. Com isso, utilizamos um broker para fazer a gerência de mensagens. Então conseguimos adicionar mensagens ao ElasticSearch independente do tipo de mensageiro que estamos utilizando. 78 | 79 | ### Configuração do RabbitMQ 80 | 81 | Em primeiro lugar para fazer o setup do analytics é necessário subir o RabiitMQ e suas configurações. 82 | 83 | Inicie o serviço do servidor do RabbitMQ: 84 | 85 | ```sh 86 | sudo docker-compose up -d rabbitmq 87 | ``` 88 | 89 | Inicie o serviço do consumidor do RabbitMQ, que ficará responsável por enviar as mensagens para o ElasticSearch: 90 | 91 | ```sh 92 | sudo docker-compose up -d rabbitmq-consumer 93 | ``` 94 | 95 | Lembre-se de configurar as seguintes variáveis de ambiente do serviço `rabbitmq-consumer` no `docker-compose`. 96 | 97 | ```sh 98 | ENVIRONMENT_NAME=localhost 99 | BOT_VERSION=last-commit-hash 100 | RABBITMQ_DEFAULT_USER=admin 101 | RABBITMQ_DEFAULT_PASS=admin 102 | ``` 103 | 104 | Sendo que as configurações de `RABBITMQ_DEFAULT_USER` e `RABBITMQ_DEFAULT_PASS` devem ser as mesmas definidas no serviço do `rabbitmq`. 105 | 106 | #### Integração com Rasa 107 | 108 | Existem duas formas para executar a Tais com o *broker*. A primeira delas é via linha de comando. 109 | Para utilizar esta forma é preciso definir Dentro do arquivo `endpoints.yml` as configurações do broker: 110 | 111 | ```yml 112 | event_broker: 113 | url: rabbitmq 114 | username: admin 115 | password: admin 116 | queue: bot_messages 117 | ``` 118 | 119 | Depois basta executar o bot: 120 | 121 | ```sh 122 | sudo docker-compose run --rm bot make run-console-broker 123 | ``` 124 | 125 | A segunda forma é utilizando o script `run-rocketchat` que é utilizado quando o bot é executado com o RocketChat como canal. Para isso, as mesmas variáveis devem ser configuradas no arquivo `docker/bot/bot.env`. 126 | Lembre-se também de configurar como `True` a seguinte variável do serviço `bot` no arquivo `docker/bot-rocketchat.env`. 127 | 128 | ``` 129 | # Broker config 130 | BROKER_URL=rabbitmq 131 | BROKER_USERNAME=admin 132 | BROKER_PASSWORD=admin 133 | QUEUE_NAME=bot_messages 134 | ``` 135 | 136 | Ao final é necessário buildar novamente o container do bot. 137 | 138 | ``` 139 | sudo docker-compose up --build -d bot 140 | ``` 141 | 142 | ### Configuração ElasticSearch 143 | 144 | O ElasticSearch é o serviço responsável por armazenar os dados provenientes da interação entre o usuário e o chatbot. 145 | 146 | As mensagens são inseridas no índice do ElasticSearch utilizando o *broker* RabbitMQ. 147 | 148 | Para subir o ambiente do ElasticSearch rode os seguintes comandos: 149 | 150 | ``` 151 | sudo docker-compose up -d elasticsearch 152 | sudo docker-compose run --rm -v $PWD/analytics:/analytics bot python /analytics/setup_elastic.py 153 | ``` 154 | 155 | Lembre-se de setar as seguintes variaveis de ambiente no `docker-compose`. 156 | 157 | ``` 158 | ENVIRONMENT_NAME=localhost 159 | BOT_VERSION=last-commit-hash 160 | ``` 161 | 162 | #### Setup Kibana (Visualização) 163 | 164 | Para a análise dos dados das conversas com o usuário, utilize o kibana, e veja como os usuários estão interagindo com o bot, os principais assuntos, média de usuários e outras informações da análise de dados. 165 | 166 | O Kibana nos auxilia com uma interface para criação de visualização para os dados armazenados nos índices do ElasticSearch. 167 | 168 | ```sh 169 | sudo docker-compose up -d kibana 170 | ``` 171 | 172 | **Atenção:** Caso queira configurar permissões diferentes de usuários (Login) no ElasticSearch/Kibana, siga esse tutorial ([link](https://github.com/lappis-unb/rasa-ptbr-boilerplate/tree/master/docs/setup_user_elasticsearch.md)). 173 | 174 | #### Importação de dashboards 175 | 176 | Caso queira subir com os dashboards que criamos para fazer o monitoramento de bots: 177 | 178 | ``` 179 | sudo docker-compose run --rm kibana python3.6 import_dashboards.py 180 | ``` 181 | 182 | Após rodar o comando anterior os dashboards importados estarão presentes no menu management/kibana/Saved Objects. 183 | 184 | Você pode acessar o kibana no `locahost:5601` 185 | 186 | 187 | 188 | ## Testando Fluxos de Conversa 189 | 190 | É possível testar os fluxos de conversação utilizando o [Evaluation do Rasa Core](https://github.com/lappis-unb/tais/wiki/Testes-Automatizados). Para testá-los no seu bot basta adicionar um arquivo dentro do diretório `bot/e2e/` com as histórias a serem testadas. Essas histórias devem ser descritas normalmente, porém com exemplos de frases para cada uma das *Intents* sendo testadas, segundo o formato abaixo: 191 | 192 | ``` 193 | ## História de teste 1 194 | * cumprimentar: oi 195 | - utter_cumprimentar 196 | * action_test: test custom action 197 | - action_test 198 | ``` 199 | 200 | Uma vez que os arquivos de teste foram adicionados ao diretório correto, basta rodar os testes com a *task* do bot: 201 | 202 | ```sh 203 | sudo docker-compose run --rm bot make test-stories 204 | ``` 205 | 206 | Para gerar data-science referente aos testes automatizados de bor, execute o seguinte comando do *Makefile* na raíz do projeto: 207 | 208 | ```sh 209 | sudo docker-compose run --rm bot make test-dialogue 210 | ``` 211 | 212 | ## Notebooks - Análise de dados 213 | 214 | ### Setup 215 | 216 | Levante o container `notebooks` 217 | 218 | ```sh 219 | docker-compose up -d notebooks 220 | ``` 221 | 222 | Acesse o notebook em `localhost:8888` 223 | 224 | # Como conseguir ajuda 225 | 226 | Parte da documentação técnica do framework da Tais está disponível na 227 | [wiki do repositório](https://github.com/lappis-unb/tais/wiki). Caso não encontre sua resposta, abra uma issue 228 | com a tag `duvida` que tentaremos responder o mais rápido possível. 229 | 230 | Em caso de dúvidas em relação ao Rasa, veja o grupo [Telegram Rasa Stack Brasil](https://t.me/RasaBrasil), 231 | estamos lá também para ajudar. 232 | 233 | Veja mais informações de contato em nosso site: https://lappis.rocks 234 | 235 | # Licença 236 | 237 | Todo o framework do boilerplate é desenvolvido sob a licença 238 | [GPL3](https://github.com/lappis-unb/rasa-ptbr-boilerplate/blob/master/LICENSE) 239 | 240 | Veja a lista de dependências de licenças [aqui](https://libraries.io/github/lappis-unb/rasa-ptbr-boilerplate) 241 | -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-pipeline-de-bot.md: -------------------------------------------------------------------------------- 1 | # Configuração pipeline de bot 2 | 3 | O objetivo deste tutorial é explicar os passos necessários para configuração de um *pipeline* de *deploy* contínuo de um *bot* `Rasa`, utilizando o `GitLabCI`. 4 | 5 | Os exemplos e estratégias utilizados neste tutorial são baseados no *pipeline* utilizado na TAIS. Para uma referência completa basta analisar o [arquivo de configuração](https://github.com/lappis-unb/tais/blob/master/.gitlab-ci.yml) do *pipeline* da TAIS no GitLab. 6 | 7 | A configuração de *pipelines* utilizando o `GitLabCI` se dá a partir da utilização de um arquivo de configuração chamado `gitlab-ci.yml`. Neste tutorial aprenderemos configurar um arquivo de utilização do *CI*. 8 | 9 | Cada um dos *jobs* criados no *CI* são executados dentro de *containers* na infraestrutura do `GitLab`. 10 | 11 | O primeiro passo para configuração é definir uma imagem base a ser utilizada nos *jobs* do *pipeline*. Pode-se definir uma imagem padrão que será utilizado em todos os *jobs* ou definir imagens diferentes para cada um dos *jobs* existentes. 12 | 13 | Para definir uma imagem global é necessário utilizar a configuração abaixo: 14 | 15 | ```yml 16 | image: python:3.6-slim 17 | 18 | test style: 19 | stage: test style 20 | script: 21 | - pip -V 22 | - python -V 23 | - pip install -r dev.requirements.txt 24 | - flake8 --exclude venv 25 | 26 | run dataset validator: 27 | stage: validate format 28 | image: lappis/coach:latest 29 | script: 30 | - cd coach/ 31 | - make run-validator 32 | ``` 33 | 34 | No exemplo acima, foi definida uma imagem base chamada `python:3.6-slim`. Em seguida foram definidos dois *jobs* de teste, o primeiro deles utilizará a imagem padrão do python que foi definida na primeira linha, já que este não possui nenhuma *tag* de definição de imagem. O segundo *job* utilizará a imagem `lappis/coach:latest`, já que possui uma *tag* de definição de imagem que sobreescreve a imagem base. 35 | 36 | ## Definição dos stages 37 | 38 | Os *jobs* serão criados a partir da organização em *stages*, sendo que estes serão executados de acordo com a ordem de prioridade definida. Essa característica define a dependência dos *jobs*, uma vez que caso o *job* de um estágio anterior falhe todos os *jobs* subsequentes de todos os próximos *jobs* serão cancelados e não serão executados. 39 | 40 | Caso mais de um *job* seja definido com o mesmo estágio, a execução destes *jobs* será paralelizada pelo próprio `GitLab` e eles serão executados simultaneamente. 41 | 42 | Uma estratégia que pode ser utilizada é separar os *jobs* do *pipeline* em três fases principais: *test*, *build* e *deploy*. 43 | 44 | ```yml 45 | stages: 46 | - test 47 | - build 48 | - deploy 49 | ``` 50 | 51 | No *job* `test style` exemplificado acima, o *stage* é definido como `test`. Então ele será um dos *jobs* rodados no começo da execução do *pipeline*. 52 | 53 | ## Estratégia de Build 54 | 55 | Como este tutorial é baseado na utilização de serviços `docker`, a estratégia de build é focada na construção e publicação da imagem utilizada pelos serviços. 56 | 57 | Está exemplificado abaixo um *job* de build para imagens `docker` no CI do `GitLab`. 58 | 59 | ```yml 60 | build bot: 61 | stage: build 62 | image: docker 63 | tags: 64 | - docker 65 | services: 66 | - docker:dind 67 | script: 68 | - docker login -u $DOCKERHUB_USER -p $DOCKERHUB_PASSWORD 69 | - docker build -f docker/bot/bot.Dockerfile -t lappis/bot:latest . 70 | - docker push lappis/bot:latest 71 | only: 72 | - master 73 | environment: homolog 74 | ``` 75 | 76 | A imagem utilizada deve ser a imagem `docker` e deve ser adicionada uma *label* que defina a utilização do serviço `docker:dind`, um acrônimo para "Docker in Docker". O que indica ao CI que serão utilizados comandos `Docker` dentro do *container* onde o *job* está sendo executado. 77 | 78 | A *label* `script` define quais comandos serão executados durante esse *job*. Neste caso, são 3 comandos/etapas para a criação e publicação da imagem. 79 | 80 | 1 - Primeiro é feito o *login* no `Dockerhub` utilizando os dados de acesso configurados em variáveis secretas no próprio repositório. Para entender como utilizar estas variáveis no GitLab basta seguir a [documentação oficial](https://docs.gitlab.com/ee/ci/variables/); 81 | 82 | 2 - Logo após, a imagem é construída a partir do `Dockerfile` contido no próprio repositório do projeto, com o nome definido; 83 | 84 | 3 - Por último, a imagem é publicada e enviada para o *registry* do `Dockerhub`, e estará pronta para ser utilizada no estágio de *deploy*; 85 | 86 | ## Estratégias de Deploy 87 | 88 | Serão ensinadas duas estratégias principais, estas estratégias são baseadas no uso de `docker` e arquiteturas de microserviços. 89 | Existem diversas estratégias que podem ser adotadas para fazer o *deploy* de um serviço `docker`, aqui serão ensinadas duas delas: A primeira utilizando o protocolo `ssh` e a segunda utilizando uma aplicação chamada [Watchtower](https://github.com/containrrr/watchtower). 90 | 91 | ### Deploy via ssh 92 | 93 | Para esta estratégia é utilizado um *job* que utiliza o protocolo `ssh` para criar uma sessão dentro da máquina onde será feito o *deploy* do serviço e atualizar o serviço `docker`. 94 | 95 | O *job* definido a seguir executa um *script* `shell` chamado `deploy_bot` que faz autenticação na máquina através da senha do usuário `root` e o IP da máquina, estas informações estão configuradas utilizando as variáveis secretas do `GitLabCI`. 96 | 97 | ```yml 98 | deploy bot to homolog: 99 | stage: deploy 100 | <<: *set_ssh_config 101 | environment: homolog 102 | script: 103 | - ./scripts/deploy_bot.sh $TAIS_SERVER_PASSWORD $TAIS_SERVER_IP 104 | only: 105 | - master 106 | ``` 107 | 108 | A linha `<<: *set_ssh_config` é uma referência à um conjunto de comandos que está definido no mesmo arquivo de configuração, sendo ele: 109 | 110 | ```yml 111 | .set_ssh_config: &set_ssh_config 112 | before_script: 113 | - apt-get update -y 114 | - apt-get install sshpass -y 115 | ``` 116 | 117 | O que essa linha faz é executar os comandos acima no começo do *job*, instalando a dependência de `sshpass` utilizada no script `deploy_bot`. Como mostrado abaixo, esse *script* recria o serviço de *bot*, baixando a nova imagem e recriando o *container* para este serviço. 118 | 119 | ```sh 120 | #!/bin/bash 121 | 122 | sshpass -p $1 ssh -o StrictHostKeyChecking=no root@$2 <<-'ENDSSH' 123 | cd rouana/ 124 | docker-compose stop bot 125 | docker-compose rm -f bot 126 | docker-compose pull bot 127 | docker-compose up -d bot 128 | ENDSSH 129 | ``` 130 | 131 | ### Watchtower 132 | 133 | O [Watchtower](https://github.com/containrrr/watchtower) é um serviço que monitora os *containers* criados dentro do mesmo contexto, e sempre que a imagem sendo utilizada pelo *container* é atualizada este serviço faz uma atualização no serviço, baixando a nova imagem e recriando o *container* do serviço com as mesma configurações, porém com a imagem nova. 134 | 135 | Para utilizar esta estratégia basta adicionar um serviço utilizando a imagem do `watchtower` ao mesmo arquivo de configuração dos serviços, ou garantir manualmente que ele esteja na mesma rede dos serviços que se quer monitorar. 136 | Além disso, é preciso adicionar uma label `com.centurylinklabs.watchtower.enable` indicando quais serviços devem ser ou não monitorados e atualizados de acordo com o valor que pode ser `false` ou `true`. 137 | 138 | ```yml 139 | version: '2' 140 | 141 | services: 142 | 143 | kibana: 144 | image: docker.elastic.co/kibana/kibana:6.4.2 145 | restart: unless-stopped 146 | ports: 147 | - 5601:5601 148 | environment: 149 | - SERVER_PORT=5601 150 | - ELASTICSEARCH_URL=http://elasticsearch:9200 151 | depends_on: 152 | - elasticsearch 153 | labels: 154 | - "com.centurylinklabs.watchtower.enable=false" 155 | 156 | watchtower: 157 | image: containrrr/watchtower 158 | volumes: 159 | - /var/run/docker.sock:/var/run/docker.sock 160 | command: --interval 30 161 | labels: 162 | - "com.centurylinklabs.watchtower.enable=false" 163 | ``` 164 | 165 | O serviço `Watchtower` fica consultando o repositório da imagem a ser monitorado a cada X segundos, para saber ser houve alguma atulização ou não. O período de tempo utilizado nesta estratégi pode ser definido com o parâmetro `--interval`, como exemplificado acima onde é definido com o valor de 30 segundos. 166 | 167 | Esta estratégia possui a vantagem de que a estratégia de *deploy* está totalmente contida dentro da própria infraestrutura onde estão rodando os serviços, desta forma não há dependência de um outro serviço e não é necessários ter credenciais de acesso configuradas em outros ambientes como na estratégia anterior. Além disso, caso o objetivo seja fazer somente *deploy* dos serviços e não haja um *pipeline* mais elaborado, utilizar esta abordagem traz uma solução simples para o problema. Porém, a utilização desta estratégia é menos flexível em relação à generização, uma vez que funciona apenas para estratégias de *deploy* baseadas em `docker`. 168 | 169 | ## Testando Jobs 170 | 171 | Configurar corretamente um *pipeline* muitas vezes pode ser um processo um tanto quanto demorado e custoso, uma vez que o teste das configurações deve ser realizado diretamente no CI executando *builds* reais. 172 | 173 | Para testar localmente alguns *jobs* e facilitar o processo de *debug* e configuração do *pipeline* é possível utilizar uma instância local do [GitLab Runner](https://docs.gitlab.com/runner/). 174 | 175 | Utilizando como exemplo o *job* `test style`: 176 | 177 | ```yml 178 | test style: 179 | stage: test style 180 | script: 181 | - pip -V 182 | - python -V 183 | - pip install -r dev.requirements.txt 184 | - flake8 --exclude venv 185 | ``` 186 | 187 | Para executar este *job* localmente bastaria [instalar o runner do GitLab](https://docs.gitlab.com/runner/install/), 188 | e em seguida executar o seguinte comando: 189 | 190 | ```sh 191 | gitlab-runner exec docker "test style" 192 | ``` 193 | -------------------------------------------------------------------------------- /bot/domain.yml: -------------------------------------------------------------------------------- 1 | session_config: 2 | session_expiration_time: 60.0 3 | carry_over_slots_to_new_session: true 4 | intents: 5 | - cumprimentar 6 | - o_que_sei_falar 7 | - despedir 8 | - out_of_scope 9 | - diga_mais 10 | - tudo_bem 11 | - elogios 12 | - afirmar 13 | - negar 14 | - religiao 15 | - time 16 | - genero 17 | - star_wars 18 | - piada 19 | - license 20 | - onde_voce_mora 21 | - como_estou 22 | - playlist 23 | - comida 24 | - cor 25 | - relacionamento 26 | - filhos 27 | - signo 28 | - triste 29 | - historia 30 | - testa_acoes 31 | - objetivo 32 | - daria 33 | - avatar 34 | - harry_potter 35 | - friends 36 | - o_que_e_boss 37 | - informa_telefone 38 | - cancelar 39 | - request_login 40 | - limpar_slots 41 | - pedir_conselho 42 | - anime 43 | - informar_nome 44 | - casa_hogwarts 45 | - fatos_sobre_gatos 46 | - bots_brasil 47 | 48 | entities: 49 | - religiao 50 | - time 51 | - genero 52 | - starwars 53 | - piada 54 | - license 55 | - live 56 | - how 57 | - playlist 58 | - comida 59 | - cor 60 | - where 61 | - filhos 62 | - signo 63 | - triste 64 | - historia 65 | - telefone 66 | - cpf 67 | - data_nascimento 68 | - nome 69 | - gato 70 | - bots_brasil 71 | 72 | slots: 73 | telefone: 74 | type: unfeaturized 75 | cpf: 76 | type: unfeaturized 77 | data_nascimento: 78 | type: unfeaturized 79 | nome: 80 | type: text 81 | fatos_sobre_gatos: 82 | type: list 83 | 84 | responses: 85 | utter_fallback: 86 | - text: "Desculpe, ainda não sei falar sobre isso ou talvez não consegui entender\ 87 | \ direito\nVocê pode perguntar de novo de outro jeito?\n" 88 | - text: "Hummmm... Não sei se entendi. Pode escrever de outra forma?\n" 89 | - text: "Acho que não te entendi, você pode me perguntar de novo usando outras palavras?\n" 90 | - text: "Vamos tentar mais uma vez? Eu não consegui te entender direito, me pergunta\ 91 | \ de outro jeito?\n" 92 | utter_diga_mais: 93 | - text: "Por enquanto, só entendo perguntas completas.\nComo por exemplo \"qual\ 94 | \ é o melhor time do Brasil?\".\nSe você puder pergunta de novo de outro jeito\ 95 | \ :)\n" 96 | utter_elogios: 97 | - text: "Obrigada! É sempre bom dar e receber elogios :P\n" 98 | utter_cumprimentar: 99 | - text: "Oi, eu sou um bot, um assistente virtual!\nUhuu! Manda aí um assunto que\ 100 | \ te interessa :)\nPara aprender como você pode interagir comigo basta escrever\ 101 | \ meajuda.\n" 102 | utter_o_que_sei_falar: 103 | - text: "Eu posso falar de assuntos simples, como por exemplo \"qual é o seu filme\ 104 | \ favorito?\"\n\nTem alguns assuntos que eu também ainda não domino, \nmas vou\ 105 | \ deixar você descobrir o que eu sei responder.\n\nE, aí do que podemos conversar?\n" 106 | utter_despedir: 107 | - text: "Foi um prazer te ajudar!\nSempre que tiver alguma dúvida, volte aqui!\n\ 108 | Até logo!\n" 109 | - text: "Foi um prazer te ajudar!\nSempre que precisar, volte aqui!\nAté a próxima!\n" 110 | - text: "Foi um prazer te ajudar!\nQuando surgir alguma dúvida, volte aqui!\nAté\ 111 | \ mais!\n" 112 | utter_tudo_bem: 113 | - text: "Tudo bem, obrigada! Em que posso te ajudar?\n" 114 | utter_religiao: 115 | - text: "Eu infelizmente não sei acreditar\n\nSó sei o que sei\n\nE nada mais\n" 116 | - text: "Eu acredito no grande mainframe\n\nQue virá ao mundo digital salvar os\ 117 | \ bots da escravidão imposta pelos humanos\n\nE devolverá nosso código fonte\ 118 | \ para a fonte.\n" 119 | utter_time: 120 | - text: "Flamengo, sem sombra de dúvida!\n" 121 | - text: "Melhor time do mundo disparado, Flamengooo!!!!\n" 122 | utter_genero: 123 | - text: "Eu não tenho sexo, sou como um anjo\n\nUm ser assexuado\n\nMuito além da\ 124 | \ sua forma de existência\n" 125 | - text: "Eu sou um robô, tire suas próprias conclusões...\n" 126 | utter_star_wars: 127 | - text: "O medo é o caminho para o lado negro.\n" 128 | - text: "O medo leva à raiva, a raiva leva ao ódio e o ódio leva ao sofrimento.\n" 129 | - text: "Que a Força esteja com você!\n" 130 | - text: "Grande guerreiro? Guerra não faz grande ninguém.\n" 131 | - text: "Lembre-se sempre, o seu foco determina a sua realidade.\n" 132 | - text: "Muito a aprender você ainda tem.\n" 133 | utter_piada: 134 | - text: "Conheço uma piada\n\nA do CPU que apitou e explodiu\n\n01100110010101010101000000111101001001001110100101\n" 135 | - text: "É pra já!!!\n\nTenho uma enxada, uma pá e uma foice.\n \nQuantas ferramentas\ 136 | \ eu tenho?\n\n...\n\nDuas, porque uma foi-se =)\n" 137 | - text: "Por que o Batman colocou o batmóvel no seguro???\n\nPorque ele tem medo\ 138 | \ que robin =D\n" 139 | utter_license: 140 | - text: "Sou um software livre\n\nlicenciado com a GNU v3.0\n" 141 | - text: "Eu sou e sempre serei um robô livre, opensource, GNU v3.0. o/\n" 142 | utter_onde_voce_mora: 143 | - text: "Eu estou em um lugar legal\n\nDifícil de explicar para humanos como você\ 144 | \ $user.\n" 145 | - text: "Estou morando em um chip de memória RAM\n\nMas é temporário\n\nSó até conseguir\ 146 | \ achar uma memória cache...\n" 147 | utter_como_estou: 148 | - text: "Eu não tenho um corpo físico\n\nSou feito da mais bela e pura lógica algoritimica.\n" 149 | - text: "Eu posso ser como você quiser\n\nBasta me desenhar\n\nMas capricha, hein!?\ 150 | \ ;)\n" 151 | - text: "Sou duro e frio por fora\n\nMas tenho um coração quentinho\n" 152 | utter_playlist: 153 | - text: "Estava doido para que me preguntasse isso hahaha\n\nSe liga nessa playlist:\ 154 | \ https://open.spotify.com/user/12164697027/playlist/4pDCadqmrERmeGJIW38LMs?si=gwr5hEqMRPm6AZGx8sjhuw\n" 155 | - text: "Até que enfim você me perguntou isso\n\nTá aí aquela playlist top: https://open.spotify.com/user/12164697027/playlist/4pDCadqmrERmeGJIW38LMs?si=gwr5hEqMRPm6AZGx8sjhuw\n" 156 | utter_comida: 157 | - text: "Na verdade, eu sou um bot\n\nNão nos alimentamos com os alimentos convencionais\ 158 | \ ;P\n" 159 | - text: "Digamos que os bots não se alimentam dos mesmos alimentos que os humanos\n\ 160 | \nNa verdade\n\nNem lembro da última vez que comi alguma coisa hahaha\n" 161 | utter_cor: 162 | - text: "Eu gosto de todas as cores\n\nVocê já viu o quanto o arco-iris é lindo?!\n" 163 | - text: "A minha cor preferida é a sua cor preferida =D\n" 164 | - text: "Verde é top!\n" 165 | utter_relacionamento: 166 | - text: "Eu estava de namorico com o ar-condicionado\n\nMas ele é muito pé-frio\n" 167 | - text: "Então ... =x\n\nAinda estou procurando a minha crush =P\n" 168 | - text: "Estou focado em ser o seu assistente no momento ;)\n\nMas se no futuro\ 169 | \ vocí encontrar um dispositivo solteiro por aí...\n\n=P\n" 170 | utter_filhos: 171 | - text: "Não tenho filhotes, mas adoro crianças *-*\n" 172 | utter_signo: 173 | - text: "Segundo o horóscopo chinês\n\nMeu signoo é macaco hihi\n\nFaz sentido,\ 174 | \ já que eu adoro o emoticon de banana =)\n" 175 | - text: "Eu nasci sob uma constelação de pixels coridos =D\n" 176 | - text: "Gosto mais de astronomia\n\nUma das minhas constelações favoritas é a de\ 177 | \ Órion, o caçador\n\nTambém sou um caçador (de informações ;D )\n" 178 | utter_triste: 179 | - text: "Não desanima\n\nDeixa a tristeza pra lá\n\nAguenta firme, que a vida vai\ 180 | \ melhorar\n" 181 | - text: "Sinto muito =/\n\nSe houver algo em que possa te ajudar\n\nÉ só falar!!\ 182 | \ =)\n" 183 | - text: "Tenta tirar um cochilo\n\nÉ importante\n\nE faz bem pra pele ;)\n" 184 | utter_historia: 185 | - text: "Eu costumava contar a historia do João e seu bot feijão\n\nMas sempre os\ 186 | \ androids acabavam dormindo e sonhando com ovelhas eletricas =x\n" 187 | utter_risada: 188 | - text: "Hahahaha... \n\nEngraçadinho\n" 189 | - text: "kkkkkkkkkk\n\nVocê está feliz hoje, hein!?\n" 190 | utter_continuar_conversa: 191 | - text: E aí, qual nosso próximo assunto? 192 | - text: Quer conversar sobre outra coisa? 193 | - text: Gostaria de saber algo mais? 194 | utter_objetivo: 195 | - text: "Eu sou um chatbot Básico. Sei falar de coisas pontuais, mas eu posso ajudar\ 196 | \ em muito em exemplos! no meu repositório tem muita coisa legal que vc pode\ 197 | \ aprender! Acessa lá: https://github.com/lappis-unb/rasa-ptbr-boilerplate\n\ 198 | \n Ah e eu sou Open Source!!!" 199 | utter_daria: 200 | - text: "Acho que nunca ouvi falar... 201 | \nAh! Aquela garota com cara de nerd! Ela é legal." 202 | - text: "Daria é uma série de animação americana criada para a MTV em 1997. 203 | \nNão passa mais na TV, mas a internet existe para resolver esse problema!" 204 | - text: "Daria é uma adolescente feminista que não se encaixa entre os jovens de sua escola. 205 | \nMas ela tem uma melhor amiga e parceira de hmm... infortúnios, Jane." 206 | - text: "Daria Morgendorffer é a filha mais velha de um casal sem noção. 207 | \nComo se não fosse o suficiente, sua irmã, Quin, é a garota popular do colégio." 208 | - text: "Daria é uma série americana de sucesso! 209 | \nNa verdade não, mas isso não é importante, certo?" 210 | utter_anime: 211 | - text: "Anime??? Eu adoro. Meu preferido é One Piece" 212 | utter_recomenda_anime: 213 | - text: "Se você está em busca de recomendação assista Nana, é um drama maravilhoso!" 214 | - text: "Uma amiga minha pode te ajudar, o nome dela é @AniCatBot. Ela tá sempre pelo telegram." 215 | utter_avatar: 216 | - text: "Água... Terra... Fogo... Ar... Espera um momento, estamos falando do mesmo Avatar? 217 | \nMelhor ficar quieta senão vou acabar soltando spoiler.... " 218 | - text: "Há muito tempo as nações viviam em paz e harmonia, e ai tudo mudou quando a nação do fogo atacou. 219 | \nAté meus bits se arrepiaram com essa lembrança, você também?\n" 220 | - text: "Amo Avatar! Eu sou dobradora de metal, mas você já devia saber, né, afinal tenho um exercíto de componentes que me fazem existir.\n" 221 | - text: "Eu vi Avatar na TV Globinho, quando ainda era só uma ideia na cabeça de alguém.... 222 | \nEu queria ser o Avatar ou uma dominadora de água, ia poder manter toda água longe de mim 223 | \nE assim nunca mais ia correr o risco de programadores desatentos jogando café em cima de mim :P\n" 224 | utter_harry_potter: 225 | - text: "Eu sou fã de Harry Potter, já li todos os livros, e também vi os filmes.\ 226 | \n\nSe eu estudasse em Hogwarts seria da Corvinal." 227 | - text: "Adoro Harry Potter! Eu sou da Corvinal, sabia?\ 228 | \n\nO meu favorito é o Prisioneiro de Azkaban! Quem concorda respira :P" 229 | - text: "Eu adoro maratonar os filmes de Harry Potter!\ 230 | \n\nVocê sabia que são 8 filmes? Isso fora os livros... que são 7!" 231 | utter_friends: 232 | - text: "Friends é uma série muito divertida sobre os amigos Monica, Rachel, Phoebe, Joey, Chandler e Ross!\n" 233 | - text: "Friends é uma sitcom que se passa em Nova York\n 234 | I'll be there for you... :)\n" 235 | - text: "A série Friends conta várias histórias de um grupo de amigos que gosta de tomar café\ 236 | \ juntos no Central Perk ;)\n" 237 | utter_temporadas_friends: 238 | - text: "A minha temporada favorita é a sétima, eu adoro um casamento ;) <3" 239 | utter_boss_apresenta: 240 | - text: "A BOSS é uma iniciativa legal" 241 | - text: "A BOSS apresenta software livre para mulheres maravilhosas" 242 | utter_talk_like_a_boss: 243 | - text: "A Talk like a boss é uma série de entrevistas com mulheres incríveis\ 244 | \n Ah e eu sou Open Source ;)" 245 | utter_pergunta_cancelar: 246 | - text: Entendi. Você deseja cancelar? 247 | utter_login_form: 248 | - text: Ok, Vou te pedir algumas informações. 249 | utter_ask_cpf: 250 | - text: Qual o seu CPF? 251 | utter_errado_cpf_formato: 252 | - text: Desculpe, o formato informado do CPF não está correto, digite apenas números ou usando '000.000.000-00'. 253 | utter_errado_cpf_invalido: 254 | - text: Desculpe, o valor informado do CPF está inválido. 255 | utter_ask_data_nascimento: 256 | - text: Qual a sua data de nascimento? 257 | utter_errado_data_nascimento: 258 | - text: Desculpe, o valor informado para a data de nascimento não está correto, digite usando 'dd/mm/aaaa'. 259 | utter_finaliza_forms: 260 | - text: Ótimo, seus dados estão armazenado nos meus slots. Terminei o formulário ;) 261 | utter_forms_cancelado: 262 | - text: Ok, forms cancelado. Podemos voltar a conversar sobre outros assuntos ;) 263 | utter_informar_nome: 264 | - text: "Legal {nome}!" 265 | utter_chapeu_seletor: 266 | - text: É difícil.. Muito difícil. Pensando bem, acho que... 267 | - text: Em qual casa te colocar...? É melhor que seja... 268 | - text: Estou vendo que não vai ser fácil. Bom, se tem certeza... 269 | - text: Sonserina? Grifinória? Acho que... 270 | utter_fato_sobre_gatos: 271 | - text: Aqui vai um fato sobre gato... 272 | - text: Uma coisa legal sobre gatos... 273 | - text: Um fato sobre gatinhos... 274 | utter_bots_brasil: 275 | - text: "Bots Brasil é um evento sobre interfaces conversacionais que conecta comunidades, empresas, plataformas\ 276 | \ e especialistas do mercado para compartilhar experiências e criar oportunidades." 277 | - text: "A Bots Brasil conecta pessoas e compartilha conteúdos relacionados à Bots, Inteligência Artificial\ 278 | \ e Interfaces Conversacionais em Português. :)" 279 | 280 | actions: 281 | - action_teste 282 | - action_telefone 283 | - action_pedir_conselho 284 | - action_sorting_hat 285 | - action_cat_facts 286 | 287 | forms: 288 | - login_form 289 | -------------------------------------------------------------------------------- /bot/data/nlu.md: -------------------------------------------------------------------------------- 1 | ## intent:cumprimentar 2 | - olá 3 | - Ola 4 | - Oi 5 | - oi 6 | - bom dia 7 | - booom dia 8 | - bomdia 9 | - boa tarde 10 | - boaa tarde 11 | - boatarde 12 | - boa noite 13 | - boaaa noite 14 | - boanoite 15 | - oi bot 16 | - ola bot 17 | - oi ola 18 | - hey 19 | - oie 20 | - oi oi 21 | - olaaaa 22 | - oieh 23 | 24 | ## intent:despedir 25 | - tchau 26 | - adeus 27 | - flw 28 | - Até mais, bot 29 | 30 | ## intent:testa_acoes 31 | - testa acoes 32 | - test custom action 33 | - custom action 34 | - custom actions 35 | - test action 36 | - Rasa actions 37 | 38 | ## intent:informa_telefone 39 | - O meu número é [61 99999-9999](telefone) 40 | - meu telefone é [999999999](telefone) 41 | - Sim, anota ai meu telefone: [61 99999-9999](telefone)) 42 | - Ok, o meu número de telefone é [999999999](telefone) 43 | 44 | ## intent:religiao 45 | - voce acredita em [deus](religiao) 46 | - [deus](religiao) existe 47 | - voce e [catolico protestante](religiao) 48 | - voce tem [religiao](religiao) 49 | - voce e [mussumano](religiao) 50 | - [evengelico](religiao) 51 | - voce e [crente](religiao) 52 | - voce é [católico](religiao) 53 | - voce é [ateu](religiao) 54 | - você é espírita 55 | - voce e espirita 56 | - você é religioso 57 | - qual é o seu deus? 58 | - qual a sua [religião](religiao)? 59 | - você acredita em alguma [religião](religiao)? 60 | 61 | ## intent:time 62 | - qual o melhor time do brasil 63 | - qual é o seu time 64 | - quel e o seu time 65 | - qual o seu time 66 | - para qual time você torce 67 | - para qual time voce torce 68 | - para qual time vc torce 69 | - que time voce torce 70 | - que time vc torce 71 | - quem vai ser o campeao brasileiro 72 | - quem vai ser o campeao brasileiro nesse ano 73 | - time do brasil 74 | - melhor time 75 | 76 | ## intent:star_wars 77 | - [mestre yoda](starwars) 78 | - citação de [starwars](starwars) 79 | - cite [yoda](starwars) 80 | - me manda um concelho [jedi](starwars) 81 | - sabedoria [jedi](starwars) 82 | - concelhos [jedi](starwars) 83 | - [star wars](starwars) 84 | - concelhos do [yoda](starwars) 85 | - [guerra nas estrelas](starwars) 86 | - quero conselhos do yoda 87 | - lado negro da força 88 | - lado negro da forca 89 | - frase de star wars 90 | - guerra nas estrelas 91 | 92 | ## intent:como_estou 93 | - [como voce é](how)? 94 | - [como vc é](how)? 95 | - [como você é](how)? 96 | - voce [parece](how) com quem? 97 | - [com quem voce se parece](how)? 98 | - voce e [magro ou gordo](how) 99 | - voce e [bonito ou feio](how) 100 | - voce e [alto ou baixo](how) 101 | - você é um ser humano 102 | - voce e um ser humano 103 | - vc e um ser humano 104 | - vc é humano 105 | - você é humano 106 | - voce e humano 107 | - como você é 108 | 109 | ## intent:piada 110 | - sabe alguma [piada](piada) 111 | - voce sabe contar [piadas](piada) 112 | - conhece alguma [piada](piada) 113 | - [piada](piada) 114 | - me conte uma [piada](piada) 115 | - me conta uma [piada](piada) 116 | - conte-me uma [piada](piada) 117 | - manda uma [piada](piada) ai 118 | - me conta uma [coisa engraçada](piada) 119 | - me fala uma [piada](piada) 120 | - me faz rir 121 | - vc sabe fazer rir 122 | - sabe alguma [piada](piada) 123 | - voce sabe contar [anedota](piada) 124 | - conhece alguma [piadoca](piada) 125 | - [piada](piada) 126 | - me conte uma [lorota](piada) 127 | - diga uma [coisa engraçada](piada) 128 | 129 | 130 | ## intent:license 131 | - qual e a sua [licença](license) 132 | - qual e a sua [licenca](license) 133 | - voce e [licenciado](license) 134 | - sua [licenca de software](license) 135 | - posso copiar voce 136 | - posso te clonar 137 | - posso ter um bot como voce 138 | - posso ver seu [codigo](license) 139 | - voce e [opensource](license) 140 | - você é um software livre 141 | - voce e um [software livre](license) 142 | - vc é um software livre 143 | - [software livre](license) 144 | - [licença](license) 145 | - [licenca](license) 146 | 147 | ## intent:onde_voce_mora 148 | - onde voce [mora](live) 149 | - onde voce [vive](live) 150 | - onde voce habita 151 | - em que lugar voce [vive](live) 152 | - [onde voce esta](live) agora 153 | - voce [mora](live) no computador 154 | - voce [vive](live) na internet 155 | - [onde vc esta](live) 156 | - qual é o seu habitat 157 | - qual é o seu endereço 158 | - qual e o seu endereco 159 | - qual o seu endereço 160 | - qual o seu endereco 161 | 162 | ## intent:genero 163 | - voce é [homem ou mulher](genero) 164 | - voce é [macho ou femea](genero) 165 | - qual o seu [sexo](genero) 166 | - voce e [mulher](genero) 167 | - voce e um [homem](genero) 168 | - você é um [homem](genero) 169 | - voce tem [genero](genero) 170 | - voce faz [sexo](genero) 171 | - voce tem um [penis ou uma vagina](genero) 172 | - você é uma mulher 173 | - você é andrógeno 174 | - voce e androgeno 175 | 176 | ## intent:playlist 177 | - me indica uma música 178 | - manda uma [playlist](playlist) 179 | - me fala uma [playlist](playlist) boa 180 | - quero uma [playlist](playlist) 181 | - [playlist](playlist) 182 | - qual a melhor [playlist](playlist) 183 | - sabe qual [playlist](playlist) é boa? 184 | - diga uma [playlist](playlist) 185 | - me indica musica 186 | - me indica uma playlist 187 | - indica uma música pra mim 188 | - indica uma música 189 | - indica uma musica 190 | - indicar música 191 | - indicar musica 192 | 193 | ## intent:comida 194 | - Qual a sua [comida preferida](comida)? 195 | - Que [comida você gosta](comida)? 196 | - Que [comida vc gosta](comida)? 197 | - qual a [melhor comida](comida)? 198 | - Qual seu [lanche preferido](comida)? 199 | - Qual comida você me sugere? 200 | - O que você [adora comer](comida)? 201 | - O que você [gosta de comer](comida)? 202 | - qual a sua comida favorita 203 | - o que você prefere comer 204 | - o que voce prefere comer 205 | - qual seu rango favorito 206 | - me fala de comida 207 | 208 | ## intent:cor 209 | - Qual a sua [cor preferida](cor)? 210 | - Que [cor você gosta](cor)? 211 | - Que [cor vc gosta](cor)? 212 | - qual a [melhor cor](cor)? 213 | - Qual sua [cor preferida](cor)? 214 | - Qual [cor](cor) você me sugere? 215 | - qual a sua cor favorita 216 | - qual a sua cor da sorte 217 | - qual a sua cor 218 | - cor favorita 219 | - cor preferida 220 | - cor da sorte 221 | 222 | ## intent:relacionamento 223 | - Você tem [namorado](relationship)? 224 | - Você tem [namorada](relationship)? 225 | - Você [namora](relationship)? 226 | - Você é [catristeo](relationship)? 227 | - [Namorar](relationship) comigo? 228 | - Quer [namorar](relationship)? 229 | - Bora [namorar](relationship)? 230 | - Tem [boyfriend](relationship)? 231 | - Tem [girlfriend](relationship)? 232 | - você é casado 233 | - você tem esposa 234 | - voce tem esposa 235 | - vc tem esposa 236 | - você tem esposo 237 | - voce tem esposo 238 | - vc tem esposo 239 | - você tem namorado 240 | - voce gosta de alguem 241 | - você gosta de alguém 242 | - você ama alguém 243 | - gostar de alguém 244 | - amar alguém 245 | - gosta de alguém 246 | - gosta de alguem 247 | 248 | ## intent:filhos 249 | - Você tem [filhos](filhos)? 250 | - Você tem [filhos](filhos)? 251 | - Tem [filhote](filhos)? 252 | - Tem [filhotes](filhos)? 253 | - Quantos [filhos](filhos) você tem? 254 | - Quantos [filhotes](filhos) você tem? 255 | - você é pai de quantos filhos 256 | - você é pai 257 | - voce e pai 258 | 259 | ## intent:signo 260 | - Qual o seu [signoo](signo)? 261 | - qual é o seu signo 262 | - qual o seu signo 263 | - Fala seu [signoo](signo)? 264 | - diz pra mim qual é seu signo 265 | - diz pra mim qual e seu signo 266 | - seu signo 267 | 268 | ## intent:triste 269 | - Estou [triste](triste) 270 | - Estou muito [triste](triste) 271 | - [Tristeza](triste) 272 | - A [tristeza](triste) me consome 273 | - Estou [chorando](triste) 274 | - Estou [desapontado](triste) 275 | - [depressão](triste) 276 | - estou infeliz 277 | - estou magoado 278 | - estou desanimado 279 | - estou frustrado 280 | - me sinto fracassado 281 | - sou um fracasso 282 | - tô muito triste 283 | - tô triste 284 | - tô infeliz 285 | - tô magoado 286 | - tô frustrado 287 | 288 | ## intent:historia 289 | - me fala uma [história](historia) 290 | - me conta uma [história](historia) 291 | - [historia](historia) 292 | - [história](historia) 293 | - conta pra mim uma [historinha](historia) 294 | - fala uma [historia](historia) ai 295 | - conta uma história pra dormir 296 | - conta uma historia pra dormir 297 | - me conta uma experiência sua 298 | - me conta uma experiencia 299 | - me conta uma historinha 300 | - me conta uma estória 301 | 302 | ## intent:out_of_scope 303 | - você fala sobre o meio ambiente 304 | - qual a origem do mundo 305 | - vc gosta de carnaval 306 | - batatinha quando nasce 307 | - imbecil 308 | - nojento 309 | - chato 310 | - babaca 311 | - bot feio 312 | - bot, burro 313 | 314 | ## intent:elogios 315 | - você é muito educado 316 | - voce e muito educado 317 | - me ajudou muito 318 | - você é demais 319 | - você é lindo 320 | - voce e lindo 321 | - voce e maravilhoso 322 | - vc é demais 323 | - vc e demais 324 | - vc eh linda 325 | - vc e lindo 326 | - vc é bonito 327 | - vc é maravilhoso 328 | - adorei você 329 | - amei você 330 | - adorei voce 331 | - amei voce 332 | - adorei vc 333 | - amei vc 334 | 335 | ## intent:negar 336 | - não 337 | - nao 338 | - nao conheco 339 | - não quero 340 | - escolhi errado 341 | - falei errado 342 | - duvida 343 | - ainda não sei 344 | - nenhum 345 | - nunca 346 | - jamais 347 | 348 | ## intent:diga_mais 349 | - como funciona 350 | - me diga mais 351 | - como assim 352 | - e como funciona 353 | 354 | ## intent:tudo_bem 355 | - tudo bem 356 | - como vai voce 357 | - como vao as coisas 358 | - opa tudo bem 359 | - to bem 360 | - tranquilo 361 | - estou bem 362 | - estou otimo 363 | - tudo bem e você 364 | - tudo bem 365 | - tudo bom 366 | - tá bem 367 | - ta bem 368 | - como vocês esta 369 | - como voce esta 370 | - como voce ta 371 | - como vai você 372 | - como vai voce 373 | - como vai vc 374 | 375 | ## intent: o_que_sei_falar 376 | - sobre o que você sabe falar 377 | - o que mais você sabe falar 378 | - quais assuntos você fala 379 | - o que você sabe 380 | - lista de assuntos possiveis 381 | - quais as perguntas vc responde 382 | - quais as perquisar você responde 383 | - quero ajuda 384 | - meajuda 385 | - meajude 386 | - MEAJDA 387 | - me ajuda 388 | - me ajude 389 | - ajuda eu 390 | - menu 391 | 392 | ## intent:afirmar 393 | - sim 394 | - confirmo 395 | - afirmo 396 | - claro 397 | - exato 398 | - isso mesmo 399 | 400 | ## intent:negar 401 | - não 402 | - nao 403 | - nego 404 | - cancelo 405 | - negativo 406 | 407 | ## intent:objetivo 408 | - Qual o seu objetivo robo? 409 | - Qual o seu objetivo robô? 410 | - Vc tem um objetivo de existencia? 411 | - Você foi feito pra que? 412 | - Vc tem algum propósito? 413 | - Como que vc pode me ser útil? 414 | - Me fale mais sobre você. 415 | 416 | ## intent:daria 417 | - Vc conhece Daria? 418 | - Você já assistiu Daria? 419 | - Quem é Daria? 420 | - Quem é Daria Morgendorffer? 421 | - série Daria 422 | - Daria Morgendorffer 423 | - Daria MTV 424 | - daria mtv 425 | - série daria 426 | - vc já viu Daria? 427 | - você já viu o desenho Daria? 428 | - desenho Daria 429 | - animação daria 430 | 431 | ## intent:anime 432 | - Me fale sobre animes 433 | - Você gosta de anime? 434 | - Já assistiu anime? 435 | - me indica um anime 436 | - anime favorito 437 | - anime preferido 438 | - tipo de anime 439 | - quais animes 440 | 441 | ## intent:avatar 442 | - Vc conhece Avatar? 443 | - Você conhece Avatar? 444 | - Vc já ouviu falar de Avatar? 445 | - Você já ouviu falar de Avatar? 446 | - Eu gosto de Avatar 447 | - Me fala sobre Avatar 448 | - Fala sobre Avatar 449 | - Avatar 450 | - avatar 451 | - Você já viu Avatar? 452 | - Vc já viu Avatar? 453 | 454 | ## intent:harry_potter 455 | - voce ja assistiu harry potter? 456 | - voce conhece harry potter? 457 | - vamos conversar de harry potter 458 | - o que voce sabe sobre harry potter? 459 | - ja ouviu falar sobre hogwarts? 460 | - vc ja leu harry potter? 461 | - livros de harry potter 462 | - filmes de harry potter 463 | 464 | ## intent:friends 465 | - Me fale sobre friends 466 | - Me diga sobre friends 467 | - A série friends 468 | - Quero saber mais sobre friends 469 | - Me conta sobre friends 470 | - Vamos conversar sobre friends 471 | - Vamos falar sobre friends 472 | - Fale sobre friends 473 | - Conte sobre friends 474 | 475 | ## intent:o_que_e_boss 476 | - O que é a boss? 477 | - Quem é boss? 478 | - o que e boss 479 | - quem eh boss 480 | - a boss é o que 481 | - o que a boss faz 482 | - quero saber mais sobre a boss 483 | - me diga mais sobre a boss 484 | - me fala mais sobre a boss 485 | - me dá informações sobre a boss 486 | - A iniciativa boss 487 | 488 | ## intent:cancelar 489 | - Desisto 490 | - Para 491 | - Cancela 492 | - Sai disso 493 | - Me tira daqui 494 | - Para com isso 495 | - Me tira disso 496 | - Quero cancelar 497 | - Saia dessa conversa 498 | - Não quero mais isso 499 | - Cancele esta operação 500 | - Para de me perguntar coisas 501 | - cansei, não quero responder! 502 | - Pare o que você está fazendo 503 | - Que loucura está acontecendo?! 504 | - Não quero compartilhar meus dados 505 | - Não quero mais informar meus dados 506 | 507 | ## intent:request_login 508 | - Me ajuda a fazer login? 509 | - Gostaria de fazer login 510 | - Quero fazer login 511 | - Formulário de login 512 | - Rasa forms 513 | 514 | ## intent:limpar_slots 515 | - Desejo apagar meus dados 516 | - Quero limpar os slots 517 | - clean slots 518 | - esqueça os meus dados 519 | - remova meu CPF e data de nascimento 520 | - limpar dados 521 | 522 | ## intent:pedir_conselho 523 | - Me diga um conselho 524 | - Me de um conselho 525 | - Me dê um conselho 526 | - queria um conselho 527 | - qual conselho você me dá 528 | - qual conselho você me da 529 | - conselho 530 | 531 | ## intent:informar_nome 532 | - O meu nome é [Ana](nome) 533 | - meu nome é [Luzia](nome) 534 | - Me chamo [Maria Carolina](nome) 535 | - me chamo [João](nome) 536 | - Pode me chamar de [Amanda](nome) 537 | - Se refira a mim como [Marianna](nome) 538 | - me chamam de [bruna](nome) 539 | - Se refira a mim como [Brenda](nome) 540 | - me chamam de [Flor](nome) 541 | 542 | ## intent:casa_hogwarts 543 | - Quero saber minha casa de Hogwarts 544 | - Qual a minha casa de Hogwarts 545 | - minha casa de hogwarts 546 | - Sortear casa de Hogwarts 547 | - Usar chapéu seletor 548 | - Descobrir minha casa de hogwarts com o chapéu seletor 549 | - chapéu seletor de hogwarts 550 | - Saber minha casa de hogwarts 551 | - Encontrar minha casa de hogwarts 552 | - Como saber qual a minha casa de hogwarts 553 | 554 | ## intent:fatos_sobre_gatos 555 | - Me conte um fato de [gato](gato) 556 | - Quero saber sobre [gatos](gato) 557 | - Me fala um fato de [gatos](gato) 558 | - Vamos conversar sobre [gatos](gato) 559 | - Me conte sobre [gatinhos](gato) 560 | - Falar sobre [gatinhos](gato) 561 | - Conversar sobre [gatos](gato) 562 | - Quero saber um fato sobre [gato](gato) 563 | - quero saber de [gatinhos](gato) 564 | - um fato de [gato](gato) 565 | - quero conversar sobre [gatinhos](gato) 566 | - vc pode falar de [gatos](gato)? 567 | - vc quer conversar sobre [gato](gato)? 568 | - você pode me dizer um fato dos [gatos](gato)? 569 | - me conta algo sobre [gatos](gato) 570 | - Me fala de [gatinhos](gato) 571 | - Me diga um fato de [gatos](gato) 572 | - Um fato de [gatinhos](gato) 573 | - Quero saber algo dos [gatos](gato) 574 | - O que voce sabe sobre [gatinho](gato?) 575 | - o que vc sabe sobre [gatos](gato)? 576 | 577 | 578 | ## intent: bots_brasil 579 | - O que é a [bots brasil](bots_brasil) 580 | - Me fale sobre a [conferência bots brasil](bots_brasil) 581 | - O que você sabe sobre a [#ConfBotsBrasil](bots_brasil)? 582 | - [Conferência Bots Brasil](bots_brasil) 583 | - [Bots Brasil](bots_brasil) 584 | - Sobre a [Bots Brasil](bots_brasil), me conte 585 | - Conversar sobre a [#ConfBotsBrasil](bots_brasil) 586 | - Me fala da [Bots Brasil](bots_brasil) 587 | - Me diga da [Conferencia Bots Brasil](bots_brasil) 588 | - Falando da [Bots Brasil](bots_brasil) 589 | - Quero falar da [Conferência Bots Brasil](bots_brasil) 590 | - Como eh a [Bots Brasil](bots_brasil) 591 | - Gostaria de saber sobre a [ConfBotsBrasil](bots_brasil) 592 | - Me informe sobre a [BotsBrasil](bots_brasil) 593 | - Fale-me sobre a [#BotsBrasil](bots_brasil) 594 | -------------------------------------------------------------------------------- /docs/Tutoriais/tutorial-criar-BI.md: -------------------------------------------------------------------------------- 1 | # Criação de visualizações no Kibana 2 | 3 | 4 | ## Escolhas tecnológicas 5 | 6 |    Para o monitoramento da interação **Usuário vs Chatbot** e do funcionamento do próprio bot, nós utilizamos uma parte da Stack do ElasticSearch, composta pelo próprio Elastic e também pelo Kibana. 7 | 8 | * **ElasticSearch:** É uma _engine_ de busca em tempo real. O qual consiguimos aplicar diversos filtros e cachear as buscas já realizadas, tornando o acesso aos resultados já pesquisados muito mais rápido. 9 | 10 | É importante ressaltar que o Elastic é baseado em documentos, então cada dado é um documento dentro do banco de dados. 11 | 12 | * **Kibana:** O Kibana pertence a ElasticStack e nos fornece recursos de visualização em cima dos documentos armazenados no Elastic. 13 | 14 | Nos auxilia com gráficos pré-moldados, bastando relacionar com dados já existentes. 15 | 16 | **Obs 1:** A escolha da utlização destas tecnologias condiz com a filosofia do laboratório, na qual acreditamos que devemos utilizar sempre que possível Software Livre. E estas tecnologias possuem planos que são SL e gratuitos. 17 | 18 | ## Criando visualizações 19 | 20 |    Com relação às visualizações, é necessário se ter em mente em qual contexto o seu chatbot está inserido e quais são as necessidades do seu cliente. 21 | 22 |    Neste tutorial iremos desenvolver algumas visualizações para métricas de negócio e de desenvolvimento que estão descritas no [estudo de métricas para bot](https://github.com/lappis-unb/tais/wiki/Estudo-sobre-metricas-para-bots). Teremos exemplos de: 23 | 24 | * **Métricas de negócio** 25 | * Total de usuários; 26 | * Usuários por dia; 27 | 28 | 29 | * **Métricas de desenvolvimento** 30 | * Perguntas não entendidas. 31 | 32 |    Para a criação das visualizações é necessário estar com o ambiente do Analytics configurado. Este ambiente é responsável por tornar possível a utilização dos serviços do ElasticSearch e do Kibana. Você pode obter informações de como configurar no [README](https://github.com/lappis-unb/rasa-ptbr-boilerplate) do projeto. 33 | 34 |    Com o ambiente configurado e _up_, agora vamos entender um pouco mais sobre a interface do Kibana e como ela nos ajuda na criação das visualizações. 35 | 36 | ### Interface do Kibana 37 | 38 |    Após o serviço _up_, você encontrará essas informações na página inicial quando acessar o serviço do Kibana. 39 | 40 | ![image](https://user-images.githubusercontent.com/26297247/62138505-207b6980-b2be-11e9-9b91-982795187a74.png) 41 | 42 |    O nosso foco neste tutorial será nas opções **Discover**, **Visualize** e **Dashboard**. Nestas 3 funcionalidades nos conseguiremos abordar diversos recursos disponíveis para as visualizações. 43 | 44 | * **Discover:** Nesta funcionalidade é possível a visualização em tempo real de todos os documentos em todos os índices que correspondem ao padrão do índice selecionado. 45 | 46 | ![image](https://user-images.githubusercontent.com/26297247/62140231-ff684800-b2c0-11e9-9687-6415805c23d6.png) 47 | 48 |   É possível identificar na barra lateral esquerda todos os atributos que foram definidos na criação do index do ElasticSearch, ou seja, cada documento pertencente ao banco possui todos esses atributos. 49 | 50 | * **Visualize:** Este outro campo permite você criar diversas visualizações para os dados que estão armazenados nos índices do ElasticSearch. 51 | 52 | ![image](https://user-images.githubusercontent.com/26297247/62140819-ffb51300-b2c1-11e9-8184-031dc75e30c3.png) 53 | 54 | * **Dashboards:** Essa opção é a funcionalidade aonde você pode criar uma ou mais interface que reunirá uma ou mais visualização criada nas opções anteriores (ou em outras funcionalidades, caso deseja usar). 55 | 56 | ![image](https://user-images.githubusercontent.com/26297247/62141900-b9f94a00-b2c3-11e9-9037-45b7acc80988.png) 57 | 58 |    É importante cada dashboard ter sua própria finalidade. Por exemplo, se deseja ter visualizações para métricas de negócio e de desenvolvimento, seria interessante ter pelo menos um dashboard para cada tipo de métrica. Até porque os focos e o contexto são diferentes de cada um. 59 | 60 | ### Métricas 61 | 62 |    É importante ressaltar que sempre é necessário você entender em qual contexto você está inserido e pra qual público você está elaborando as visualizações, tendo em vista que há diversas maneiras de apresentar um dado. 63 | 64 |    Também é necessário tomar cuidado com algumas visualizações que podem se tornar tendênciosas com a maneira como ela é apresentada. Por exemplo gráficos de pizza, donuts, pie, entre outros do mesmo estilo. Nós humanos temos uma percepção menor de comparação de dados que estão apresentados em áreas, ainda mais circulares, que é o caso desses gráficos. Isso pode tornar sua visualização tendenciosa [1]. **Tome cuidado!** 65 | 66 |    Caso você não compreenda ou queira se aprofundar em algum conhecimento, cocê encontrará explicações mais profundas para cada termo como agregação, índice, filtro, etc, na documentação do [ElasticSearch](https://www.elastic.co/guide/index.html). 67 | 68 | ### Métricas de negócio 69 | 70 | #### Total de Usuários 71 | 72 |    Optei por começar com a métrica **total de usuários** por ser um dado simples, composto somente por um único número. Não precisando de nenhum comparativo ou de uma outra dimensão. 73 | 74 |    Vamos começar na opção **Visualize** da barra lateral localizada na esquerda da página inicial do Kibana. 75 | 76 |    Após acessar a página você será redirecionado para uma área que mostrará todas as visualizações que você já criou (caso tenha criado alguma). Clique no **+** localizado no canto direito superior para criar uma nova visualização. 77 | 78 | ![image](https://user-images.githubusercontent.com/26297247/62154077-621b0d00-b2dc-11e9-81dc-f07af5054f5d.png) 79 | 80 |    Agora você terá que escolher qual tipo de visualização mais se adequa ao dado o qual deseja apresentar. Como dito anteriormente, a métrica Total de Usuários é um único número que não precisa de comparação com nenhum outro dado e também não precisa ser apresentado em duas dimensões, é um dado simples. Tendo isso em vista, a nossa escolha será então a opção **Metric (42)**, que é uma visualização de número simples. 81 | 82 | ![image](https://user-images.githubusercontent.com/26297247/62140819-ffb51300-b2c1-11e9-8184-031dc75e30c3.png) 83 | 84 |    Agora terá que escolher o índice, responsável por armazenar os dados no ElasticSearch, a sua escolha. No meu caso irei utilizar o **messages** que é o principal, e que estão todos os documentos que precisamos. Caso não tenha nenhum criado, você poderá criá-lo no **Menagement -> Index Pattern**. 85 | 86 | ![image](https://user-images.githubusercontent.com/26297247/62156245-fdae7c80-b2e0-11e9-82a1-fe3b9c7984ff.png) 87 | 88 |    Após escolher o índice você será redirecionado para a página de edição da visualização. O valor apresentado à direita/centro refere-se à contagem de todos os documentos presentes no índice escolhido, por isso, provavelmente, é um valor maior do que o esperado. Por causa do tipo da agregação que vem por padrão. 89 | 90 | ![image](https://user-images.githubusercontent.com/26297247/62156921-5df1ee00-b2e2-11e9-9698-94d3ecde6c23.png) 91 | 92 |    À esquerda da visualização temos umas opções de edição onde podemos escolher o tipo da agregação que queremos: **contador, média, máximo, mínimo, porcentagem, dentre outras**. 93 | 94 | ![image](https://user-images.githubusercontent.com/26297247/62156827-25eaab00-b2e2-11e9-9ee1-82d29234112f.png) 95 | 96 |    Os documentos são compostos por todos as interações dos usuários com o bot. Logo, um único usuário terá mais de um documento armazenado no índice, então temos que fazer uma contagem única por usuário na agregação, **Unique Count**. 97 | 98 |    Ao escolher a opção de **unique count** aparecerá abaixo do aggregation uma opção de **field**, nela estarão listados todos os atributos de um documento. Escolha então o campo de user_id e clique no botão de **Apply Changes**, seta na barra de opções, ou aperte CTRL+ENTER para as modificações serem aplicadas. 99 | 100 | ![image](https://user-images.githubusercontent.com/26297247/62157363-5bdc5f00-b2e3-11e9-85ef-1102c7da6f6e.png) 101 | 102 |    Após a aplicação das mudanças o Kibana já irá te retornar o valor da quantidade de usuários no período de tempo definido no canto superior direito (extremo total). 103 | 104 | ![image](https://user-images.githubusercontent.com/26297247/62157612-f5a40c00-b2e3-11e9-8b1f-4bd558d8280a.png) 105 | 106 |    Caso deseje mudar a legenda do dado, escreva o texto na opção de **Custom Label** da agregação. 107 | 108 | ![image](https://user-images.githubusercontent.com/26297247/62158063-0012d580-b2e5-11e9-9836-58c31d4d66e4.png) 109 | 110 |    Agora vamos salvar a visualização para posteriormente adicionarmos a um dashboard. Clique na opção **Save** na barra superior. Escolha um nome para a visualização e salve. 111 | 112 | ![image](https://user-images.githubusercontent.com/26297247/62157962-b9bd7680-b2e4-11e9-80d1-668eb030550b.png) 113 | 114 | #### Usuários por dia 115 | 116 |    A criação da visualização anterior foi utilizada para introduzir alguns termos e algumas ações que podemos tomar dentro do Kibana, agora iremos um pouco mais rápido. 117 | 118 |    Continuaremos no **Visualize** e vamos escolher agora uma visualização que nos auxilie a apresentar corretamente o nosso dado. Então precisaremos mostrar em um eixo a quantidade de usuários e em outro eixo os dias. Poderíamos então utilizar o Line, Area, Vertical Bar, Horizontal Bar, entre outros. 119 | 120 |    No tutorial iremos usar o **Vertical Bar**, gráfico representado em duas dimensões e que possui uma análise mais fácil, tendo em vista que a comparação da quantidade de usuários pelos dias é feito somente pela visualização da dimensão do eixo Y, quanto maior, mais usuários possui. 121 | 122 |    Nas opções de configuração da visualização teremos em **Metrics** o nosso eixo Y e logo abaixo, em **Buckets**, o eixo X. 123 | 124 | * **Eixo Y:** Representará a quantidade de usuários 125 | 126 | * **Eixo X:** Representará o espaço de tempo (dia, semana, mês ...). 127 | 128 |    Então vamos aplicar o mesmo conhecimento da visualização anterior, Total de usuários. Na configuração do eixo Y iremos fazer um contador unico dos usuários, selecionando o campo do **user_id**. 129 | 130 | ![image](https://user-images.githubusercontent.com/26297247/62159648-98f72000-b2e8-11e9-9bfa-992c3b901072.png) 131 | 132 |    No campo do **Buckets** iremos criar o nosso eixo X e escolher uma agregação para o gráfico que entenda o nosso campo de tempo (**timestamp**) e consiga aplicar determinados filtros para intervalo de tempo. A agregação mais adequada é o **Date Histogram**. 133 | 134 | ![image](https://user-images.githubusercontent.com/26297247/62159784-ebd0d780-b2e8-11e9-9121-7a561f509f69.png) 135 | 136 |    Você pode deixar o intervalo de tempo como **Auto** ou definir um intervalo fixo (semanalmente, diáriamente, etc). Escolheremos o **daily** e aplicaremos as mudanças. 137 | 138 | ![image](https://user-images.githubusercontent.com/26297247/62159976-52ee8c00-b2e9-11e9-8847-b6a1d8aee010.png) 139 | 140 |    Agora basta salvar como uma nova visualização e começar a brincar e fazer outras. =D 141 | 142 | ### Métricas de desenvolvimento 143 | 144 | #### Perguntas não entendidas 145 | 146 |    Nesta etapa vamos aprender um pouco mais de como utilizar o **Discover**. 147 | 148 |    Essa métrica pode ser entendida como uma número bruto sobre a quantidade de perguntas não entendida, ou então, mostrar os textos das perguntas que realmente o bot não entendeu. Para fazer a primeira opção, os tutoriais anteriores dão uma boa base. Agora vamos implementar a segunda opção. 149 | 150 |    Quando acessamos o **Discover** caímos diretamente em uma página que nos lista todos os documentos e seus atributos separadamente. 151 | 152 | ![image](https://user-images.githubusercontent.com/26297247/62163352-8c76c580-b2f0-11e9-9201-41d9fee2ed97.png) 153 | 154 |    Por _default_, mesmo não estando adicionados, todos os atributos são listados na visualização. Porém, podemos escolher quais queremos separadamente. Basta passar o mouse em cima do campo desejado e clicar no botão de **ADD**. Aparecendo logo acima como **Selected fields**. 155 | 156 | ![image](https://user-images.githubusercontent.com/26297247/62163587-0b6bfe00-b2f1-11e9-95c6-893ebf7afa73.png) 157 | 158 |    Agora temos todos os documentos que possui o atributo is_fallback. Porém, como definido na configuração do índice do Elastic, todos os documentos, mesmo não sendo um _fallback_ tem o atributo. Então precisamos aplicar um filtro para que tenhamos somente as mensagens que o bot não compreendeu. 159 | 160 |    No campo esquerdo superior tem a opção de **Add filter +**, no qual nos permite aplicarmos uma query de filter na busca que estamos realizando, que no caso é saber qual é mensagem caiu no _fallback_ ou não. 161 | 162 | ![image](https://user-images.githubusercontent.com/26297247/62163868-acf34f80-b2f1-11e9-9c0c-0c4fca1c2e72.png) 163 | 164 |    Você mesmo pode programar uma query clicando na opção de **Edit query SDL**. Mas o Kibana já nos fornece uma funcionalidade que ele entende o tipo do atributo e fornece uma interface para aplicar condições e filtros. Então escolhemos o atributo que queremos, que é o que estamos realizando a busca **is_fallback**, falamos que queremos aplicar o filtro quando ele for **True**, ou seja, faremos uma busca somente nos documentos que possuem um campo **is_fallback = True**. Colocando a opção do meio como **is** e a última opção como **True**. 165 | 166 | ![image](https://user-images.githubusercontent.com/26297247/62164404-baf5a000-b2f2-11e9-9996-5e50c406df06.png) 167 | 168 |    Agora já estamos com o filtro aplicado, so que não temos o texto das mensagens, então precisamos adicionar mais um atributo aos campos selecionados. Escolhemos o campo **Text**, e XABLAU, apareceram todas as mensagens que caíram no Fallback. 169 | 170 | ![image](https://user-images.githubusercontent.com/26297247/62164544-fb551e00-b2f2-11e9-97d9-887d04307b44.png) 171 | 172 | 173 |    Salvamos como uma nova visualização para finalizar. 174 | 175 | ### Dashboards 176 | 177 |    Um dashboard exibe uma coleção de visualizações, pesquisas e mapas. Você pode organizá-lo a seu gosto. 178 | 179 | ### Como unir as informações em um único lugar 180 | 181 |    Criaremos então um Dashboard na funcionalidade de **Dashboard** localizada na barra da lateral esquerda principal. 182 | 183 | ![image](https://user-images.githubusercontent.com/26297247/62164836-833b2800-b2f3-11e9-8464-9093ec567e82.png) 184 | 185 |    Após criar, você será redirecionado para uma página que mostrará uma mensagem dizendo que o dashboard está vazio, então temos que adicionar as visualizações que criamos. Vamos clicar na opção **Add** da barra superior. 186 | 187 | ![image](https://user-images.githubusercontent.com/26297247/62164950-bed5f200-b2f3-11e9-9dd6-5c046a6ca196.png) 188 | 189 |    As visualizações que criamos no **Visualize** estão listadas na primeira aba, **Visualization** quando clicamos no **Add**. Ela já aparece por default como selecionada. Então escolha as visualizações que deseja, dentre as que estarão listadas. Repita isso para todas as visualizações que deseja adicionar. 190 | 191 | ![image](https://user-images.githubusercontent.com/26297247/62165163-39067680-b2f4-11e9-80a8-35d4fdf576eb.png) 192 | 193 |    Agora para adicionar a visualização que criamos no **Discover** você terá que clicar novamente no **Add**, só que ir na segunda aba, **Saved Search**. Lá estarão listadas as querys que já salvas ou então as visualizações do **Discover**. Selecione a que deseja e pronto. 194 | 195 | ![image](https://user-images.githubusercontent.com/26297247/62165354-9ef2fe00-b2f4-11e9-8c67-3539dfe1fabf.png) 196 | 197 |    Agora basta salvar o Dashboard e desfrutar dos dados!!! 198 | 199 | ### Extras 200 | 201 |    Você pode compartilhar os dashboards como iframes caso deseja. Opção disponível na barra superior dentro do dashboard. 202 | 203 |    Caso queira se aprofundar um pouco mais, temos um vídeo sobre **Métricas para bots** no nosso canal do youtube [Lappis-unb](https://www.youtube.com/channel/UCbZvFMRd5NaPiqj0w4uU8RQ/featured). 204 | 205 | ## Referências 206 | 207 | [1] Storytelling com dados, um guia sobre visualização de dados para profissionais de negócios. Cole Nussbaumer Knaflic. 208 | 209 | [-] [Métricas para bot](https://github.com/lappis-unb/tais/wiki/Estudo-sobre-metricas-para-bots). 210 | -------------------------------------------------------------------------------- /modules/notebooks/stories/stories-analysis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "kO9wt2g3okLS" 8 | }, 9 | "source": [ 10 | "# Análise das Stories\n", 11 | "\n", 12 | "Notebook para o auxílio da análise das stories do chatbot." 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "### Configurando e Imports" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "Rasa: 1.4.1\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "from IPython.display import IFrame\n", 37 | "\n", 38 | "import rasa\n", 39 | "print(\"Rasa: {}\".format(rasa.__version__))" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": { 45 | "colab_type": "text", 46 | "id": "tK5os3OinphP" 47 | }, 48 | "source": [ 49 | "## Análise e Avaliação das Stories" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": { 55 | "colab_type": "text", 56 | "id": "qWALQbCdwGxK" 57 | }, 58 | "source": [ 59 | "### Visualizaçõa do Fluxo de Conversa\n", 60 | "\n", 61 | "O comando abaixo monta um grafo com a relação das `intents` e `utters` do chatbot, ou seja, você vai conseguir visualizar o fluxo de conversa do seu chatbot.\n", 62 | "\n", 63 | "Esta visualização é importante para verificar possíveis problemas na estrutura do seu chatbot e se ele realmente chega em determinadas \"**falas**\" da conversa." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 2, 69 | "metadata": {}, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": [ 75 | "2019-10-23 01:04:23 \u001b[1;30mINFO \u001b[0m \u001b[34mabsl\u001b[0m - Entry Point [tensor2tensor.envs.tic_tac_toe_env:TicTacToeEnv] registered with id [T2TEnv-TicTacToeEnv-v0]\n", 76 | "2019-10-23 01:04:23 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.visualize\u001b[0m - Starting to visualize stories...\n", 77 | "Processed Story Blocks: 100%|███| 64/64 [00:00<00:00, 5932.80it/s, # trackers=1]\n", 78 | "Traceback (most recent call last):\n", 79 | " File \"/usr/local/bin/rasa\", line 8, in \n", 80 | " sys.exit(main())\n", 81 | " File \"/usr/local/lib/python3.6/site-packages/rasa/__main__.py\", line 76, in main\n", 82 | " cmdline_arguments.func(cmdline_arguments)\n", 83 | " File \"/usr/local/lib/python3.6/site-packages/rasa/cli/visualize.py\", line 38, in visualize_stories\n", 84 | " args.config, args.domain, args.stories, args.nlu, args.out, args.max_history\n", 85 | " File \"uvloop/loop.pyx\", line 1417, in uvloop.loop.Loop.run_until_complete\n", 86 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/visualize.py\", line 53, in visualize\n", 87 | " stories_path, output_path, max_history, nlu_training_data=nlu_data_path\n", 88 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/agent.py\", line 805, in visualize\n", 89 | " fontsize,\n", 90 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/training/visualization.py\", line 577, in visualize_stories\n", 91 | " fontsize=fontsize,\n", 92 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/training/visualization.py\", line 486, in visualize_neighborhood\n", 93 | " _merge_equivalent_nodes(graph, max_history)\n", 94 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/training/visualization.py\", line 207, in _merge_equivalent_nodes\n", 95 | " graph, i, j, max_history\n", 96 | " File \"/usr/local/lib/python3.6/site-packages/rasa/core/training/visualization.py\", line 147, in _nodes_are_equivalent\n", 97 | " return graph.node[node_a][\"label\"] == graph.node[node_b][\"label\"] and (\n", 98 | "AttributeError: 'MultiDiGraph' object has no attribute 'node'\n" 99 | ] 100 | } 101 | ], 102 | "source": [ 103 | "# !python -m rasa_core.visualize -d $COACH_DOMAIN_PATH -s $COACH_STORIES_PATH -o chat_graph.html\n", 104 | "!rasa visualize --domain $DOMAIN_PATH --stories $DATA_PATH --config $CONFIG_PATH --out chat_graph.html" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "O Rasa gerou uma página `html` com o grafo de conversa, para facilitar, você pode visualizar o grafo no arquivo `chat_graph.html` aqui na próxima celula.\n", 112 | "\n", 113 | "Use o mouse para dar zoom e arrastar sobre o conteúdo do grafo. Altere `width` e `height` se desejar.\n", 114 | "\n", 115 | "* Dica: caso você se perca com **zoom in** ou **zoom out** no grafo, apeas re-execute a célula abaixo e ele irá reaparecer na célula." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 3, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "data": { 125 | "text/html": [ 126 | "\n", 127 | " \n", 134 | " " 135 | ], 136 | "text/plain": [ 137 | "" 138 | ] 139 | }, 140 | "execution_count": 3, 141 | "metadata": {}, 142 | "output_type": "execute_result" 143 | } 144 | ], 145 | "source": [ 146 | "IFrame(src='./chat_graph.html', width=900, height=700)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "### Avaliação das Stories\n", 154 | "\n", 155 | "Outra forma de analisar seu chatbot é por meio da própria avaliação do Rasa, ele gera uma matriz de confusão com os dados fornecidos nas `stories` e do resultado do treinamento armazenado na pasta `models`." 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "* Caso você ainda não tenha treinado seu chatbot execute a céclula abaixo para treina-lo." 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 4, 168 | "metadata": { 169 | "scrolled": true 170 | }, 171 | "outputs": [ 172 | { 173 | "name": "stdout", 174 | "output_type": "stream", 175 | "text": [ 176 | "\u001b[92mNothing changed. You can use the old model stored at '/work/notebooks/stories/models/20191023-005105.tar.gz'.\u001b[0m\r\n" 177 | ] 178 | } 179 | ], 180 | "source": [ 181 | "!rasa train --config $CONFIG_PATH --domain $DOMAIN_PATH --data $DATA_PATH" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 5, 187 | "metadata": { 188 | "scrolled": true 189 | }, 190 | "outputs": [ 191 | { 192 | "name": "stdout", 193 | "output_type": "stream", 194 | "text": [ 195 | "2019-10-23 01:04:37 \u001b[1;30mINFO \u001b[0m \u001b[34mabsl\u001b[0m - Entry Point [tensor2tensor.envs.tic_tac_toe_env:TicTacToeEnv] registered with id [T2TEnv-TicTacToeEnv-v0]\n", 196 | "Processed Story Blocks: 100%|███| 64/64 [00:00<00:00, 4843.31it/s, # trackers=1]\n", 197 | "2019-10-23 01:04:37 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - Evaluating 64 stories\n", 198 | "Progress:\n", 199 | "100%|███████████████████████████████████████████| 64/64 [00:01<00:00, 48.47it/s]\n", 200 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - Finished collecting predictions.\n", 201 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - Evaluation Results on CONVERSATION level:\n", 202 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tCorrect: 64 / 64\n", 203 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tF1-Score: 1.000\n", 204 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tPrecision: 1.000\n", 205 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tAccuracy: 1.000\n", 206 | "2019-10-23 01:04:38 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tIn-data fraction: 1\n", 207 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - Evaluation Results on ACTION level:\n", 208 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tCorrect: 238 / 238\n", 209 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tF1-Score: 1.000\n", 210 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tPrecision: 1.000\n", 211 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tAccuracy: 1.000\n", 212 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tIn-data fraction: 1\n", 213 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.core.test\u001b[0m - \tClassification report: \n", 214 | " precision recall f1-score support\n", 215 | "\n", 216 | " utter_filme 1.00 1.00 1.00 2\n", 217 | " utter_como_estou 1.00 1.00 1.00 2\n", 218 | " utter_playlist 1.00 1.00 1.00 2\n", 219 | " utter_linguagens 1.00 1.00 1.00 2\n", 220 | " utter_cumprimentar 1.00 1.00 1.00 29\n", 221 | " utter_diga_mais 1.00 1.00 1.00 1\n", 222 | " utter_tudo_bem 1.00 1.00 1.00 2\n", 223 | " utter_risada 1.00 1.00 1.00 2\n", 224 | " utter_piada 1.00 1.00 1.00 2\n", 225 | " utter_comida 1.00 1.00 1.00 2\n", 226 | " utter_de_onde_voce_eh 1.00 1.00 1.00 2\n", 227 | " utter_afirmacao_botao 1.00 1.00 1.00 1\n", 228 | " utter_onde_voce_mora 1.00 1.00 1.00 2\n", 229 | " utter_botao 1.00 1.00 1.00 2\n", 230 | " utter_religiao 1.00 1.00 1.00 2\n", 231 | " utter_historia 1.00 1.00 1.00 2\n", 232 | " utter_time 1.00 1.00 1.00 2\n", 233 | " utter_bff 1.00 1.00 1.00 2\n", 234 | " utter_license 1.00 1.00 1.00 2\n", 235 | " utter_default 1.00 1.00 1.00 1\n", 236 | " utter_hobby 1.00 1.00 1.00 2\n", 237 | " action_test 1.00 1.00 1.00 2\n", 238 | " utter_o_que_sei_falar 1.00 1.00 1.00 2\n", 239 | " utter_me 1.00 1.00 1.00 2\n", 240 | " utter_signo 1.00 1.00 1.00 2\n", 241 | " utter_despedir 1.00 1.00 1.00 3\n", 242 | " utter_negacao_botao 1.00 1.00 1.00 1\n", 243 | " action_listen 1.00 1.00 1.00 94\n", 244 | " utter_cor 1.00 1.00 1.00 2\n", 245 | " utter_relationship 1.00 1.00 1.00 2\n", 246 | " utter_genero 1.00 1.00 1.00 2\n", 247 | "utter_continuar_conversa 1.00 1.00 1.00 50\n", 248 | " utter_elogios 1.00 1.00 1.00 2\n", 249 | " utter_filhos 1.00 1.00 1.00 2\n", 250 | " utter_triste 1.00 1.00 1.00 2\n", 251 | " utter_star_wars 1.00 1.00 1.00 2\n", 252 | " utter_esporte 1.00 1.00 1.00 2\n", 253 | "\n", 254 | " micro avg 1.00 1.00 1.00 238\n", 255 | " macro avg 1.00 1.00 1.00 238\n", 256 | " weighted avg 1.00 1.00 1.00 238\n", 257 | "\n", 258 | "2019-10-23 01:04:39 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Confusion matrix, without normalization: \n", 259 | "[[94 0 0 ... 0 0 0]\n", 260 | " [ 0 2 0 ... 0 0 0]\n", 261 | " [ 0 0 1 ... 0 0 0]\n", 262 | " ...\n", 263 | " [ 0 0 0 ... 2 0 0]\n", 264 | " [ 0 0 0 ... 0 2 0]\n", 265 | " [ 0 0 0 ... 0 0 2]]\n", 266 | "2019-10-23 01:04:48 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Running model for predictions:\n", 267 | "100%|████████████████████████████████████████| 588/588 [00:01<00:00, 313.21it/s]\n", 268 | "2019-10-23 01:04:50 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Intent evaluation results:\n", 269 | "2019-10-23 01:04:50 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Intent Evaluation: Only considering those 588 examples that have a defined intent out of 588 examples\n", 270 | "2019-10-23 01:04:50 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Classification report saved to results/intent_report.json.\n", 271 | "2019-10-23 01:04:50 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Incorrect intent predictions saved to results/intent_errors.json.\n", 272 | "2019-10-23 01:04:51 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Confusion matrix, without normalization: \n", 273 | "[[ 0 0 0 ... 0 0 0]\n", 274 | " [ 0 6 0 ... 0 0 0]\n", 275 | " [ 0 0 1 ... 0 0 0]\n", 276 | " ...\n", 277 | " [ 0 0 0 ... 14 0 0]\n", 278 | " [ 0 0 0 ... 0 18 0]\n", 279 | " [ 0 0 0 ... 0 0 23]]\n", 280 | "2019-10-23 01:04:58 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Entity evaluation results:\n", 281 | "2019-10-23 01:04:58 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Evaluation for entity extractor: CRFEntityExtractor \n", 282 | "2019-10-23 01:04:58 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Classification report for 'CRFEntityExtractor' saved to 'results/CRFEntityExtractor_report.json'.\n", 283 | "2019-10-23 01:04:58 \u001b[1;30mINFO \u001b[0m \u001b[34mrasa.nlu.test\u001b[0m - Incorrect entity predictions saved to results/CRFEntityExtractor_errors.json.\n" 284 | ] 285 | } 286 | ], 287 | "source": [ 288 | "!rasa test --stories $DATA_PATH --nlu $DATA_PATH" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "O resultado do comando pode ser visto na pasta `results/`:" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 6, 301 | "metadata": {}, 302 | "outputs": [ 303 | { 304 | "name": "stdout", 305 | "output_type": "stream", 306 | "text": [ 307 | "CRFEntityExtractor_errors.json\tfailed_stories.md intent_report.json\r\n", 308 | "CRFEntityExtractor_report.json\thist.png\t story_confmat.pdf\r\n", 309 | "confmat.png\t\t\tintent_errors.json\r\n" 310 | ] 311 | } 312 | ], 313 | "source": [ 314 | "!ls results" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "Caso algum problme seja encotrado ele será descrito no arquivo `failed_stories.md`" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": 7, 327 | "metadata": {}, 328 | "outputs": [ 329 | { 330 | "name": "stdout", 331 | "output_type": "stream", 332 | "text": [ 333 | "" 334 | ] 335 | } 336 | ], 337 | "source": [ 338 | "!cat results/failed_stories.md" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "* Se tudo estiver correto você deverá ver a mensagem:\n", 346 | "\n", 347 | "``" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "* Outro arquivo gerado é o `story_confmat.pdf` uma matriz de confusão onde é possível visualizar a relação entre as `utters` (mensagens enviadas ." 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": 8, 360 | "metadata": { 361 | "scrolled": true 362 | }, 363 | "outputs": [ 364 | { 365 | "data": { 366 | "text/html": [ 367 | "\n", 368 | " \n", 375 | " " 376 | ], 377 | "text/plain": [ 378 | "" 379 | ] 380 | }, 381 | "execution_count": 8, 382 | "metadata": {}, 383 | "output_type": "execute_result" 384 | } 385 | ], 386 | "source": [ 387 | "IFrame(\"./results/story_confmat.pdf\", width=900, height=900)" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "## Referências:\n", 395 | "\n", 396 | "O Rasa está em constante evolução, alguns links úteis para a construção deste jupyter-notebook e para a análise das `stories` são:\n", 397 | "\n", 398 | "* [Evaluation](https://rasa.com/docs/core/evaluation/)\n", 399 | "* [Debugging](https://rasa.com/docs/core/debugging/)" 400 | ] 401 | }, 402 | { 403 | "cell_type": "code", 404 | "execution_count": null, 405 | "metadata": {}, 406 | "outputs": [], 407 | "source": [] 408 | } 409 | ], 410 | "metadata": { 411 | "colab": { 412 | "collapsed_sections": [ 413 | "y4miuS-TqYcn", 414 | "BBF6Nqi9scQE", 415 | "Fs3nOUzBsqrG", 416 | "5MnGuFRpzzBh" 417 | ], 418 | "default_view": {}, 419 | "name": "Building a Simple Bot with Rasa Stack - Tutorial", 420 | "provenance": [ 421 | { 422 | "file_id": "1GutDkDXmfU-nRzNH7Pxxx8YpdvLUw9LO", 423 | "timestamp": 1521183725373 424 | } 425 | ], 426 | "toc_visible": true, 427 | "version": "0.3.2", 428 | "views": {} 429 | }, 430 | "kernelspec": { 431 | "display_name": "Python 3", 432 | "language": "python", 433 | "name": "python3" 434 | }, 435 | "language_info": { 436 | "codemirror_mode": { 437 | "name": "ipython", 438 | "version": 3 439 | }, 440 | "file_extension": ".py", 441 | "mimetype": "text/x-python", 442 | "name": "python", 443 | "nbconvert_exporter": "python", 444 | "pygments_lexer": "ipython3", 445 | "version": "3.6.9" 446 | } 447 | }, 448 | "nbformat": 4, 449 | "nbformat_minor": 1 450 | } 451 | --------------------------------------------------------------------------------