Function Callingで「tool not found」になる原因|ツール名・登録漏れ・スキーマを確認

Function Callingで「tool not found」になる原因|ツール名・登録漏れ・スキーマを確認 AI開発

AIエージェントやFunction Callingを実装していると、モデルがツールを呼び出した直後に「tool not found」「Tool ‘xxx’ is not available」「unknown tool」といったエラーが出ることがあります。

このエラーで最初に確認したいのは、LLMそのものではなく、モデルが返したツール名とアプリ側で実行できるツールの対応関係です。

Function Callingでは、モデルに関数の存在を教えるだけでは処理は完了しません。モデルが返したnameを受け取り、その名前に対応する実際の関数をアプリ側で実行する必要があります。

そのため、ツール名の1文字違い、登録漏れ、条件付きツールの無効化、API形式の混同などでも「tool not found」が発生します。

この記事では、Function Callingでツールが見つからなくなる代表的な原因と確認方法を解説します。ツールを何度も繰り返し呼び出してしまう問題は別の原因であり、Function Callingが無限ループする原因|ツール実行回数と終了条件の設計で扱っています。

スポンサーリンク

「tool not found」は何が見つからないエラーなのか

Function Callingでは、モデル自身がJavaScriptやPythonの関数を直接探して実行しているわけではありません。

アプリからモデルへ「利用可能なツール」として名前や引数のスキーマを渡すと、モデルは必要に応じてfunction_callを返します。OpenAIのResponses APIでは、Function Callの出力にnameargumentscall_idなどが含まれ、アプリ側がそのnameに対応する処理を実行します。

たとえばモデルから次のような呼び出しが返ってきたとします。

モデルが返すfunction_call
{
  "type": "function_call",
  "name": "get_weather",
  "arguments": "{\"location\":\"Tokyo\"}"
}

アプリ側にはget_weatherという名前に対応する実装が必要です。

src/dispatch.js
const functions = {
  get_weather: getWeather
};

const fn = functions[toolCall.name];

if (!fn) {
  throw new Error(`tool not found: ${toolCall.name}`);
}

ここでモデルがget_weatherを返したのに、アプリ側へgetWeatherしか登録されていなければ、実際の関数が存在していても検索には失敗します。

つまり「tool not found」は、多くの場合「コード内に関数が存在しない」という意味ではなく、Function Callingで使われている名前から実行対象を解決できなかった状態です。

OpenAI Agents SDKでも、モデルが返したFunction Toolの名前が現在のAgentで利用可能なツールと一致しない場合、デフォルトではModelBehaviorErrorになります。現在はtool_not_found_behavior="return_error_to_model"を指定し、エラーをモデルへ返して別のツールを選ばせる動作も用意されています。

最初にツール名が完全一致しているか確認する

もっとも単純で、最初に確認したい原因がツール名の不一致です。

次の定義ではモデルへget_weatherというツールを公開しています。

src/tools.js
const tools = [
  {
    type: "function",
    name: "get_weather",
    description: "指定した都市の天気を取得する",
    parameters: {
      type: "object",
      properties: {
        location: {
          type: "string"
        }
      },
      required: ["location"],
      additionalProperties: false
    },
    strict: true
  }
];

ところが実行側が次のようになっていると一致しません。

不一致の例
const functions = {
  getWeather: getWeather
};

get_weathergetWeatherは別の文字列です。

修正するなら、モデルへ公開する名前とディスパッチ側のキーを統一します。

修正例
const functions = {
  get_weather: getWeather
};

大文字と小文字、アンダースコア、ハイフン、単数形と複数形も確認してください。

search_usersearch_usersのような違いも、人間には似ていますがFunction Callingのディスパッチ処理では別名として扱われます。

ツールをモデルに渡しただけで実行できるとは限らない

Function Callingでは、モデルへ渡すツール定義と、実際に実行する関数の登録を別々に管理しているアプリがあります。

たとえば次のtoolsによってモデルはget_weatherの存在を知ることができます。

src/tools.js
const tools = [
  {
    type: "function",
    name: "get_weather",
    description: "天気を取得する",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string" }
      },
      required: ["location"],
      additionalProperties: false
    },
    strict: true
  }
];

しかし実行側が空なら、モデルが正常にget_weatherを選んでも実行できません。

登録漏れの例
const functions = {};

この場合、Function Callingそのものには成功しているため、「AIが存在しないツールを作った」と勘違いしやすくなります。

レスポンスに返されたnameをログへ出し、同時にアプリ側で現在登録されているツール名も確認すると切り分けやすくなります。

