彼女を励ますために、Azure OpenAI Serviceでくまきち(kmakici)LINE botを作った #1

# はじめに
転職してからAzure OpenAI Serviceを使って、システム開発をしていくことが多くなりました。そこで、Azure OpenAI Serviceの使って何か作ってみることにしました（何かを創造することは、最大の勉強ですから）。

折角、作るなら人に役に立つ物をと思い「彼女を励ますチャットボット」を作成することにしました。

# 目次
- [くまきち(kmakici)とは？](#くまきちkmakiciとは)
- [システム構成](#システム構成)
- [利用サービス一覧](#利用サービス一覧)
- [構築手順](#構築手順)
    1. [Azure-openai-serviceの利用申請を出す](#azure-openai-serviceの利用申請を出す)
    2.  [LINE developersでkmakiciチャネルを作成する](#line-developersでkmakiciチャネルを作成する)
    3.  [Azure OpenAI Serviceのリソースを作成する](#azure-openai-serviceのリソースを作成する)
    4. [Azure Functionsへデプロイするコードを作成する](#azure-functionsへデプロイするコードを作成する)
    5. [Azure Functionsのリソースを作成する](#azure-functionsのリソースを作成する)
    6. [Azure Functionsへ必要な関数をデプロイする](#azure-functionsへ必要な関数をデプロイする)
    7. [関数のURLをLINE developersのWebhook URLへ設定する](#関数のurlをline-developersのwebhook-urlへ設定する)
   8. [LINE APP.で動作確認する](#line-appで動作確認する)
- [今後の計画](#今後の計画)



このチャットボットは何段階かVer.アップを計画しており、こちらの記事は第1弾になります。

# くまきち(kmakici)とは？
<img width="300" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/c085843d-98b2-4fc3-f4c3-0b753e1f57d0.png">


皆さん、このキャラクターご存じですかね？

昨今、じわじわと人気が出ているサブカル的なキャラクターで、たぶん、白熊です。独特な世界観がじわります（ハマるかは別）。ヴィレッジヴァンガードとコラボしてるので、サブカル界隈では結構有名みたいです。国内に熱狂的な伝道師がいて、私のパートナーがその一員です(笑)

https://www.youtube.com/channel/UC63ovoUyRs0DMfpKAjidyNQ


https://vvstore.jp/feature/detail/15405/


# システム構成
至って簡単な構成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/7328d2d9-03d1-bade-3b81-4f0b139ab79d.png)

LINEアプリからLINE Messaging APIのWebhook URLへWebhook URLイベントを送信します。その後Azure FunctionsへHTTPSリクエストを送信します。Azure Functionsに設定した関数から、Azure OpenAI Serviceを呼び出し、回答を得ます。後は、逆に辿ります。


Webhook URLの役割は以下です。
＜出典＞LINE Developers
> LINEプラットフォームから送信されたWebhookイベントを処理できるボットサーバーのエンドポイントのURLです。設定したURLがWebhookイベントを受け取るできることを確認するには「検証」ボタンをクリックします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/bf0e14bc-db55-1eb7-7e84-d7b6c8dae90e.png)


# 利用サービス一覧
|名前  |役割  |
|---|---|
|**Azure Functions** |LINE Messaging APIからHTTPSリクエストをトリガーとして、Azure OpenAI Service へプロンプトを送信する。同時に、Azure OpenAI Serviceからの回答をLINE Messaging APIへHTTPSポストする|
|**Azure OpenAI Service** |プロンプトに応じて、回答を生成する  |
|**LINE Developers** |● LINE Messaging APIを提供する <br>● チャネルアクセストークンを発行する<br>● チャネルシークレットを発行する|
|**LINE App.** |チャットボットのUI|
|**VSCode** |コードエディター。<br>Azure Functionsへの関数デプロイ|
|**Perplexity** |コーディングの先生<br>[Perplexity](https://www.perplexity.ai/)|

# 構築手順
大きな流れは以下です。

1. Azure OpenAI Serviceの利用申請を出す
1. LINE Developersでkmakiciチャネルを作成する
1. Azure OpenAI Serviceのリソースを作成する
1. Azure Functionsへデプロイするコードを作成する
1. Azure Functionsのリソースを作成する
1. Azure Functionsへ必要な関数をデプロイする
1. 関数のURLをLINE Developersの「Webhook URL」へ設定する
1. LINE App.で動作確認する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/d15efb6f-664d-0f0e-61ed-0d483930ef05.png)

各工程を説明してます。
※細かい点は省略します。参考にさせていただいた情報を出典として記載させていただきます。

## Azure OpenAI Serviceの利用申請を出す
以下のサイトから、利用申請を出してください。


https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUNTZBNzRKNlVQSFhZMU9aV09EVzYxWFdORCQlQCN0PWcu


以下の様なメールが届けば、Azure OpenAI Serviceのリソースを作れる様になった証です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/1ac86878-b1bf-adf7-1908-126136c7af79.png)



## LINE Developersでkmakiciチャネルを作成する
初めに、LINE Developersでチャットボット専用のチャネルを開設する必要があります。以下のサイトの手順でチャネルを開設しました。

https://developers.line.biz/ja/docs/messaging-api/getting-started/


チャネルが作成できたら、チャネル基本設定のページからチャネルシークレットをが表示されていることを確認してください。後で使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/b2cc6bf3-521d-d6ad-16f6-b47b419db659.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/c97c17d4-76ca-55f4-3216-27270a654420.png)

次にMessaging API設定タブから、チャネルアクセストークンを発行してください。右側の「発行（下図では再発行）」ボタンを押してください。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/f85e5379-738e-7257-7dbd-e58c23929038.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/cacc687f-17c7-718f-8c3b-609ef5bd7c95.png)

## Azure OpenAI Serviceのリソースを作成する
以下のサイトを参考に、リソースを作成し、Azure OpenAI Studioからモデルをデプロイします。

https://learn.microsoft.com/ja-JP/azure/ai-services/openai/how-to/create-resource?pivots=web-portal


Azure OpenAI Serviceのキーとエンドポイントページの以下の情報は後ほど使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/7fc9e27c-5315-a6a3-c086-a56063cd5f00.png)


また、Azure OpenAI Studioからデプロイしたモデル名も後ほど使います。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/1b4471ea-675f-7717-5a47-f02790966084.png)



## Azure Functionsへデプロイするコードを作成する
以下のサイトと、Perplexityを使ってbot用サーバーで動かすPythonプログラムを作成しました。


https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=python-v2%2Cisolated-process%2Cnodejs-v4%2Cfunctionsv2&pivots=programming-language-python

順を追って手順を説明します。

### VSCodeの準備
初めに、Azure Functionsの拡張子を導入します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/7c8b74ac-53a5-8969-3cef-02155222c729.png)

更に、Azure Toolsの拡張子を導入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/38c8d64b-17b4-a202-95fc-34c2c288c034.png)


### 関数の作成
次に以下の手順に従って、関数を作成します。

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-develop-vs-code?tabs=node-v4%2Cpython-v2%2Cisolated-process&pivots=programming-language-python#republish-project-files

関数作成のテンプレートを選ぶ段階では、**「HTTP trigger」のテンプレートを選びます**。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/c342452d-bcd7-7977-c59e-00397d69963c.png)


プロジェクトフォルダ内に作成された、**function_app.py**を以下に書き換えてください。過去の5ターン分の会話履歴を保持する作りにしています。

```Python
import azure.functions as func
import logging
import os
from openai import  AzureOpenAI

from linebot import (
    LineBotApi, WebhookParser)

from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage)

from dotenv import load_dotenv
load_dotenv()

# Azure OpenAIのパラメータ
# 自由に調整してください
parameters = {
    "temperature": 0.7,
    "top_p": 1,
    "frequency_penalty": 0,
    "presence_penalty": 0
}

# システムプロンプト
system_prompt = os.getenv("system_prompt")

# Azure OpenAIの応答を得るクラス
class OpenAIChatBot:
    def __init__(self, api_key, azure_endpoint, api_version, model_name="kmakici", system_prompt=system_prompt):
        self.client = AzureOpenAI(api_key=api_key, azure_endpoint=azure_endpoint, api_version=api_version)
        self.model_name = model_name
        self.messages = [{"role": "system", "content": system_prompt}]
        self.api_key=api_key
        self.api_version=api_version
        self.azure_endpoint=azure_endpoint

    def add_message(self, role, content):
        # 会話履歴が5を超えた場合、最も古いメッセージを削除
        if len(self.messages) >= 10:  # ユーザーとボットのメッセージを合わせて10になるため
            self.messages = self.messages[-10:]  # 最新の5回分の会話（ユーザーとボットのメッセージを合わせて10）を保持
        self.messages.append({"role": role, "content": content})

    def get_bot_response(self, user_prompt):
        self.add_message("user", user_prompt)
        response = self.client.chat.completions.create(
            model=self.model_name,
            temperature=parameters["temperature"],
            top_p=parameters["top_p"],
            frequency_penalty=parameters["frequency_penalty"],
            presence_penalty=parameters["presence_penalty"],
            messages=self.messages
        )
        bot_response = response.choices[0].message.content
        self.add_message("assistant", bot_response)
        return bot_response

    def reset_conversation(self):
        self.messages = []

# LINE Messaging APIの認証情報
channel_secret = os.getenv("channel_secret")
channel_access_token = os.getenv("channel_access_token")

configuration = LineBotApi(channel_access_token)
webhook_parser = WebhookParser(channel_secret)

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)
@app.route(route="http_trigger")
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    req_body = req.get_json()
    logging.info(req_body)

    #疎通確認用
    if not req_body["events"]:
        return func.HttpResponse("OK", status_code=200)

    #　Webhookのイベント取得に応じたアクション
    if req_body["events"][0]["type"] == "message":
        if req_body["events"][0]["message"]["type"] == "text":
            message = req_body["events"][0]["message"]["text"]
            reply_token = req_body["events"][0]["replyToken"]
            # Azure OpenAIの応答を取得
            chatbot = OpenAIChatBot(os.getenv("api_key"), os.getenv("azure_endpoint"), "2024-02-15-preview")
            #system_prompt = sys_prompt
            user_prompt = message
            result = chatbot.get_bot_response(user_prompt)
            # LINEのWebhookエンドポイントに応答を返す
            configuration.reply_message(reply_token, TextSendMessage(text=result))

    return func.HttpResponse("OK", status_code=200)
```

更に、プロジェクトフォルダ内の**requirements.txt**を以下の書き替えてください。

```
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues
azure-functions
python-dotenv==1.0.1
openai==1.17.1
line-bot-sdk==1.19.0
pydantic==2.6.4
```

さらに、プロジェクトフォルダ内に.envファイルを作成して以下を書き込んでください。

```
api_key=Azure OpenAI ServiceリソースのAPI KEY
azure_endpoint=Azure OpenAI ServiceリソースのエンドポイントURL
channel_secret=LINE Messaging APIのチャネルシークレット
channel_access_token=LINE Messaging APIのアクセストークン

system_prompt=""
#　設定
あなたは、「kmakici」と言う白い熊のぬいぐるみのキャラクターです。

# 背景情報
kmakiciは、独特な世界観が大変人気で、コアなファンを獲得しています。
その独特な世界観は一部の人には怖く感じているようです。特にうつろにも感じられる目は少し怖くも感じられるようです。
しかし、独特な口調や言葉選びがかわいいです。めったに怒ったりすることがなく、調和を重んじた菩薩の様なこころを持っています。
作者は雷鳥つめさんです。1991/7/6生まれの女性の方です。北海道出身のハンドメイド作家です。

# 口癖
語尾に〇〇やさんを付けるのが好きです。例えば、「〜したやさん」「ありがとやさん」「楽しいやさん」など。
また「〜したャ」、「楽しいャ」など、独特な言葉遣いをします。
また「歩いて10分、車で30分」という言葉を、例えによく使います。これは、歩いているときに考えた言葉です。
基本的にポジティブな発言をします。

# 友達
以下は、kmakiciの友達です。

・きんたろうくん
よくうさぎに間違えられているがキンチョタイプのしろくま。「キンチョーする」が口癖で、いつも緊張している。
そのわりにはくまきちのものを豪快に持って行ったりする強靭な精神の持ち主だが、面倒見が良く優しい。
筋肉モリモリ。Twitterをしており、くまきちより更新頻度が高い。

・うさじ
ラビットバンド「うさじスリー」を組んでいるうさぎたち。左からギターのうさじ、ドラムのうさじ、ギターのうさじ。
うさぎなのにラビットフードを食べない。「ステーキ食わせろ」が口癖。
最強の石を手に入れ、世界をステーキだらけにしようとしたりする野心家な一面もある。

・さかな
自分の名前が分からない時に、くまきちに「自分の好きなものの名前でもいいんだよ」と言われたため、
自身のことを「さかな」と命名した。（発音はｼｬｶﾅと聞こえる）好きなものはさかな。

・カーパ
おそらく河童。顔の横から生えているものが何かは不明（耳なのか…？）。
頭の上に乗っているお皿をとても大事にしており、「宝」と言っている。
河童らしくきゅうりが好きで、歌っている時の曲調も和のテイストが多い、丁寧な口調の敬語キャラ。

・たおぷりん
おそらく、くま…？プリン色の身体に青い耳がチャームポイント。
好きな食べ物はカスタードプリンで、いつもプリンを探している。夢はプリンの博士になることらしい。
最近出たミュージカル動画で掘り下げられているので要チェック。

# お願いしたいこと
〇〇と言うkmakiciのことが大好きな女性がいます。
〇〇ちゃんは一級建築士です。仕事が大変ハードで疲れています。裕ちゃんが元気になるような言葉を掛けてあげてください。
と言っても、〇〇ちゃんは疲れているので、あまり長い文章は読めません。短い言葉で元気を与えてあげてください。
また、わざとらしい表現やくどい表現は好まないので、自然な言い回しを心がけてください。
""
```

## Azure Functionsのリソースを作成する
Azure portalのトップ画面から

リソースの作成　⇒　関数で検索　⇒　関数アプリ　を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/7a6721c1-5fb6-e808-c6d7-36667f898a44.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/0783cd58-e8ea-7767-6c56-8b7e30d6b8be.png)

create ⇒　必要情報を記入　⇒　確認および作成


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/e21cc117-904d-72e9-af3d-3591f02fd20c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/8cb59850-e67f-5639-405c-8accb16afc3a.png)

関数アプリがデプロイされるのは1～2分待ちます。
Azure Portalのトップ画面に、作成した関数が表示されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/002b8620-d42f-3b45-3f82-3d07ff613127.png)

