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
Theme

Chamada de função

Dê aos modelos acesso a novos recursos e dados para que possam seguir instruções e responder a prompts.

A chamada de função (também conhecida como chamada de ferramenta) oferece aos modelos da OpenAI uma maneira poderosa e flexível de interagir com sistemas externos e acessar dados que não fizeram parte do treinamento. Este guia mostra como conectar um modelo aos dados e às ações que seu aplicativo oferece. Você verá como usar ferramentas do tipo função (definidas por um schema JSON) e ferramentas personalizadas que recebem e retornam texto livre.

Em sessões da Agents API, use Funções para registrar funções e processar solicitações de ação da sessão. Os exemplos deste guia mostram integrações com a Responses API e com a API chat completions.

Se seu aplicativo tiver muitas funções ou schemas grandes, você pode combinar a chamada de função com a pesquisa de ferramentas para adiar o carregamento de ferramentas pouco usadas até que o modelo precise delas. Somente o gpt-5.4 e modelos posteriores oferecem suporte a tool_search.

O GPT-6 Astra e o GPT-6.1 Sol exigem a Responses API para chamadas de ferramenta. Os exemplos da API chat completions usam o GPT-5.6 Terra com o raciocínio desativado. Consulte o guia de migração para atualizar uma integração existente.

Como funciona

Antes de passar aos exemplos práticos, vamos conhecer alguns termos fundamentais para entender como funciona a chamada de ferramenta.

Fluxo de chamada de ferramenta

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

  1. Envie uma solicitação ao modelo com as ferramentas que ele pode chamar.
  2. Receba uma chamada de ferramenta do modelo.
  3. Execute o código no aplicativo com a entrada fornecida na chamada.
  4. Envie uma segunda solicitação ao modelo com a saída da ferramenta.
  5. Receba a resposta final do modelo (ou outras chamadas de ferramenta).

Diagrama das etapas da chamada de função

Com a Responses API, seu aplicativo pode continuar esse fluxo por quantas chamadas de ferramenta a tarefa exigir. Se quiser usar um framework que cuide das etapas recorrentes de orquestração desse ciclo, veja a comparação entre a Responses API e o Agents SDK.

Exemplo de ferramenta do tipo função

Veja o fluxo completo de chamada de ferramenta com uma função get_horoscope que consulta o horóscopo do dia para um signo.

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",
        "strict": True,
        "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"],
            "additionalProperties": False,
        },
    },
]


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

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(response.output_text)

Ao usar modelos de raciocínio como o GPT-5 ou o o4-mini, envie de volta, junto com as saídas das chamadas de ferramenta, todos os itens de raciocínio que o modelo retornou nas respostas que continham essas chamadas. Para saber como preservar o histórico que deu origem a esses itens, consulte as boas práticas para alterar o prefixo.

Como definir funções

Normalmente, você declara as funções no parâmetro tools de cada solicitação à API. Com a pesquisa de ferramentas, seu aplicativo também pode adiar o carregamento de funções para um momento posterior da interação. Em ambos os casos, o schema de cada função que pode ser chamada segue a mesma estrutura. A definição de uma função tem as seguintes propriedades:

Campo Descrição
type Deve ser sempre function
name Nome da função (por exemplo, get_weather)
description Detalhes sobre quando e como usar a função
parameters Schema JSON que define os argumentos de entrada da função
strict Indica 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 parameters é definido por um schema JSON, você pode aproveitar recursos como tipos de propriedades, enumerações, descrições, objetos aninhados e objetos recursivos.

Como definir namespaces

Use namespaces para agrupar ferramentas relacionadas por domínio, como crm, billing ou shipping. Os namespaces ajudam a organizar ferramentas semelhantes e são especialmente úteis quando o modelo precisa escolher entre ferramentas destinadas a sistemas ou finalidades diferentes, como uma ferramenta de pesquisa para o CRM e outra para o 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
      }
    }
  ]
}

Pesquisa de ferramentas

Se precisar dar ao modelo acesso a muitas ferramentas, você pode adiar o carregamento de algumas ou de todas elas com tool_search. A ferramenta tool_search permite que o modelo procure ferramentas relevantes, adicione-as ao contexto e passe a usá-las. Somente o gpt-5.4 e modelos posteriores oferecem suporte a esse recurso. Saiba mais no guia de pesquisa de ferramentas.

