ちゃんと掛け算ができるChatGPTを作ってみる【function call APIの使い方】
はじめに
ChatGPTで誰でも簡単にプラグインを作れそうな仕組みが発表されたので、早速使ってみました。
まず、ChatGPTには「拡張機能」と「プラグイン」という似た概念がありますが、両者は全くの別物です。「拡張機能」はChatGPTの脳はそのままでUIを拡張するもの、「プラグイン」はChatGPTの脳を外部のサーバーに置き換えてしまうためのものです。
ChatGPTでプラグインを使う最大のメリットは、「ChatGPT自身が学習していない範囲の事象を正確に応答する」ということだと考えています。
すなわち、ChatGPTのプラグイン開発を行えば、例えば社内ドキュメントや、現場のセンシングデータなど、ChatGPTの学習に使われていない「閉じた情報」にアクセスして、業務活用することも考えられます。
(余談ですがChatGPTはAPI利用の場合、送信したデータは学習には使われませんが、OpenAIに送ることには変わりないので、個人情報などはマスキングする必要があると思います。)
また、あまり知られてないですが、ChatGPTは桁数が上がると正確な計算ができなくなります。過去にYoutubeでも紹介していましたが、簡単な計算問題でもプロンプトを使わない場合、下のように平気で間違えます。
そのため、今回は新しく発表されたfunction callingという機能を使って、計算プラグインを自作することにより、wolfram alphaのように正確な計算をChatGPTに実行させ、その感触を確かめてみようと思います。
API(関数)を準備する
まずは必要なパッケージのインポートとAPIキーの読み込み。
import openai
import json
openai.api_key = "YOUR_OPENAI_API_KEY"
続いて四則演算の関数を用意。
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiple(a, b):
return a * b
def divide(a, b):
return a / b
ChatGPTが正しく関数を利用するために説明を用意します。
function_description = [
{
"name": "calculate",
"description": "2つの変数を計算する",
"parameters": {
"type": "object",
"properties": {
"order": {
"type": "string",
"description": "「足す、引く、掛ける、割る」いずれかの演算方法",
},
"a": {
"type": "string",
"description": "1つめの数値",
},
"b": {
"type": "string",
"description": "2つめの数値",
}
},
"required": ["order", "a", "b"],
},
},
]
これで準備完了です。
実行すべきAPIを判定させる
早速実行してみます。
message = "1243かける342は?"
def get_order(message, function_description):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[{"role": "user", "content": message}],
functions=function_description,
function_call="auto",
)
order_response = response["choices"][0]["message"]
return order_response
order_response = get_order(message, function_description)
function_name = order_response["function_call"]["name"]
order = json.loads(order_response["function_call"]["arguments"])
print("判定内容:")
print("function", function_name)
print("order", order.get("order"))
print("a", order.get("a"))
print("b", order.get("b"))
これで以下の結果が返ってきます。
判定内容:
function calculate
order 掛ける
a 1243
b 342
ちゃんと変数と演算方法を認識してくれています。
実行結果を使って、応答文を生成する
まずはAPIを実行する仕組みを作ります。
def run_calculator(function_name, order):
if function_name == "calculate":
if order.get("order") == "足す":
function_response = add(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "引く":
function_response = subtract(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "掛ける":
function_response = multiple(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "割る":
function_response = divide(float(order.get("a")), float(order.get("b")))
else:
return None
else:
return None
return function_response
これでも実行自体は事足りますが、せっかくChatGPTを使っているので、応答文も自然な文にしてみます。
ということで、応答文を作成させます。
def get_response(message, function_name, function_response):
result_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[
{"role": "user", "content": message},
order_response,
{
"role": "function",
"name": function_name,
"content": str(function_response),
},
],
)
result = result_response["choices"][0]["message"]["content"]
return result
if order_response.get("function_call"):
function_response = run_calculator(function_name, order)
if function_response is not None:
result = get_response(message, function_name, function_response)
else:
result = "不適切な判定結果"
else:
result = "関数が検出されませんでした"
print("応答結果:")
print(result)
これを実行すると以下のように表示されます。
応答結果:
1243かける342は425106です。
当然ChatGPTの脳ではなく、上記のmultiple関数の実行結果なので必ず正確なものが返ってきます。
所感
驚くべきことは関数の説明を自然言語で行うことでした。本来はベクトル類似度などを計算する仕組みが必要ですが、すべてマスキングされていてAIエンジニアでなくとも簡単にAPIを作れるのが凄いと思います。ただ、上記のfunction_descriptionでは、APIの判定結果としてorderに「掛ける」ではなく、「multiple」と英語で返ってしまうことがあったので、関数説明には英語を使ったり、プロンプトを工夫する必要があると思いました。
(enumで解決できました。下記全文に反映済み)
コードサンプル全文
import openai
import json
openai.api_key = "YOUR_OPENAI_API_KEY"
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiple(a, b):
return a * b
def divide(a, b):
return a / b
def get_order(message, function_description):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[{"role": "user", "content": message}],
functions=function_description,
function_call="auto",
)
order_response = response["choices"][0]["message"]
return order_response
def run_calculator(function_name, order):
if function_name == "calculate":
if order.get("order") == "足す":
function_response = add(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "引く":
function_response = subtract(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "掛ける":
function_response = multiple(float(order.get("a")), float(order.get("b")))
elif order.get("order") == "割る":
function_response = divide(float(order.get("a")), float(order.get("b")))
else:
return None
else:
return None
return function_response
def get_response(message, function_name, function_response):
result_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[
{"role": "user", "content": message},
order_response,
{
"role": "function",
"name": function_name,
"content": str(function_response),
},
],
)
result = result_response["choices"][0]["message"]["content"]
return result
if __name__ == '__main__':
function_description = [
{
"name": "calculate",
"description": "2つの変数を計算する",
"parameters": {
"type": "object",
"properties": {
"order": {
"type": "string",
"description": "「足す、引く、掛ける、割る」いずれかの演算方法",
"enum": ["足す", "引く", "掛ける", "割る"],
},
"a": {
"type": "string",
"description": "1つめの数値",
},
"b": {
"type": "string",
"description": "2つめの数値",
}
},
"required": ["order", "a", "b"],
},
},
]
message = "1243かける342は?"
order_response = get_order(message, function_description)
function_name = order_response["function_call"]["name"]
order = json.loads(order_response["function_call"]["arguments"])
print("判定内容:")
print("function", function_name)
print("order", order.get("order"))
print("a", order.get("a"))
print("b", order.get("b"))
if order_response.get("function_call"):
function_response = run_calculator(function_name, order)
if function_response is not None:
result = get_response(message, function_name, function_response)
else:
result = "不適切な判定結果"
else:
result = "関数が検出されませんでした"
print("応答結果:")
print(result)
この記事が気に入ったらサポートをしてみませんか?