Использование вызова функций с помощью службы Azure OpenAI (предварительная версия)

Последние версии gpt-35-turbo и gpt-4 настраиваются для работы с функциями и могут определять, когда и как должна вызываться функция. Если одна или несколько функций включены в запрос, модель определяет, следует ли вызывать какие-либо функции в зависимости от контекста запроса. Когда модель определяет, что должна вызываться функция, она реагирует на объект JSON, включая аргументы для функции.

Модели сформулируют вызовы API и выходные данные структуры на основе указанных функций. Важно отметить, что в то время как модели могут создавать эти вызовы, необходимо выполнить их, гарантируя, что вы остаетесь под контролем.

На высоком уровне можно разбить работу с функциями на три шага:

  1. Вызов API завершения чата с функциями и входными данными пользователя
  2. Используйте ответ модели для вызова API или функции
  3. Снова вызовите API завершения чата, включая ответ от функции, чтобы получить окончательный ответ

Внимание

function_call Параметры functions устарели с выпуском 2023-12-01-preview версии API. Для замены functions используется tools параметр. Для замены function_call используется tool_choice параметр.

Поддержка вызовов функций

Вызов параллельной функции

  • gpt-35-turbo (1106)
  • gpt-35-turbo (0125)
  • gpt-4 (1106-preview)
  • gpt-4 (0125-preview)
  • gpt-4 (визуально-предварительная версия)
  • gpt-4 (2024-04-09)
  • gpt-4o (2024-05-13)
  • gpt-4o-mini (2024-07-18)

Поддержка параллельной функции была добавлена в версию API 2023-12-01-preview

Базовый вызов функций с инструментами

  • Все модели, поддерживающие параллельные вызовы функций
  • gpt-4 (0613)
  • gpt-4-32k (0613)
  • gpt-35-turbo-16k (0613)
  • gpt-35-turbo (0613)

Пример вызова одного инструмента или функции

Сначала мы продемонстрируем простой вызов функции, который может проверить время в трех жестких расположениях с помощью одного инструмента или функции. Мы добавили инструкции печати, чтобы упростить выполнение кода.

import os
import json
from openai import AzureOpenAI
from datetime import datetime
from zoneinfo import ZoneInfo

# Initialize the Azure OpenAI client
client = AzureOpenAI(
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    api_version="2024-05-01-preview"
)

# Define the deployment you want to use for your chat completions API calls

deployment_name = "<YOUR_DEPLOYMENT_NAME_HERE>"

# Simplified timezone data
TIMEZONE_DATA = {
    "tokyo": "Asia/Tokyo",
    "san francisco": "America/Los_Angeles",
    "paris": "Europe/Paris"
}

def get_current_time(location):
    """Get the current time for a given location"""
    print(f"get_current_time called with location: {location}")  
    location_lower = location.lower()
    
    for key, timezone in TIMEZONE_DATA.items():
        if key in location_lower:
            print(f"Timezone found for {key}")  
            current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
            return json.dumps({
                "location": location,
                "current_time": current_time
            })
    
    print(f"No timezone data found for {location_lower}")  
    return json.dumps({"location": location, "current_time": "unknown"})

def run_conversation():
    # Initial user message
    messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    #messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

    # Define the function for the model
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_current_time",
                "description": "Get the current time in a given location",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city name, e.g. San Francisco",
                        },
                    },
                    "required": ["location"],
                },
            }
        }
    ]

    # First API call: Ask the model to use the function
    response = client.chat.completions.create(
        model=deployment_name,
        messages=messages,
        tools=tools,
        tool_choice="auto",
    )

    # Process the model's response
    response_message = response.choices[0].message
    messages.append(response_message)

    print("Model's response:")  
    print(response_message)  

    # Handle function calls
    if response_message.tool_calls:
        for tool_call in response_message.tool_calls:
            if tool_call.function.name == "get_current_time":
                function_args = json.loads(tool_call.function.arguments)
                print(f"Function arguments: {function_args}")  
                time_response = get_current_time(
                    location=function_args.get("location")
                )
                messages.append({
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": "get_current_time",
                    "content": time_response,
                })
    else:
        print("No tool calls were made by the model.")  

    # Second API call: Get the final response from the model
    final_response = client.chat.completions.create(
        model=deployment_name,
        messages=messages,
    )

    return final_response.choices[0].message.content

# Run the conversation and print the result
print(run_conversation())

Выходные данные:

Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_pOsKdUlqvdyttYB67MOj434b', function=Function(arguments='{"location":"San Francisco"}', name='get_current_time'), type='function')])
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
The current time in San Francisco is 09:24 AM.

Если мы используем развертывание модели, поддерживающее параллельные вызовы функций, мы можем преобразовать это в параллельный пример вызова функции, изменив массив сообщений, чтобы запросить время в нескольких расположениях вместо одного.

Для этого переключите комментарии в следующих двух строках:

    messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    #messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

Чтобы выглядеть следующим образом, и снова запустите код:

    #messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

