0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントが道具をうまく使えないのは、モデルが悪いんじゃなくて「道具棚」が雑だからかもしれません — Function Calling(ツール)設計の実践ガイド

0
Posted at

最近、AIエージェントを触っていて、こんな経験ありませんか。

モデルをもっと賢いものに変えた。プロンプトも何度も書き直した。なのに、エージェントは相変わらず不安定。変なツールを呼んだり、引数を間違えたり、ツールの返事を読み違えて変な方向に進んでいったり。

正直に言うと、僕も最初は「モデルが悪いんかな」「プロンプトが下手なんかな」と思っていました。でも、いろんな失敗を観察していくうちに、だんだん見え方が変わってきたんです。

犯人は、モデルの賢さでもプロンプトでもなくて、AIに渡している「道具棚」の設計が雑だっただけ、というケースがすごく多い。

今日は、その「道具棚」——つまり Function Calling(ツール)の設計 について、AI開発にまだ不慣れな方でも分かるように、でも仕事で明日から使える粒度まで、じっくり書いていきます。コード例もプロンプト例も厚めに入れました。


そもそも「Function Calling」「ツール」って何の話?

まずここから。専門用語っぽいので、ひとことで噛み砕きます。

Function Calling(関数呼び出し)というのは、ざっくり言うと「AIが、外の世界に手を伸ばすための唯一の口」のことです。

LLM(ChatGPTやClaudeみたいな大規模言語モデル)って、そのままだと文章を返すことしかできません。データベースを見ることも、ファイルを消すことも、メールを送ることもできない。脳みそはあるけど、手がない状態。

そこで人間側が、「これ使っていいよ」とあらかじめ道具(=関数)を何個か渡しておきます。たとえば get_order(注文を調べる) とか send_email(メールを送る) とか。AIは「今この道具を、この引数で使いたい」とリクエストを出し、実際の処理はこっちのプログラムが実行して、結果をAIに返す。この一連の仕組みが Function Calling、渡す道具のことを ツール(Tool) と呼びます。

ここで大事なのは、こういう感覚です。

AIにとってツールは、料理人にとっての包丁やまな板みたいなもの。

どんなに腕のいい料理人でも、切れない包丁とぐらぐらのまな板を渡されたら、いい仕事はできない。逆に、道具がちゃんとしていれば、そこそこの料理人でも安定したアウトプットを出せる。AIエージェントもまったく同じなんですよね。


AIが道具を使いこなせないとき、だいたいこの3つで失敗している

2026年に入って、ツール利用(Tool Use)の失敗パターンはかなり研究で分類が進みました。難しい話は抜きにして、現場でよく起きる失敗を3つに絞ると、こうです。

  1. 間違った道具を選ぶ — 注文を「確認するだけ」でいいのに、いきなり「返金する」ツールを呼んでしまう
  2. 引数を間違える — 必要な項目を埋めなかったり、ありえない値(マイナスの金額とか)を渡したりする
  3. 道具の返事を読み違える — エラーが返ってきているのに成功したと勘違いして、次の処理に進んでしまう

しかも厄介なのが、ツール呼び出しって 引数がひとつズレただけで、下流の結果が大きく狂う という性質があること。文章なら多少ブレても読めますが、amount: 1000amount: 100000 になったら、それは事故です。

ここでつい、「じゃあプロンプトでもっと細かく指示すればいいんだ」と考えがちなんですが……ちょっと待ってください。そこが今日いちばん伝えたいポイントなんです。


直し方の「主語」を、プロンプトから道具に変える

多くの人は、AIが失敗すると プロンプトを直そうとします。 「もっと丁寧に指示しよう」「気をつけてって書こう」と。

でも、考えてみてほしいんです。包丁が切れなくて料理が失敗したとき、料理人に「次はもっと気をつけて切ってね」と口で言い続けるのと、包丁そのものを研ぐのと、どっちが効くか。