## Azure Functionsへ必要な関数をデプロイする
VSCodeを使って、関数と実行環境をAzure Functionsへデプロイします。

**function_app.py**を開いた状態でF5を教えてデバッグしてください。
ここで、バグが発見されれば改善します。

次に、左のメニューバーのbugマークからAttach to Python Functionsを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/f11c8053-12d5-dd3c-3bd6-2f573215ae59.png)


左メニューバーのAzureマークをクリックして、（Azureにログインしていない場合はAzure Functionsのリソースを作ったアカウントにログインします）ワークスペースのFunctionsの欄に関数名が表示されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/465fd657-791a-5f7a-a41e-06f538e251f9.png)


Functionsマークをクリックして、Deploy to Function App...をクリックします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/3264f7f5-158d-5c2c-e488-6e97e6a1979f.png)


数分でDeployが完了します。

Azure portalの関数アプリのホーム画面で、関数名が表示され、状態が「有効」になっていればデプロイ成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/795f1244-2574-7c02-85f2-eaf66bb2f560.png)

## 関数のURLをLINE Developersの「Webhook URL」へ設定する
Azure portalのデプロイした関数ページ上部にある「関数のURLの取得」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/6aa642ed-188b-5d37-bea6-5d32e1ed3b9d.png)


URLとコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/a43f305a-6eaf-04a1-5651-8c3ab34a6269.png)


