サーバーでの大規模処理の通知をGPT使って面白おかしくしてみた

　機械学習やデータ分析でサーバーで何日かかかるの動かすなんてことは日常茶飯事でしょう．そんな時に進捗などをメールで簡単に知らせてくれるよう大体のサーバーなどでは設定されていることかと思います．
　ただ，そんなサーバーから送られてくるメールは事務的でつまらないものが多いです．そんな，つまらないメールを面白くするためにGPTを使って遊んでみようという趣旨です．






# この記事で伝えたいこと　

- 長時間かかるプロジェクトの進行状況をメールで通知する方法．
- OpenAIのGPTを活用して，進行状況に応じたユーモラスなコメントを自動生成する方法．
- Pythonで実装した状況報告の自動化システムを詳細に説明．

# 解決したい課題

　終わってからこんなはずじゃなかったを防止するためにも，大規模なデータの処理には進行状況の追跡が以外に重要だったりします．そのために．サーバーからメールが送られてきたりするのですが，この送られてくるつまらないメールをなんとかしたい！
　そこで，OpenAIからもらったクレジットが少し残っていたので，つまらないメールをOpenAIのGPTを使ってなんとかしていきます．
　
　ちょっと面白くなるだけで，チーム内で話が盛り上がったりして雰囲気が良くなります．
　実際にどんな感じでメールが変わったのかみてみましょう．

## サーバーから送られてくるようなメール
　こんな事務的なメールでは楽しくありません．
　あーなんだ動いたんだ，くらいの感じでしかありません．別にいいっちゃいいんですが，これで納得しては話が終わってしまうのでGPTを使ったものを見てみましょう．

![スクリーンショット_2023-07-14_19_12_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235810/25e0b1dd-4a81-1854-38e9-d035d6b8bb32.png)



## 今回変えてみたメール
　やってることが違うので完全に同じメールとなんとかしたというわけではないのです．
　ただ，進行状況の情報（経過時間，処理済，未処理データの数など）を使って面白いコメントを生成して送ってもらうように変えています．

　なんやかんや絵文字も使ってるあたりがすごいなと思います．

　あと，深夜のおかしいテンションで作ってたものなので，タイポがすごいです．


![スクリーンショット_2023-07-14_19_23_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235810/465493ef-f724-b82f-c676-57660ca472bb.png)



# GPTを使って進行状況に合わせて通知を面白くする方法
　実際にGPTを使って進行状況に合わせて文言を作成して，送信する方法について解説します．

　GPT-3.5-turboとGmailを使用している関係で以下のものが必要になります．

- **OpenAIのAPI Key**
- **Googleのアプリパスワード**

## APIとアプリパスワードの準備
### OpenAPI Key
　OpenAPI Keyの取得方法は下記の記事が一番簡潔でわかりやすいと思います．
　ざっくりいうと，

**1. OpenAIのアカウント作成**
**2. ProfileのところからAPI作成**

の二段構成です．基本的には，そんなに詰まるところはないのですが，2段階認証の電話番号に届くメッセージを自動でMacに入れてもらってたら認識してくれなく，手打ちしたら認識してくれました．その程度くらいしか詰まるところはないと思います．

https://auto-worker.com/blog/?p=6988

### Googleのアプリパスワード
 Pythonのプログラムからメールを送るために今回はGmailを使います．そこで，Gmailのアプリパスワードが必要になります．
　メールサーバー立てなくても簡単にメールを送れるのは非常に便利ですようね．

　実際に，アプリパスワードの取得方法は以下のサイトが参考になります．

 ざっくりいうと，

**1. Googleアカウントで2段階認証を設定**
**2. アプリパスワードを取得す**

https://qiita.com/eito_2/items/ef77e44955e43f31ba78#smtpサーバに接続する


## ツリー
　プログラムのディレクトリ構造は以下のような感じです．

```
  |---main.py
  |---comment.py
  |---email.py
```




## OpenAIのGPTでコメントを生成するプログラム
　OpenAIのGPT-3.5-turboを使ってコメントを生成するプログラムの一例です．
　工夫している点といえば，何回か進捗状況に応じてメールを送ることを想定しているのでpromptをいくつか用意して，ランダムに選択するようにしました．

:::note warn
OpenAIのAPI Keyは環境変数として設定しておく必要があるので，注意してください．
```bash
export OPENAI_API_KEY="sk-******************"
```
:::