AIエージェントの安定化って、実は 「プロンプトエンジニアリング」より「道具側の設計」で決まる部分がめちゃくちゃ大きい んですよね。これは最近よく言われる Context Engineering(文脈の設計) の、いちばん実装に近い部分だと僕は捉えています。AIに渡す道具の形・説明・制約そのものが、AIの判断材料(=文脈)になっているからです。

ここから先は、その「道具の設計」を具体的に5つの原則に分けて、コードつきで見ていきます。全部、今日から1個ずつ試せるものです。


ツール設計の5原則

原則1:1つの道具に、1つの仕事だけ持たせる

いちばんやりがちなのが、便利だからと「なんでもできる万能ツール」を作ってしまうこと。

{
  "name": "manage_order",
  "description": "注文を管理する(取得・更新・キャンセル・返金)",
  "input_schema": {
    "type": "object",
    "properties": {
      "action": { "type": "string" },
      "data": { "type": "string" }
    }
  }
}

一見スマートですが、これはAIにとって地獄です。action に何を入れればいいのか、data の形式は何なのか、AIは毎回推測することになる。しかも「取得(読むだけ)」と「返金(お金が動く)」が同じ道具に同居しているので、読むだけのつもりが誤って返金を呼ぶ 事故が起きやすい。

2026年のベストプラクティスは、はっきりしています。1つの万能ツールに read・write・delete を詰め込まず、責務ごとに分割する。 こうすると、AIの判断面(選択肢の広さ)が狭くなって迷いにくくなるし、ログも追いやすくなるし、「この危ない操作だけ承認制にする」みたいな制御もかけやすくなります。

道具棚の整理整頓は、こんなイメージです。

  • get_order … 注文を取得する(読むだけ・安全)
  • update_shipping_address … 配送先だけ変更する(書くけど低リスク)
  • refund_payment … 返金する(お金が動く・高リスク)

特に 「読む道具」と「書く道具」を分ける のは効果が大きいです。読むだけの道具は気軽に何度呼んでもいいけど、書く道具は慎重に。この線引きが道具のレベルで効いてきます。

原則2:スキーマで「言わせない・間違えさせない」

スキーマというのは、その道具が受け取る引数の「型」や「ルール」を定義したものです。ここをゆるく作ると、AIは自由すぎて間違えます。逆に ガチガチに縛ると、そもそも間違った値を作れなくなる。

さっきの manage_order を、refund_payment 単体として、スキーマをしっかり書き直すとこうなります。

{
  "name": "refund_payment",
  "description": "確定済みの注文に返金する。明確な返金指示があるときだけ使う。内容の確認だけなら get_order を使い、このツールは使わない。",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "pattern": "^ord_[a-zA-Z0-9]+$",
        "description": "返金対象の注文ID。get_order で取得した値をそのまま渡す"
      },
      "amount": {
        "type": "integer",
        "minimum": 1,
        "description": "返金額(円)。注文の支払額以下であること"
      },
      "reason": {
        "type": "string",
        "enum": ["customer_request", "defective", "duplicate_charge"],
        "description": "返金理由。3種類から必ず1つ選ぶ"
      }
    },
    "required": ["order_id", "amount", "reason"],
    "additionalProperties": false
  }
}

ポイントを噛み砕きます。

  • pattern … IDの形を正規表現で固定。それっぽい嘘のIDを作れなくする
  • minimum … 金額は1以上の整数。マイナスや0、小数を弾く
  • enum … 理由は3択だけ。自由記述させず、選ばせる
  • required … 必須項目を明示。埋め忘れを防ぐ
  • additionalProperties: false … 余計な項目を勝手に足させない

ちなみに、この input_schema の形は、たとえばAnthropicのMessages APIで tools として渡すスキーマとそのまま同じ構造です。スキーマは「AIに守らせる契約書」 だと思ってください。契約が曖昧だと、AIは曖昧に振る舞う。契約がカチッとしていれば、AIもカチッと動きます。

原則3:descriptionは「人間向けの説明」じゃなく「AIへの取扱説明書」

ここ、意外と見落とされがちなんですが、めちゃくちゃ大事です。

