For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション
Theme

Function Calling

モデルが指示に従ってプロンプトに応答する際に、新しい機能やデータを利用できるようにします。

Function Calling(ツール呼び出しとも呼ばれます)を使うと、OpenAI のモデルが外部システムと柔軟に連携し、学習データに含まれないデータにアクセスできます。このガイドでは、アプリケーションが提供するデータや操作をモデルから利用できるようにする方法を説明します。JSON スキーマで定義する関数ツールと、自由形式のテキストを入出力に使うカスタムツールを取り上げます。

Agents API のセッションでは、関数を使って関数を登録し、セッションのアクションリクエストを処理します。このガイドの例では、Responses API と Chat Completions での実装を示します。

アプリケーションに多数の関数や大規模なスキーマがある場合は、Function Calling とツール検索を組み合わせると、使用頻度の低いツールの読み込みを遅らせ、モデルが必要とするときだけ読み込めます。gpt-5.4 以降のモデルのみが tool_search に対応しています。

GPT-6 Astra と GPT-6.1 Sol でツールを呼び出すには、Responses API が必要です。Chat Completions の例では、推論を無効にした GPT-5.6 Terra を使用します。既存の実装を更新するには、移行ガイドを参照してください。

ツール呼び出しの仕組み

まず、ツール呼び出しの基本用語を確認します。そのうえで、具体例を使って実装方法を説明します。

ツール呼び出しの流れ

ツール呼び出しでは、アプリケーションとモデルが OpenAI API を介して複数回やり取りします。大まかな流れは次の 5 ステップです。

  1. 呼び出し可能なツールを含むリクエストをモデルに送信します。
  2. モデルからツール呼び出しを受け取ります。
  3. ツール呼び出しの入力を使って、アプリケーション側でコードを実行します。
  4. ツールの出力を含む 2 回目のリクエストをモデルに送信します。
  5. モデルから最終応答または追加のツール呼び出しを受け取ります。

Function Calling の手順を示す図

Responses では、タスクに必要な回数だけこの流れを繰り返せます。このループに伴う一連の処理をまとめて管理できるフレームワークが必要な場合は、Responses API と Agents SDK の比較を参照してください。

関数ツールの例

指定した星座の今日の運勢を取得する get_horoscope 関数を例に、ツール呼び出しの一連の流れを見てみましょう。

ツール呼び出しの実装例
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)

GPT-5 や o4-mini などのリーズニングモデルを使う場合、モデルの応答でツール呼び出しとともに返された推論項目は、すべてツール呼び出しの出力と一緒にモデルに送り返す必要があります。これらの推論項目の生成に至るまでのやり取りの履歴を保持する方法については、プレフィックス変更時のベストプラクティスを参照してください。

関数の定義

通常、関数は各 API リクエストの tools パラメーターで宣言します。ツール検索を使うと、アプリケーションは読み込みを遅らせた関数をやり取りの途中で読み込むこともできます。どちらの場合も、呼び出し可能な関数は同じ形式のスキーマを使用します。関数定義には次のプロパティがあります。

フィールド 説明
type 常に function を指定します。
name 関数名(例:get_weather)
description 関数をいつ、どのように使うかについての詳しい説明
parameters 関数の入力引数を定義する JSON スキーマ
strict 関数呼び出しに厳格モードを適用するかどうか

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
}

parameters は JSON スキーマで定義するため、プロパティの型、列挙型、説明、ネストしたオブジェクト、再帰的なオブジェクトなど、多くの機能を利用できます。

名前空間の定義

名前空間を使うと、関連するツールを crm、billing、shipping などの業務領域ごとにまとめられます。名前空間は似たツールを整理するのに役立ちます。特に、CRM 用の検索ツールとサポートチケットシステム用の検索ツールなど、対象システムや用途の異なるツールをモデルが使い分ける必要がある場合に便利です。

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

ツール検索

モデルから多数のツールを利用できるようにする必要がある場合、tool_search を使って一部またはすべてのツールの読み込みを遅らせることができます。tool_search ツールを使うと、モデルは関連するツールを検索し、自身のコンテキストに追加してから利用できます。対応しているのは gpt-5.4 以降のモデルのみです。詳しくはツール検索ガイドを参照してください。

