For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegação principal

Chamada de função

Dê aos modelos acesso a novas funcionalidades e dados que eles possam usar para seguir instruções e responder a prompts.

A chamada de função (também conhecida como chamada de ferramenta) oferece uma maneira poderosa e flexível de os modelos da OpenAI interagirem com sistemas externos e acessarem dados além daqueles usados em seu treinamento. Este guia mostra como conectar um modelo aos dados e às ações disponibilizados pelo seu aplicativo. Mostraremos como usar ferramentas de função (definidas por um esquema JSON) e ferramentas personalizadas que trabalham com entradas e saídas de texto em formato livre.

Para sessões da API de Agentes, use Funções para registrar funções e lidar com solicitações de ação da sessão. Os exemplos deste guia mostram as integrações com a API Responses e o Chat Completions.

Se seu aplicativo tiver muitas funções ou esquemas grandes, você pode combinar a chamada de função com a pesquisa de ferramentas para adiar o carregamento de ferramentas pouco usadas e carregá-las apenas quando o modelo precisar delas. Somente gpt-5.4 e modelos posteriores oferecem suporte a tool_search.

O GPT-6 Astra exige a Responses API para chamadas de ferramentas. Os exemplos de Chat Completions usam GPT-5.6 por compatibilidade. Consulte o guia de migração para atualizar uma integração existente.

Como funciona

Vamos começar entendendo alguns termos essenciais sobre chamadas de ferramentas. Depois de estabelecer esse vocabulário comum, mostraremos como fazer essas chamadas com exemplos práticos.

O fluxo de chamadas de ferramentas

A chamada de ferramenta é uma conversa em várias etapas entre seu aplicativo e um modelo por meio da API da OpenAI. Esse fluxo tem cinco etapas principais:

  1. Faça uma requisição ao modelo com ferramentas que ele possa chamar
  2. Receba uma chamada de ferramenta do modelo
  3. Execute código no aplicativo com a entrada da chamada de ferramenta
  4. Faça uma segunda requisição ao modelo com a saída da ferramenta
  5. Receba uma resposta final do modelo (ou mais chamadas de ferramentas)

Diagrama das etapas de chamada de função

Com a Responses, seu aplicativo pode continuar esse fluxo por quantas chamadas de ferramentas a tarefa exigir. Se você quiser um framework que reúna a orquestração recorrente desse ciclo, veja como a Responses API se compara ao Agents SDK.

Exemplo de ferramenta de função

Vamos ver um fluxo completo de chamadas de ferramentas para uma função get_horoscope que consulta o horóscopo diário de um signo astrológico.

Exemplo completo de chamada de ferramenta
from openai import OpenAI
import json

client = OpenAI()

# 1. Define a list of callable tools for the model
tools = [
    {
        "type": "function",
        "name": "get_horoscope",
        "description": "Get today's horoscope for an astrological sign.",
        "parameters": {
            "type": "object",
            "properties": {
                "sign": {
                    "type": "string",
                    "description": "An astrological sign like Taurus or Aquarius",
                },
            },
            "required": ["sign"],
        },
    },
]


def get_horoscope(sign):
    return f"{sign}: Next Tuesday you will befriend a baby otter."


# Create a running input list we will add to over time
input_list = [{"role": "user", "content": "What is my horoscope? I am an Aquarius."}]

# 2. Prompt the model with tools defined
response = client.responses.create(
    model="gpt-6-astra",
    tools=tools,
    input=input_list,
)

# Save function call outputs for subsequent requests
input_list += response.output

for item in response.output:
    if item.type == "function_call":
        if item.name == "get_horoscope":
            # 3. Execute the function logic for get_horoscope
            sign = json.loads(item.arguments)["sign"]
            horoscope = get_horoscope(sign)

            # 4. Provide function call results to the model
            input_list.append(
                {
                    "type": "function_call_output",
                    "call_id": item.call_id,
                    "output": horoscope,
                }
            )

print("Final input:")
print(input_list)

response = client.responses.create(
    model="gpt-6-astra",
    instructions="Respond only with a horoscope generated by a tool.",
    tools=tools,
    input=input_list,
)