Boas práticas para definir funções

  1. Escolha nomes claros e descritivos para as funções e escreva descrições de parâmetros e instruções detalhadas.

    • Descreva explicitamente a finalidade da função e de cada parâmetro (incluindo seu formato), além do significado da saída.
    • Use o prompt do sistema para descrever quando usar cada função e quando evitar seu uso. 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 namespace concisa. O namespace ajuda o modelo a escolher o que carregar. A descrição da função o ajuda a usar a ferramenta corretamente após o carregamento.
  2. Aplique boas práticas de engenharia de software.

    • Crie 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.
    • Faça o teste do estagiário. Uma pessoa em início de carreira conseguiria usar a função corretamente só com as informações que você deu ao modelo? (Se não conseguir, que perguntas ela faria? Inclua as respostas no prompt.)
  3. Reduza o trabalho do modelo e use código sempre que possível.

    • Evite pedir ao modelo que preencha argumentos cujos valores você já conhece. Por exemplo, se você já obteve um order_id em um menu anterior, não inclua um parâmetro order_id. 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() depois de query_location(), incorpore a lógica de marcação à função de consulta.
  4. Disponibilize poucas funções inicialmente para aumentar a precisão.

    • Avalie o desempenho com diferentes quantidades de funções.
    • Procure disponibilizar menos de 20 funções no início de cada turno, mas considere esse número apenas uma referência.
    • Use a pesquisa de ferramentas para adiar o carregamento de grupos grandes de ferramentas ou de ferramentas pouco usadas, em vez de disponibilizar tudo de início.
  5. Aproveite os recursos da OpenAI.

    • Gere e refine schemas de funções no Playground.
    • Considere usar o ajuste fino para aumentar a precisão da chamada de função quando houver muitas funções ou tarefas difíceis. (Exemplo no Cookbook)

Uso de tokens

Internamente, as funções são inseridas na mensagem do sistema com uma sintaxe que o modelo aprendeu durante o treinamento. Assim, as definições das funções disponíveis contam para o limite de contexto do modelo e são cobradas como tokens de entrada. Se atingir o limite de tokens, sugerimos reduzir o número de funções carregadas inicialmente, encurtar as descrições quando possível ou usar a pesquisa de ferramentas para carregar as ferramentas adiadas somente quando necessárias.

Se a especificação das ferramentas definir muitas funções, você também pode usar o ajuste fino para reduzir o número de tokens usados.

Como processar chamadas de função

Quando o modelo chama uma função, você precisa executá-la e retornar o resultado. Como as respostas do modelo podem incluir zero, uma ou várias chamadas, é uma boa prática preparar seu código para processar várias.

O array output da resposta contém uma entrada cujo type tem o valor function_call. Cada entrada desse tipo contém um call_id (usado posteriormente para enviar o resultado da função), um name e os 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 estiver usando a pesquisa de ferramentas, você também poderá ver itens tool_search_call e tool_search_output antes de um function_call. Após o carregamento da função, processe a chamada como neste exemplo.

Execução das chamadas de função e inclusão dos 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 à função correspondente. Veja uma possível implementação:

Execução das chamadas de função e inclusão dos 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

Em geral, o resultado que você passa na mensagem function_call_output deve ser uma string. Você escolhe o formato (JSON, códigos de erro, texto simples etc.). O modelo interpreta a 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 a função não tiver valor de retorno (por exemplo, send_email), retorne uma string que indique sucesso ou falha, como "success".

Inclusão dos resultados na resposta

Depois de adicionar os resultados a input, envie a entrada atualizada de volta ao modelo para obter a resposta final.

Envio dos resultados de volta ao modelo
response = client.responses.create(
    model="gpt-6-astra",
    input=input_messages,
    tools=responses_tools,
)
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 decide quando usar ferramentas e quantas usar. Você pode impor um comportamento específico com o parâmetro tool_choice.

  1. Automática: (Padrão) O modelo pode chamar zero, uma ou várias funções. tool_choice: "auto"
  2. Obrigatória: O modelo deve chamar uma ou mais funções. tool_choice: "required"
  3. Função imposta: O modelo deve chamar exatamente uma função específica. tool_choice: {"type": "function", "name": "get_weather"}
  4. Ferramentas permitidas: Limite as chamadas do modelo a um subconjunto das ferramentas disponíveis.

Quando usar allowed_tools

Configure uma lista allowed_tools se quiser disponibilizar apenas um subconjunto de ferramentas nas solicitações ao modelo e manter a lista completa que você envia. Assim, você pode 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 obter o mesmo comportamento que teria se não passasse nenhuma função.

Ao usar 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ção em paralelo

Nos modelos compatíveis a partir do GPT-5, as funções podem ser chamadas em paralelo mesmo quando há ferramentas integradas disponíveis. Ferramentas integradas não podem fazer parte de um lote de chamadas de função em paralelo.

O modelo pode optar por chamar várias funções em um único turno. Para impedir isso, defina parallel_tool_calls como false. Assim, o modelo fará no máximo uma chamada de ferramenta.