関数定義のベストプラクティス

  1. 関数名、パラメーターの説明、指示は明確かつ具体的に記述してください。

    • 関数の目的と各パラメーターの用途(および形式)を明示し、出力が何を表すのかを説明してください。
    • 各関数を使う場面と使わない場面をシステムプロンプトに記述してください。 原則として、モデルが何をすべきかを正確に伝えます。
    • 具体例やエッジケースを含めてください。 特に、同じ失敗を繰り返す場合に役立ちます。(注意:例を追加すると、リーズニングモデルでは性能が低下することがあります。)
    • 読み込みを遅らせるツールでは、詳しい使い方は関数の説明に記述し、名前空間の説明は簡潔にしてください。 モデルは名前空間の説明をもとに読み込む対象を選び、関数の説明をもとに読み込んだツールを正しく使います。
  2. ソフトウェアエンジニアリングのベストプラクティスを適用してください。

    • 関数の動作を予測しやすく、直感的に理解できるようにしてください(驚き最小の原則)。
    • 列挙型を使い、オブジェクトの構造を工夫して、無効な状態を表現できないようにしてください。たとえば、toggle_light(on: bool, off: bool) では無効な引数の組み合わせでも呼び出せてしまいます。
    • インターンでも使えるか確認してください。 モデルに与えた情報だけで、インターンなど初めて使う人が関数を正しく使えるでしょうか。使えない場合は、どのような質問が出るかを確認し、その答えをプロンプトに追加してください。
  3. モデルの負担を減らし、可能な処理はコードで実行してください。

    • 既知の引数をモデルに指定させないでください。 たとえば、前のメニューで order_id が判明している場合は、order_id パラメーターを含めず、submit_refund() を引数なしで定義し、コード内で order_id を渡してください。
    • 常に連続して呼び出す関数はまとめてください。 たとえば、mark_location() を必ず query_location() の直後に呼び出す場合は、場所をマークする処理を検索関数に組み込みます。
  4. 精度を高めるため、最初に利用できる関数の数を絞ってください。

    • 関数の数を変えて性能を評価してください。
    • 各ターンの開始時に利用できる関数は 20 個未満を目安にしてください。 これは厳密な上限ではありません。
    • ツール検索を使い、すべてのツールを最初から公開するのではなく、規模の大きいツール群や使用頻度の低いツールの読み込みを遅らせてください。
  5. OpenAI のリソースを活用してください。

    • 関数スキーマの生成と改善には Playground を利用できます。
    • 関数が多い場合やタスクが難しい場合は、ファインチューニングで Function Calling の精度を高めることを検討してください(Cookbook)。

トークン使用量

内部では、モデルが学習した構文で関数定義がシステムメッセージに挿入されます。そのため、呼び出し可能な関数の定義もモデルのコンテキスト上限に算入され、入力トークンとして課金されます。トークン上限に達する場合は、最初に読み込む関数の数を減らす、可能な範囲で説明を短くする、またはツール検索を使って必要なときだけツールを読み込むことをおすすめします。

ツールの仕様に多数の関数を定義している場合は、ファインチューニングでトークン使用量を削減することもできます。

関数呼び出しの処理

モデルが関数を呼び出したら、アプリケーションはその関数を実行し、結果を返す必要があります。モデルの応答に含まれる呼び出しは 0 件、1 件、または複数件なので、複数件を処理できるように実装してください。

応答の output 配列には、type の値が function_call の項目が含まれます。各項目には call_id(後で関数の結果を送信する際に使用)、name、JSON 形式にエンコードされた arguments があります。

複数の関数呼び出しを含む応答例
[
    {
        "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\"}"
    }
]

ツール検索を使っている場合、tool_search_call と tool_search_output の項目が function_call の前に現れることもあります。関数を読み込んだ後は、ここで示した方法で関数呼び出しを処理してください。

関数呼び出しの実行と結果の追加
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),
        }
    )

上の例では、call_function という関数が各呼び出しを対応する処理に振り分けると仮定しています。実装例を示します。

関数呼び出しの実行と結果の追加
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}")

結果の形式

function_call_output メッセージで渡す結果は通常、文字列にします。形式は JSON、エラーコード、プレーンテキストなどから自由に選べます。モデルは必要に応じてその文字列を解釈します。

画像やファイルを返す関数の場合、文字列の代わりに画像またはファイルのオブジェクトの配列を渡せます。

戻り値のない関数(例:send_email)では、"success" など、成功または失敗を示す文字列を返してください。

