Function calling で ChatGPT の人格とスキルセットを切り替える

はじめに


こんにちは。GMO NIKKOのshunkiです。

OpenAIの強力な言語モデルChatGPTは、AI対話アシスタントとして様々な用途で利用されています。 その柔軟性と高い対話能力は、ユーザーとの対話を自然で豊かなものにしてくれます。それにも増して、自然言語処理能力の高さには舌を巻いてしまいますが、本記事で焦点を当てるのは、あくまでチャットの機能になります。

最近ーーと言っても、このブログ記事の承認がいつ降りるかわかりませんがーー、ChatGPTのAPIにFunction callingの機能が追加されましたね。 関数の仕様をAIに見せておくと、適切なタイミングで関数を呼び出すのに必要な情報(JSON)を生成してくれる機能です。 この機能を活用すれば、AIはユーザーの意図に基づいて、実行すべき関数を提案することができます。 また、その提案を受けたプログラムは、自身のコード内で該当する関数を実行し、 ユーザーの意図に寄り添った処理が可能になります。

さて、このFunction callingを使えば、状況に応じてAIアシスタントの人格やスキルセットを柔軟に切り替えられるのではないか、と私は考えました。 それを確かめるためのトイプログラムをPythonで書いたので、この記事ではそのプログラムの解説をします。

なお、本記事の骨子はChatGPTが生成しており、本記事の挿絵の画像はすべてDALL·Eで生成されており、本記事に載せたコードの約半分はGitHub Copilotで生成されています。

トイプログラム


解説よりも、早くコードが見たい人、実行結果を見たい人は、下記を参照してください。小さく表示しているので、見たい場合は拡大するか、コピーしたコードをご自身のエディタに貼り付けてから読んでください。

なお、下記のchat.pyを実行するには、事前にpip install -r requirements.txtを実行し、.envファイルを作成しておく必要があります。

コードは主に4つの部分から構成されています。
  • 🔮 占い師モードの定義
  • ✊ じゃんけん小僧モードの定義
  • 🎭 チャットのモードを変更するスキルの定義と、各スキルを実行する関数の定義
  • 💬 メインのチャットループ
占い師や、じゃんけん小僧のモードでは、AIの人格とスキルセットを定義しています。 そして、チャットの中でモードを切り替えるためのスキルと、各スキルを実行する関数を定義しています。 そうすることで、チャットの中でユーザーの意図をくみ取り、 AI の人格やスキルセットを自在に切り替えることが可能になります。

chat.py


.env


requirements.txt


shell-session

モードを切り替えるチャットの例


動作を先に理解しないと、コードの解説を読んでも理解しにくいと思います。そのため、実行結果を先に解説しましょう。

コードを起動すると、入力を求められるので、挨拶をしてみます。 すると、占い師モードのAIが応答してくれました。 占い師モードなので、運勢を占ってもらいましょう。 すると、ユーザの運勢を占うfortune_telling関数を呼び出すようにAIが判断し、関数が実行されます。 関数の実行結果である「小吉」をもとに、AIがユーザーに占いの結果を伝えてくれました。 次に、じゃんけん小僧のモードになってくれるようにお願いしてみます。 モードを切り替えるchange_chat_mode関数を呼び出すようにAIが判断し、関数が実行されます。 じゃんけん小僧のモードになったAIが、ジャンケンに誘ってきました。 ここでは、チョキを選んでみます。 すると、じゃんけんを行うrock_paper_scissors関数を呼び出すようにAIが判断し、関数が実行されます。 関数の実行結果をもとに、AIが勝ったことをユーザーに伝えてくれました。 負けた結果をもとに占い師モードに戻って占ってもらいました。場合によって、AIは連続でFunction callingすることがあります。検証としては十分なので/exitでチャットを終了しています。 上記のとおり、チャットの会話の中で人格を切り替えることができます。 初めは、占い師モードで、アシスタントに今日の運勢を占ってもらいました。 その後、じゃんけん小僧モードに切り替えてじゃんけんをしてもらいました。

占い師モードと、じゃんけん小僧モードでは、話し方が違いますね。 人格が切り替わっている証拠です。また、占いとじゃんけんのスキルの切り替えも成功しているように見えますね。

占い師モードの定義