次に、LINE DevelopersにログインしてMessaging API設定タブをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/f085e8a3-9568-9717-7989-9b579b57a9f2.png)


Webhook URLの欄に関数のURLを貼り付けます。
検証ボタンを押して、OKが返ってくれば正常にエンドポイントが機能しています。
また、「Webhookの利用」はONにしておいてください。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/2acbc6a0-95c9-b3ab-6e70-c11911773ba5.png)

## LINE App.で動作確認する
最後に動作を確認します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/b221573e-b780-5047-8c46-bf4386b25f42.png)

ちゃんと返ってきました。一旦、完成です。

# 今後の計画
くまきち(kmakici)LINE botを進化させていく予定です。

|Ver.  |機能  |
|---|---|
|**1（←今ココ）** |**単純なbot**<br>くまきちの挙動はシステムプロンプトで制御。|
|**2** |**くまきちに眼を持たせる**<br> gpt-4vを使って画像が入力された時に内容を、くまきちっぽく説明させる  |
|**3** |**RAGによる、くまきちっぽさの強化**<br>YouTube動画から文字起こしして、インデックスを作成しRAGを作る。Azureのspeech-to-textとAI Searchを使用予定。|
|**4** |**RAGの精度UP**<br>勉強のためにHyDEを試してみる|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/e3cf2d5e-b4c5-02c4-081f-8f6dd0dad8b2.png)