ツールの description(説明文)を、人間用のドキュメントみたいに「注文を返金する」とだけ書いて終わりにしていませんか。AIはこの説明文を読んで「いつこの道具を使うか」を判断します。つまり description は、AIにとっての取扱説明書そのもの なんです。

良い取扱説明書には、最低この4つを入れます。

  1. その道具が何をするか(副作用、つまり「何が変わるか」も書く)
  2. 使うべき状況
  3. 使ってはいけない状況と、その代わりに使う道具
  4. 各引数の意味と制約(できれば例つき)

特に3番、「使わない時」を書くのが効きます。さっきのスキーマでも、わざわざこう書きました。

内容の確認だけなら get_order を使い、このツールは使わない。

これ1行があるだけで、「ちょっと注文を見たいだけ」のときに返金ツールを誤発火する事故が、ぐっと減ります。人間の新人さんに道具を渡すときと一緒ですよね。「これは○○のときに使う。△△のときは絶対使わずに、隣のこっちを使ってね」って添えるだけで、事故が減る。AIにも同じ親切をしてあげる、というだけの話です。

原則4:エラーは「AIが次の一手を打てる」形で返す

道具がエラーを返すこと自体は、悪いことじゃありません。問題は その返し方 です。

よくあるダメな返し方は、プログラムの内部エラー(スタックトレース)をそのままAIに投げること。人間でも読みたくないあれを渡されても、AIは「で、次どうすればいいの?」が分からず、同じ失敗を繰り返したり、無限ループに入ったりします。

目指すのは、「AIがそのエラーを読んで、自分で次の正しい一手を打てる」エラー です。具体的には、機械可読な error コードと、次にどうすべきかの hint をセットで返す。そして何より、副作用を起こす前に、自分で入力を検証する。 AIの引数を鵜呑みにしないことです。

Pythonで書くとこんな感じになります。

from typing import Any

def refund_payment(args: dict[str, Any]) -> dict[str, Any]:
    # 1) まず自分で入力を検証する(AIの引数を信用しない)
    order_id = args.get("order_id")
    amount = args.get("amount")

    if not isinstance(order_id, str) or not order_id.startswith("ord_"):
        return {
            "ok": False,
            "error": "INVALID_ORDER_ID",
            "hint": "order_id は 'ord_' で始まる文字列です。先に get_order で注文を確認してください。",
        }
    if not isinstance(amount, int) or amount <= 0:
        return {
            "ok": False,
            "error": "INVALID_AMOUNT",
            "hint": "amount は1以上の整数(円)で渡してください。",
        }

    order = db_find_order(order_id)
    if order is None:
        return {
            "ok": False,
            "error": "ORDER_NOT_FOUND",
            "hint": f"{order_id} は存在しません。list_orders で有効なIDを確認してください。",
        }
    if amount > order["paid_amount"]:
        return {
            "ok": False,
            "error": "AMOUNT_TOO_LARGE",
            "hint": f"返金できるのは {order['paid_amount']} 円までです。",
        }

    # 2) 実行はいちばん最後。ここまで来て初めて副作用を起こす
    refund = payment_gateway.refund(order_id, amount)
    return {"ok": True, "refund_id": refund.id, "refunded": amount}

このコードのキモは2つです。

ひとつは、実行(返金)を関数のいちばん最後に置いていること。 それより前は全部チェックです。お金を動かす前に、おかしな入力を全部はじいている。もうひとつは、エラーの hint「次にどの道具を使えばいいか」まで教えていること。 たとえば「先に get_order で確認してください」と書いてあれば、AIはちゃんとその通りに動き直してくれます。エラーが、ダメ出しじゃなくて道案内になっているわけです。

原則5:危ない操作には「最小権限・確認ゲート・空打ち・冪等」を仕込む

最後は、いちばん大事な安全の話です。