デバッグログ
console.log("requested tool:", toolCall.name);
console.log("registered tools:", Object.keys(functions));

ここでrequested toolには存在するのにregistered toolsへ同名がなければ、原因はほぼ登録処理です。

条件付きでツールを登録している場合にも注意する

開発環境では正常なのに本番環境だけ「tool not found」になる場合は、条件付き登録を確認してください。

たとえばAPIキーが存在するときだけツールを登録する実装があります。

src/register-tools.js
const functions = {};

if (process.env.WEATHER_API_KEY) {
  functions.get_weather = getWeather;
}

この状態で本番環境にWEATHER_API_KEYが設定されていなければ、get_weatherは登録されません。

ところがモデルへ送るtoolsにはget_weatherが残っていると、モデルから見ると利用可能なのに、実行側には存在しないという状態になります。

認証情報、Feature Flag、ユーザー権限、環境変数、契約プランによってツールを有効化しているシステムでは、モデルへ公開するツール一覧と実行可能なツール一覧を同じ条件から生成する設計にしておくとズレを減らせます。

Responses APIとChat Completionsの定義形式を混ぜていないか確認する

OpenAI APIを使っている場合は、Responses APIとChat Completions APIのFunction Calling形式を混同していないかも確認してください。

現在のOpenAI公式ドキュメントでは、Chat CompletionsではFunction定義がfunctionオブジェクトの内側に入る一方、Responses APIではnameparametersをFunction Toolの直下へ記述します。OpenAIも移行ガイドで、両APIではFunction Callingのリクエスト形式と返却形式が異なると説明しています。

Chat Completionsでは次のような形式です。

Chat Completions形式
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "天気を取得する",
    "parameters": {
      "type": "object"
    }
  }
}

Responses APIでは次のような形になります。

Responses API形式
{
  "type": "function",
  "name": "get_weather",
  "description": "天気を取得する",
  "parameters": {
    "type": "object"
  }
}

古いサンプルコードや別API向けの記事をそのままコピーした場合、ツール定義が正しく読み込まれないことがあります。

この場合は必ずしも文字通りの「tool not found」になるとは限らず、APIリクエスト自体が拒否されたり、ツールが想定どおりモデルへ公開されなかったりすることもあります。

「Function Callingのサンプルだから同じ」と考えず、使用しているエンドポイントに合った形式か確認してください。

JSON Schemaの不備も確認する

ツール名と登録処理に問題がなければ、parametersのJSON Schemaも確認します。

ただし重要なのは、Schemaエラーとtool not foundは本来別の問題という点です。

Schemaが不正だから必ず「tool not found」になるわけではありません。APIによってはリクエスト時点でエラーになり、フレームワークによってはツールの登録に失敗した結果として後から「見つからない」状態になることがあります。

OpenAIはFunction Callingでstrict: trueを推奨しています。Strict Modeでは、各objectにadditionalProperties: falseが必要で、propertiesに定義したフィールドはすべてrequiredへ含める必要があります。任意項目は型へnullを含める形で表現します。

たとえば次のように定義できます。

src/tools.js
{
  type: "function",
  name: "get_weather",
  description: "指定した都市の天気を取得する",
  strict: true,
  parameters: {
    type: "object",
    properties: {
      location: {
        type: "string"
      },
      unit: {
        type: ["string", "null"],
        enum: ["celsius", "fahrenheit"]
      }
    },
    required: ["location", "unit"],
    additionalProperties: false
  }
}

Strict Modeの条件を満たしていない場合、OpenAI APIではリクエストが拒否され、問題となっている制約についてエラーが返されます。

そのため、ツールがまったく候補に出てこない場合は、アプリ起動時やAPIリクエスト時にSchema関連のエラーが出ていないかログをさかのぼって確認することも重要です。

tool_choiceで存在しないツールを強制していないか確認する

Function Callingでは、モデルへツール選択を任せるだけでなく、特定のツールを強制することもできます。

OpenAIではtool_choiceを使って特定のFunctionを指定できます。

たとえば次のような指定です。

tool_choiceの例
tool_choice: {
  type: "function",
  name: "get_weather"
}

この設定を残したままget_weathertoolsから削除したり、名前をfetch_weatherへ変更したりすると、設定同士の整合性が崩れます。

ツール名を変更したときは、Function定義だけでなく、tool_choice、許可ツール一覧、ディスパッチテーブル、テストコード、プロンプト内で明示しているツール名まで検索すると安全です。

プロンプトに古いツール名が残っていないか確認する