# 5. The model should be able to give a response!
print("Final output:")
print(response.model_dump_json(indent=2))
print("\n" + response.output_text)

Observe que, para modelos de raciocínio como GPT-5 ou o4-mini, todos os itens de raciocínio retornados nas respostas do modelo com chamadas de ferramentas também devem ser enviados de volta com as saídas dessas chamadas.

Definição de funções

As funções geralmente são declaradas no parâmetro tools de cada requisição de API. Com a pesquisa de ferramentas, seu aplicativo também pode carregar funções cujo carregamento foi adiado em um momento posterior da interação. Em ambos os casos, cada função disponível para chamada usa a mesma estrutura de esquema. A definição de uma função tem as seguintes propriedades:

CampoDescrição
typeDeve ser sempre function
nameO nome da função (por exemplo, get_weather)
descriptionDetalhes sobre quando e como usar a função
parametersEsquema JSON que define os argumentos de entrada da função
strictIndica se o modo estrito deve ser aplicado à chamada de função

Veja um exemplo de definição da função get_weather

{
  "type": "function",
  "name": "get_weather",
  "description": "Retrieves current weather for the given location.",
  "parameters": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City and country e.g. Bogotá, Colombia"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Units the temperature will be returned in."
      }
    },
    "required": ["location", "units"],
    "additionalProperties": false
  },
  "strict": true
}

Como os parameters são definidos por um esquema JSON, você pode aproveitar seus diversos recursos, como tipos de propriedades, enumerações, descrições, objetos aninhados e objetos recursivos.

Definição de espaços de nomes

Use espaços de nomes para agrupar ferramentas relacionadas por domínio, como crm, billing ou shipping. Os espaços de nomes ajudam a organizar ferramentas semelhantes e são especialmente úteis quando o modelo precisa escolher entre ferramentas que atendem a sistemas ou finalidades diferentes, como uma ferramenta de pesquisa para seu CRM e outra para seu sistema de chamados de suporte.

{
  "type": "namespace",
  "name": "crm",
  "description": "CRM tools for customer lookup and order management.",
  "tools": [
    {
      "type": "function",
      "name": "get_customer_profile",
      "description": "Fetch a customer profile by customer ID.",
      "parameters": {
        "type": "object",
        "properties": {
          "customer_id": { "type": "string" }
        },
        "required": ["customer_id"],
        "additionalProperties": false
      }
    },
    {
      "type": "function",
      "name": "list_open_orders",
      "description": "List open orders for a customer ID.",
      "defer_loading": true,
      "parameters": {
        "type": "object",
        "properties": {
          "customer_id": { "type": "string" }
        },
        "required": ["customer_id"],
        "additionalProperties": false
      }
    }
  ]
}

Se você precisar dar ao modelo acesso a um grande ecossistema de ferramentas, poderá adiar o carregamento de algumas ou de todas elas com tool_search. A ferramenta tool_search permite que o modelo pesquise ferramentas relevantes, adicione-as ao contexto e depois as use. Somente gpt-5.4 e modelos posteriores oferecem suporte a ela. Leia o guia de pesquisa de ferramentas para saber mais.