2026年にいちばん多い設定ミスは何かというと、エージェントが、起動した人と同じ強い権限で動いてしまっている ことだそうです。ファイル書き込み、ネット送信、コマンド実行、データベースの管理者権限まで、本当はその仕事に要らないのに全部持っている。これは事故が起きたときの被害が大きすぎます。

なので、道具を設計するときは、この4つを意識します。

  • 最小権限(least privilege) … その道具に必要な権限だけ与える。削除ツールに送金権限はいらない
  • 確認ゲート … 削除・送金・公開みたいな取り返しのつかない操作は、実行前に人間のOKを挟む
  • 空打ち(dry-run) … 「実際にやらずに、何が起きるかだけ見せる」モードを用意する
  • 冪等(べきとう) … 同じ操作を2回呼んでも、結果が二重にならない作りにする(同じ注文を二重返金しない、など)

「冪等」は聞き慣れない言葉かもしれません。「何回押しても1回分の結果にしかならない」性質 のことです。エレベーターのボタンを連打しても1回しか呼ばれないのと同じ。AIはたまに同じツールを連続で呼ぶので、これがないと二重実行の事故になります。

確認ゲートを入れた例を、TypeScript(zodというライブラリで入力検証)で書いてみます。

import { z } from "zod";

// 入力スキーマ=AIに守らせる契約
const DeleteFileInput = z.object({
  path: z.string().regex(/^\/workspace\//, "操作できるのは /workspace/ 配下だけです"),
  reason: z.string().min(1, "削除理由は必須です"),
});

type Ctx = { confirm: (msg: string) => Promise<boolean> };

export async function deleteFile(rawArgs: unknown, ctx: Ctx) {
  // 1) パースに失敗したら、実行せずヒントを返す
  const parsed = DeleteFileInput.safeParse(rawArgs);
  if (!parsed.success) {
    return { ok: false, error: "INVALID_INPUT", hint: parsed.error.issues[0].message };
  }
  const { path, reason } = parsed.data;

  // 2) 破壊的操作は人間の確認ゲートを通す(最小権限の発想)
  const approved = await ctx.confirm(`${path} を削除します。理由: ${reason}。実行しますか?`);
  if (!approved) {
    return { ok: false, error: "REJECTED_BY_HUMAN", hint: "ユーザーが削除を承認しませんでした。別の方法を検討してください。" };
  }

  await fs.rm(path);
  return { ok: true, deleted: path };
}

path/workspace/ 配下だけに正規表現で縛っているのがポイントです。これで、AIが間違って /etc/ みたいな大事な場所を消そうとしても、道具のレベルで物理的に弾けます。プロンプトで「危ないファイルは消さないでね」とお願いするより、そもそも消せないように道具を作る ほうが、何十倍も確実なんですよね。


そのまま使える、ツール設計レビュー用プロンプト3本

ここまでの原則を、自分のプロジェクトに当てはめるためのプロンプトを3つ置いておきます。AIに自分の道具棚をレビューさせる、という使い方です。

プロンプト1:道具の棚卸し

あなたはAIエージェントのツール設計レビュアーです。
以下のツール一覧を、次の観点で棚卸ししてください。
- 1つのツールが複数の責務(readとwrite、複数の操作)を抱えていないか
- read系とwrite系が分離されているか
- 破壊的操作(削除・送金・公開・上書き)はどれか
出力は表形式で「ツール名 / 主な責務 / 分割案 / 危険度(低中高) / 必要な権限」。

# ツール一覧
(ここに今のツール定義を貼る)

プロンプト2:説明文(description)の書き直し

次のツールのdescriptionを、AIが「いつ使い・いつ使わないか」を迷わず判断できる
取扱説明書に書き直してください。必ず含めるもの:
1. このツールが何をするか(副作用=何が変わるかも明記)
2. 使うべき状況
3. 使ってはいけない状況と、代わりに使うべきツール名
4. 各引数の意味と制約(できれば例つき)
人間向けの曖昧な説明ではなく、モデルが迷わない具体的な文にしてください。

# 対象ツール
(nameと現状のdescription、input_schemaを貼る)

プロンプト3:エラー設計のレビュー

次のツール実装のエラーハンドリングをレビューしてください。
判定基準は「AIがこのエラーを受け取って、次の正しい一手を自分で打てるか」です。
- スタックトレースや内部例外をそのまま返していないか
- error(機械可読なコード)とhint(次にどうすべきか)が揃っているか
- 副作用を起こす前に、無効な入力を検証して弾いているか
改善版のコードと、改善した理由を箇条書きで出してください。

# 対象コード
(ここに実装を貼る)

この3本を順番に回すだけで、自分の道具棚がだいぶ整います。AIに自分の道具を点検させる、というのは、なかなか気持ちのいい使い方です。


人間とAI、それぞれどこを持つのか

この記事の核心は、「AIが賢くなれば全部解決する」じゃないところにあります。道具を設計するのは、最後まで人間の仕事 なんです。役割を整理しておきます。

工程 AIに任せること 人間が設計・判断すること
ツールの実行 引数を組み立てて呼び出す どの道具を棚に並べるか
入力 スキーマに沿った値を生成する スキーマ(型・必須・enum・範囲)を定義する
ツール選択 状況に応じて選ぶ descriptionに使う/使わない条件を書く
エラー回復 hintを読んで次の手を打つ errorとhintの形式を設計する
権限 与えられた範囲だけで動く 最小権限とread/write分離を決める
破壊的操作 必要性を提案する 確認ゲートの有無を決め、承認/却下する
全体の品質 動いてログを残す ログを読み、道具棚を改善し続ける

こうやって並べると、はっきりします。AIに任せるのは「道具を使うこと」。人間が持つのは「道具を設計し、危ないところに鍵をかけ、棚を整え続けること」。AI時代に強いエンジニアって、賢いプロンプトを書く人じゃなくて、AIが安全に使える道具棚を設計できる人 なのかもしれません。


明日からの4ステップ

大きく構えなくて大丈夫です。小さく1個ずつでいい。

  1. 今あるツール(or これから作る予定のツール)を全部書き出して、「読む系・書く系・壊す系」で色分けする
  2. いちばん危ない道具を1つだけ選んで、スキーマをガチガチに(必須・enum・範囲)する。descriptionに「使わない時」を1行足す
  3. その道具のエラーを、errorhint の形に書き換える。実行の前に入力を検証する数行を足す
  4. 取り返しのつかない操作に、確認ゲートを1個だけ付けてみる

これだけで、エージェントの安定感はかなり変わってきます。モデルを乗り換えなくても、今日の自分の手で直せる。そこがいいところです。


おわりに — 道具棚を整えるのは、明日の自分とAIへのプレゼント

最後に、ちょっとだけ僕の感じていることを。

道具棚をちゃんと設計するのって、正直、地味な作業です。スキーマを縛って、説明文を直して、エラーを書き換えて、確認ゲートを足して……。派手さはない。でも、これをやっておくと、未来がだいぶ楽になるんですよね。

僕は何かを設計するとき、いつも「明日の自分が、今日の自分にあざっすって言ってくれるかな」と問いかけるようにしています。雑な道具棚を放置したら、明日の自分(とAI)が事故処理に追われる。逆に、今日ちょっと手間をかけて道具を整えておけば、明日の自分は安心してAIに手を動かしてもらえる。

道具の設計って、未来の自分とAIへのプレゼントを、今日のうちに用意しておく作業 なんだと思います。しかも、一度ちゃんと作った道具は、使い捨てのプロンプトと違って、ずっと再利用できる資産になる。書いたぶんだけ積み上がっていく。

AIがどんどん賢くなっていくこれからの時代、僕らの仕事は「AIの代わりに手を動かすこと」から、「AIが安全に手を動かせる場所を設計すること」へ、静かに移っていくのかなと感じています。

その第一歩として、まずは自分の道具棚をひとつ、整えてみる。今日のその小さなひと手間に、きっと明日の自分が「あざっす」って言ってくれるはずです。

ここまで読んでくださって、ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?