Будут созданы следующие выходные данные:

Выходные данные:

Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_IjcAVz9JOv5BXwUx1jd076C1', function=Function(arguments='{"location": "San Francisco"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_XIPQYTCtKIaNCCPTdvwjkaSN', function=Function(arguments='{"location": "Tokyo"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_OHIB5aJzO8HGqanmsdzfytvp', function=Function(arguments='{"location": "Paris"}', name='get_current_time'), type='function')])
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
Function arguments: {'location': 'Tokyo'}
get_current_time called with location: Tokyo
Timezone found for tokyo
Function arguments: {'location': 'Paris'}
get_current_time called with location: Paris
Timezone found for paris
As of now, the current times are:

- **San Francisco:** 11:15 AM
- **Tokyo:** 03:15 AM (next day)
- **Paris:** 08:15 PM

Параллельные вызовы функций позволяют выполнять несколько вызовов функций вместе, что позволяет параллельно выполнять и извлекать результаты. Это уменьшает количество вызовов API, которые необходимо выполнить и могут повысить общую производительность.

Например, в нашем простом приложении времени мы извлекли несколько раз одновременно. Это привело к сообщению о завершении чата с тремя вызовами функций в массиве tool_calls , каждый из которых имеет уникальный id. Если вы хотите ответить на эти вызовы функций, вы добавите три новых сообщения в беседу, каждый из которых содержит результат одного вызова функции, со tool_call_id ссылкой id на него tools_calls.

Чтобы принудительно вызвать определенную функцию, задайте tool_choice параметр с определенным именем функции. Вы также можете принудительно создать сообщение для пользователя, задав для tool_choice: "none"модели.

Примечание.

Поведение по умолчанию (tool_choice: "auto") предназначено для модели, чтобы решить, следует ли вызывать функцию и, если да, какую функцию следует вызывать.

Параллельные вызовы функций с несколькими функциями

Теперь мы продемонстрируем еще один пример вызова функции- то время с двумя различными инструментами и функциями, определенными.

import os
import json
from openai import AzureOpenAI
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

# Initialize the Azure OpenAI client
client = AzureOpenAI(
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    api_version="2024-05-01-preview"
)

# Provide the model deployment name you want to use for this example

deployment_name = "YOUR_DEPLOYMENT_NAME_HERE" 

# Simplified weather data
WEATHER_DATA = {
    "tokyo": {"temperature": "10", "unit": "celsius"},
    "san francisco": {"temperature": "72", "unit": "fahrenheit"},
    "paris": {"temperature": "22", "unit": "celsius"}
}

# Simplified timezone data
TIMEZONE_DATA = {
    "tokyo": "Asia/Tokyo",
    "san francisco": "America/Los_Angeles",
    "paris": "Europe/Paris"
}

def get_current_weather(location, unit=None):
    """Get the current weather for a given location"""
    print(f"get_current_weather called with location: {location}, unit: {unit}")  
    
    for key in WEATHER_DATA:
        if key in location_lower:
            print(f"Weather data found for {key}")  
            weather = WEATHER_DATA[key]
            return json.dumps({
                "location": location,
                "temperature": weather["temperature"],
                "unit": unit if unit else weather["unit"]
            })
    
    print(f"No weather data found for {location_lower}")  
    return json.dumps({"location": location, "temperature": "unknown"})

def get_current_time(location):
    """Get the current time for a given location"""
    print(f"get_current_time called with location: {location}")  
    location_lower = location.lower()
    
    for key, timezone in TIMEZONE_DATA.items():
        if key in location_lower:
            print(f"Timezone found for {key}")  
            current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
            return json.dumps({
                "location": location,
                "current_time": current_time
            })
    
    print(f"No timezone data found for {location_lower}")  
    return json.dumps({"location": location, "current_time": "unknown"})

def run_conversation():
    # Initial user message
    messages = [{"role": "user", "content": "What's the weather and current time in San Francisco, Tokyo, and Paris?"}]

    # Define the functions for the model
    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 name, e.g. San Francisco",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            }
        },
        {
            "type": "function",
            "function": {
                "name": "get_current_time",
                "description": "Get the current time in a given location",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city name, e.g. San Francisco",
                        },
                    },
                    "required": ["location"],
                },
            }
        }
    ]

    # First API call: Ask the model to use the functions
    response = client.chat.completions.create(
        model=deployment_name,
        messages=messages,
        tools=tools,
        tool_choice="auto",
    )

    # Process the model's response
    response_message = response.choices[0].message
    messages.append(response_message)

    print("Model's response:")  
    print(response_message)  

    # Handle function calls
    if response_message.tool_calls:
        for tool_call in response_message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            print(f"Function call: {function_name}")  
            print(f"Function arguments: {function_args}")  
            
            if function_name == "get_current_weather":
                function_response = get_current_weather(
                    location=function_args.get("location"),
                    unit=function_args.get("unit")
                )
            elif function_name == "get_current_time":
                function_response = get_current_time(
                    location=function_args.get("location")
                )
            else:
                function_response = json.dumps({"error": "Unknown function"})
            
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": function_response,
            })
    else:
        print("No tool calls were made by the model.")  

    # Second API call: Get the final response from the model
    final_response = client.chat.completions.create(
        model=deployment_name,
        messages=messages,
    )

    return final_response.choices[0].message.content