応答への結果の反映

結果を input に追加し、モデルに送り返すと、最終応答を取得できます。

モデルへの結果の送信
response = client.responses.create(
    model="gpt-6-astra",
    input=input_messages,
    tools=responses_tools,
)
最終応答
"It's about 15°C in Paris, 18°C in Bogotá, and I've sent that email to Bob."

追加設定

ツールの選択

デフォルトでは、いつ、いくつのツールを使うかをモデルが判断します。tool_choice パラメーターで動作を指定できます。

  1. 自動(デフォルト):関数を呼び出さないか、1 つ以上呼び出します。tool_choice: "auto"
  2. 必須:1 つ以上の関数を呼び出します。 tool_choice: "required"
  3. 関数を指定:指定した関数だけを 1 回呼び出します。 tool_choice: {"type": "function", "name": "get_weather"}
  4. 許可するツール:モデルが呼び出せるツールを、利用可能なツールの一部に限定します。

allowed_tools の用途

allowed_tools の一覧を設定すると、モデルに渡すツール一覧は変更せずに、各リクエストで利用できるツールを一部に限定できます。これにより、プロンプトキャッシュによるコスト削減効果を最大限に活かせます。

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

tool_choice を "none" に設定すると、関数を渡さなかった場合と同じ動作になります。

ツール検索を使う場合も、tool_choice はそのターンで呼び出し可能なツールに適用されます。一部のツールを読み込んだ後、モデルが使えるツールをその範囲に限定したい場合に特に便利です。

並列 Function Calling

GPT-5 以降の対応モデルでは、組み込みツールも利用可能な状態で、関数を並列に呼び出せます。ただし、並列で実行する関数呼び出しのバッチに組み込みツールを含めることはできません。

モデルは 1 ターンで複数の関数を呼び出す場合があります。これを防ぐには parallel_tool_calls を false に設定してください。ツール呼び出しは 0 件または 1 件に限定されます。

注意:現在、ファインチューニング済みのモデルが 1 ターンで複数の関数を呼び出すと、それらの呼び出しでは厳格モードが無効になります。

gpt-4.1-nano-2025-04-14 に関する注意:この gpt-4.1-nano のスナップショットでは、並列ツール呼び出しを有効にすると、同じツールに対する呼び出しが複数含まれる場合があります。このスナップショットでは、並列ツール呼び出しを無効にすることをおすすめします。

厳格モード

strict を true に設定すると、関数呼び出しが確実に関数スキーマに従うようになります。厳格モードは常に有効にすることをおすすめします。

厳格モードは内部で構造化出力を使用するため、次の要件があります。

  1. additionalProperties は false に設定する必要があります。これは parameters 内のすべてのオブジェクトに適用されます。
  2. properties のすべてのフィールドを required に指定する必要があります。

任意のフィールドを表すには、null を type の選択肢に追加します(下の例を参照)。

strict: true を指定してスキーマが上記の要件を満たしていない場合、リクエストは拒否され、不足している制約の詳細が返されます。strict を省略した場合の動作は API によって異なります。Responses では、可能であればスキーマを厳格モードに適合するように正規化します。適合させられない場合は、厳格モードを使わないベストエフォートの Function Calling に切り替わります。その場合、応答に含まれるツールには strict: false と表示されます。Chat Completions では、デフォルトで厳格モードは無効です。Responses で厳格モードを使わず、ベストエフォートの Function Calling を維持するには、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
    }
}

Playground で生成したスキーマでは、すべて厳格モードが有効です。

厳格モードを有効にすることをおすすめしますが、次の制限があります。

  1. JSON スキーマの一部の機能には対応していません(対応するスキーマを参照)。

ファインチューニング済みのモデルには、次の制限もあります。

  1. スキーマは初回のリクエスト時に追加の処理を経て、キャッシュされます。リクエストごとにスキーマが異なる場合、レイテンシが増加することがあります。
  2. スキーマは性能向上のためにキャッシュされるので、ゼロデータ保持の対象外です。

ストリーミング

ストリーミングを使うと、モデルが引数を生成している間に、どの関数を呼び出しているかを表示して進捗を伝えられます。引数自体もリアルタイムで表示できます。

関数呼び出しのストリーミングは、通常の応答のストリーミングと同じ仕組みです。stream を true に設定すると、さまざまな種類の event オブジェクトを受け取ります。

