はじめに
皆さん、こんにちは!
エンジニアの細川です。
皆さん、ChatGPTのFunction calling使ってますか?
今回はFunction callingを手軽にさくっとノーコードで試してみようということで、Postman flowsを使って試していこうと思います!
Function callingって?
既にご存知の方も多いと思いますが、軽く説明してみます!
既にご存知の方はこちらまで読み進めてみてください!
Function callingはChatGPTが外部関数の呼び出しが必要かどうか判断し、呼び出すタイミングを教えてくれる仕組みとなります!
まず、Function callingがなぜ必要かというとずばり、ChatGPTなどのLLMの応答の不安定さにあります。
LLMは自然言語で利用でき、非常に便利なのですが、ChatGPTからのレスポンスをもとに、他のAPIを呼び出すときには応答の不安定さが弱点となってしまいます。
プロンプト内で「{content: “xxxx”}
のようなjson形式で出力して」のように指定したとしても、違う形式で返ってきたり、余分な文字列がついていたりして、他のAPIを呼んだりするときにはエラーとなってしまいます。
また、レスポンスが特定の値のときのみ、外部APIにリクエストを投げたい場合も多々あると思いますが、その条件分岐もresponse形式が正しくないとエラーとなってしまいます。そういったLLMの弱点を解決する方法がFunction callingとなります。
Function callingの具体的な方法①リクエスト
実際にどのようにFunction callingを行うか説明します。
まず、エンドポイントですが、通常のChatGPTのAPIなどのエンドポイントと変わりません。
Azure OpenAIを利用している場合は以下のようになるかと思います。
POST https://{Azure OpenAIのエンドポイント}.openai.azure.com/openai/deployments/{デプロイメント名}/chat/completions?api-version=2024-06-01
そして、RequestBody部分が以下のようになります。
リクエストBody1
{
"messages": [
{
"role": "user",
"content": "今日の東京の天気は?"
}
],
"functions": [
{
"name": "get_weather",
"description": "与えられた地域の天気を取得します。",
"parameters": {
"type": "object",
"properties": {
"area": {
"type": "string",
"description": "天気予報取得対象の地域"
}
},
"required": ["area"],
"additionalProperties": false
}
}
],
"function_call": "auto"
}
messages
プロパティは通常時の呼び方と同じくuserからのプロンプトの想定です。
それにfunctions
という項目を追加しています。
functions
では呼び出したい外部APIの名前、説明、呼び出すためのパラメータなどを指定します。詳細についてはドキュメントをご参照ください。
また、function_call
というプロパティについては、autoにしておくと、投げたプロンプトによって、関数呼び出しが必要かどうかをChatGPT側が自動で判断してくれるようになります。
例えば、上の例だと天気の外部関数を登録しているので、「東京の天気は?」と聞くと外部関数を呼び出してくれますが、「今日の晩御飯はの献立は?」のように関係のない質問を投げると通常通りにGPTが返答をしてくれるようになります。
追記
先ほど教えてもらったのですが、functions
とfunction_call
は現在非推奨となっており、tools
とtool_choice
というプロパティになっているそうです。今はまだ使えそうですが、近々使えなくなるかもしれない点にご注意ください!(参考)
Function callingの具体的な方法②レスポンス
上記のように、リクエストボディーにfunctions
を含めてリクエストを送ると、chatGPTが外部関数呼び出しが必要かどうかを判断し、必要な場合は以下のようなレスポンスを返してくれます。
レスポンスBody
{
"choices": [
{
"content_filter_results": {},
"finish_reason": "function_call",
"index": 0,
"logprobs": null,
"message": {
"content": null,
"function_call": {
"arguments": "{\n\"area\": \"東京\"\n}",
"name": "get_weather"
},
"role": "assistant"
}
}
],
"created": 1723356892,
"id": "chatcmpl-9uw8q1ZXVFuvthhCPiqmmFHyI7lH0",
"model": "gpt-35-turbo-16k",
"object": "chat.completion",
"prompt_filter_results": [
{
"prompt_index": 0,
"content_filter_results": {}
}
],
"system_fingerprint": null,
"usage": {
"completion_tokens": 16,
"prompt_tokens": 82,
"total_tokens": 98
}
}
まず、choices
の中のfinish_reason
というプロパティに注目します。
外部関数呼び出しを行う場合は、このfinish_reason
がfunction_call
という値になっています。
リクエストで、functions
を渡していても、外部関数呼び出しが不要だとGPTが判断した場合はこのfinish_reason
がstop
という値で返ってきます。
つまり、finish_reason
がfunction_call
の時に外部関数を呼び出すようにアプリを実装することで、アプリをいつ呼び出すかを判断できるようになります。
続いて、message
の中身を見てみましょう。
"message": {
"content": null,
"function_call": {
"arguments": "{\n\"area\": \"東京\"\n}",
"name": "get_weather"
},
"role": "assistant"
}
message
の中身は呼び出す関数の種類とパラメータが返されています。
このarguments
に返ってくるのはrequestを投げるときに指定したparameters
の設定に応じた形式で返されます。
今回の場合は以下のような形式で、ユーザーが投げた質問から自動でパラメータの形式にしてくれます。
{
"area": string
}
これをそのまま使って外部関数の呼び出しを行うことができます。
そして外部関数から受け取った結果を再びGPTに渡してあげることで、外部関数の結果をもとにした応答をユーザーに返すことができます。
リクエストBody2
{
"messages": [
{
"role": "user",
"content": "東京の天気は、どうですか?"
},
{
"role": "function",
"name": "get_weather",
"content": "東京の天気は雨のち曇り"// <= ここに外部関数の結果を入れる。
}
],
"functions": [
{
"name": "get_weather",
"description": "与えられた地域の天気を取得します。",
"parameters": {
"type": "object",
"properties": {
"area": {
"type": "string",
"description": "天気予報取得対象の地域"
}
},
"required": ["area"],
"additionalProperties": false
}
}
],
"function_call": "auto"
}
以上がざっくりとしたFunction callingの方法になります。
ではこれをノーコードでパパっと作るために利用するPostman flowsの紹介をしていきます。
Postman flowsとは
Postman flowsはノーコードでAPIのフローを作れるツールとなります。
画面上にリクエストを送信するブロックなどを配置し、レスポンスをグラフに出力したり、1つ目のAPIのレスポンスをもとに2つ目のAPIにリクエストを送信するものなどを手軽に作成することができます。
Postmanのサイドバーのフローを押すとフロー作成画面に遷移し、そこから新しいフローを作成することができます。
今回はこのツールを使ってFunction callingをパット試してみたいと思います!
今回Function callingで呼び出すAPI
今回、Function callingで呼び出す外部関数の想定として、一つ簡単なAzure Functionsを作ってみました。
module.exports = async function (context, req) {
context.log(`${req.body.area}の天気予報`);
const SupportedAreas = {
Tokyo: "東京",
Osaka: "大阪"
};
switch (req.body.area) {
case SupportedAreas.Tokyo:
context.res = {
body: "東京の天気は雨のち晴れです。めっちゃ暑いです!"
};
break;
case SupportedAreas.Osaka:
context.res = {
body: "大阪の天気は霧です。寒いです。"
};
break;
default:
context.res = {
status: 400,
body: "未対応の地域です。"
}
break;
}
}
リクエストに応じて東京、もしくは大阪の天気を返す簡単なAPIです。
これを使ってFunction callingを試していきます。
事前にPostman上で用意しておくリクエスト
Postman flowsは既に保存済みのリクエストをブロックとして配置して使っていくので事前に以下の3つのリクエストをPostman上に保存しておきます。
- ChatGPTにユーザーからの質問想定で投げるリクエスト
- 外部関数を呼び出すリクエスト
- 外部関数の結果を踏まえてChatGPTに投げるリクエスト
それぞれの詳細は以下のようになります。
ちなみに{{question}}
などの二重波カッコで囲われているものはPostmanでは変数として認識されます。
ChatGPTにユーザーからの質問想定で投げるリクエスト
// Endpoint(Azure OpenAIの場合)
https://{azureのOpenAIエンドポイント}/openai/deployments/{デプロイ名}/chat/completions?api-version=2024-06-01
// Header
api-key: Azure OpenAIのAPIキー
// RequestBody
{
"messages": [
{
"role": "user",
"content": "{{question}}"
}
],
"functions": [
{
"name": "get_weather",
"description": "与えられた地域の天気を取得します。",
"parameters": {
"type": "object",
"properties": {
"area": {
"type": "string",
"description": "天気予報取得対象の地域"
}
},
"required": ["area"],
"additionalProperties": false
}
}
],
"function_call": "auto"
}
外部関数を呼び出すリクエスト
// Endpoint
関数のエンドポイント
// RequestBody
{{WeatherArea}}
外部関数の結果を踏まえてChatGPTに投げるリクエスト
// Endpoint(Azure OpenAIの場合)
https://{azureのOpenAIエンドポイント}/openai/deployments/{デプロイ名}/chat/completions?api-version=2024-06-01
// Header
api-key: Azure OpenAIのAPIキー
// RequestBody
{
"messages": [
{
"role": "user",
"content": "東京の天気は、どうですか?"
},
{
"role": "function",
"name": "get_weather",
"content": "{{WeatherResponse}}"
}
],
"functions": [
{
"name": "get_weather",
"description": "与えられた地域の天気を取得します。",
"parameters": {
"type": "object",
"properties": {
"area": {
"type": "string",
"description": "天気予報取得対象の地域"
}
},
"required": ["area"],
"additionalProperties": false
}
}
],
"function_call": "auto"
}
この3つのリクエストを用意できたら、いよいよPostman flowsで作っていきましょう!
Postman flowを使ってパッと実践
実際に作ってみたフローがこちらになります。
流れとしては、最初にGPTにユーザーの質問想定でリクエストを投げ、外部関数呼び出しが必要でなければそのまま出力を表示し、外部関数呼び出しが必要であれば、Azure Functionsにリクエストを投げ、その結果を含めてもう一度GPTに投げて最終的に質問の回答を出力しています。
start直後のブロックのquestionという部分で質問を入力する想定です。
例えば「東京の天気は?」と入力すると。右下の緑のブロックに以下のように返答が出力されます。
それに対して、「今日は卵かけご飯を食べます」のように関係のない質問を投げると、中央辺りの水色のブロックに外部関数呼び出しをしなかった場合の返答が出力されます。
このようにPostman flowsを使うことでほとんど実装しなくても手軽にfunction callingを試すことができます!
デプロイ
実は、Postman flowsはAPIとして公開することができます。Web Hookを作成し、Publishすることでブラウザなどから叩けるURLを発行してくれます。ただし、現時点では、レスポンスをカスタムできず、任意のレスポンスを返すことができないためあまり実用には向いていないかもしれません。ただ、レスポンスのカスタムにも近々対応するようで、もし対応されれば、Function callingを組み込んだアプリケーションとして実際に利用できるかもしれません。
まとめ
- Function callingを利用すると、ChatGPTをアプリに組み込みやすくなる!
- 外部関数の呼び出しタイミングや、JSON形式の安定出力など
- Postman flowsを使うことで、ほぼノーコードでFunction callingを試すことができる!
終わりに
今回はPostman flowsを使ってFunction callingを試してみました。アプリを作るのは少しハードルが高いかもしれませんがPostman flowsを使えば、ノーコードでお試しできるので皆さんもぜひ試してみてください!!
他にも生成AIのブログがジャンジャン出る予定ですので、ぜひチェックしてみてください!
参考にさせていただいた記事
https://zenn.dev/microsoft/articles/azure-openai-add-function-calling