Share via


如何搭配 Azure OpenAI 服務使用函式呼叫 (預覽)

最新版的 gpt-35-turbo 和 gpt-4 經過微調,可與函式搭配運作,而且能夠同時判斷應該呼叫函式的時機和方式。 如果您的要求中包含一或多個函式,模型會根據提示的內容來判斷是否應該呼叫任何函式。 當模型判斷應該呼叫函式時,它會以 JSON 對象回應,包括函式的自變數。

模型會根據您指定的函式來制定 API 呼叫和結構數據輸出。 請務必注意,雖然模型可以產生這些呼叫,但您必須執行這些呼叫,以確保您保持控制權。

進一步來説,您可以將使用函式分成三個步驟:

  1. 使用您的函式和使用者的輸入呼叫聊天完成 API
  2. 使用模型的回應來呼叫您的 API 或函式
  3. 再次呼叫聊天完成 API,包括來自函式的回應以取得最終回應

重要

functionsfunction_call 參數已隨著 API 版本的發行2023-12-01-preview而淘汰。 的取代 functionstools 參數。 的取代 function_calltool_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 個新訊息,每個訊息都包含一個 tool_call_id 函式呼叫的結果,並參考 id from tools_calls

以下提供 OpenAI 範例 get_current_weather 的修改版本。 如同 OpenAI 的原始範例,提供基本結構,但不是功能完整的獨立範例。 嘗試在不進一步修改的情況下執行此程式代碼會導致錯誤。

在此範例中,會定義單一函式get_current_weather。 模型會多次呼叫函式,並在將函式回應傳回模型之後,決定下一個步驟。 它會回應使用者面向的資訊,告知使用者三藩市、東京和巴黎的溫度。 視查詢而定,它可能會選擇再次呼叫函式。

若要強制模型呼叫特定函式,請使用特定函式名稱來設定 tool_choice 參數。 您也可以藉由設定 tool_choice: "none"來強制模型產生使用者面向的訊息。

注意

默認行為 (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())

在聊天完成 API 中使用函式 (已淘汰)

函式呼叫適用於 2023-07-01-preview API 版本,且可搭配版本 0613 的 gpt-35-turbo、gpt-35-turbo-16k、gpt-4 和 gpt-4-32k 使用。

若要搭配聊天完成 API 使用函式呼叫,您需要在要求中包含兩個新的屬性: functionsfunction_call。 您可以在要求中包含一或多個 functions ,並深入瞭解如何在定義函式一 節中定義函 式。 請記住,函式會在背後插入系統訊息中,因此函式會計入您的權杖使用量。

當提供函式時,預設 會 function_call 設定為 "auto" ,而且模型會決定是否應該呼叫函式。 或者,您可以將 function_call 參數設定 {"name": "<insert-function-name>"},以強制 API 呼叫特定函式,或者您可以將參數設定為 "none",以防止模型呼叫任何函式。

注意

OpenAI Python 連結庫版本 0.28.1 已被取代。 建議使用 1.x。 如需從 移至 的資訊,請參閱我們的移轉指南1.x0.28.1


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。

使用函式呼叫

下一節將詳細說明如何有效地搭配聊天完成 API 使用函式。

定義函式

函式有三個主要參數: namedescriptionparameters。 模型會使用 description 參數來判斷呼叫函式的時機和方式,因此請務必提供一段有意義的描述解釋函式的功能。

parameters 是個 JSON 結構描述物件,描述函式接受的參數。 您可以在 [JSON 結構描述參考] 中深入了解 JSON 結構描述物件。

如果您想要描述不接受任何參數的函式,請使用 {"type": "object", "properties": {}} 做為 parameters 屬性的值。

使用函式管理流程

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."}

減少錯誤

另一個提示工程有價值的地方是減少函式呼叫中的錯誤。 模型會定型以產生符合您定義的架構的函式呼叫,但模型會產生不符合您定義之架構的函式呼叫,或嘗試呼叫未包含的函式。

如果您發現模型正在產生未提供的函式呼叫,請嘗試在系統訊息中包含顯示 "Only use the functions you have been provided with." 的句子。

負責任地使用呼叫函式

就像任何 AI 系統一樣,使用函式呼叫來整合語言模型與其他工具和系統,可能會帶來潛在的風險。 請務必了解函式呼叫可能會出現的風險,並採取措施以確保您負責地使用功能。

以下是一些可協助您安全可靠地使用函式的秘訣:

  • 驗證函式呼叫: 一律驗證模型所產生的函式呼叫。 這包括檢查參數、所呼叫的函式,以及確保呼叫符合預期的動作。
  • 使用受信任的資料和工具: 僅使用來自受信任和已驗證來源的資料。 函式輸出中不受信任的資料可用來指示模型以您預期以外的方式撰寫函式呼叫。
  • 遵循最低權限的原則: 只授與函式執行其作業所需的最低存取權。 如果函式遭到誤用或惡意探索,這會降低潛在影響。 例如,如果您使用函式呼叫來查詢資料庫,則應該只為應用程式提供資料庫的唯讀存取權。 您也不應該只依賴將函式定義中的功能排除為安全性控制項。
  • 請考慮實際影響: 請注意您計畫執行之函式呼叫的實際影響,特別是觸發動作,例如執行程式碼、更新資料庫或傳送通知。
  • 實作使用者確認步驟: 特別是針對採取動作的函式,我們建議包含一個步驟讓使用者在執行動作之前進行確認。

若要深入了解我們對如何負責任地使用 Azure OpenAI 模型的建議,請參閱 Azure OpenAI 模型的負責任 AI 做法的概觀

下一步