Programovatelné volání nástrojů pomocí rozhraní API pro odpovědi Azure OpenAI

Programové volání nástrojů pomocí kódu umožňuje modelu používat kód k orchestraci nástrojů během požadavku Responses API. Namísto vyžadování samostatného kroku modelu pro každé vyvolání nástroje může model vygenerovat program, který volá nástroje a zpracovává jejich výsledky. Program může provádět operace, jako je větvení nebo iterace.

Tento článek vysvětluje, kdy je programové vyvolávání nástrojů užitečné a jak se liší od volání funkcí, paralelního volání nástrojů a vyhledávání nástrojů.

Důležité informace o dostupnosti

Před implementací programového volání nástrojů ověřte, že tuto funkci podporují vaše nasazení modelu Azure OpenAI a verze sady SDK. Nepředpokládejte, že podpora rozhraní API pro odpovědi také vytváří podporu pro programové volání nástrojů.

Informace o nastavení koncového bodu a ověřování najdete v tématu Použití rozhraní API pro odpovědi Azure OpenAI.

Programatické volání nástrojů a volání funkcí

Při volání standardní funkce se vaše aplikace obvykle účastní každého kroku volání nástrojů:

Model -> volání nástroje -> aplikace spouští nástroj -> model -> další volání nástroje -> aplikace spouští nástroj -> model

S programovým voláním nástrojů může model generovat kód, který orchestruje více operací nástroje:

Model – vygenerovaný program –> operace nástrojů –> výsledek programu –> model –>

Tento přístup může snížit počet kroků modelu potřebných pro pracovní postupy zahrnující několik nástrojů.

Tyto toky znázorňují koncepční rozdíl v orchestraci. Nespecifikují, které operace vaše aplikace nebo služba provádí.

Příklad: Porovnejte zásoby s poptávkou

Tento příklad v Pythonu zpřístupňuje dvě funkce definované aplikací, které vracejí ukázková data o inventáři a poptávce. Model používá programové vyvolávání nástrojů k porovnání výsledků. Vaše aplikace spustí funkce a vrátí jejich výsledky, aby modul runtime mohl pokračovat v běhu vygenerovaného programu.

Bloky kódu tvoří jeden skript. Zkombinujte je v zobrazeném pořadí.

klient Azure OpenAI

Použijte nasazení Azure OpenAI, které podporuje programové volání nástrojů, a verzi sady OpenAI SDK, která obsahuje output_schema, allowed_callers a informace o volajícím. Nainstalujte balíčky openai a azure-identity do prostředí Python:

pip install --upgrade openai azure-identity

Nakonfigurujte ověřování Microsoft Entra ID, jak je popsáno v tématu Použití rozhraní API pro odpovědi OpenAI Azure. Vaše identita potřebuje oprávnění k volání vašeho nasazení Azure OpenAI.

Nahraďte YOUR-RESOURCE-NAME názvem svého prostředku. Hodnota model později v příkladu je název nasazení Azure, nikoli identifikátor katalogu modelů.

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)

Funkce definované aplikací

Funkce vrací pevná ukázková data: 42 dostupných jednotek a 31 požadovaných jednotek pro zadanou skladovou položku. Nahraďte jejich implementace zdroji dat o inventáři a poptávce ve vaší aplikaci.

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

Definice nástrojů

Každá funkce deklaruje vstupní schéma a output_schema popis výsledku JSON. Nastavení allowed_callers na ["programmatic"] zpřístupní funkci vygenerovaným programům místo přímých volání modelu.

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

Přidejte funkci poptávky a schopnost programového volání nástrojů do stejného seznamu:

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"},
])

Výsledky nástrojů a informace o volajícím

Aplikace předává volání funkcí podle názvu a serializuje každý výsledek. Zachová informace o volání caller, aby modul runtime mohl pokračovat ve správném programu. Neznámé názvy nástrojů a neplatné argumenty vyvolávají chyby místo vracení smyšlených výsledků.

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

Pokračování odpovědi

Nahraďte YOUR-MODEL-DEPLOYMENT názvem podporovaného nasazení Azure OpenAI.

S store=False aplikace pokračuje v konverzaci. Před přidáním výsledků nástroje zachovejte všechny výstupní položky, včetně položek programu a uvažování. Smyčka se zastaví na konečné odpovědi nebo odmítnutí a vyvolá chybu, pokud se nepodaří získat odpověď nebo dojde k dosažení limitu požadavků.

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")

Konečná odpověď by měla vysvětlit, že zásoby pokrývají poptávku: 42 dostupných jednotek minus 31 požadovaných jednotek, zbývá 11 jednotek. Formulace se může lišit. Odmítnutí se vytiskne místo toho, pokud model odmítne žádost.

Aplikace spouští funkce inventáře a poptávky, zatímco vygenerovaný program koordinuje jejich použití a zpracovává výsledky.

Kdy použít programové vyvolávání nástrojů

Zvažte volání programatického nástroje, když úkol:

  • Vyžaduje několik souvisejících volání nástrojů.
  • Obsahuje podmíněnou logiku.
  • Je třeba iterace nad výsledky nástrojů.
  • Provádí mezilehlé zpracování dat před vytvořením odpovědi.
  • Jinak by bylo nutné provést opakované kroky modelu ke koordinaci nástrojů.

U jednoduchých pracovních postupů, které zahrnují jednu nebo dvě nezávislé funkce, může být jednodušší implementovat standardní volání funkcí.

Programové volání nástrojů a paralelní volání nástrojů

Paralelní volání nástrojů a programové volání nástrojů řeší různé potřeby orchestrace.

Paralelní volání nástrojů použijte, když lze několik nezávislých operací provádět současně. Například načtěte počasí pro pět nezávislých míst.

Programové volání nástrojů použijte v případě, že operace nástrojů vyžadují koordinaci, průběžný výpočet, větvení nebo iteraci. Můžete například načíst objednávky zákazníka, vybrat objednávky, které odpovídají podmínce, a dotazovat se na informace o expedici těchto objednávek.

Ovládací prvky vyhledávání nástrojů určují, které definice nástrojů se načtou do kontextu modelu.

Programatické volání nástrojů řídí, jak model orchestruje nástroje po jejich zpřístupnění.

Rozdíl je mezi zjišťováním nástrojů a orchestrací nástrojů. Jejich koncepční vztah neurčuje, jestli vaše nasazení podporuje použití obou funkcí ve stejném požadavku.

Bezpečnostní aspekty

Považujte vygenerované programy za nedůvěryhodný kód.

Při zpřístupnění nástrojů:

  • Dodržujte princip nejnižších oprávnění.
  • Ověřte argumenty nástroje.
  • Omezte přístup k citlivým operacím.
  • Ověřujte a autorizujte operace nezávisle na výstupu modelu
  • Protokolujte spouštění nástrojů pro auditování a diagnostiku bez zaznamenávání tajných kódů nebo nepotřebných citlivých dat
  • Vyhněte se zpřístupnění nepotřebných nástrojů nebo dat.

Nespoléhejte na modelem vygenerovaný kód jako na hranici oprávnění.