Práticas recomendadas para definir funções

  1. Escreva nomes de funções, descrições de parâmetros e instruções claros e detalhados.

    • Descreva explicitamente a finalidade da função e de cada parâmetro (e seu formato), além do que a saída representa.
    • Use o prompt do sistema para descrever quando usar (e quando não usar) cada função. Em geral, diga ao modelo exatamente o que fazer.
    • Inclua exemplos e casos extremos, especialmente para corrigir falhas recorrentes. (Observação: adicionar exemplos pode prejudicar o desempenho dos modelos de raciocínio.)
    • Para ferramentas com carregamento adiado, inclua orientações detalhadas na descrição da função e mantenha a descrição do espaço de nomes concisa. O espaço de nomes ajuda o modelo a escolher o que carregar; a descrição da função ajuda a usar corretamente a ferramenta carregada.
  2. Aplique as práticas recomendadas de engenharia de software.

    • Torne as funções previsíveis e intuitivas. (princípio da menor surpresa)
    • Use enumerações e a estrutura dos objetos para evitar estados inválidos. Por exemplo, toggle_light(on: bool, off: bool) permite chamadas inválidas.
    • Passe no teste do estagiário. Um estagiário ou outra pessoa consegue usar a função corretamente apenas com as informações que você deu ao modelo? (Se não, quais perguntas essa pessoa faz? Adicione as respostas ao prompt.)
  3. Reduza a carga sobre o modelo e use código sempre que possível.

    • Não faça o modelo preencher argumentos cujos valores você já conhece. Por exemplo, se você já tem um order_id obtido de um menu anterior, não inclua um parâmetro order_id. Em vez disso, defina submit_refund() sem parâmetros e passe o order_id no seu código.
    • Combine funções que são sempre chamadas em sequência. Por exemplo, se você sempre chama mark_location() após query_location(), basta mover a lógica de marcação para a chamada da função de consulta.
  4. Mantenha pequeno o número de funções disponíveis inicialmente para obter maior precisão.

    • Avalie o desempenho com diferentes quantidades de funções.
    • Procure disponibilizar menos de 20 funções no início de cada turno ao mesmo tempo, embora essa seja apenas uma sugestão flexível.
    • Use a pesquisa de ferramentas para adiar o carregamento de partes grandes ou pouco usadas do seu conjunto de ferramentas, em vez de expor tudo desde o início.
  5. Aproveite os recursos da OpenAI.

    • Gere e refine esquemas de funções no Playground.
    • Considere o ajuste fino para aumentar a precisão das chamadas de função quando houver muitas funções ou tarefas difíceis. (cookbook)

Uso de tokens

Internamente, as funções são inseridas na mensagem do sistema em uma sintaxe com a qual o modelo foi treinado. Isso significa que as definições de funções disponíveis para chamada contam para o limite de contexto do modelo e são cobradas como tokens de entrada. Se você atingir os limites de tokens, sugerimos limitar o número de funções carregadas inicialmente, encurtar as descrições quando possível ou usar a pesquisa de ferramentas para que as ferramentas com carregamento adiado sejam carregadas apenas quando necessário.

Também é possível usar o ajuste fino para reduzir o número de tokens usados se houver muitas funções definidas na sua especificação de ferramentas.

Tratamento de chamadas de função

Quando o modelo chama uma função, você deve executá-la e retornar o resultado. Como as respostas do modelo podem incluir nenhuma, uma ou várias chamadas, a prática recomendada é assumir que haverá várias.

O array output da resposta contém uma entrada com o campo type definido como function_call. Cada entrada contém um call_id (usado posteriormente para enviar o resultado da função), um name e arguments codificados em JSON.

Exemplo de resposta com várias chamadas de função
[
    {
        "id": "fc_12345xyz",
        "call_id": "call_12345xyz",
        "type": "function_call",
        "name": "get_weather",
        "arguments": "{\"location\":\"Paris, France\"}"
    },
    {
        "id": "fc_67890abc",
        "call_id": "call_67890abc",
        "type": "function_call",
        "name": "get_weather",
        "arguments": "{\"location\":\"Bogotá, Colombia\"}"
    },
    {
        "id": "fc_99999def",
        "call_id": "call_99999def",
        "type": "function_call",
        "name": "send_email",
        "arguments": "{\"to\":\"bob@email.com\",\"body\":\"Hi bob\"}"
    }
]

Se você estiver usando a pesquisa de ferramentas, também poderá ver itens tool_search_call e tool_search_output antes de um function_call. Depois que a função for carregada, trate a chamada de função da mesma forma mostrada aqui.

Execute as chamadas de função e acrescente os resultados
input_messages += response.output

for tool_call in response.output:
    if tool_call.type != "function_call":
        continue

    name = tool_call.name
    args = json.loads(tool_call.arguments)

    result = call_function(name, args)
    input_messages.append(
        {
            "type": "function_call_output",
            "call_id": tool_call.call_id,
            "output": json.dumps(result),
        }
    )

No exemplo acima, usamos uma função hipotética call_function para encaminhar cada chamada. Veja uma possível implementação:

