GPTによる情報抽出の信頼性を向上させる、Function callingの利用事例

はじめに

本記事では、OpenAIから公開されて1か月経過した執筆時点(2023/07/13)でも活用事例の公開が少ないFunction callingについて、二通りの応用例をご紹介します。

おことわり

  • 再現のためのスクリプトについて、現状公開予定はありません。
  • 記事の誤り、推奨される方法等がありましたらご指摘いただけますと幸いです。
  • 本記事は読者層を明確に想定した上で書かれたものではありません。就職先として弊社を考えている学生の方々、GPTの活用を検討されているお客様、勉強中のエンジニアなど、特定の個人に刺さる内容であれば幸いです。

Function callingとは

開発者が事前に関数を記述しておき、必要に応じて実行する関数とその引数をモデルに選択させるためのAPIです。OpenAIから、関数呼び出しのために微調整された(fine tuned)モデルを利用できるAPIとして公開されました。

また本来の用途とは異なりますが、モデルが選択した関数や引数は JSON 形式で返されるため、本来は自然言語で返されるモデルの返答を構造化する方法としても知られています。

Function callingを使うと何が嬉しいの?

多くの方は先述の紹介を読んでも、主に以下の理由から見出し通りの疑問を持つでしょう。

  • チャット機能のユーザー向けではないこと
  • Function callingの公開前から、既に生成モデルに構造化データを生成させるためのGuidanceSemantic kernelのようなライブラリが開発・公開されていたり、プロンプトでの指定から構造化データを出力できたりと、ある程度同じ目的に対する手法が確立されていたこと

前者についてはその通りで、本APIはチャットアシスタントやデータ分析処理などの開発者が利用することを目的としており、したがって本記事も技術的にどのような利用方法があるか、事例を解説することを目的とします。

後者に対して、Function callingは指定したスキーマや関数名について高い精度で応答が可能な点APIの仕様が簡単なため導入コストが低い点で従来の手法より優れています。

出力の精度が高いことは、後の関数実行や構造化データの後処理などを含めた、一連の処理の信頼性を向上させます。

さらに従来のリクエストに関数の情報を追加するだけで実行でき、従来のレスポンスに加わって選択された関数名と引数が返されるようなシンプルな仕様なため、保守性も高い手法といえます。

どう使えばいいの?

本記事では、Function callingを用いた2通りの用途について、以下の応用例をご紹介します。

  • 関数を選択させ、引数を生成させる ReAct Agent:ユーザーの依頼や質問に対してタスクを切り分け、どの順序で実行するか、どの関数を使用するかを決定し、実行して回答する機能
  • 構造化された応答をさせる 側面付きの感情分析:商品レビューの文章について、言及された商品の側面(価格,大きさ,質感など)に対するユーザーの感情を分析し、構造化した分析結果を返す機能

Function calling使ってみた

関数を選択させ、引数を生成させる(ReAct Agent)

ReActとは、大規模言語モデルのもつ推論 (思考連鎖の促しなど) と行動 (行動計画の作成など)の能力の相乗効果により、モデルが行動計画を誘導、追跡、更新するだけでなく、行動計画を処理することで、現実の問題を解くためのアプローチです。

今回はユーザーの依頼や質問に対してタスクを切り分け、どの順序で実行するか、どの関数を使用するかをチャットボットが自律的に決定し、実行して回答するスクリプトを作成しました。

このチャットボットには以下の関数を持たせています。

  • SerpSearch:GPTの訓練に使用されていない、最近の情報を得るための検索エンジンを実行する関数
  • Calculator:自然言語の入力をpythonの計算ライブラリで認識できる計算式に変換し、計算する関数

Langchain Tool dataclassや自作の関数のインスタンスをtoolsリストに入れておけば、以下の形でFunction callingが認識する関数を作成できます。

functions = [
    {
        "name": str(t.name),                         # 関数の名前
        "description": str(t.description),           # 関数の説明文
        "parameters": {                              # 関数の引数
            "type":"object",
            "properties":{                           # 関数の引数の定義
                "query":{
                    "type":"string",
                },
            },
        },
        "required": ["query"],
    } for t in tools
]