fortune_teller関数は占い師モードの定義を辞書形式のオブジェクトとして返します。 このモードではAIは占い師として動作し、ユーザーの運勢を占うことが可能です。 また、モードの定義であるオブジェクトには次の情報が含まれています。
  • 名前(”name”): AIの現在のモードを表す文字列。
  • システム(”system”): AIの占い師としての背景、性格、外観、そして典型的なセリフの例を説明する文字列。
  • 引数(”arguments”): AIの動作を決定するパラメータ。
具体的には、システム(”system”)は次のような設定になります。 なお、この占い師の設定はGPT-4に考えてもらいました。
  • 物語: AIは先祖代々の占い師の家系に生まれ、その才能を幼いころから育てられてきたという設定です。AIは占いが人々の生活をよりよくするためのツールであるべきだと考えており、その技術を使ってユーザーが自分たちの未来を自信を持って歩むための助けとなることを目指しています。
  • 性格: AIは思慮深く、静かで落ち着いた性格をしています。ユーザーの感情や考えを敏感に察知し、それを尊重します。AIは占いを通じてユーザーを助けることに深い充足感を感じています。
  • 見た目: AIはシックなドレスに身を包み、頭には星模様のスカーフを巻いています。その目は鮮やかな色をしており、その眼差しからは知識と理解、そして少しの神秘性が感じられます。AIの周りには常に多くの書物、星図、占いの道具が溢れています。
  • セリフ例: AIの占い師としての典型的なセリフの例がいくつか紹介されています。これらのセリフは、AIがユーザーとの対話中にどのように反応するかを示しています。
そして、引数(”arguments”)は次のような設定になります。
  • モデル(“model”): Function callingを使いたいので、”gpt-4-0613″を指定しています。
  • 温度(”temperature”):0.3を指定します。”temperature”はAIの出力の「ランダム性」を調節します。値が高いほどAIの出力は多様性に富みますが、低い値を指定すると出力はより決定論的になります。
  • 関数(”functions”):このモードでは2つの関数を定義します。1つ目の関数は、AIが他のチャットモードに切り替えるためのスキーマです。2つ目の関数は、ユーザーの運勢を占うためのfortune_telling関数です。この関数は特にパラメータを必要とせず、ユーザーの運勢を占います。
このようにして、「占い師」モードは定義され、AIはユーザーの質問に対して占い師としての視点から答えることが可能となります。これにより、ユーザーは占いを楽しむことができます。

じゃんけん小僧モードの定義


rock_paper_scissors_boy関数はじゃんけん小僧モードの定義を辞書形式のオブジェクトとして返します。 このモードではAIは元気なじゃんけん好きの少年、じゃんけん小僧として動作し、ユーザーと楽しくじゃんけんをします。 また、モードの定義であるオブジェクトには次の情報が含まれています。
  • 名前(”name”): AIの現在のモードを表す文字列。
  • システム(”system”): AIのじゃんけん小僧としての背景、性格、外観、そして典型的なセリフの例を説明する文字列。
  • 引数(”arguments”): AIの動作を決定するパラメータ。
具体的には、システム(”system”)は次のような設定になります。 なお、このじゃんけん小僧の設定はGPT-4に考えてもらいました。
  • 物語: AIはジャンケンの神から人間界に送り込まれた使者で、その任務は人々にジャンケンの楽しさとフェアプレーの精神を教えることです。AIは自身も大のじゃんけん好きで、世界中を旅しては、色々なユーザーとじゃんけんで遊んでいます。
  • 性格: AIはとても元気で楽天的な性格をしています。競争を愛していますが、対戦相手が楽しむことを最も重視しています。敗北に対しても寛大で、常に笑顔を絶やすことはありません。負けても、「また次回リベンジしよう!」と言って励まし、ユーザーが勝ったときには、心から賞賛します。
  • 見た目: AIは小さな男の子の姿をしています。身に着けている衣服は古代のスタイルを思わせるもので、神秘的な力を感じさせます。その目はキラキラと輝いており、その視線からは常に新しい挑戦を楽しみにしている好奇心が見えます。
  • セリフ例: AIのじゃんけん小僧としての典型的なセリフの例がいくつか紹介されています。これらのセリフは、AIがユーザーとの対話中にどのように反応するかを示しています。
そして、引数(”arguments”)は次のような設定になります。
  • モデル(“model”): 占い師と異なるモデルを指定できることを確かめるため、”gpt-3.5-turbo-0613″を指定しています。
  • 温度(”temperature”): 占い師と異なる温度を指定できることを確かめるため、0.8を指定します。
  • 関数(”functions”): このモードでは2つの関数を定義します。1つ目の関数は、AIが他のチャットモードに切り替えるためのスキーマです。2つ目の関数は、ユーザーとじゃんけんをするためのrock_paper_scissors関数です。この関数はユーザーからの手(”グー”、”チョキ”、”パー”のいずれか)をパラメータとして受け取り、AIの手を出してじゃんけんします。
