Программный вызов инструментов с помощью API Azure OpenAI Responses

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

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

Вопросы доступности

Перед реализацией программного вызова инструментов убедитесь, что развертывание модели Azure OpenAI и версия пакета SDK поддерживают эту функцию. Не предполагайте, что поддержка Responses API также означает поддержку программного вызова инструментов.

Сведения о настройке конечной точки и проверке подлинности см. в разделе "Использование API ответов Azure OpenAI".

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

При стандартном вызове функций приложение обычно участвует в каждом шаге вызова инструментов:

Модель —> вызов> инструмента — приложение выполняет инструмент —> модель> — следующий вызов> инструмента — приложение выполняет инструмент —> модель

При программном вызове инструментов модель может создать код, который управляет несколькими операциями инструментов:

Модель -> созданная программа -> операции инструмента -> результат программы -> модель

Такой подход может уменьшить количество итераций модели, необходимых для рабочих процессов, в которых участвуют несколько инструментов.

Эти потоки иллюстрируют концептуальное различие в оркестрации. Они не указывают, какие операции выполняет ваше приложение или служба.

Пример: Сравните запасы со спросом

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

Блоки кода формируют один скрипт. Объедините их в указанном порядке.

клиент Azure OpenAI

Используйте развертывание Azure OpenAI, поддерживающее программный вызов инструментов, и версию пакета SDK OpenAI, включающую output_schema, allowed_callers и сведения о вызывающем объекте. Установите пакеты openai и azure-identity в среде Python:

pip install --upgrade openai azure-identity

Настройте проверку подлинности Microsoft Entra ID, как описано в разделе Использование API Responses Azure OpenAI. Вашей учетной записи требуется разрешение на вызов развертывания Azure OpenAI.

Замените YOUR-RESOURCE-NAME на имя вашего ресурса. Значение model далее в примере — это имя развертывания Azure, а не идентификатор каталога моделей.

import json

from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from openai import OpenAI

# Use the Azure OpenAI v1 endpoint and Microsoft Entra ID authentication.
endpoint = "https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1/"
credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(
    credential, "https://cognitiveservices.azure.com/.default"
)
openai = OpenAI(base_url=endpoint, api_key=token_provider)

Определяемые приложением функции

Функции возвращают фиксированные примеры данных: 42 доступных единиц и 31 запрошенных единиц для указанного номера SKU. Замените их реализации на ваши источники данных об инвентаре и спросе в приложении.

def get_inventory(sku):
    return {"sku": sku, "available_units": 42}


def get_demand(sku):
    return {"sku": sku, "requested_units": 31}


implementations = {
    "get_inventory": get_inventory,
    "get_demand": get_demand,
}

Определения инструментов

Каждая функция объявляет входную схему и output_schema, описывающий результат JSON. Установка allowed_callers в ["programmatic"] делает функцию доступной для созданных программ, а не для прямых вызовов модели.

tools = [
    {
        "type": "function",
        "name": "get_inventory",
        "description": "Return the SKU and available inventory units.",
        "strict": True,
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "available_units": {"type": "number"},
            },
            "required": ["sku", "available_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    }
]

Добавьте функцию спроса и возможность программного вызова инструментов в тот же список:

tools.extend([
    {
        "type": "function",
        "name": "get_demand",
        "description": "Return the SKU and requested demand units.",
        "strict": True,
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "requested_units": {"type": "number"},
            },
            "required": ["sku", "requested_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    },
    {"type": "programmatic_tool_calling"},
])

Результаты инструмента и информация о вызывающей стороне

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

def run_tool(call):
    run = implementations.get(call.name)
    if run is None:
        raise ValueError(f"Unknown tool: {call.name}")

    arguments = json.loads(call.arguments)
    if (
        not isinstance(arguments, dict)
        or set(arguments) != {"sku"}
        or not isinstance(arguments["sku"], str)
    ):
        raise ValueError(f"Invalid arguments for tool: {call.name}")

    result = run(**arguments)
    output = {
        "type": "function_call_output",
        "call_id": call.call_id,
        "output": json.dumps(result),
    }
    if call.caller is not None:
        output["caller"] = call.caller.model_dump(exclude_none=True)
    return output

Продолжение ответа

Замените YOUR-MODEL-DEPLOYMENT именем поддерживаемого развертывания Azure OpenAI.

При этом store=Falseприложение продолжит беседу. Сохраните каждый выходной элемент, включая элементы программы и элементы рассуждений, прежде чем добавлять результаты средства. Цикл останавливается на окончательном ответе или отказе и вызывает ошибку, если не удаётся получить ответ или достигнуто ограничение запроса.

input_items = [
    {"role": "user", "content": "Compare inventory with demand for sku_123."}
]
MAX_REQUESTS = 10
for _ in range(MAX_REQUESTS):
    response = openai.responses.create(
        model="YOUR-MODEL-DEPLOYMENT",
        store=False, input=input_items, tools=tools,
    )
    if response.status != "completed":
        raise RuntimeError(f"Response ended with status {response.status}")
    input_items.extend(
        item.model_dump(exclude_none=True) for item in response.output
    )
    calls = [item for item in response.output if item.type == "function_call"]
    if calls:
        input_items.extend(run_tool(call) for call in calls)
        continue
    if response.output_text:
        print(response.output_text)
        break
    refusals = [
        part.refusal for item in response.output if item.type == "message"
        for part in item.content if part.type == "refusal"
    ]
    if refusals:
        print("\n".join(refusals))
        break
else:
    raise RuntimeError("Programmatic tool calling exceeded the request limit")

Окончательный ответ должен объяснить, что запасы покрывают спрос: 42 доступные единицы минус 31 запрошенная единица = 11 единиц. Формулировка может отличаться. Вместо этого выводится отказ, если модель отклоняет запрос.

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

Когда следует использовать вызов инструментов программным способом

Рассмотрите возможность программного вызова средства, если задача:

  • Требуется несколько связанных вызовов инструментов.
  • Содержит условную логику.
  • Требуется итерация по результатам инструмента.
  • Выполняет промежуточную обработку данных перед созданием ответа.
  • В противном случае потребовались бы повторные ходы модели для координации инструментов.

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

Программный вызов инструментов и параллельный вызов инструментов

Параллельный вызов инструментов и программный вызов инструментов решают разные потребности оркестрации.

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

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

Поиск инструментов определяет, какие определения инструментов загружаются в контекст модели.

Программное управление вызовом инструментов определяет, как модель оркестрирует инструменты после того, как они становятся доступными.

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

Вопросы безопасности

Обрабатывать созданные программы как ненадежный код.

При предоставлении доступа к инструментам:

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

Не полагайтесь на созданный моделью код в качестве границы авторизации.