Step1: 行動計画を作成し、最初のアクションを決定する

今回、チャットボットに解かせる質問文はこちら
Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?

質問文はLangchain ReAct Agentのデモから拝借しました。「レオナルド・ディカプリオのガールフレンドは誰か?彼女の年齢の0.43乗はいくつになるか?」という問いです。質問の前半は検索しないと最新の情報を得られず、後半は計算モジュールを使用しないと解けないような内容です。

質問を受けたチャットボットは行動計画と関数呼び出しを反復し、最終的に得た解答や実行結果をユーザーに返します。 ユーザーが質問を渡すと、GPTからの最初のレスポンスを受け取ります。

Bot 🤖:
I need to find out who Leo DiCaprio's girlfriend is and her current age. Then, I need to calculate her current age raised to the power of 0.43.
Action: Use the SerpSearch function to search for information about Leo DiCaprio's girlfriend.
(翻訳:レオ・ディカプリオのガールフレンドが誰なのか、そして彼女の現在の年齢を調べる必要があります。次に、彼女の現在の年齢を 0.43 乗して計算する必要があります。
アクション: SerpSearch 関数を使用して、レオ・ディカプリオのガールフレンドに関する情報を検索します。)

質問の解を得るまでのタスクを「レオ・ディカプリオのガールフレンドが誰なのか調べる」「彼女の現在の年齢を調べる」「彼女の現在の年齢を 0.43 乗して計算する」の3つに分解して、最初のタスクを解くためのアクションを設定しているようです。

関数呼び出しも同時に受け取ります。今回、関数名は “SerpSearch" 、検索は "Leo DiCaprio girlfriend" の入力で行うように生成されています。

function call 📞:
"function_call": {
 "name": "SerpSearch",
 "arguments": {
    “query": "Leo DiCaprio girlfriend"
 }
}

Step2: 行動結果に沿って推論とアクションを反復する

最初のレスポンスを受けて、検索エンジンの関数に "Leo DiCaprio girlfriend" のクエリを渡して実行します。 関数の実行結果 ”Camila Morrone” を再度GPTに渡すと、次のレスポンスを受け取ります。

Bot 🤖:
In order to answer the question, I need to find the current age of Leo DiCaprio's girlfriend, Camila Morrone, and raise it to the power of 0.43.\nAction: Perform a search to find Camila Morrone's current age.
(翻訳:質問に答えるには、レオ ディカプリオのガールフレンド、カミラ モローネの現在の年齢を見つけて、0.43 乗する必要があります。
アクション: 検索を実行して、カミラ モローネの現在の年齢を見つけます。)

関数呼び出しでは、関数名は “SerpSearch" 、検索は "Camila Morrone current age" で行うように生成されています。

function call 📞:
"function_call": {
 "name": "SerpSearch",
 "arguments": {
  "query": "Camila Morrone current age"
 }
}

もう一度検索エンジンの関数を実行して、実行結果 “26 years old” をGPTに渡します。 次のレスポンスがこちら

Bot 🤖:
I now know that Camila Morrone is 26 years old. I can now calculate her current age raised to the power of 0.43.
Action: Calculate Camila Morrone's current age raised to the power of 0.43.
(翻訳:カミラ・モローネが26歳であることがわかりました。彼女の現在の年齢を 0.43 乗して計算できるようになりました。
アクション: カミラ モローネの現在の年齢を 0.43 乗して計算します。)

関数呼び出しでは、関数名は “Calculator" 、計算は "26^0.43" で行うように生成されています。

function call 📞:
"function_call": {
 "name": "Calculator",
 "arguments": {
  "query": "26^0.43"
 }
}

Step3: 最後に得た解答をユーザーに返す

計算の実行結果を得て、ついにGPTは質問の解を得られました。 最後のレスポンスがこちら。