Execute as chamadas de função e acrescente os resultados
def call_function(name, args):
    if name == "get_weather":
        return get_weather(**args)
    if name == "send_email":
        return send_email(**args)
    raise ValueError(f"Unknown function: {name}")

Formatação dos resultados

O resultado que você passa na mensagem function_call_output normalmente deve ser uma string, em um formato à sua escolha (JSON, códigos de erro, texto simples etc.). O modelo interpretará essa string conforme necessário.

Para funções que retornam imagens ou arquivos, você pode passar um array de objetos de imagem ou arquivo em vez de uma string.

Se sua função não tiver valor de retorno (por exemplo, send_email), retorne uma string que indique sucesso ou falha, como "success".

Incorporação dos resultados à resposta

Depois de acrescentar os resultados a input, você pode enviá-los de volta ao modelo para obter uma resposta final.

Envie os resultados de volta ao modelo
response = client.responses.create(
    model="gpt-6-astra",
    input=input_messages,
    tools=responses_tools,
)

print(response.output_text)
Resposta final
"It's about 15°C in Paris, 18°C in Bogotá, and I've sent that email to Bob."

Configurações adicionais

Escolha de ferramentas

Por padrão, o modelo determina quando e quantas ferramentas usar. Você pode impor um comportamento específico com o parâmetro tool_choice.

  1. Automático: (Padrão) Chama zero, uma ou várias funções. tool_choice: "auto"
  2. Obrigatório: Chama uma ou mais funções. tool_choice: "required"
  3. Função forçada: Chama exatamente uma função específica. tool_choice: {"type": "function", "name": "get_weather"}
  4. Ferramentas permitidas: Restringe as chamadas de ferramentas que o modelo pode fazer a um subconjunto das ferramentas disponíveis para ele.

Quando usar allowed_tools

Você pode configurar uma lista allowed_tools se quiser disponibilizar apenas um subconjunto de ferramentas nas requisições ao modelo, sem modificar a lista de ferramentas enviada, para maximizar a economia com o cache de prompts.

"tool_choice": {
    "type": "allowed_tools",
    "mode": "auto",
    "tools": [
        { "type": "function", "name": "get_weather" },
        { "type": "function", "name": "search_docs" }
    ]
  }
}

Você também pode definir tool_choice como "none" para simular o comportamento de não enviar nenhuma função.

Quando você usa a pesquisa de ferramentas, tool_choice continua se aplicando às ferramentas que podem ser chamadas naquele momento do turno. Isso é especialmente útil depois de carregar um subconjunto de ferramentas, quando você quer limitar o modelo a esse subconjunto.

Chamada de funções em paralelo

Nos modelos compatíveis a partir do GPT-5, as funções podem ser chamadas em paralelo quando ferramentas integradas também estão disponíveis. As ferramentas integradas não podem ser incluídas em um lote de chamadas de funções em paralelo.

O modelo pode optar por chamar várias funções em um único turno. Você pode impedir isso definindo parallel_tool_calls como false, o que garante que sejam chamadas exatamente zero ou uma ferramenta.

Observação: Atualmente, se você estiver usando um modelo com ajuste fino e ele chamar várias funções em um turno, o modo estrito será desativado para essas chamadas.

Observação sobre gpt-4.1-nano-2025-04-14: Esta versão de gpt-4.1-nano pode, às vezes, incluir várias chamadas à mesma ferramenta se as chamadas de ferramentas em paralelo estiverem ativadas. Recomendamos desativar esse recurso ao usar esta versão.

Modo estrito

Definir strict como true garante que as chamadas de funções sigam o esquema da função de forma confiável, em vez de apenas tentarem segui-lo sem garantia de conformidade. Recomendamos sempre ativar o modo estrito.

Internamente, o modo estrito usa nosso recurso de saídas estruturadas e, por isso, impõe alguns requisitos:

  1. additionalProperties deve ser definido como false para cada objeto em parameters.
  2. Todos os campos em properties devem ser marcados como required.

Você pode indicar campos opcionais adicionando null como uma opção de type (veja o exemplo abaixo).