Observação: Atualmente, se um modelo com ajuste fino chamar várias funções no mesmo turno, o modo estrito será desativado para essas chamadas.

Observação sobre gpt-4.1-nano-2025-04-14: Essa versão do gpt-4.1-nano às vezes gera várias chamadas à mesma ferramenta quando as chamadas em paralelo estão ativadas. Recomendamos desativar esse recurso ao usar essa versão.

Modo estrito

Defina strict como true para garantir que as chamadas de função sigam o schema da função de forma confiável. Sem essa garantia, o modelo apenas tenta seguir o schema. Recomendamos sempre ativar o modo estrito.

Internamente, o modo estrito usa o recurso de saídas estruturadas, o que impõe dois requisitos:

  1. additionalProperties deve ter o valor false em cada objeto de parameters.
  2. Todos os campos de properties devem ser marcados como required.

Para indicar campos opcionais, adicione null como uma opção de type (veja o exemplo abaixo).

Se você enviar strict: true e o schema não atender aos requisitos acima, a API rejeitará a solicitação e indicará quais restrições faltam. Se você omitir strict, o comportamento padrão dependerá da API. A Responses API tenta normalizar o schema para o modo estrito. Se não for possível torná-lo compatível, a chamada de função passa a operar sem a garantia de conformidade do modo estrito. Nesse caso, a ferramenta na resposta exibirá strict: false. As solicitações à API chat completions usam o modo não estrito por padrão. Para desativar o modo estrito na Responses API e manter a chamada de função 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 schemas gerados no Playground têm o modo estrito ativado.

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

  1. O modo estrito não oferece suporte a todos os recursos de schema JSON. (Consulte os schemas compatíveis.)

Para modelos com ajuste fino, há limitações específicas:

  1. Os schemas passam por processamento adicional na primeira solicitação e depois ficam em cache. Se os schemas variarem de uma solicitação para outra, a latência poderá aumentar.
  2. Os schemas ficam em cache para melhorar o desempenho e não são elegíveis para zero retenção de dados.

Streaming

Com o streaming, você pode mostrar o progresso da chamada, indicando qual função o modelo está chamando enquanto ele preenche os argumentos. Também é possível exibir os argumentos em tempo real.

O streaming de chamadas de função funciona como o 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",
        "strict": True,
        "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\"}"}}

Nesse caso, em vez de reunir os fragmentos em uma única string content, você os reúne para formar o objeto JSON codificado em arguments.

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

Campo Descrição
response_id ID da resposta à qual pertence a chamada de função
output_index Índice do item de saída na resposta. Identifica cada chamada de função na resposta.
item Item da chamada de função em andamento, com os campos name, arguments e id

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

Campo Descrição
response_id ID da resposta à qual pertence a chamada de função
item_id ID do item da chamada de função ao qual pertence o delta
output_index Índice do item de saída na resposta. Identifica cada chamada de função na resposta.
delta Delta do campo arguments.

O trecho de código abaixo mostra como reunir os objetos delta em um objeto tool_call final.

Acúmulo dos deltas de tool_call
final_tool_calls = {}

for event in stream:
    if (
        event.type == "response.output_item.added"
        and event.item.type == "function_call"
    ):
        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
Resultado acumulado em final_tool_calls[0]
{
    "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, a API emite um evento do tipo response.function_call_arguments.done. Esse evento contém a chamada de função completa, incluindo os seguintes campos:

Campo Descrição
response_id ID da resposta à qual pertence a chamada de função
output_index Índice do item de saída na resposta. Identifica cada chamada de função na resposta.
item Item da chamada de função, com os campos name, arguments e id.

Ferramentas personalizadas

As ferramentas personalizadas funcionam de maneira semelhante às ferramentas do tipo função definidas por schema JSON. A diferença é que, em vez de receber instruções explícitas sobre a entrada exigida pela ferramenta, o modelo pode passar uma string arbitrária como entrada. Isso permite evitar o uso desnecessário de JSON para encapsular uma resposta ou aplicar uma gramática personalizada à resposta (veja a seguir).

O exemplo de código a seguir cria uma ferramenta personalizada que espera receber como resposta uma string de texto com 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)

Como nos exemplos anteriores, o array output contém uma chamada de ferramenta gerada pelo modelo. Desta vez, a entrada da chamada é fornecida em 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 determinado formato. Para ferramentas personalizadas, você pode fornecer uma CFG que restrinja o texto que o modelo envia como entrada à ferramenta.

Use o parâmetro grammar ao configurar uma ferramenta personalizada para fornecer sua própria CFG. Atualmente, há suporte a duas sintaxes para definir gramáticas: lark e regex.

CFG com Lark