Bot 🤖:
I have calculated that Camila Morrone's current age raised to the power of 0.43 is 4.059182145592686.
FinalAnswer: 4.059182145592686
(翻訳:カミラ・モローネの現在の年齢を 0.43 乗すると、4.059182145592686 と計算されました。 
最終回答: 4.059182145592686)

チャットボットは「レオナルド・ディカプリオのガールフレンドは誰か?彼女の年齢の0.43乗はいくつになるか?」という問いに対し、タスクを分解して順に実行することで、最終回答の4.059182145592686を導きました。

このようなボットに任意の関数を持たせることで、社内ドキュメントに関する質問応答、議事録の要約、議事録からのメールやスケジュールの自動作成のような、業務の効率化に活用できそうです。

構造化された応答をさせる(側面付きの感情分析)

もうひとつの例として、Function callingが関数の引数を構造化データ(JSON)で返すことを利用して、テキストの入力データのタグ付けをGPTに実行させることが可能です。

ここではOpenAI API Function callingを使用して、商品レビューの中でユーザが言及した商品の側面と、側面に対するユーザーの感情をタグ付けする事例をご紹介します。

構造化データ利用を目的としてFunction callingを使う際は、得たい構造化データのスキーマを、関数の引数として生成させます。

標準的なスキーマのテンプレート:

functions = [
    {
        "name": "func_hoge",                         # 関数名
        "description": "this function is hogehoge",  # 関数の説明文
        "parameters": {                              # 関数の引数
            "type":"object",
            "properties":{                           # 関数の引数の定義
                "hoge_or_fuga":{                     # 引数の名前(任意の文字列)
                    "type":"string",                 # 引数の型
                    "description": "this arg is hogefuga" # 引数の説明文
                    "enum": ["hoge", "fuga"]         # 引数の選択肢リスト
                },
            },
        },
        "required": ["query"],                       # 優先して生成してほしい引数リスト
    }
]

今回は商品レビューを扱うので、入力されたレビュー文章を分割して、言及内容と感情を取得(生成)できるようなスキーマを指定します。引数を入れ子にすることで、辞書型の引数も生成できます。

感情分析のためのスキーマの例

functions = [{
    "name": "sentiment_analyzer",
    "description": "analyse sentiment",
    "parameters": {
        "type": "object",
        "properties": {       
            "sentences": {                                # 入力文リストを取得するための引数
                "type": "object",
                "description": "list of sentence",
                "properties": {
                    "sentence": {                         # ひとつの入力文が入る引き数
                        "type": "string",
                        "description": "parsed sentences identified from input"
                    },
                    "aspect": {                           # 言及された商品の側面を取得するための引数
                        "type": "object",
                        "description": "entity class",
                        ..
                    },
										..
                },
            },
        },
    "required": ["sentences", "aspects"],
    },
},]

作成したスキーマを持たせ、商品レビュー文と一緒にGPTへ送った際に得られる結果の例をご紹介します。

(本記事は使用例の紹介を目的とするため、既存手法との性能比較は割愛します)

今回はUCSDの公開するAmazonレビューデータセットより、カレンダーを購入した顧客のレビュー文章10件を使用して、感情分析の結果を生成してみました。

出力後に手作業で、レビュー文中から商品の側面に言及している部分を赤字に、感情を表現する部分に下線を引いています。

主に言及された側面はcalender(商品全般)、art/photo/print(カレンダーの写真)、customer feelings(顧客の感情)の3点で、10件のうちNo. 6以外の9件は側面のクラスが正しく判定できているように見えます。感情の判定は10件すべてについて正しそうです。

このような生成タスクをより大きなデータセットで実施して、グラフによる可視化で商品同士を比較したり、商品の強み/弱みを分析するのも面白そうです。

おわりに

ご紹介した2例のように、Function callingを利用すれば、情報抽出や外部関数を使用するシステムを低コストで構築できます。

Function callingはOpenAIのモデル名を”gpt-4-0613”または"gpt-3.5-turbo-0613”に指定し、引数functionsを追加するだけで使用できます。ぜひ一度お試しください!

ShtockData

お問い合わせフォーム

お問い合わせ項目を選択してください