Se você enviar strict: true e seu esquema não atender aos requisitos acima, a requisição será rejeitada com detalhes sobre as restrições ausentes. Se você omitir strict, o comportamento padrão dependerá da API: as requisições à Responses tentarão normalizar seu esquema para o modo estrito quando possível e recorrerão à chamada de funções sem modo estrito, sem garantia de conformidade, se não for possível tornar o esquema compatível com o modo estrito. Quando isso acontecer, a ferramenta na resposta mostrará strict: false. As requisições à Chat Completions continuam sem modo estrito por padrão. Para desativar o modo estrito na Responses e manter a chamada de funções sem modo estrito e sem garantia de conformidade, defina explicitamente strict: false.

{
    "type": "function",
    "name": "get_weather",
    "description": "Retrieves current weather for the given location.",
    "strict": true,
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "City and country e.g. Bogotá, Colombia"
            },
            "units": {
                "type": ["string", "null"],
                "enum": ["celsius", "fahrenheit"],
                "description": "Units the temperature will be returned in."
            }
        },
        "required": ["location", "units"],
        "additionalProperties": false
    }
}

Todos os esquemas gerados no playground têm o modo estrito ativado.

Embora recomendemos ativar o modo estrito, ele tem algumas limitações:

  1. Alguns recursos de esquemas JSON não são compatíveis. (Veja os esquemas compatíveis.)

Especificamente para modelos com ajuste fino:

  1. Os esquemas passam por processamento adicional na primeira requisição e depois são armazenados em cache. Se seus esquemas variarem entre as requisições, isso poderá aumentar a latência.
  2. Os esquemas são armazenados em cache para melhorar o desempenho e não são elegíveis para zero retenção de dados.

Streaming

O streaming pode ser usado para mostrar o progresso, indicando qual função é chamada enquanto o modelo preenche seus argumentos e até exibindo esses argumentos em tempo real.

O streaming de chamadas de função é muito semelhante ao streaming de respostas comuns: você define stream como true e recebe diferentes objetos event.

Streaming de chamadas de função
from openai import OpenAI

client = OpenAI()

tools = [
    {
        "type": "function",
        "name": "get_weather",
        "description": "Get current temperature for a given location.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City and country e.g. Bogotá, Colombia",
                }
            },
            "required": ["location"],
            "additionalProperties": False,
        },
    }
]

stream = client.responses.create(
    model="gpt-6-astra",
    input=[{"role": "user", "content": "What's the weather like in Paris today?"}],
    tools=tools,
    stream=True,
)

for event in stream:
    print(event)