関数呼び出しのストリーミング
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)
出力イベント
{"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\"}"}}

ただし、チャンクを 1 つの content 文字列にまとめる代わりに、JSON オブジェクトを文字列にエンコードした arguments を組み立てます。

モデルが 1 つ以上の関数を呼び出すと、関数呼び出しごとに response.output_item.added 型のイベントが発行されます。このイベントには次のフィールドが含まれます。

フィールド 説明
response_id 関数呼び出しを含む応答の ID
output_index 応答内の出力項目のインデックス。応答内の個々の関数呼び出しを識別します。
item 処理中の関数呼び出しの項目。name、arguments、id フィールドを含みます。

続いて、response.function_call_arguments.delta 型のイベントを順次受け取ります。各イベントの delta には arguments フィールドの差分が入ります。イベントには次のフィールドが含まれます。

フィールド 説明
response_id 関数呼び出しを含む応答の ID
item_id 差分が属する関数呼び出し項目の ID
output_index 応答内の出力項目のインデックス。応答内の個々の関数呼び出しを識別します。
delta arguments フィールドの差分

以下のコードは、各 delta を集約して最終的な tool_call オブジェクトを組み立てる例です。

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
集約後の final_tool_calls[0]
{
    "type": "function_call",
    "id": "fc_1234xyz",
    "call_id": "call_2345abc",
    "name": "get_weather",
    "arguments": "{\"location\":\"Paris, France\"}"
}

モデルが関数の呼び出しを終えると、response.function_call_arguments.done 型のイベントが発行されます。このイベントには、次のフィールドを含む関数呼び出し全体が格納されます。

フィールド 説明
response_id 関数呼び出しを含む応答の ID
output_index 応答内の出力項目のインデックス。応答内の個々の関数呼び出しを識別します。
item 関数呼び出しの項目。name、arguments、id フィールドを含みます。

カスタムツール

カスタムツールの仕組みは、JSON スキーマで定義する関数ツールとほぼ同じです。ただし、ツールが必要とする入力をモデルに明示する代わりに、モデルが任意の文字列を入力としてツールに渡せます。応答を不必要に JSON で包みたくない場合や、応答に独自の文法を適用したい場合に便利です。文法については後述します。

以下は、モデルの応答として Python コードを含む文字列を受け取るカスタムツールの作成例です。

カスタムツールの呼び出し例
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)

これまでの例と同様に、output 配列にはモデルが生成したツール呼び出しが含まれます。ただし、今回はツール呼び出しの入力がプレーンテキストで渡されます。

[
  {
    "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"
  }
]

文脈自由文法

文脈自由文法(CFG)は、指定した形式に適合するテキストの生成方法を定める規則の集合です。カスタムツールに CFG を指定すると、モデルがそのツールに渡すテキストの形式を制約できます。

カスタムツールの設定時に grammar パラメーターで独自の CFG を指定できます。現在、文法の定義には lark と regex の 2 種類の CFG 構文を使用できます。

Lark CFG

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)

ツールの出力は、定義した Lark CFG に従った形式になります。

[
  {
    "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"
  }
]

文法の記述には Lark をベースにした構文を使用します。モデルのサンプリングは LLGuidance で制約します。Lark の次の機能には対応していません。

  • 字句解析器の正規表現での先読み・後読み
  • 字句解析器の正規表現での最短一致の修飾子(*?、+?、??)
  • 終端記号の優先順位
  • テンプレート
  • インポート(組み込みの %import common を除く)
  • %declare 宣言

独自の文法を試すには、Lark IDE の利用をおすすめします。

文法の複雑さの抑制

文法には、ツールに必要な規則とパターンだけを含めてください。文法が複雑すぎると OpenAI API がエラーを返す場合があるため、API で使用する前に、目的の文法が対応範囲内にあることを確認してください。

Lark の文法を意図どおりに仕上げるには工夫が必要です。単純な文法ほど安定して動作します。複雑な文法では、モデルが学習時の分布から外れないように、文法の定義自体、プロンプト、ツールの説明を繰り返し調整する必要があることがよくあります。

正しいパターンと誤ったパターン

正しい例(範囲を限定した単一の終端記号):

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, ]*\./

