Share via


Azure OpenAI Service (プレビュー) で関数呼び出しを使用する方法

gpt-35-turbo と gpt-4 の最新バージョンは、関数と協働するように微調整されており、関数を呼び出す必要があるタイミングと方法の両方を判断できます。 要求に 1 つ以上の関数が含まれている場合、モデルはプロンプトのコンテキストに基づいて、いずれかの関数を呼び出す必要があるかどうかを判断します。 モデルは、関数を呼び出す必要があると判断すると、その関数の引数を含む JSON オブジェクトで応答します。

モデルは、すべて指定された関数に基づいて、API 呼び出しを作成し、データ出力を構造化します。 モデルはこれらの呼び出しを生成できますが、それらを実行するのは自分であり、制御を行うのは依然として自分であることに注意することが重要です。

大まかに言うと、関数の操作は次の 3 つのステップに分けることができます。

  1. 関数とユーザーの入力を使用して Chat Completions API を呼び出す
  2. モデルの応答を使用して API または関数を呼び出す
  3. Chat Completions API をもう一度呼び出し、関数からの応答を含め、最終的な応答を取得する

重要

API の 2023-12-01-preview バージョンのリリースに伴い、functions および function_call パラメーターは非推奨になりました。 functions に置き換わるのは tools パラメーターです。 function_call に置き換わるのは tool_choice パラメーターです。

並列関数呼び出し

並列関数呼び出しは、次でサポートされています。

サポートされているモデル

  • gpt-35-turbo (1106)
  • gpt-35-turbo (0125)
  • gpt-4 (1106-Preview)
  • gpt-4 (0125-Preview)

API のサポート

並列関数のサポートは、API バージョン 2023-12-01-preview で最初に追加されました

並列関数呼び出しを使用すると、複数の関数呼び出しを同時に実行できるため、並列実行と結果の取得が可能になります。 これにより、必要な API 呼び出しの数が減り、全体的なパフォーマンスが向上します。

たとえば、単純な天気アプリでは、複数の場所の天気を同時に取得したい場合があります。 これにより、tool_calls 配列内にそれぞれ一意の id を持つ 3 つの関数呼び出しを含むチャット完了メッセージが生成されます。 これらの関数呼び出しに応答する場合は、それぞれ 1 つの関数呼び出しの結果を含む 3 つの新しいメッセージとともに、tools_calls から id を参照する tool_call_id を会話に追加します。

以下に、OpenAI の get_current_weather 例の変更後のバージョンを示します。 この例は、OpenAI の元のものと同様に、基本的な構造を提供するものですが、完全に機能するスタンドアロンの例ではありません。 このコードを変更せずに実行しようとすると、エラーが発生します。

この例では、1 つの関数 get_current_weather が定義されています。 モデルはこの関数を複数回呼び出し、関数の応答をモデルに返した後、次のステップを決定します。 サンフランシスコ、東京、パリの気温を 伝えるユーザー向けのメッセージで応答します。 クエリによっては、関数を再度呼び出すことが選択される場合があります。

モデルで特定の関数を強制的に呼び出すには、tool_choice パラメータに特定の関数名を設定します。 また、tool_choice: "none" を設定して、モデルにユーザー向けメッセージを生成させることもできます。

Note

既定の動作 (tool_choice: "auto") は、モデルが関数を呼び出すかどうかと、呼び出す場合はどの関数を呼び出すかを独自に決定することです。

import os
from openai import AzureOpenAI
import json

client = AzureOpenAI(
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
  api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
  api_version="2024-03-01-preview"
)


# Example function hard coded to return the same weather
# In production, this could be your backend API or an external API
def get_current_weather(location, unit="fahrenheit"):
    """Get the current weather in a given location"""
    if "tokyo" in location.lower():
        return json.dumps({"location": "Tokyo", "temperature": "10", "unit": unit})
    elif "san francisco" in location.lower():
        return json.dumps({"location": "San Francisco", "temperature": "72", "unit": unit})
    elif "paris" in location.lower():
        return json.dumps({"location": "Paris", "temperature": "22", "unit": unit})
    else:
        return json.dumps({"location": location, "temperature": "unknown"})