Eventos de saída
{"type":"response.output_item.added","response_id":"resp_1234xyz","output_index":0,"item":{"type":"function_call","id":"fc_1234xyz","call_id":"call_1234xyz","name":"get_weather","arguments":""}}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"{\""}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"location"}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"\":\""}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"Paris"}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":","}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":" France"}
{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"\"}"}
{"type":"response.function_call_arguments.done","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"arguments":"{\"location\":\"Paris, France\"}"}
{"type":"response.output_item.done","response_id":"resp_1234xyz","output_index":0,"item":{"type":"function_call","id":"fc_1234xyz","call_id":"call_1234xyz","name":"get_weather","arguments":"{\"location\":\"Paris, France\"}"}}

No entanto, em vez de agregar os fragmentos em uma única string content, você os agrega em um objeto JSON arguments codificado.

Quando o modelo chama uma ou mais funções, um evento do tipo response.output_item.added é emitido para cada chamada de função. Esse evento contém os seguintes campos:

CampoDescrição
response_idO ID da resposta à qual a chamada de função pertence
output_indexO índice do item de saída na resposta. Ele representa as chamadas de função individuais na resposta.
itemO item da chamada de função em andamento, que inclui os campos name, arguments e id

Em seguida, você recebe uma série de eventos do tipo response.function_call_arguments.delta que contêm o delta do campo arguments. Esses eventos contêm os seguintes campos:

CampoDescrição
response_idO ID da resposta à qual a chamada de função pertence
item_idO ID do item da chamada de função ao qual o delta pertence
output_indexO índice do item de saída na resposta. Ele representa as chamadas de função individuais na resposta.
deltaO delta do campo arguments.

Veja abaixo um trecho de código que demonstra como agregar os deltas em um objeto tool_call final.

Acumulando deltas de tool_call
final_tool_calls = {}

for event in stream:
    if event.type == "response.output_item.added":
        final_tool_calls[event.output_index] = event.item
    elif event.type == "response.function_call_arguments.delta":
        index = event.output_index

        if final_tool_calls[index]:
            final_tool_calls[index].arguments += event.delta
final_tool_calls[0] acumulado
{
    "type": "function_call",
    "id": "fc_1234xyz",
    "call_id": "call_2345abc",
    "name": "get_weather",
    "arguments": "{\"location\":\"Paris, France\"}"
}

Quando o modelo termina de chamar as funções, um evento do tipo response.function_call_arguments.done é emitido. Esse evento contém a chamada de função completa, incluindo os seguintes campos:

CampoDescrição
response_idO ID da resposta à qual a chamada de função pertence
output_indexO índice do item de saída na resposta. Ele representa as chamadas de função individuais na resposta.
itemO item da chamada de função, que inclui os campos name, arguments e id.

Ferramentas personalizadas

As ferramentas personalizadas funcionam de forma muito semelhante às ferramentas de função baseadas em esquemas JSON. Porém, em vez de receber instruções explícitas sobre a entrada exigida pela ferramenta, o modelo pode enviar uma string arbitrária como entrada para ela. Isso é útil para evitar encapsular uma resposta em JSON sem necessidade ou para aplicar uma gramática personalizada à resposta (mais detalhes abaixo).

O exemplo de código a seguir mostra como criar uma ferramenta personalizada que espera receber, como resposta, uma string de texto contendo código Python.

Exemplo de chamada de ferramenta personalizada
from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-astra",
    input="Use the code_exec tool to print hello world to the console.",
    tools=[
        {
            "type": "custom",
            "name": "code_exec",
            "description": "Executes arbitrary Python code.",
        }
    ],
)
print(response.output)

Assim como antes, o array output contém uma chamada de ferramenta gerada pelo modelo. Desta vez, porém, a entrada da chamada de ferramenta é fornecida como texto simples.

[
  {
    "id": "rs_6890e972fa7c819ca8bc561526b989170694874912ae0ea6",
    "type": "reasoning",
    "content": [],
    "summary": []
  },
  {
    "id": "ctc_6890e975e86c819c9338825b3e1994810694874912ae0ea6",
    "type": "custom_tool_call",
    "status": "completed",
    "call_id": "call_aGiFQkRWSWAIsMQ19fKqxUgb",
    "input": "print(\"hello world\")",
    "name": "code_exec"
  }
]

Gramáticas livres de contexto

Uma gramática livre de contexto (CFG) é um conjunto de regras que definem como produzir texto válido em um determinado formato. Para ferramentas personalizadas, você pode fornecer uma CFG que restrinja o texto que o modelo envia como entrada para a ferramenta.

Você pode fornecer uma CFG personalizada usando o parâmetro grammar ao configurar uma ferramenta personalizada. Atualmente, oferecemos suporte a duas formas de sintaxe de CFG para definir gramáticas: lark e regex.

CFG Lark

Exemplo de gramática livre de contexto Lark
from openai import OpenAI

client = OpenAI()

grammar = """
start: expr
expr: term (SP ADD SP term)* -> add
| term
term: factor (SP MUL SP factor)* -> mul
| factor
factor: INT
SP: " "
ADD: "+"
MUL: "*"
%import common.INT
"""

response = client.responses.create(
    model="gpt-6-astra",
    input="Use the math_exp tool to add four plus four.",
    tools=[
        {
            "type": "custom",
            "name": "math_exp",
            "description": "Creates valid mathematical expressions",
            "format": {
                "type": "grammar",
                "syntax": "lark",
                "definition": grammar,
            },
        }
    ],
)
print(response.output)

A saída da ferramenta deve então seguir a CFG Lark que você definiu:

