OpenAI のテクノロジーを使ってカイル君を 2023 年に召喚する
はじめに
思いついたので作ってみました。OpenAI のテクノロジーを使ってかつての Office アシスタントであるカイル君をチャットボットとして 2023 年に召喚します。
記事を書いた時点ではまだ自然な会話に特化した ChatGPT の API が利用可能な状態ではありませんでしたので、今回は GPT-3 (具体的には GPT-3.5 と呼ばれるモデルのひとつであるtext-davinci-003
) をチャットエンジンとして使います。
今後もし ChatGPT API や 新しい Bing の API が公開されれば、より自然な会話やチャット検索ができるボットが簡単に作れるようになるかもしれません。
【追記】2023 年 3 月 1 日に ChatGPT API が公開されましたので、新しいバージョンを作りました。
作り方
以下のサービスを組み合わせて作ります。トライアル期間を使い切っていない場合はすべて無料で済ませることもできます。
サービス | 役割 | 無料要素 |
---|---|---|
LINE Messaging API | UI・ボット | フリープラン |
DALL-E 2 | アイコンデザイン | フリークレジット |
Azure Functions | ロジック | Azure の無料アカウント |
OpenAI API | チャットエンジン | フリートライアル |
1. アイコン
ダリ先生にボットの顔であるアイコンを描いてもらいます。
1.1. DALL-E 2 サインアップ
Google アカウントか Microsoft アカウントでサインアップします。
1.2. 画像生成
サインアップが完了したらプロンプトエンジニアリングを繰り返して画像を生成します。プロンプトは英語で入力する必要がありますので、必要に応じて DeepL 先生に手伝ってもらいます。
試行錯誤の結果、似た感じのイルカが出てきたのでこれを使います。本当はカイル君が持っていた「ホタテ貝の形の PC」も再現したかったのですが、ダリ先生がなかなか理解してくれなかったので諦めました。
2. LINE ボット
2.1. ベース部分
LINE Messaging API と Azure Functions でまずはオウム返し LINE ボットを作ります。手順については以下の記事が今回使う言語とやりたいことにとてもマッチしていましたのでそのまま参考にさせていただきました。
チャネルアイコン
途中、チャネルアイコンにはダリ先生に描いてもらった画像を設定します。
あいさつメッセージ
友だち追加時のあいさつメッセージも設定しておきます。
2.2. テスト
完成したら友だち追加して会話してみます。この段階ではまだこちらの入力をそのまま返してくるだけです。
3. チャットエンジン
カイル君が実際に会話できるようにチャットエンジンを組み込んでいきます。
3.1. OpenAI サインアップ
OpenAI にGoogle アカウントか Microsoft アカウントでサインアップします。
3.2. OpenAI API キー取得
以下を参考にして OpenAI の API キーを取得します。
3.3. Azure Functions 環境変数設定
取得した API キーは LINE チャネルアクセストークン / チャネルシークレットと同じ要領で Azure Functions の環境変数に OPENAI_API_KEY
という名前で設定します。
3.3. 関数実装
LINE ボットの Webhook に設定した Azure Functions の関数 (オウム返し) を以下のように変更して上書きデプロイします。
requirements.txt
azure-functions
line-bot-sdk
openai
__init__.py
import logging
import os
import azure.functions as func
import openai
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
openai_api_key = os.getenv('OPENAI_API_KEY', None)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
prohibited_sentences = [
'お前を消す方法',
'お前をけす方法',
'おまえを消す方法',
'おまえをけす方法',
'おまえをけすほうほう'
]
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
signature = req.headers['x-line-signature']
body = req.get_body().decode('utf-8')
logging.info('Request body: ' + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
func.HttpResponse(status_code=400)
return func.HttpResponse('OK')
def generate_response(prompt):
openai.api_key = openai_api_key
response = openai.Completion.create(
engine='text-davinci-003',
prompt=prompt,
max_tokens=1000,
temperature=0.5
)
return response['choices'][0]['text'].replace('\n', '')
@handler.add(MessageEvent, message=TextMessage)
def message_text(event):
if (event.message.text in prohibited_sentences):
response = ''
else:
response = generate_response(event.message.text)
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=response)
)
generate_response
関数はユーザーからの入力をモデル (text-davinci-003
) に渡して生成されたテキストを受け取ります。
モデルの temperature
パラメータは 0 ~ 2 の間で設定します。小さくするほど毎回同じような内容を生成するようになり、大きくするほどランダムな内容を生成するようになります。今回はややランダム性を持たせたかったので、とりあえず 0.5 に設定しました。
max_tokens
パラメータは生成されるテキストの最大トークン数を設定しています。日本語の 1,000 トークンは画面のサイズにもよりますが大体 LINE の一画面がいっぱいになるくらいの文量になります。
本筋ではありませんが、トークンについては以下でまとめています。
会話
検証
会話してみます。こちらの入力に対しておおむね自然な返しができています。突然中国語で Excel の話を始めたりと、かつての Office アシスタントの名残を感じます。
ちなみに、背後で動いている text-davinci-003
が多言語対応モデルですので、日本限定アシスタントだったカイル君も多言語を扱うことができるようになっています。
OpenAI について聞いてみました。モデルの temperature
を高めに設定していますので毎回微妙に答えが変わります。Q&A ボットのように使いたい場合は temperature
を低めにすると良いかもしれません。
ただし、あくまでも Text Completion によって入力 (プロンプト) に対するそれらしい続き部分を生成しているだけですので、回答内容が正確とは限りません。(今回は正しいことを言っているようです)
かつての仕事もこなせるようです。
例のホタテ貝の形のパソコンのことを英語で聞いてみたところ、数年前に売ってしまって今は持っていないそうです。
Office アシスタント引退後のことを聞いてみたところ、ちょっとテンションが上がりすぎてしまいました。投資に相当な思い入れがあったようです。
チューニング
少々チューニングを行います。generate_response
関数を以下のように変更して上書きデプロイします。ここではモデルのパラメータに frequency_penalty
を追加しています。frequency_penalty
は -2 ~ 2 の間で設定します。正の値を設定すると同じトークンが出現する頻度に対してペナルティが課され、同じ内容を繰り返しづらくなります。上記のようなことになって欲しくないですので、今回はとりあえず 1 に設定します。
__init__.py
# (略)
def generate_response(prompt):
openai.api_key = openai_api_key
response = openai.Completion.create(
engine='text-davinci-003',
prompt=prompt,
max_tokens=1000,
temperature=0.5,
frequency_penalty=1
)
return response['choices'][0]['text'].replace("\n", "")
# (略)
参考
frequency_penalty
と似たパラメータに presence_penalty
があります。こちらも -2 ~ 2 の間で設定を行います。正の値を設定すると、そのトークンが既に出現している場合 (頻度ではなく出現しているか否か) にペナルティが課されます。必然的に既出でないトークンを使おうとするため、新しいトピックについて生成を促したい場合に設定します。いま気になるのは繰り返しですので、今回は特に設定を行いません。
再検証
再度同じ質問を投げかけてみます。今度はしっかりとした受け答えができるようになりました。引退後は色々なことをしていたようです。
これでカイル君の召喚は完了です。
お前を消す方法)
おわりに (以上です。🍵
Discussion
はじめまして!
記事のカイル君面白かったです!
読んでいただきありがとうございます。面白いと言っていただけて嬉しいです!
いろんな意味でインターネット接続必至な時代ですよね。ネット接続してないPCでは使い物にならない感じでしょうか?
用途によるとは思いますが、大規模言語モデルを使っている機能の多くはオンライン前提になってくるかもしれませんね。