次のように複数の規則や終端記号に分割しないでください。この例では、規則を使って自由形式のテキストを複数の終端記号に割り振ろうとしています。字句解析器は自由形式の部分に貪欲にマッチするため、意図した位置で区切れなくなります。

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

入力から終端記号をどこで切り出すかは、終端記号の定義だけで決まります。小文字で記述する規則は、この処理に影響しません。「アンカー間の自由形式のテキスト」を扱う場合は、全体を 1 つの正規表現の終端記号として定義し、字句解析器が意図した構造に 1 回だけマッチするようにしてください。

終端記号と規則の違い

Lark では、字句解析器のトークンを終端記号(慣例では UPPERCASE)で表し、構文解析器の生成規則を規則(慣例では lowercase)で表します。対応する機能の範囲内で意図どおりに動作させるには、文法を明示的に記述して不要な複雑さを避け、終端記号と規則の役割を明確に分けてください。

終端記号の正規表現には Rust の regex クレートの構文を使用します。Python の re モジュールの構文とは異なります。

基本原則とベストプラクティス

字句解析と構文解析の実行順序

CFG の規則が適用される前に、字句解析器が終端記号に貪欲にマッチします(最長一致が優先されます)。終端記号を複数の規則に分けてマッチの仕方を制御しようとしても、字句解析器の動作を決めるのは終端記号の正規表現だけです。

自由形式のテキストからパターンを切り出すには単一の終端記号を使用

任意のテキストに埋め込まれたパターン(たとえば、アンカー間に任意の文字列が入る自然言語の文)を認識するには、そのパターンを 1 つの終端記号で表現してください。自由形式のテキストにマッチする終端記号と構文解析の規則を交互に並べないでください。貪欲にマッチする字句解析器は意図した境界で止まらず、モデルが学習時の分布から外れる可能性が非常に高くなります。

独立したトークンの組み合わせには規則を使用

規則は、明確に区切られた終端記号(数値、キーワード、句読点など)を組み合わせて、より大きな構造を作るのに適しています。2 つの終端記号の間に入るテキストの制約には適していません。

終端記号は対象と範囲を絞り、単独で完結するように定義

文字クラスを明示し、範囲を限定した量指定子({0,10} など)を使ってください。上限のない * を多用するのは避けます。「ピリオドまでの任意のテキスト」が必要な場合は、/[^.\n]{0,10}*\./ のようなパターンを優先し、/.+\./ のようにマッチ範囲が際限なく広がるものは避けてください。

規則はトークンの組み合わせに使い、正規表現内部の制御には使わない

規則の適切な使用例:

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

空白の明示的な処理

範囲を限定しない %ignore ディレクティブに頼らないでください。無制限に無視するよう指定すると、文法が複雑になりすぎたり、モデルが学習時の分布から外れたりする場合があります。空白を許可する箇所には、空白を表す終端記号を明示的に組み込んでください。

トラブルシューティング

  • 文法が複雑すぎて API に拒否される場合は、規則と終端記号を単純化し、範囲を限定していない %ignore を削除してください。
  • カスタムツールに予期しないトークンが渡される場合は、終端記号のマッチ範囲が重複していないか、字句解析器の貪欲なマッチがどう働いているかを確認してください。
  • モデルの出力が学習時の分布から外れる場合(出力が極端に長くなったり、同じ内容を繰り返したりして、構文上は正しくても意味が誤っている場合)は、次の方法を試してください。
    • 文法の制約を厳しくします。
    • プロンプトにフューショットの例を追加します。ツールの説明には、文法の解説と、その文法を踏まえて推論し、文法に従うよう求める指示を加えます。結果を見ながら両方を調整してください。
    • 推論強度を上げて試します(例:medium から high に変更)。

Regex CFG

正規表現による文脈自由文法の例
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)

ツールの出力は、定義した Regex CFG に従った形式になります。

[
  {
    "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"
  }
]

Lark 構文と同様に、正規表現には Rust の regex クレートの構文を使用します。Python の re モジュールの構文とは異なります。

正規表現の次の機能には対応していません。

  • 先読み・後読み
  • 最短一致の修飾子(*?、+?、??)

基本原則とベストプラクティス

パターンは 1 行で記述

入力の改行にマッチさせるには、エスケープシーケンス \n を使ってください。パターンを複数行に分けて記述できる詳細モードや拡張モードは使用しないでください。

正規表現はパターン文字列をそのまま指定

パターンを // で囲まないでください。