def run_conversation():
    # Step 1: send the conversation and available functions to the model
    messages = [{"role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?"}]
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "Get the current weather in a given location",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city and state, e.g. San Francisco, CA",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            },
        }
    ]
    response = client.chat.completions.create(
        model="<REPLACE_WITH_YOUR_MODEL_DEPLOYMENT_NAME>",
        messages=messages,
        tools=tools,
        tool_choice="auto",  # auto is default, but we'll be explicit
    )
    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls
    # Step 2: check if the model wanted to call a function
    if tool_calls:
        # Step 3: call the function
        # Note: the JSON response may not always be valid; be sure to handle errors
        available_functions = {
            "get_current_weather": get_current_weather,
        }  # only one function in this example, but you can have multiple
        messages.append(response_message)  # extend conversation with assistant's reply
        # Step 4: send the info for each function call and function response to the model
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions[function_name]
            function_args = json.loads(tool_call.function.arguments)
            function_response = function_to_call(
                location=function_args.get("location"),
                unit=function_args.get("unit"),
            )
            messages.append(
                {
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": function_response,
                }
            )  # extend conversation with function response
        second_response = client.chat.completions.create(
            model="<REPLACE_WITH_YOUR_1106_MODEL_DEPLOYMENT_NAME>",
            messages=messages,
        )  # get a new response from the model where it can see the function response
        return second_response
print(run_conversation())

Chat Completions API での関数の使用 (非推奨)

関数呼び出しは 2023-07-01-preview API バージョンで使用でき、gpt-35-turbo、gpt-35-turbo-16k、gpt-4、gpt-4-32k のバージョン 0613 で動作します。

Chat Completions API で関数呼び出しを使用するには、要求に functionsfunction_call という 2 つの新しいプロパティを含める必要があります。 要求には 1 つ以上の functions を含めることができ、関数の定義方法については、「関数の定義」セクションで詳細を確認できます。 関数は内部的にシステム メッセージに挿入されるため、関数はトークンの使用量に対して不利に働くことに留意してください。

関数が提供されると、既定では function_call"auto" に設定され、モデルは関数を呼び出す必要があるかどうかを判断します。 代わりに、function_call パラメーターを {"name": "<insert-function-name>"} に設定して、API が特定の関数を呼び出すことを強制するか、パラメーターを "none" に設定して、モデルがどの関数も呼び出さないようにすることもできます。

Note

OpenAI Python ライブラリ バージョン 0.28.1 は非推奨です。 1.x の使用を推奨します。 0.28.1 から 1.x への移行については、移行ガイドを参照してください。


import os
import openai

openai.api_key = os.getenv("AZURE_OPENAI_API_KEY")
openai.api_version = "2023-07-01-preview"
openai.api_type = "azure"
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT")

messages= [
    {"role": "user", "content": "Find beachfront hotels in San Diego for less than $300 a month with free breakfast."}
]

functions= [  
    {
        "name": "search_hotels",
        "description": "Retrieves hotels from the search index based on the parameters provided",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The location of the hotel (i.e. Seattle, WA)"
                },
                "max_price": {
                    "type": "number",
                    "description": "The maximum price for the hotel"
                },
                "features": {
                    "type": "string",
                    "description": "A comma separated list of features (i.e. beachfront, free wifi, etc.)"
                }
            },
            "required": ["location"]
        }
    }
]  

response = openai.ChatCompletion.create(
    engine="gpt-35-turbo-0613", # engine = "deployment_name"
    messages=messages,
    functions=functions,
    function_call="auto", 
)

print(response['choices'][0]['message'])
{
  "role": "assistant",
  "function_call": {
    "name": "search_hotels",
    "arguments": "{\n  \"location\": \"San Diego\",\n  \"max_price\": 300,\n  \"features\": \"beachfront,free breakfast\"\n}"
  }
}

モデルが関数を呼び出す必要があると判断した場合、API からの応答には function_call プロパティが含まれます。 function_call プロパティには、呼び出す関数の名前と、その関数に渡す引数が含まれます。 引数は、関数を呼び出すために解析して使用できる JSON 文字列です。

場合によっては、モデルは contentfunction_call の両方を生成します。 たとえば、上記のプロンプトに対してコンテンツは、function_call と連携して "はい。あなたの基準に合うサンディエゴのホテルをいくつか見つけるのをお手伝いすることができます" などと述べる可能性があります。

関数呼び出しの操作