# Run the conversation and print the result
print(run_conversation())

Выходные данные

Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_djHAeQP0DFEVZ2qptrO0CYC4', function=Function(arguments='{"location": "San Francisco", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_q2f1HPKKUUj81yUa3ITLOZFs', function=Function(arguments='{"location": "Tokyo", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_6TEY5Imtr17PaB4UhWDaPxiX', function=Function(arguments='{"location": "Paris", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_vpzJ3jElpKZXA9abdbVMoauu', function=Function(arguments='{"location": "San Francisco"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_1ag0MCIsEjlwbpAqIXJbZcQj', function=Function(arguments='{"location": "Tokyo"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_ukOu3kfYOZR8lpxGRpdkhhdD', function=Function(arguments='{"location": "Paris"}', name='get_current_time'), type='function')])
Function call: get_current_weather
Function arguments: {'location': 'San Francisco', 'unit': 'celsius'}
get_current_weather called with location: San Francisco, unit: celsius
Weather data found for san francisco
Function call: get_current_weather
Function arguments: {'location': 'Tokyo', 'unit': 'celsius'}
get_current_weather called with location: Tokyo, unit: celsius
Weather data found for tokyo
Function call: get_current_weather
Function arguments: {'location': 'Paris', 'unit': 'celsius'}
get_current_weather called with location: Paris, unit: celsius
Weather data found for paris
Function call: get_current_time
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
Function call: get_current_time
Function arguments: {'location': 'Tokyo'}
get_current_time called with location: Tokyo
Timezone found for tokyo
Function call: get_current_time
Function arguments: {'location': 'Paris'}
get_current_time called with location: Paris
Timezone found for paris
Here's the current information for the three cities:

### San Francisco
- **Time:** 09:13 AM
- **Weather:** 72°C (quite warm!)

### Tokyo
- **Time:** 01:13 AM (next day)
- **Weather:** 10°C

### Paris
- **Time:** 06:13 PM
- **Weather:** 22°C

Is there anything else you need?

Внимание

Ответ JSON может не всегда быть допустимым, поэтому необходимо добавить дополнительную логику в код, чтобы иметь возможность обрабатывать ошибки. В некоторых случаях использования может потребоваться использовать точное настройка для повышения производительности вызова функций.

Проектирование запросов с функциями

При определении функции в рамках запроса сведения внедряются в системное сообщение с использованием определенного синтаксиса, на который была обучена модель. Это означает, что функции используют маркеры в запросе и могут применять методы проектирования запросов для оптимизации производительности вызовов функций. Модель использует полный контекст запроса, чтобы определить, должна ли вызываться функция, включая определение функции, системное сообщение и сообщения пользователя.

Улучшение качества и надежности

Если модель не вызывает функцию, когда или как вы ожидаете, вы можете попытаться улучшить качество.

Дополнительные сведения в определении функции

Важно предоставить значимые 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.".

Использование вызовов функций ответственно

Как и любая система ИИ, используя вызов функции для интеграции языковых моделей с другими инструментами и системами, представляет потенциальные риски. Важно понимать риски, которые могут представлять вызов функций и принимать меры, чтобы обеспечить ответственное использование возможностей.

Ниже приведены несколько советов, которые помогут вам безопасно и безопасно использовать функции:

  • Проверка вызовов функций. Всегда проверяйте вызовы функций, созданные моделью. Это включает проверку параметров, вызываемую функцию и обеспечение соответствия вызова предполагаемому действию.
  • Используйте доверенные данные и средства. Используйте только данные из доверенных и проверенных источников. Ненадежные данные в выходных данных функции можно использовать для указания модели записывать вызовы функций, отличные от предполагаемого.
  • Следуйте принципу наименьшей привилегии: предоставьте только минимальный доступ, необходимый для выполнения функции. Это снижает потенциальное влияние, если функция используется неправильно или используется. Например, если вы используете вызовы функций для запроса базы данных, вы должны предоставить приложению доступ только для чтения к базе данных. Вы также не должны зависеть исключительно от исключения возможностей в определении функции в качестве элемента управления безопасностью.
  • Рассмотрим влияние реального мира: учитывайте реальное влияние вызовов функций, которые планируется выполнить, особенно те, которые активируют такие действия, как выполнение кода, обновление баз данных или отправка уведомлений.
  • Реализуйте действия подтверждения пользователей. Особенно для функций, которые выполняют действия, рекомендуется включить шаг, в котором пользователь подтверждает действие перед выполнением.

Дополнительные сведения о том, как использовать модели Azure OpenAI ответственно, см. в статье " Обзор методик ответственного искусственного интеллекта" для моделей Azure OpenAI.

Следующие шаги