形態素解析を使ったchatbotをvscode上で実装

## はじめに
* DialogflowやBotpressのような**シナリオ型**のchatbot
* あいさつをしたらあいさつを返してくれるような**辞書型**のchatbot
* **openaiのAPIキー**を取得したchatbot
簡易的なものですがそれらをvscode上で一つにして実装しました。

Pythonとvscode自体のインストールと、Pythonの基本的なコードの説明は割愛させていただきます。

## システム要件

今回実装した機能はこちらになります。
* **シナリオ型**
日記の作成をしてくれる
お店や旅行の予約をしてくれる
* **辞書型**
あいさつしたらあいさつを返してくれる
天気を聞いたら天気を返してくれる
* **openaiの呼び出し**
対応しない会話に対して応答してくれる

**以下がデモンストレーションになります。**
![デモンストレーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206726/fac5e8f4-9c14-3032-7d26-6589311b329b.png)

## システム構成
システム構成は以下になります。
* **my_chat_bot** (プロジェクト)
　｜ ・scenario
　｜　　｜blog.py
　｜　　｜booking.py
　｜　　｜res.py
　｜ ・venv(仮想環境)
　｜ gpt2.py
　｜ main.py
　｜ my_chat_bot.py
　｜ weather.py

下記が完成時イメージになります。
![システム構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206726/3f962a7e-11d7-e1ef-ea52-55545a1340b0.png)

## 解説
main.py、my_chat_bot.py、gpt2.pyとscenarioの中のblog.py、booking.py、res.pyについて解説し、その他の解説は割愛させていただきます。

まずは、仮想環境の準備と必要なライブラリをインストールするためにコマンドの実行をしていきます。下記のコマンドをコマンドプロンプトでルートディレクトリ直下で順に実行してください。
```
# 仮想環境の準備
python.exe -m virtualenv venv
.\venv\Scripts\activate

# 必要なライブラリ
python.exe -m pip install -U pip
python.exe -m pip install mecab-python3 unidic-lite
python.exe -m pip install openai
python.exe -m pip install sentence-transformers # ←今回は使わない
```
必要なものは以上ですが、各自足りないものはインストールしてください。

