Azure OpenAI 응답 API를 사용하여 프로그래밍 방식 도구 호출

프로그래밍 방식 도구 호출을 통해 모델은 코드를 사용하여 응답 API 요청 중에 도구를 오케스트레이션할 수 있습니다. 모델은 각 도구 호출에 대해 별도의 모델 턴을 요구하는 대신 도구를 호출하고 결과를 처리하는 프로그램을 생성할 수 있습니다. 프로그램은 분기 또는 반복과 같은 작업을 수행할 수 있습니다.

이 문서에서는 프로그래밍 방식 도구 호출이 유용한 시기와 함수 호출, 병렬 도구 호출 및 도구 검색과 어떻게 다른지 설명합니다.

가용성 고려 사항

프로그래밍 방식 도구 호출을 구현하기 전에 Azure OpenAI 모델 배포 및 SDK 버전이 이 기능을 지원하는지 확인합니다. 응답 API에 대한 지원도 프로그래밍 방식 도구 호출에 대한 지원을 의미한다고 가정하지 마세요.

엔드포인트 및 인증 설정은 Azure OpenAI 응답 API 사용을 참조하세요.

프로그래밍 방식 도구 호출 및 함수 호출

표준 함수 호출을 사용하면 애플리케이션이 일반적으로 각 도구 호출 단계에 참여합니다.

모델 -> 도구 호출 -> 애플리케이션이 도구를 실행 -> 모델 -> 다음 도구 호출 -> 애플리케이션이 도구를 실행 -> 모델

프로그래밍 방식 도구 호출을 사용하면 모델은 여러 도구 작업을 오케스트레이션하는 코드를 생성할 수 있습니다.

모델 -> 프로그램 생성 -> 도구 작업 -> 프로그램 결과 -> 모델

이 방법을 사용하면 여러 도구와 관련된 워크플로에 필요한 모델 턴 수를 줄일 수 있습니다.

이러한 흐름은 오케스트레이션의 개념적 차이를 보여 줍니다. 애플리케이션 또는 서비스가 어떤 작업을 실행하는지 지정하지 않습니다.

예: 인벤토리와 수요 비교

이 Python 예제에서는 샘플 인벤토리 및 수요 데이터를 반환하는 두 개의 애플리케이션 정의 함수를 노출합니다. 모델은 프로그래밍 방식 도구 호출을 사용하여 결과를 비교합니다. 애플리케이션은 함수를 실행하고 런타임이 생성된 프로그램의 실행을 재개할 수 있도록 결과를 반환합니다.

코드 블록은 하나의 스크립트를 이룹니다. 표시된 순서대로 결합합니다.

Azure OpenAI 클라이언트

프로그래밍 방식 도구 호출을 지원하는 Azure OpenAI 배포와 output_schema, allowed_callers, 그리고 호출자 정보를 포함하는 OpenAI SDK 버전을 사용합니다. Python 환경에 openai 및 azure-identity 패키지를 설치하세요:

pip install --upgrade openai azure-identity

Azure OpenAI Responses API 사용에 설명된 대로 Microsoft Entra ID 인증을 구성합니다. ID에는 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)

애플리케이션에서 정의한 함수

함수는 지정된 SKU에 대해 사용 가능한 단위 42개와 요청된 단위 31개 등 고정 샘플 데이터를 반환합니다. 애플리케이션에서 그 구현을 사용자의 인벤토리 및 수요 데이터 원본으로 대체합니다.

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,
}

도구 정의

각 함수는 입력 스키마와 JSON 결과를 설명하는 output_schema 설명을 선언합니다. 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"},
])

도구 결과 및 호출자 정보

애플리케이션은 이름으로 함수 호출을 디스패치하고 각 결과를 serialize합니다. 런타임이 올바른 프로그램을 재개할 수 있도록 호출 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개가 남습니다. 문구는 다를 수 있습니다. 모델이 요청을 거부하는 경우 대신 거부 응답이 인쇄됩니다.

애플리케이션은 인벤토리 및 수요 함수를 실행하고, 생성된 프로그램은 해당 함수들의 사용을 조정하며 결과를 처리합니다.

프로그래매틱 도구 호출을 사용해야 하는 경우

작업이 다음과 같은 경우 프로그래밍 방식 도구 호출을 고려합니다:

  • 여러 관련 도구 호출이 필요합니다.
  • 조건부 논리를 포함합니다.
  • 도구 결과를 순회해야 합니다.
  • 답변을 생성하기 전에 중간 데이터 처리를 수행합니다.
  • 그렇지 않으면 도구를 조정하기 위해 반복된 모델 턴이 필요합니다.

하나 또는 두 개의 독립 함수를 포함하는 간단한 워크플로의 경우 표준 함수 호출을 구현하는 것이 더 쉬울 수 있습니다.

프로그래매틱 도구 호출 및 병렬 도구 호출

병렬 도구 호출 및 프로그래밍 방식 도구 호출은 서로 다른 오케스트레이션 요구 사항을 해결합니다.

여러 독립 작업을 동시에 실행할 수 있는 경우 병렬 도구 호출 을 사용합니다. 예를 들어 5개의 독립적인 위치에 대한 날씨를 검색합니다.

도구 작업에 조정, 중간 계산, 분기 또는 반복이 필요한 경우 프로그래매틱 도구 호출을 사용합니다. 예를 들어 고객의 주문을 검색하고, 조건과 일치하는 주문을 선택하고, 해당 주문에 대한 배송 정보를 쿼리합니다.

도구 검색은 모델의 컨텍스트에 로드되는 도구 정의를 제어합니다.

프로그래매틱 도구 호출은 모델이 도구를 사용할 수 있게 된 후 도구를 오케스트레이션하는 방식을 제어합니다.

구분은 도구 검색과 도구 오케스트레이션 사이에 있습니다. 개념적 관계는 배포에서 동일한 요청에 두 기능을 모두 사용할 수 있는지 여부를 나타내지 않습니다.

보안 고려 사항

생성된 프로그램을 신뢰할 수 없는 코드로 처리합니다.

도구를 노출하는 경우:

  • 최소 권한 원칙을 따르세요.
  • 도구 인수의 유효성을 검사합니다.
  • 중요한 작업에 대한 액세스를 제한합니다.
  • 모델 출력과 독립적으로 작업(operation)을 인증하고 권한을 부여합니다.
  • 비밀 또는 불필요한 민감한 데이터를 기록하지 않고 감사 및 진단을 위한 로그 도구 실행을 기록합니다
  • 불필요한 도구 또는 데이터를 노출하지 않습니다.

모델 생성 코드를 권한 부여 경계로 사용하지 마세요.