Chamada de ferramenta programática com a API OpenAI Responses do Azure

A chamada de ferramenta programática permite que um modelo use código para orquestrar ferramentas durante um pedido da Responses API. Em vez de exigir um turno de modelo separado para cada invocação de ferramenta, o modelo pode gerar um programa que chama ferramentas e processa os seus resultados. O programa pode realizar operações como ramificação ou iteração.

Este artigo explica quando a chamada programática de ferramentas é útil e como difere da chamada de funções, chamada paralela de ferramentas e pesquisa de ferramentas.

Considerações sobre disponibilidade

Antes de implementar a chamada de ferramenta programática, confirme se a implantação do seu modelo Azure OpenAI e a versão do SDK suportam a funcionalidade. Não presuma que o suporte para a API Responses também estabelece suporte para chamadas programáticas de ferramentas.

Para configuração de endpoint e autenticação, veja Usar a API de Respostas OpenAI do Azure.

Chamada programática de ferramentas e chamada de funções

Com a chamada de função padrão, a sua aplicação normalmente participa em cada etapa de chamada de ferramenta:

Modelo -> chamada de ferramenta -> aplicação executa ferramenta -> modelo -> próxima chamada de ferramenta -> aplicação executa ferramenta -> modelo

Com a chamada de ferramenta programática, o modelo pode gerar código que orquestra múltiplas operações de ferramentas:

Modelo -> programa gerado -> operações de ferramenta -> resultado do programa -> modelo

Esta abordagem pode reduzir o número de turnos de modelo necessários para fluxos de trabalho envolvendo várias ferramentas.

Estes fluxos ilustram a diferença conceptual na orquestração. Eles não especificam quais as operações que a sua aplicação ou o serviço executa.

Exemplo: Compare o stock com a procura

Este exemplo em Python expõe duas funções definidas pela aplicação que retornam dados de exemplo de inventário e procura. O modelo utiliza chamada programática de ferramentas para comparar os resultados. A sua aplicação executa as funções e devolve os seus resultados para que o runtime possa retomar a execução do programa gerado.

Os blocos de código formam um único script. Combine-os pela ordem mostrada.

Azure OpenAI client

Utilize uma implementação OpenAI do Azure que suporte chamada de ferramentas programáticas e uma versão do SDK OpenAI que inclua output_schema, allowed_callers, e informações do chamador. Instale os pacotes openai e azure-identity no seu ambiente Python:

pip install --upgrade openai azure-identity

Configure a autenticação do Microsoft Entra ID conforme descrito em Use the Azure OpenAI Responses API. A sua identidade precisa de permissão para invocar a sua implementação do Azure OpenAI.

Substitui YOUR-RESOURCE-NAME pelo nome do teu recurso. O model valor mais adiante no exemplo é o nome da sua implementação no Azure, não um identificador de catálogo de modelos.

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)

Funções definidas pela aplicação

As funções retornam dados de amostra fixos: 42 unidades disponíveis e 31 unidades solicitadas para o SKU especificado. Substitua as implementações deles pelas suas fontes de dados de inventário e procura na sua aplicação.

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

Definições de ferramentas

Cada função declara um esquema de entrada e um output_schema que descreve o resultado JSON. Definir allowed_callers para ["programmatic"] torna a função disponível para programas gerados em vez de chamadas diretas ao modelo.

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

Adicione a função de demanda e a capacidade de chamada de ferramenta programática à mesma lista:

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

Resultados das ferramentas e informações do autor da chamada

A aplicação despacha chamadas de função pelo nome e serializa cada resultado. Mantenha a informação da caller chamada para que o runtime possa retomar o programa correto. Nomes desconhecidos de ferramentas e argumentos inválidos geram erros em vez de devolver resultados fabricados.

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

Continuação da resposta

Substitua YOUR-MODEL-DEPLOYMENT pelo nome da sua implantação suportada do Azure OpenAI.

Com store=False, a aplicação conduz a conversa para a frente. Conserve todos os itens de saída, incluindo itens de programa e de raciocínio, antes de adicionar os resultados das ferramentas. O ciclo termina numa resposta final ou recusa e gera um erro se uma resposta falhar ou se o limite de pedidos for atingido.

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

A resposta final deve explicar que o inventário cobre a procura: 42 unidades disponíveis menos 31 unidades solicitadas deixam 11 unidades. A redação pode variar. Em vez disso, é impressa uma recusa se o modelo recusar o pedido.

A aplicação executa as funções de inventário e procura, enquanto o programa gerado coordena a sua utilização e processa os resultados.

Quando usar a chamada de ferramentas programáticas

Considere a chamada de ferramenta programática quando uma tarefa:

  • Requer várias chamadas de ferramentas relacionadas.
  • Contém lógica condicional.
  • É necessária iteração sobre os resultados da ferramenta.
  • Realiza o processamento intermédio dos dados antes de produzir uma resposta.
  • Caso contrário, exigiria interações repetidas do modelo para coordenar ferramentas.

Para fluxos de trabalho simples que envolvem uma ou duas funções independentes, a chamada de função padrão pode ser mais fácil de implementar.

Chamada programática de ferramentas e chamada paralela de ferramentas

A chamada paralela de ferramentas e a chamada programática de ferramentas respondem a diferentes necessidades de orquestração.

Utilize a chamada paralela de ferramentas quando várias operações independentes podem ser executadas ao mesmo tempo. Por exemplo, recolha informações meteorológicas de cinco locais independentes.

Utilize a chamada programática de ferramentas quando as operações da ferramenta requerem coordenação, computação intermédia, ramificação ou iteração. Por exemplo, recuperar as encomendas de um cliente, selecionar encomendas que correspondam a uma condição e consultar informações de envio dessas encomendas.

A pesquisa de ferramentas controla quais as definições de ferramentas que são carregadas no contexto do modelo.

A chamada programática de ferramentas controla como o modelo orquestra as ferramentas depois de estarem disponíveis.

A distinção é entre descoberta de ferramentas e orquestração de ferramentas. A relação conceptual deles não estabelece se a sua implantação suporta o uso de ambas as funcionalidades no mesmo pedido.

Considerações de segurança

Trate os programas gerados como código não confiável.

Ao expor ferramentas:

  • Siga os princípios do menor privilégio.
  • Validar argumentos da ferramenta.
  • Restringir o acesso a operações sensíveis.
  • Autenticar e autorizar operações independentemente da saída do modelo.
  • Registe a execução da ferramenta para auditoria e diagnóstico sem registar segredos ou dados sensíveis desnecessários.
  • Evite expor ferramentas ou dados desnecessários.

Não confie no código gerado pelo modelo como limite de autorização.