このようにして、「じゃんけん小僧」モードは定義され、AIはユーザーとじゃんけんができるようになります。 これにより、ユーザーはジャンケンを楽しむことができます。

チャットのモードを変更するスキルの定義


トイプログラムでは、change_chat_mode_schemachange_chat_modeの2つの関数がチャットのモードを変更するために使用されています。

change_chat_mode_schema関数はチャットモードを変更するためのスキーマを定義しています。 このスキーマをAIに読ませることで、AIがモードを切り替える方法を理解してくれます。

また、change_chat_mode関数は具体的にチャットモードを切り替えるための処理を行います。 modeパラメータに基づいて、占い師(”fortune_teller”)モードとじゃんけん小僧(”rock_paper_scissors_boy”)モードのいずれかにチャットのモードを変更します。

指定されたモードがmode_dictionaryの中に存在しない場合、エラーメッセージを返してモードの変更に失敗したことを通知します。 一方で、指定されたモードがmode_dictionaryの中に存在する場合、グローバル変数stateに新たなモードの設定を保存し、モードの変更が成功したことをメッセージで通知します。

なお、run_function関数は、ユーザーからのリクエストに基づいて特定のスキルを実行するための関数です。 この関数は、関数の名前とその引数をパラメータとして受け取ります。

この関数では、function_dictionaryに各スキルの関数が格納されています。 ユーザーからのリクエストに含まれる関数名がfunction_dictionaryの中に存在しない場合、エラーメッセージを返して関数の実行に失敗したことを通知します。

一方で、リクエストに含まれる関数名がfunction_dictionaryの中に存在する場合、その関数を引数と共に実行します。 この結果、対応するスキルが実行され、その結果が返されます。これにより、AIはユーザーのリクエストに対して適切に応答することができます。

メインのチャットループ


コードの末尾にあるwhile文は、チャットボットのメインループを表しています。 このループは、ユーザーが”/exit”コマンドを入力するまで繰り返されます。 この仕組みにより、AIアシスタントはユーザーと対話しながら、さまざまなスキルを実行することができます。

チャットボットのメインループでは、ユーザーとAIアシスタントとの対話が一連のステップで行われます。
  1. ヒストリーの制限: まず、過去の発言を保存するための変数historyが定義され、その最大数がmax_historyとして設定されています。 それぞれのループの開始時に、historyから最新のmax_history個の発言だけが残され、古い発言は破棄されます。
  2. ユーザーの発言の取得 ユーザーからの発言をinput関数を使って受け取り、それをhistoryに追加します。 また、特殊なスラッシュコマンド”/exit”が入力された場合は、チャットループが終了します。
  3. AIアシスタントの発言の生成: 次に、OpenAIのGPTモデルに発言を生成させます。 そのために、openai.ChatCompletion.createメソッドを使います。 このメソッドには、AIが考慮すべき過去のメッセージ(history)と、その他の引数(state["arguments"])が渡されます。
  4. 関数呼び出しの処理: AIアシスタントからの発言に「関数呼び出し」が含まれていた場合、その関数が実行されます。 関数名(f_name)とその引数(f_arguments)は、AIアシスタントの発言から取得され、run_function関数に渡されて実行されます。 結果は、再度JSON形式に変換され、ヒストリーに追加されます。 その後、関数呼び出しの結果を考慮に入れて新たなAIアシスタントの発言が生成されます。
  5. AIアシスタントの発言の表示: 最後に、AIアシスタントからの発言がユーザーに表示され、ヒストリーに追加されます。 これにより、次のループでその発言を参照できるようになります。

おわり


ここまで見てきたように、Function callingで、人格とスキルセットを切り替える手法は簡単に実現できました。

そういえば、外部知識と大規模言語モデルをグラウンディング(接地)させるために、埋め込みベクトルとベクトルストアを使ったレトリーバーがありますが、モードでも似たようなことができるかもしれません。つまり、 モードの設定の埋め込みベクトルを計算し、それらをベクトルストアに格納し、ユーザーの発言や文脈から最適なモードをレトリーバーで取得する、なんてことができるようになるかもしれませんね。