Function名を変更した後、システムプロンプトやDeveloper Messageに古い名前が残っているケースもあります。

たとえば以前はsearch_databaseという名前だったものをsearch_productsへ変更したにもかかわらず、プロンプトに次の指示が残っている状態です。

古いプロンプト
商品を探す場合は search_database を使用してください。

モデルへ渡している正式なツール一覧にはsearch_databaseがなければ、通常は利用可能なツールから判断しますが、フレームワークや独自実装によっては古い名前を生成する原因になります。

ツール名を変更するときはコードだけでなく、プロンプト、README、Few-shot例、テスト用会話履歴、保存済みAgent設定なども確認してください。

会話履歴に古いFunction Callが残っているケース

長時間動作するAgentでは、古い会話履歴が原因になることもあります。

途中でツール名を変更したにもかかわらず、過去の会話に旧Function名のTool Callが残っていると、履歴を再利用したときに整合性が崩れる可能性があります。

特にセッションをデータベースへ保存しているシステムでは、コードをデプロイしたからといって既存セッションまで更新されるわけではありません。

ツール構成を大きく変更した直後だけエラーが増えた場合は、新しい会話でも再現するか確認してください。

新規セッションでは正常で、既存セッションだけ失敗するなら、古い履歴や保存済みAgent状態を疑えます。

ツール名の衝突にも注意する

規模が大きくなると、逆に同じ名前を複数の場所で登録する問題も発生します。

たとえばユーザー検索用と管理者検索用の両方でsearch_userという名前を使うと、どちらを呼び出すべきか分からなくなります。

OpenAI Agents SDKにはFunction ToolやHandoffの名前が衝突した場合を扱うtool_name_collision_policyがあり、現在のデフォルトでは警告を出して現在のdispatch対象だけを公開し、"error"を指定するとモデルを呼ぶ前にUserErrorにできます。

大規模なAgentでは、ツール名を短くしすぎず、

映割が分かる名前
search_customer
search_product
search_order
update_order_status

のように役割が分かる名前へ分離したほうが管理しやすくなります。

「AIが存在しないツールを作った」と決めつけない

モデルが本当に存在しないFunction名を生成する可能性はあります。

ただし「tool not found」が発生したときに、最初からハルシネーションと判断するのはおすすめできません。

実際には、モデルが返した名前は正しいものの実行側への登録だけ抜けている、開発環境と本番環境でツール構成が違う、古いtool_choiceが残っている、ツール名を変更したのにプロンプトだけ更新されていない、といったアプリ側の不整合でも同じ症状になります。

まず実際にモデルが返したtoolCall.nameを記録し、そのリクエストでモデルへ渡したツール一覧、実行側の登録一覧の3つを比較すると原因を切り分けやすくなります。

tool not foundを調べるためのログを残しておく

Function Callingを本番運用するなら、エラーが発生してからログを追加するより、最初からツール呼び出しの情報を記録しておくほうが安全です。

最低限、モデルが要求したツール名、呼び出しID、引数、現在利用可能なツール名を追跡できる状態にしておくと原因を特定しやすくなります。

共通ログ
console.log({
  callId: toolCall.call_id,
  requestedTool: toolCall.name,
  arguments: toolCall.arguments,
  availableTools: Object.keys(functions)
});

本番環境では引数にメールアドレス、アクセストークン、個人情報などが含まれる可能性があるため、そのまま保存せず必要に応じてマスキングしてください。

ツール名だけでも、「モデルが間違った名前を返したのか」「実行側の登録が欠けていたのか」を判断する大きな手掛かりになります。

OpenAI Agents SDKならtool_not_found_behaviorも利用できる

OpenAI Agents SDKを利用している場合、ツール名の解決に失敗したときの挙動を変更できます。

デフォルトでは、モデルが現在のAgentに存在しないFunction Toolを呼ぶとModelBehaviorErrorになります。tool_not_found_behavior="return_error_to_model"を指定すると、エラーをFunction Callの結果としてモデルへ返し、利用可能な別のツールを選び直させることができます。

src/run_agent.py
from agents import Agent, RunConfig, Runner

agent = Agent(
    name="Assistant",
    tools=[...]
)

result = await Runner.run(
    agent,
    "必要なツールを使って処理してください",
    run_config=RunConfig(
        tool_not_found_behavior="return_error_to_model"
    )
)

これは一時的なモデル側のツール選択ミスに対する復旧策として便利ですが、登録漏れそのものを直す機能ではありません。

本来存在するはずのツールが未登録なら、毎回モデルへエラーを返して再試行させるのではなく、ツール登録処理を修正する必要があります。