[
  {
    "id": "rs_6890ed2b6374819dbbff5353e6664ef103f4db9848be4829",
    "type": "reasoning",
    "content": [],
    "summary": []
  },
  {
    "id": "ctc_6890ed2f32e8819daa62bef772b8c15503f4db9848be4829",
    "type": "custom_tool_call",
    "status": "completed",
    "call_id": "call_pmlLjmvG33KJdyVdC4MVdk5N",
    "input": "4 + 4",
    "name": "math_exp"
  }
]

As gramáticas são especificadas usando uma variação de Lark. A amostragem do modelo é restringida pelo LLGuidance. Alguns recursos do Lark não são compatíveis:

  • Asserções de contexto nas expressões regulares do analisador léxico
  • Modificadores não gulosos (*?, +?, ??) nas expressões regulares do analisador léxico
  • Prioridades dos terminais
  • Modelos
  • Importações (exceto a importação integrada %import common)
  • Diretivas %declare

Recomendamos usar o Lark IDE para experimentar gramáticas personalizadas.

Limite a complexidade da gramática

Limite sua gramática às regras e aos padrões de que sua ferramenta precisa. A API da OpenAI pode retornar um erro se a gramática for complexa demais. Por isso, verifique se a gramática desejada é compatível antes de usá-la na API.

Aperfeiçoar gramáticas Lark pode ser difícil. Gramáticas menos complexas funcionam com mais confiabilidade, enquanto as mais complexas costumam exigir ajustes sucessivos na própria definição da gramática, no prompt e na descrição da ferramenta para garantir que o modelo não opere fora da distribuição.

Padrões corretos e incorretos

Correto (um único terminal com limites definidos):

start: SENTENCE
SENTENCE: /[A-Za-z, ]*(the hero|a dragon|an old man|the princess)[A-Za-z, ]*(fought|saved|found|lost)[A-Za-z, ]*(a treasure|the kingdom|a secret|his way)[A-Za-z, ]*\./

NÃO faça isto (dividir entre regras/terminais). Essa abordagem tenta fazer com que as regras dividam o texto livre entre terminais. O analisador léxico fará a correspondência dos trechos de texto livre de forma gulosa, e você perderá o controle:

start: sentence
sentence: /[A-Za-z, ]+/ subject /[A-Za-z, ]+/ verb /[A-Za-z, ]+/ object /[A-Za-z, ]+/

Regras em minúsculas não influenciam como os terminais são extraídos da entrada; somente as definições dos terminais fazem isso. Quando precisar de “texto livre entre âncoras”, use um único terminal com uma expressão regular que abranja tudo, para que o analisador léxico faça a correspondência exatamente uma vez, com a estrutura desejada.

Terminais e regras

O Lark usa terminais para os tokens do analisador léxico (por convenção, UPPERCASE) e regras para as produções do analisador sintático (por convenção, lowercase). A maneira mais prática de se manter dentro do subconjunto compatível e evitar surpresas é manter a gramática explícita, evitar complexidade desnecessária e usar terminais e regras com uma separação clara de responsabilidades.

A sintaxe de expressões regulares usada pelos terminais é a sintaxe da crate regex do Rust, e não a do módulo re do Python.

Conceitos principais e práticas recomendadas

O analisador léxico é executado antes do analisador sintático

O analisador léxico faz a correspondência dos terminais de forma gulosa (a correspondência mais longa prevalece) antes que qualquer lógica das regras da CFG seja aplicada. Se você tentar “moldar” um terminal dividindo-o entre várias regras, essas regras não poderão orientar o analisador léxico; somente as expressões regulares dos terminais poderão fazer isso.

Prefira um único terminal ao extrair texto de trechos em formato livre

Se você precisar reconhecer um padrão inserido em um texto arbitrário (por exemplo, linguagem natural com “qualquer coisa” entre âncoras), expresse esse padrão como um único terminal. Não tente intercalar terminais de texto livre com regras do analisador sintático; o analisador léxico guloso não respeitará os limites pretendidos, e é muito provável que o modelo opere fora da distribuição.

Use regras para combinar tokens distintos