次のセクションでは、関数を Chat Completions API と合わせて効果的に使用する方法についてさらに詳しく説明します。

関数の定義

関数には namedescriptionparameters という 3 つの主要なパラメーターがあります。 description パラメーターは、モデルによって関数を呼び出すタイミングと方法を判断するために使用されるため、関数が行うことの分かりやすい説明を与えることが重要です。

parameters は、関数が受け取るパラメーターを記述する JSON スキーマ オブジェクトです。 JSON スキーマ オブジェクトの詳細については、「JSON スキーマ リファレンス」で確認できます。

パラメーターをまったく受け取らない関数を記述したい場合は、parameters プロパティの値として {"type": "object", "properties": {}} を使用します。

フローの管理と関数

Python における例。


response = openai.ChatCompletion.create(
    deployment_id="gpt-35-turbo-0613",
    messages=messages,
    functions=functions,
    function_call="auto", 
)
response_message = response["choices"][0]["message"]

# Check if the model wants to call a function
if response_message.get("function_call"):

    # Call the function. The JSON response may not always be valid so make sure to handle errors
    function_name = response_message["function_call"]["name"]

    available_functions = {
            "search_hotels": search_hotels,
    }
    function_to_call = available_functions[function_name] 

    function_args = json.loads(response_message["function_call"]["arguments"])
    function_response = function_to_call(**function_args)

    # Add the assistant response and function response to the messages
    messages.append( # adding assistant response to messages
        {
            "role": response_message["role"],
            "function_call": {
                "name": function_name,
                "arguments": response_message["function_call"]["arguments"],
            },
            "content": None
        }
    )
    messages.append( # adding function response to messages
        {
            "role": "function",
            "name": function_name,
            "content": function_response,
        }
    ) 

    # Call the API again to get the final response from the model
    second_response = openai.ChatCompletion.create(
            messages=messages,
            deployment_id="gpt-35-turbo-0613"
            # optionally, you could provide functions in the second call as well
        )
    print(second_response["choices"][0]["message"])
else:
    print(response["choices"][0]["message"])

Powershell における例。

# continues from the previous PowerShell example

$response = Invoke-RestMethod -Uri $url -Headers $headers -Body $body -Method Post -ContentType 'application/json'
$response.choices[0].message | ConvertTo-Json

# Check if the model wants to call a function
if ($null -ne $response.choices[0].message.function_call) {

    $functionName  = $response.choices[0].message.function_call.name
    $functionArgs = $response.choices[0].message.function_call.arguments

    # Add the assistant response and function response to the messages
    $messages += @{
        role          = $response.choices[0].message.role
        function_call = @{
            name      = $functionName
            arguments = $functionArgs
        }
        content       = 'None'
    }
    $messages += @{
        role          = 'function'
        name          = $response.choices[0].message.function_call.name
        content       = "$functionName($functionArgs)"
    }

    # Call the API again to get the final response from the model
    
    # these API arguments are introduced in model version 0613
    $body = [ordered]@{
        messages      = $messages
        functions         = $functions
        function_call   = 'auto'
    } | ConvertTo-Json -depth 6

    $url = "$($openai.api_base)/openai/deployments/$($openai.name)/chat/completions?api-version=$($openai.api_version)"

    $secondResponse = Invoke-RestMethod -Uri $url -Headers $headers -Body $body -Method Post -ContentType 'application/json'
    $secondResponse.choices[0].message | ConvertTo-Json
}

出力例

{
  "role": "assistant",
  "content": "I'm sorry, but I couldn't find any beachfront hotels in San Diego for less than $300 a month with free breakfast."
}

これらの例では、検証やエラー処理をまったく行っていないので、それらをコードに追加するようにしたくなるでしょう。

関数の操作の完全な例については、「関数呼び出しに関するサンプル ノートブック」を参照してください。 さらに複雑なロジックを適用して、複数の関数呼び出しを合わせて連鎖させることもできます。これもサンプル内でカバーされています。

プロンプト エンジニアリングと関数

要求の一部として関数を定義すると、その詳細は、モデルがトレーニングされた特定の構文を使用してシステム メッセージに挿入されます。 これは、関数がプロンプトでトークンを使用すること、および自分でプロンプト エンジニアリング手法を適用して関数呼び出しのパフォーマンスを最適化できることを意味します。 モデルは、関数定義、システム メッセージ、およびユーザー メッセージを含むプロンプトの完全なコンテキストを使用して、関数を呼び出す必要があるかどうかを判断します。

