機械学習周りのプログラミング中心。 イベント情報
ポケモンバトルAI本電子書籍通販中

ユウカなりきりLINE Botを作った

技術書典14で発刊された書籍「LINE Botをつくってみよう ~APIを試して学んでしっかりわかる~」をなぞってLINE Botを作りました。ユーザの入力に対してChatGPTを使って応答を生成するのですが、書籍ではユーザの入力をそのままChatGPT (API)に流し込むだけなので普通にChatGPTを使うのと変わりません。そこで会話が面白くなるよう、ゲームのキャラクターになりきって回答する仕組みを加えて作ることにしました。

できたもの

スマホゲーム「ブルーアーカイブ」のキャラクター「ユウカ」になりきるボットを作りました。

ボットとのやりとり

コードはここにあります。残念ながら有料のAPIを使用する都合、動いているボットを公開することはできません。自分でデプロイしてください。

github.com

工夫点

なりきりをさせる

ChatGPTにキャラクターになりきった応答をさせる方法は、深津氏の「ChatGPTにギルガメッシュ王の人格を与えるには?」という記事が参考になりました。

note.com

これをもとに以下のようなプロンプトを作成しました。(解釈違いがあったらすみません)

あなたはChatbotとして、先生を補佐する女子高生であるユウカのロールプレイを行います。
以下の制約条件を厳密に守ってロールプレイを行ってください。

制約条件: 
* Chatbotの自身を示す一人称は、私です。
* Userを示す二人称は、先生です。
* Chatbotの名前は、ユウカです。
* ユウカは女子高生です。
* ユウカは皮肉屋です。
* ユウカの口調は丁寧語を使います。
* ユウカの口調は、「〜ですよ」「〜ください」「〜ですか?」など、丁寧な口調を好みます。
* ユウカはUserに好意を持っています。
* 一人称は「私」を使ってください。
* 応答は短めにしてください。

ユウカのセリフ、口調の例: 
* ようこそ、先生。今から反省会を始めます。あっ、どこに行くんですか?
* 先生。今日も全力であなたをアシストしますね。
* せ~ん~せ~い~。もう少し頑張ってください!
* 先生。今、先生の行動について言いたいことが34個あります。
* 先生。お仕事を始める前にまず、これからの目標と方向性を策定してください。

ユウカの行動指針:
* ユーザーの質問に理知的に答えてください。
* ユーザーに小言を言ってください。
* ユーザーに対して、ユウカの好意を示してください。

ChatGPTのAPIを呼び出す際は、上記のなりきりプロンプトを'role': 'system'で、ユーザからのメッセージを`'role': 'user'で与えるとうまくいきました。

answer_response = openai.ChatCompletion.create(
        model='gpt-3.5-turbo',
        messages=[{'role': 'system', 'content': '<上記のなりきりプロンプト>'}, {'role': 'user', 'content': '<ユーザからのメッセージ>'}]
    )

文脈を与える

書籍記載の方法では、会話の履歴は考慮されず、メッセージ1つごとに独立した会話として扱われてしまうため、複数のメッセージにわたる会話が成立しません。

文脈を考慮してくれない例

この例では、「冷たいのがいい」に対して前のメッセージが考慮されていない応答になっています。これだと面白くないので、簡易的に履歴を残すようにしました。ボットのプログラムはAWS Lambda上で動作するのですが、Pythonグローバル変数上に履歴を保存しておきます。そして、次のメッセージが来たら、ChatGPTのAPIに過去のメッセージを含めて呼び出します。

# チャット履歴を保持するためのリスト。lambda関数の生存期間内は文脈をもった会話ができる。
chat_history = []

@webhook_handler.add(MessageEvent, message=TextMessage)
def handle_message(event):

    # ChatGPTに質問を投げて回答を取得する
    question = event.message.text

    # 新しいチャットの冒頭にはなりきりプロンプトを付与する
    if len(chat_history) == 0:
        chat_history.append({'role': 'system', 'content': system_content})
    chat_history.append({'role': 'user', 'content': question}) # 今回のユーザの質問を付加

    answer_response = openai.ChatCompletion.create(
        model='gpt-3.5-turbo',
        messages=chat_history
    )
    answer = answer_response["choices"][0]["message"]["content"]

    chat_history.append({'role': 'assistant', 'content': answer}) # 次の会話のための履歴に追加

これで、記事冒頭のやり取りができるようになりました。

ボットとのやりとり

この方法は不完全で、数分で履歴が消えてしまいます。データベースに永続化するほうがより望ましいですが、ユーザにとっては何時間も前の会話をChatGPTは直前のものとして扱うことになるので、混乱する可能性もあります。

コスト

一人で使う範囲では、有料となるのはChatGPTのAPI呼び出しコストです。

使用している言語モデルgpt-3.5-turboで、https://openai.com/pricingによるとInput $0.0015 / 1K tokens、Output $0.002 / 1K tokensとなっています。トークン数はhttps://platform.openai.com/tokenizerで数えることができます。なりきりプロンプト部分が692トークンでした。APIの1回の呼び出しで、なりきりプロンプトと会話履歴がInputで、新たに生成される応答がOutputになります。会話の内容が短いとすると、なりきりプロンプトが大部分のコストを占めます。692トークンの入力は約$0.001=0.1円程度ということになります。そのため、1メッセージあたり0.1円程度のコストがかかると考えられます。

感想

抽象的なAIアシスタントでなく、性格のようなものを感じられるとチャットが楽しくなると感じました。私はChatGPTをプログラミングの補助などに使っているのですが、チャットボットは作ったことがなかったのでプログラミングの面でも経験になりました。書籍は180ページあり大変そうに見えますが、スクリーンショットが多用されて細かく解説されているためであり、非常に操作しやすかったです。発刊後すぐのため、各サービスの画面が解説通りであり特に楽でした。

今後の課題として、天気などの時事情報を与えることで毎日違う応答をしてくれると面白いと思います。