Function Callingのツール登録は1か所で管理すると壊れにくい

「tool not found」を減らすには、モデルへ渡すFunction定義と実行側の関数テーブルを別々の場所へ手書きしないことが有効です。

たとえばツール情報を1つのオブジェクトとして管理します。

src/tool-registry.js
const toolRegistry = {
  get_weather: {
    definition: {
      type: "function",
      name: "get_weather",
      description: "指定した都市の天気を取得する",
      strict: true,
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string"
          }
        },
        required: ["location"],
        additionalProperties: false
      }
    },
    handler: getWeather
  }
};

モデルへ渡す一覧はこのRegistryから作ります。

一覧を作る
const tools = Object.values(toolRegistry).map(
  tool => tool.definition
);

実行時にも同じRegistryを使います。

実行時
const tool = toolRegistry[toolCall.name];

if (!tool) {
  throw new Error(`tool not found: ${toolCall.name}`);
}

const args = JSON.parse(toolCall.arguments);
const result = await tool.handler(args);

こうしておけば、「モデルには登録したのにhandler側には追加し忘れた」という二重管理によるミスを減らせます。

Function Callingの「tool not found」に関するよくある質問

Qモデルが返したFunction名は正しいのに実行できないのはなぜですか

Aモデルへ渡したツール定義と、実行側の関数テーブル(ディスパッチ処理)を別々に管理している場合、片方だけ更新漏れが起こりやすくなります。requestedToolとregisteredToolsの両方をログへ出し、モデルが返した名前が実行側に本当に登録されているか比較してください。

Q開発環境では動くのに本番だけtool not foundになります

AAPIキーやFeature Flag、契約プランなどの条件でツール登録を分岐している場合、その条件が本番環境で満たされていない可能性があります。モデルへ渡すツール一覧と、実行側で登録するツール一覧を同じ条件から生成する設計にすると、環境ごとのズレを防ぎやすくなります。

QResponses APIとChat Completionsで何が違いますか

AChat CompletionsではFunction定義がfunctionオブジェクトの内側にネストされますが、Responses APIではnameやparametersをFunction Toolの直下へ記述するフラットな形式です。古いサンプルコードをそのまま使うと、ツールが正しく認識されない場合があります。

Qstrict: trueを使えばtool not foundを防げますか

ASchemaの不備を早期に検出しやすくはなりますが、tool not foundとSchemaエラーは別の問題です。strict: trueはadditionalProperties: falseやrequiredの網羅などパラメータ形式の不整合を検出するもので、名前の不一致や登録漏れそのものは別途確認する必要があります。

Qtool_choiceで指定したツールが見つからないと言われます

Atool_choiceに指定した名前がtools一覧に残っているか確認してください。ツール名を変更したりtoolsから削除したりしたのにtool_choiceを更新し忘れると、設定同士の不整合でエラーになります。

QOpenAI Agents SDKでツール名が解決できない場合はどうなりますか

AデフォルトではModelBehaviorErrorが発生します。tool_not_found_behavior=”return_error_to_model”を指定すると、エラーをモデルへ返して別のツールを選び直させることができます。ただしこれは復旧策であり、登録漏れ自体の修正が必要な場合はツール登録処理側を直します。

Qモデルが存在しないツール名を作ることはありますか

A可能性はありますが、tool not foundが起きた時点でハルシネーションと決めつけないほうが安全です。実際にはツール登録漏れ、環境ごとの構成差、古いtool_choiceや古いプロンプトの残存など、アプリ側の不整合が原因であることも多いためです。

まとめ

Function Callingで「tool not found」が出た場合、最初に確認するべきなのはモデルの性能ではなく、モデルが返したFunction名とアプリ側のツール登録です。

特に多いのは、get_weathergetWeatherのような名前の不一致、モデルへ渡したツールを実行側へ登録していないケース、環境変数や権限によって本番環境だけツールが無効になっているケースです。

OpenAIを利用している場合は、Responses APIとChat CompletionsでFunction定義の形が異なる点にも注意してください。Schemaについてはstrict: trueを利用し、additionalProperties: falserequiredを正しく定義すると、引数形式の不整合を早い段階で検出しやすくなります。

原因を効率よく調べるには、モデルが返したツール名、モデルへ渡したツール一覧、アプリ側で実行可能なツール一覧を同時に比較するのが近道です。

この3つが一致していればSchemaやtool_choice、古い会話履歴を確認し、一致していなければまず登録処理やツール名を修正するとよいでしょう。