品質と信頼性の改善

モデルが自分が期待したタイミングまたは方法で関数を呼び出していない場合は、品質を向上させるために試すことができることがいくつかあります。

関数定義でより多くの詳細を提供する

関数の分かりやすい description を提供し、モデルにとって明らかではない可能性があるすべてのパラメーターの説明を提供することが重要です。 たとえば、location パラメーターの説明では、場所の形式に関する追加の詳細と例を含めることができます。

"location": {
    "type": "string",
    "description": "The location of the hotel. The location should include the city and the state's abbreviation (i.e. Seattle, WA or Miami, FL)"
},
システム メッセージ内でより多くのコンテキストを提供する

システム メッセージは、モデルにより多くのコンテキストを提供するために使用することができます。 たとえば、search_hotels という関数がある場合は、次のようなシステム メッセージを含めて、ユーザーがホテルの検索で助けを求めた際にその関数を呼び出すようにモデルに指示できます。

{"role": "system", "content": "You're an AI assistant designed to help users search for hotels. When a user asks for help finding a hotel, you should call the search_hotels function."}
明確化のための質問を行うようにモデルに指示する

場合によっては、関数でどの値を使用するべきかについて推測を行うのを防ぐために、明確化のための質問をするようにモデルに指示する必要があります。 たとえば、search_hotels では、明確化のためにユーザー要求が location の詳細を含まないかをモデルに質問させたいということが考えられます。 明確化のための質問をするようにモデルに指示するために、システム メッセージに次の例のようなコンテンツを含めることができます。

{"role": "system", "content": "Don't make assumptions about what values to use with functions. Ask for clarification if a user request is ambiguous."}

エラーの削減

プロンプト エンジニアリングが重要になり得るもう 1 つの領域は、関数呼び出し時のエラー削減です。 モデルは、自分が定義するスキーマに一致する関数呼び出しを生成するようにトレーニングされているにも関わらず、自分が定義したスキーマと一致しない関数呼び出しを生成したり、自分が含めていない関数を呼び出そうとしたりします。

モデルが指定されていない関数呼び出しを生成していることに気付いた場合は、"Only use the functions you have been provided with." という文をシステム メッセージに含めることを試してください。

責任を持った関数呼び出しの使用

他の AI システムと同様に、言語モデルを他のツールやシステムと統合するために関数呼び出しを使用することは、潜在的なリスクを生み出します。 関数呼び出しが生み出す可能性があるリスクを理解し、自分がその機能を責任を持って使用するようにするための対策を講じることが重要です。

関数を安心かつセキュリティ的に安全に使用するのに役立つヒントをいくつか次に示します。

  • 関数呼び出しの検証: モデルによって生成された関数呼び出しを常に検証します。 これには、パラメーター、呼び出される関数のチェック、および呼び出しが目的のアクションに合うものであることの確認が含まれます。
  • 信頼できるデータとツールの使用: 信頼できる検証済みのソースからのデータのみを使用します。 関数の出力内の信頼できないデータは、自分が意図したのと違う方法で関数呼び出しを記述するようモデルに指示するために利用される可能性があります。
  • 最小特権の原則の順守: 関数が自身のジョブを実行するために必要な最小限のアクセス権のみを付与します。 これは関数が誤用または悪用された場合に起こり得る影響を軽減します。 たとえば、データベースにクエリを実行するために関数呼び出しを使用する場合は、アプリケーションにはデータベースへの読み取り専用アクセス権のみを付与するべきです。 また、セキュリティ制御として関数定義の除外機能だけに依存しないようにする必要もあります。
  • 実際の影響の考慮: 実行する予定の関数呼び出し、特にコードの実行、データベースの更新、通知の送信などのアクションをトリガーする関数呼び出しの実際の影響を意識するようにします。
  • ユーザーによる確認ステップの実装: 特にアクションを実行する関数の場合は、実行前にユーザーがアクションを確認するステップを含めることをお勧めします。

Azure OpenAI モデルを責任を持って使用する方法に関する推奨事項の詳細については、「Azure OpenAI モデルのための責任ある AI プラクティスの概要」を参照してください。

次のステップ