続いてmain.pyの解説です。コードとコメントを合わせてお読みください。
```Python:main.py
# 必要なライブラリ、ファイルのimport
from my_chat_bot import respond_to_input
import scenario.res as re
import scenario.blog as sb
import scenario.booking as bk
import MeCab
import random

# res.pyから辞書型のメッセージを呼び出し、受け取る
responses = re.response()

# 分かち書きで入力されたメッセージを単語ごとに分割する関数の定義
def get_words(text):
    words = MeCab.Tagger("-Owakati").parse(text).split()
    return words

print("Welcome to my chatbot!")

# チャットボット
while True:
    user_input = input("You: ") # メッセージの受け取り
    
    if user_input in re.bye: # res.pyからbyeリストを呼び出し、
        print("またね！")    # そのリストにメッセージがあったら会話を終了する
        break
    else:
        prompt = tuple(get_words(user_input)) # 単語分割する関数を呼び出し
        flag = True # openaiを呼び出すかの判断をするためのもの
        for i in prompt:
            if i in responses:  # 辞書型の会話、辞書と一致する
                flag = False    # 単語が含まれていたらそれを返す
                bot_response = random.choice(responses[i])
                print("AI:", bot_response)
            elif i in sb.blog: # シナリオ型の会話、blogリストに
                flag = False   # 含まれてたら日記の作成を始める
                create = sb.making_blog() # 日記作成の関数呼び出し
                for b in create: # 作成された日記の出力
                    print(b)
            elif i in bk.bk:  # シナリオ型の会話、bkリストに
                flag = False  # 含まれてたら予約の開始をする
                reserve = bk.booking() # 予約の関数の呼び出し
                for r in reserve:
                    print(r) # 予約の確認の出力
                
        if flag: # シナリオ、辞書とマッチしなかった時に
            try: # flagがTrueのままとなりopenaiを呼び出す
                response = respond_to_input(user_input)
                print(f"AI: {response}")
            except Exception: # 認識できなかった時のエラー対応
                print(random.choice(re.no))
```
以後同上、コードとコメント合わせてお読みください。
次に、my_chat_bot.pyの解説です。openaiを呼び出すためのファイルです。
```Python:my_chat_bot.py
# ファイル先の関数のimport
from gpt2 import generate_response

# main.pyから呼び出される関数
# 入力されたメッセージをopenaiに送る
def respond_to_input(user_input):
    prompt = user_input
    response = generate_response(prompt)
    return response
```
今回はopenaiの処理だけをしているが、main.pyでしているシナリオ型の会話と辞書型の会話もこのファイルを通して行い、main.pyのコードをもっとスッキリさせられるのが望ましいと思われるが製作時間がなかったためこのようにさせていただきます。
次に、gpt2.pyの解説です。
```Python:gpt2.py
# 必要なライブラリのimport
import openai
import os

# 環境変数にopenaiのAPIkeyを設定し取得
openai.api_key = os.environ.get('OPEN_AI_KEY')
# 設定が分からない場合は直書きでも可能ですが、漏洩に注意！
# イメージ: openai.api_key = "xxxxx... "

# openaiに問い合わせを送信し、応答を取得する関数
def generate_response(prompt):
    response = openai.Completion.create(   
        engine="davinci",  # 使用するOpenAIのモデルを指定
        prompt=prompt,     # 生成された文章の前に与えるテキストを指定
        temperature=0.7,   # 生成されたテキストの多様性を制御
        max_tokens=2000,   # 生成されたテキストの長さを制御
        n=1,               #生成された文章の数を指定
        stop=["。", "?", "!"],  # 生成された文章が終了するトークンを指定
        frequency_penalty=0.9,  # 生成された文章で使用される単語の頻度を制御
        presence_penalty=0.9,   # 生成された文章で使用される単語の重複を制御
    )
    
    # メッセージの調整
    # 今回はopenaiの応答があまりうまくいかなかったため、
    # ある程度の調整をしました。
    text = response.choices[0].text.strip()
    res_text = ""
    replace = ["「","」","　"," ","[","]"]
    for t in text:
        if t not in replace:
            res_text += t
    res_text.replace('\n', '')
    
    return res_text
```
次に、blog.pyの解説です。
```Python:blog.py
# ファイルのimport
from weather import weather_get

# 日記作成を開始する判断基準の単語リスト
blog = ["日記","思い出","出来事"]

# 日記作成をしてくれる関数
def making_blog():
    print("何月何日の日記ですか？")
    date = "日付:"+input("You:")
    
    if date == "日付:今日":
        weather = "天気:"+weather_get()
    else:
        weather = input("その日の天気を教えて。\nYou:")
    
    event = input("今日の出来事を教えて。\nYou:")
    
    # 入力をリスト化して保存し、それを返す
    blog = ["", date, "-"*20, weather, "-"*20,event]
    return blog
```
次に、booking.pyの解説です。
```Python:booking.py
# 予約を開始する判断基準の単語リスト
bk = ["予約","行きたい","レストラン","お店"]

# 予約をしてくれる関数
def booking():
    print("予約を開始します。")
    yn = input("店名、場所名を教えて。：")
    time = input("時間を教えて。")
    name = input("名前を教えて。")
    num = input("何名様ですか。")
    
    print("予約が完了致しました。")
    # 入力をリスト化して保存し、それを返す
    res = [yn,"-"*20,time,"-"*20,name,"-"*20,num]
    
    return res
```
次に、res.pyの解説です。
```Python:res.py
# ファイルのimport
from weather import weather_get

# 辞書型の会話の応答をしてくれる辞書を返す関数
def response():
    res = {
        "こんにちは": ["こんにちは！", "やあ！", "こんにちは。"],
        "おはよう": ["おはようございます！", "おはよう！", "朝だー！"],
        "こんばんは": ["こんばんは！", "こんばんはー！", "夜だー！"],
        "ありがとう": ["どういたしまして！", "いいえ、こちらこそ！", "何でもおっしゃってください。"],
        "天気": [weather_get()+"です。"], #天気についてはAPIを取得しています
    }
    return res

# 会話(プログラム)を終了する判断をするメッセージのリスト
bye = ["バイバイ","さようなら","またね"]
# openaiのエラーに対する応答のリスト
no = ["理解できませんでした。","理解不能！","もう一回言ってくれますか？"]
```

コードの解説は以上になります。
天気のAPIに関してはおまけになりますので、最後に参考リンク先を記載のみにさせていただきます。
**以下ソースコードになります。**
[GitHubリポジトリ:my_chat_bot](https://github.com/ishizukuma/my_chat_bot.git)

## まとめ
今回は、形態素解析を使ってchatbotの実装をしました。
各それぞれの機能の実装が大方できたが、openaiの応答は自然な会話をしてくれなかった。次回の開発は、反省点を踏まえて改善されたchatbotを実装したいと思います。
コマンドで少し触れましたが、sentence_transformersライブラリを使って近似値の取得をした判断を取り入れると、さらに自然な会話になると思われます。

## 最後に
**参考にさせていただいたリンク先です。**
* openaiのAPIkeyの取得方法と実装
[Webアプリケーションから簡単にOpenAIのGPTモデルを使用してテキスト生成する方法](https://qiita.com/abenben/items/cf01407700f6470be15d)
* 天気のAPIの取得
[【Python】気象庁API から天気予報を取得してみた](https://qiita.com/ehjivh/items/121afaecad59a7e11c61)
* 続いて、授業をしてくださった**講師の方の紹介**です。
**谷一博之様**
[AIジョブカレ_人工知能技術専門プログラミングスクール](https://www.aijobcolle.com/teach/taniichi/)

**お礼の言葉**
最後までお読みいただきありがとうございました！