[Python]OpenAIの「Function calling」を使ってデータの抽出・構造化してみた
本日はOpenAI APIの新機能を活用して、ビジネスサイドの人でも役立ちそうな使い方を試してみましたので、共有してみます。
営業企画的な業務の中ではデータクレンジング、構造化作業をすることは多く、それなりに工数を払ってきましたが、8割ほどの削減になりそうな予感です。
OpenAIのFunction callingとは
OpenAIのFunction callingは、自身で定義した関数に準拠したJSONでの応答を得られる新機能です。この機能による各種GPTモデルから構造化されたデータを確実に取得することができます(今までは出力が安定せず、個別にパースが必要だった)
※本来は関数呼び出しのための構造化ですが、今回の記事では構造化自体を目的としています
詳細は公式のドキュメントをご確認ください。
https://platform.openai.com/docs/guides/gpt/function-calling
コード解説
以下は、求人情報から職種、勤務地、年収などの情報を取得するためのコードです。このコードを順番に見ていきましょう。
import openai
openai.api_key = "[your_api_key]"
content = "[求人情報]"
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[{"role": "user", "content": content}],
functions=[
{
"name": "get_job_infomation",
"description": "与えられた求人情報から職種、勤務地、年収などの情報を取得する",
"parameters": {
"type": "object",
"properties": {
"prefecture": {
"type": "string",
"description": "都道府県名, 例)東京都",
},
"salary_upper": {
"type": "string",
"description": "年収, 例)3000000",
},
"salary_lower": {
"type": "string",
"description": "年収, 例)3000000",
},
"occupation": {
"type": "string",
"description": "職種, 例)営業",
},
"company": {
"type": "string",
"description": "会社名, 例)株式会社xxx",
},
"programming_languages": {
"type": "string",
"description": "プログラミング言語",
},
"tools": {
"type": "string",
"description": "ツール 例)AWS,BigQuery",
},
"position_feature": {
"type": "string",
"description": "ポジションの特徴",
},
"company_feature": {
"type": "string",
"description": "会社の特徴",
},
"employment_status": {
"type": "string",
"description": "雇用における契約形態",
},
"industry": {
"type": "string",
"description": "業界 例)IT",
},
},
"required": ["prefecture","salary_upper","salary_lower","occupation","company","tools","programming_languages","position_feature","company_feature","employment_status","company_type","client_industry"],
},
}
],
function_call="auto",
)
output = response["choices"][0]["message"]["function_call"]["arguments"]
print(output)
OpenAIをインポートします。
このパッケージはOpenAIのAPIを利用するためのものです。
import openai
OpenAIのAPIキーを設定します
このキーはOpenAIのウェブサイトで取得できます。キーは個別に発行され、APIの利用にはこのキーが必要です。
openai.api_key = "[your_api_key]"
求人情報のテキストをcontent変数に設定します
このテキストは関数が処理する元のデータとなります。かなり適当にコピペでベタ貼りしても機能します。
content = """[求人情報]"""
ChatCompletion.createメソッド
このリクエストには以下の要素が含まれます。
model: 使用するモデルの名前。ここでは"gpt-3.5-turbo-0613"を指定しています。0613以降のバージョンでないとFunction callingは機能しません。
messages: 会話のメッセージを表すリスト。各メッセージはrole("user"または"assistant")とcontent(メッセージの内容)で構成されます。ここでは、一つの"user"メッセージであるcontentを含めています。
functionsは実行する関数のリストです。ここでは"get_job_infomation"という関数を定義しています。この関数の目的は「与えられた求人情報から職種、勤務地、年収などの情報を取得する」ことです。
functionsについて
各関数はname(関数の名前)、description(関数の説明)、parameters(関数が取るパラメータ)で構成されます。parametersは各パラメータの型、説明、そしてそのパラメータが必要かどうかを示すrequiredフィールドを含むオブジェクトです。
function_callパラメータは、モデルが関数を自動的に呼び出すべきかどうかを指定します。ここでは"auto"を指定しています。
functions=[
{
"name": "extract_job_infomation",
"description": "与えられた求人情報から職種、勤務地、年収などの情報を抽出する",
"parameters": {
"type": "object",
"properties": {
"prefecture": {
"type": "string",
"description": "都道府県名, 例)東京都",
},
"salary_upper": {
"type": "string",
"description": "年収, 例)3000000",
},
"salary_lower": {
"type": "string",
"description": "年収, 例)3000000",
},
"occupation": {
"type": "string",
"description": "職種, 例)営業",
},
"company": {
"type": "string",
"description": "会社名, 例)株式会社xxx",
},
"programming_languages": {
"type": "string",
"description": "プログラミング言語",
},
"tools": {
"type": "string",
"description": "ツール 例)AWS,BigQuery",
},
"position_feature": {
"type": "string",
"description": "ポジションの特徴",
},
"company_feature": {
"type": "string",
"description": "会社の特徴",
},
"employment_status": {
"type": "string",
"description": "雇用における契約形態",
},
"industry": {
"type": "string",
"description": "業界 例)IT",
},
},
"required": ["prefecture","salary_upper","salary_lower","occupation","company","tools","programming_languages","position_feature","company_feature","employment_status","company_type","client_industry"],
},
}
],
function_call="auto",
レスポンスの受け取り
responseはAPIからのレスポンスを受け取る変数です。最後に、レスポンスから関数の結果を取得して表示します。結果はレスポンスのchoicesフィールドの中から取得できます。
output = response["choices"][0]["message"]["function_call"]["arguments"]
print(output)
アウトプット
上記のコードの結果として得られるアウトプットは以下のような形式のJSONです。
なお、内容がわからないと判断されたものはnullで返ってくることが多いですが、稀に「不明」と入力されていることもあり、若干不安定です。また、「東京都」「東京」などについても表記揺れが発生する点は、descriptionなどの書き方に改善の余地があるかもしれません。
この抽出については事前に利用者が定義した構造化に合わせて、小さくGPTモデルの問い合わせをしているだけなので、value部分の精度は今までと変わらないモデルの性能に依存しそうです。(とはいえ、レスポンスがJSONとしてで返ってくるだけで取り扱いやコスト面は段違いに改善されますが)
{
"prefecture": "東京都",
"salary_lower": "5500000",
"salary_upper": "8500000",
"occupation": "データサイエンティスト",
"company": "xxxx株式会社",
"programming_languages": "Python",
"tools": "PostgreSQL,BigQuery,AWS,GCP",
"position_feature": "クライアント向けAI開発,自社プロダクト向けAI開発",
"company_feature": "スタートアップ",
"employment_status": "正社員",
"industry": "IT"
}
終わりに
以上のように、OpenAIのfunction callingを使うと、機械学習モデルを用いて特定の情報を抽出するなどの処理を行うことができます。これは、大量のテキストデータから特定の情報を抽出する必要がある場合などに非常に便利です。
追記(JSONの型定義など)
JSONの型定義やenumなども使えるようなので、いろんなパターン組み合わせて、スキーマ作り直してみました。少し精度が上がったような気がしますが、enum無視したアウトプットも稀に発生するので、enumもどきと思って利用したほうがよさそうです。
私はどうしてもenumを守らせたかったため、アウトプットを確認して指定したenum以外のアウトプットを出した場合は、OpenAIへのリクエストからやり直す形にしました。
#enum用
prefectures = ["北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県", "不明"]
industries = ["IT", "不動産", "金融", "自動車", "製造", "化学", "石油・ガス", "電力","新エネルギー", "水産・農林業", "鉄鋼・金属", "機械", "電機", "家電", "食品","小売", "外食", "アパレル", "物流", "航空", "海運", "鉄道", "広告", "マスコミ","出版", "エンターテイメント", "旅行・ホテル", "アミューズメント", "スポーツ","保険", "証券", "商品先物", "リース・クレジット", "建築・建設・エンジニアリング","プラント・重電", "医薬品", "医療・福祉サービス", "教育・研究開発", "コンサルティング","人材サービス", "通信", "インターネット", "ゲーム", "総合商社", "専門商社", "電子部品・デバイス", "半導体", "精密機器", "繊維", "紙・パルプ", "ゴム", "ガラス・土石","化粧品", "家具・インテリア", "農薬・肥料", "包装材", "石材・鉱業", "不明"]
#schema用
schema = {
"type": "object",
"properties": {
"prefecture": {
"type": "string",
"enum": prefectures,
},
"salary": {
"type": "object",
"description": "給与",
"properties": {
"unit": {
"type": "string",
"description": "金額の単位",
"enum": ["千円", "万円", "円"],
},
"type": {
"type": "string",
"description": "給与の種類",
"enum": ["年収", "月収", "日給", "時給"],
},
"upper": {
"type": "integer",
"description": "上限給与",
"examples": [50,400000,300,4000000],
},
"lower": {
"type": "integer",
"description": "下限給与",
"examples": [80,200000,200,2400000],
},
},
},
"created_at": {
"type": "string",
"format": "date",
},
"url": {
"type": "string",
"format": "uri",
"examples": ["https://www.google.com"],
"default": "",
},
"email": {
"type": "string",
"format": "email",
"default": "",
},
"is_product_oriented_company": {
"type": "boolean",
"description": "自社プロダクト志向の会社かどうか",
},
"occupation": {
"type": "string",
"description": "職種",
"examples": ["営業","Webエンジニア","データサイエンティスト"],
},
"company": {
"type": "string",
"description": "会社名",
"examples": ["株式会社トヨタ自動車","合同会社YSK"],
},
"programming_languages": {
"type": "array",
"items": {
"type": "string",
"description": "プログラミング言語",
"examples": ["JavaScript","Python","Ruby","PHP","Java","C#","C++","Go","Kotlin","Swift"],
},
},
"skills_and_tools": {
"type": "array",
"items": {
"type": "string",
"description": "スキル・ツール・フレームワーク・ライブラリ・OS・DB・その他",
"examples": ["AWS","Docker","Linux","MySQL","React","TypeScript","Vue.js","Windows"],
}
},
"essential_requirement": {
"type": "array",
"items": {
"type": "string",
},
"description": "必須となるスキル要件",
},
"preferred_requirement": {
"type": "array",
"items": {
"type": "string",
},
"description": "必須ではないが歓迎されるスキル要件",
},
"position_feature": {
"type": "string",
"description": "ポジションの特徴",
},
"job_responsibilities": {
"type": "string",
"description": "担当する業務内容",
},
"company_feature": {
"type": "string",
"description": "会社の特徴",
},
"employment_status": {
"type": "string",
"enum": ["正社員","契約社員","業務委託","パート・アルバイト","インターン","その他"]
},
"industries": {
"type": "array",
"items": {
"type": "string",
"enum": industries,
},
"description": "会社が属する業界",
},
},
"required": ["prefecture","job_responsibility","salary","created_at","url","email","is_product_oriented_company","occupation","company","programming_languages","skills_and_tools","position_feature","essential_requirement","preferred_requirement","company_feature","employment_status","industries"]
}