Exemplo de gramática livre de contexto com 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 que você definiu com Lark:

[
  {
    "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 com uma variante de Lark. A LLGuidance restringe a amostragem do modelo. A API não oferece suporte aos seguintes recursos de Lark:

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

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

Limite a complexidade da gramática

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

Pode ser difícil acertar todos os detalhes de uma gramática Lark. Gramáticas mais simples funcionam de forma mais confiável. Com gramáticas complexas, muitas vezes é preciso refinar a própria definição da gramática, o prompt e a descrição da ferramenta para evitar que o modelo gere saídas fora da distribuição esperada.

Padrões corretos e incorretos

Correto (um único terminal delimitado):

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 divida o padrão entre regras e terminais como no exemplo abaixo. Essa abordagem tenta usar regras para distribuir o texto livre entre os terminais. O analisador léxico consome os trechos de texto livre de forma gulosa, e você perde o controle sobre a divisão:

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

As regras em minúsculas não influenciam a divisão da entrada em terminais. Somente as definições dos terminais determinam essa divisão. Quando precisar de “texto livre entre âncoras”, represente todo o padrão em um único terminal de expressão regular para que o analisador léxico faça uma única correspondência com a estrutura pretendida.

Terminais e regras

O Lark usa terminais para representar os tokens do analisador léxico (por convenção, em UPPERCASE) e regras para as produções do analisador sintático (por convenção, em lowercase). Para usar apenas os recursos compatíveis e evitar surpresas, mantenha a gramática explícita, evite complexidade desnecessária e separe claramente as responsabilidades dos terminais e das regras.

Os terminais usam a sintaxe da biblioteca regex do Rust, que difere da sintaxe de re, o módulo de expressões regulares do Python.

Conceitos e boas práticas

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

Antes da aplicação de qualquer regra da CFG, o analisador léxico identifica os terminais de forma gulosa: prevalece a correspondência mais longa. Se você tentar delimitar um terminal dividindo-o entre várias regras, essas regras não orientarão o analisador léxico. Ele segue apenas as expressões regulares dos terminais.

Prefira um único terminal para extrair trechos de texto livre

Se precisar reconhecer um padrão em meio a texto arbitrário (por exemplo, linguagem natural com “qualquer conteúdo” entre âncoras), represente-o como um único terminal. Evite intercalar terminais de texto livre com regras do analisador sintático. A análise léxica gulosa não respeitará os limites pretendidos, e é muito provável que o modelo gere saídas fora da distribuição esperada.

Use regras para combinar tokens distintos

As regras são ideais para combinar terminais com limites explícitos (números, palavras-chave, pontuação) em estruturas maiores. Elas não são adequadas para restringir “o conteúdo entre” dois terminais.

Defina terminais específicos, delimitados e independentes

Prefira classes de caracteres explícitas e quantificadores limitados ({0,10}, em vez de usar * sem limite em toda parte). Se precisar de “qualquer texto até um ponto final”, prefira algo como /[^.\n]{0,10}*\./ a /.+\./ para evitar que o trecho capturado cresça sem controle.

Use regras para combinar tokens e deixe a correspondência interna a cargo 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 de forma explícita

Evite depender de diretivas %ignore sem limites. Elas podem tornar a gramática complexa demais, levar o modelo a gerar saídas fora da distribuição esperada ou causar ambos os problemas. Prefira inserir terminais explícitos em cada ponto em que espaços em branco são permitidos.

Solução de problemas

  • Se a API rejeitar a gramática por excesso de complexidade, simplifique as regras e os terminais e remova as diretivas %ignore sem limites.
  • Se as ferramentas personalizadas receberem chamadas com tokens inesperados, verifique se há sobreposição entre os terminais e como o analisador léxico faz a correspondência gulosa.
  • Quando o modelo gerar saídas “fora da distribuição esperada” (excessivamente longas ou repetitivas, com sintaxe válida, mas significado incorreto):
    • Torne a gramática mais restritiva.
    • Refine o prompt (adicione exemplos few-shot) e a descrição da ferramenta (explique a gramática e instrua o modelo a raciocinar e a seguir suas regras).
    • Experimente um esforço de raciocínio maior (por exemplo, passe de médio para alto).

CFG com expressão regular

Exemplo de gramática livre de contexto com expressão regular
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 deve então seguir a CFG que você definiu com a expressão regular:

[
  {
    "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 Lark, as expressões regulares usam a sintaxe da biblioteca regex do Rust, que difere da sintaxe de re, o módulo de expressões regulares do Python.

A API não oferece suporte aos seguintes recursos de expressões regulares:

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

Conceitos e boas práticas

O padrão deve ocupar uma única linha

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

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

Não delimite o padrão com //.