```python:comment.py
import openai
import random
import os

def create_comment(get_data_count:int, sum_count:int, category_name:str, elapsed_time:float) -> str:
  """_summary_

  Args:
      get_data_count (int): _description_
      sum_count (int): _description_
      category_name (str): _description_
      elapsed_time (float): _description_

  Returns:
      str: _description_
  """

  openai.api_key = os.environ["OPENAI_API_KEY"]

  # プロンプトのバリエーション
  prompts = [
      f"'{category_name}'カテゴリーのデータ{get_data_count}/{sum_count}件を処理し、それには{elapsed_time}秒かかりました。面白いコメントを教えてください！",
      f"進捗報告：'{category_name}'の{get_data_count}件のデータを{sum_count}件の中から処理しました。処理時間は{elapsed_time}秒です。楽しいコメントをつけてください！",
      f"これまでに'{category_name}'から{get_data_count}/{sum_count}件のデータを処理し、それに{elapsed_time}秒かかりました。面白い一言を教えてください！",
  ]

  # ランダムにプロンプトを選択
  prompt = random.choice(prompts)

  try:
    response = openai.ChatCompletion.create(
      model="gpt-3.5-turbo",
      messages=[
          {"role": "system", "content": "相手は遊び心あふれるエンジニアです"},
          {"role": "user", "content": prompt},
      ],
      temperature=0.9,
      max_tokens=1024
    )
    comment = response.choices[0]["message"]["content"].strip()

  except openai.error.RateLimitError:
    comment = "Rate limit exceeded. Waiting for 60 seconds before retrying."

  return comment
```

一応，クレジットがなくなったことを考えて，例外処理も追加しています．


## 進捗状況をメールで送るプログラム
　Gmailを使ってメールをpythonプログラムから送るためのソースです．
　工夫している点は特にないのです．


:::note warn
いくつか環境変数を設定しないといけないです．
```bash
export EMAIL_ACCOUNT ="アプリパスワードを取得したGmailのアカウント"
export EMAIL_PASSWORD = "アプリパスワード"

```
:::

```python:email.py
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email import policy
import smtplib
import os

def send_mail(content:str, title:str="", email:str="hogehoge@gmail.com") -> None:
    """_summary_
    
    Args:
        content (str): _description_
        title (str, optional): _description_. Defaults to "".
        email (str, optional): _description_. Defaults to "hogehoge@gmail.com".
    """
    
    account = os.environ["EMAIL_ACCOUNT"]
    password = os.environ["EMAIL_PASSWORD"]
    to_email = email
    from_email = account
    smtp_host = "smtp.gmail.com"
    smtp_port = 587
    
    subject = title
    msg = MIMEMultipart('alternative')
    msg["Subject"] = subject
    msg["To"] = to_email
    msg["From"] = from_email
    text = "This is Plain Message"
    html = content
    
    msg.attach(MIMEText(text, 'plain', policy=policy.default))
    msg.attach(MIMEText(html, 'html', policy=policy.default))
    
    # メールサーバーとの接続が終わったらそれを適切に閉じるために with ステートメントを使用．
    # これにより，接続は必要ない時点で自動的に閉じられ，リソースの無駄遣いを防ぐ．
    with smtplib.SMTP(smtp_host, smtp_port) as smtp:
        smtp.starttls()
        smtp.login(account, password)
        smtp.send_message(msg)
```

ここまで設定して，最後に`main.py`で呼びます！

## 呼び出して実際に送る`main.py`を作る

```python:main.py
import comment
import email

def main():
    get_data_count = 500
    sum_count = 1000
    category_name = "カテゴリー1"
    elapsed_time = 10.5

    comment = create_comment(get_data_count, sum_count, category_name, elapsed_time)

    content = """
        <html>
          <head></head>
          <body>
            <p>count : {get_data_count} / {sum}</p>
            <p>processing category : {category}</p>
            <p>elapsed time : {elapsed_time}</p>
            <p>ai messege : {comment}</p>
          </body>
        </html>
        """.format(get_data_count=get_data_count, sum=sum_medicine_count, category=category_name, elapsed_time=display_time, comment=comment)

    email.send_mail(content, title="件名：テスト", email="hogehoge@gmail.com")

if __name__ == '__main__':
    main()
```
これで，

```bash
python main.py
```

をやると，`email.send_mail(content, title="件名：テスト", email="hogehoge@gmail.com")`の`email`で指定したメールアドレスにメールが送信されます．


# 最後に
　今回進捗状況をコメントでしてみるということをやってみました．動的に進捗状況に合わせてコメントしてみると以外に面白いことがわかりました．
　意外に楽しかったので，記事にしてみました〜！