As regras são ideais para combinar terminais explicitamente delimitados (números, palavras-chave, pontuação) em estruturas maiores. Elas não são a ferramenta adequada para restringir “o conteúdo entre” dois terminais.

Mantenha os terminais com propósito específico, limites definidos e estrutura autossuficiente

Prefira classes de caracteres explícitas e quantificadores limitados ({0,10}, em vez de usar * sem limites por toda parte). Se precisar de “qualquer texto até um ponto final”, prefira algo como /[^.\n]{0,10}*\./ em vez de /.+\./ para evitar crescimento descontrolado.

Use regras para combinar tokens, não para controlar o funcionamento interno das expressões regulares

Exemplo de uso adequado de regras:

start: expr
NUMBER: /[0-9]+/
PLUS: "+"
MINUS: "-"
expr: term (("+"|"-") term)*
term: NUMBER

Trate os espaços em branco explicitamente

Não dependa de diretivas %ignore sem limites definidos. Usar diretivas de ignorar sem limites pode tornar a gramática complexa demais e/ou fazer com que o modelo saia da distribuição. Prefira inserir terminais explícitos em todos os pontos em que espaços em branco forem permitidos.

Solução de problemas

  • Se a API rejeitar a gramática por ser complexa demais, simplifique as regras e os terminais e remova as diretivas %ignore sem limites definidos.
  • Se as ferramentas personalizadas forem chamadas com tokens inesperados, confirme se os terminais não se sobrepõem e verifique o comportamento guloso do analisador léxico.
  • Quando o modelo sai da distribuição (isso se manifesta em saídas excessivamente longas ou repetitivas, sintaticamente válidas, mas semanticamente incorretas):
    • Torne a gramática mais restritiva.
    • Ajuste o prompt (adicione exemplos few-shot) e a descrição da ferramenta (explique a gramática e instrua o modelo a raciocinar e segui-la).
    • Experimente um esforço de raciocínio maior (por exemplo, aumente de médio para alto).

CFG com expressões regulares

Exemplo de gramática livre de contexto com expressões regulares
from openai import OpenAI

client = OpenAI()

grammar = r"^(?P<month>January|February|March|April|May|June|July|August|September|October|November|December)\s+(?P<day>\d{1,2})(?:st|nd|rd|th)?\s+(?P<year>\d{4})\s+at\s+(?P<hour>0?[1-9]|1[0-2])(?P<ampm>AM|PM)$"

response = client.responses.create(
    model="gpt-6-astra",
    input="Use the timestamp tool to save a timestamp for August 7th 2025 at 10AM.",
    tools=[
        {
            "type": "custom",
            "name": "timestamp",
            "description": "Saves a timestamp in date + time in 24-hr format.",
            "format": {
                "type": "grammar",
                "syntax": "regex",
                "definition": grammar,
            },
        }
    ],
)
print(response.output)

A saída da ferramenta deverá então seguir a CFG com expressões regulares que você definiu:

[
  {
    "id": "rs_6894f7a3dd4c81a1823a723a00bfa8710d7962f622d1c260",
    "type": "reasoning",
    "content": [],
    "summary": []
  },
  {
    "id": "ctc_6894f7ad7fb881a1bffa1f377393b1a40d7962f622d1c260",
    "type": "custom_tool_call",
    "status": "completed",
    "call_id": "call_8m4XCnYvEmFlzHgDHbaOCFlK",
    "input": "August 7th 2025 at 10AM",
    "name": "timestamp"
  }
]

Assim como na sintaxe do Lark, as expressões regulares usam a sintaxe da crate regex do Rust, e não a do módulo re do Python.

Alguns recursos de expressões regulares não são compatíveis:

  • Asserções de contexto
  • Modificadores não gulosos (*?, +?, ??)

Conceitos principais e práticas recomendadas

O padrão deve estar em uma única linha

Se precisar corresponder a uma quebra de linha na entrada, use a sequência de escape \n. Não use o modo detalhado/estendido, que permite distribuir padrões por várias linhas.

Forneça a expressão regular como uma string contendo apenas o padrão

Não coloque o padrão entre //.