AIエージェントの開発では複雑なフレームワークが注目されがちですが、中核となるツールコーリング(Tool Calling)の仕組み自体はかなり簡潔です。AIモデル自身が外部にアクセスしているわけではなく、手元のコードとの間で決められた形式のデータを循環させています。公開された技術資料をもとに、その基本的な構造と実装時の注意点を客観的に整理します。
この記事の結論
AIのツールコーリングは、モデル自身が外部へ直接アクセスするのではなく、モデルが出力したJSON形式の指示を手元のプログラムが読み取って実関数を実行し、その結果を会話履歴に戻す循環処理です。モデルはステートレスなため毎ターン全履歴が再送されます。そのため返却データの肥大化抑制や無限ループを防ぐ上限設定が重要です。
そもそもAIモデル自体が外部データを直接取得できる?
AIモデル自体には、外部の天気予報を確認したり、手元のファイルを読み取ったり、メールを送信したりする実行能力はありません。モデルが担当する役割は、あくまで受け取った文字列をもとに新しい文字列を生成することに限られています。
「ツールコーリング」という名称から、モデル自身が外部のAPIやデータベースへ直接接続しているような印象を持たれることがあります。しかし実際には、モデルと実行環境の間で交わされる「取り決め」に過ぎません。モデルは外部ツールを直接操作するのではなく、決められた書式に従って「このツールをこの引数で呼び出してほしい」というテキストを出力する仕組みになっています。
実際に外部処理を実行するのはモデルではなく手元のコード
ツールコーリングの全体像において、最も重要な原則は「モデル側は何も実行しない」という点です。処理の主導権は常に手元のプログラム側にあります。
開発者はまず、呼び出しを許可する関数の一覧をモデルに伝えます。モデルは外部情報の取得が必要だと判断した場合、通常の文章の生成を中断し、関数名とJSON形式の引数が記載された構造化ブロック(tool_use)を出力します。そのブロックを受け取った手元のプログラムが、自身の権限で実際の関数を呼び出し、得られた結果を会話履歴に追加して再度モデルに渡します。
ここには独立した記憶装置や複雑な自律モジュールは存在しません。会話履歴の配列そのものが状態(ステート)を保持し、それを巡回させるループ処理そのものがエージェントプログラムの実体となります。
JavaScript約40行で動く4つの循環ステーション
技術教材サイトbuttercup.shで公開された資料「lesson 2: tool calling」では、Vanilla JavaScriptおよびPythonを用いた最小限のエージェント実装が示されています。外部のフレームワークを導入せずとも、4つのステーションを巡回するループ処理だけでエージェントは動作します。
このループは、以下の3つの要素で組み立てられています。
- 1. 実行対象となる通常の関数
- 天候データを返したり、ローカルストレージやファイルを読み書きしたりする、ごく一般的な関数です。特別なエージェント用記述は含まれません。
- 2. ツール定義スキーマ
- 関数の名前、役割を説明する文章、引数の形状を定義したJSON Schemaです。モデルはこの説明文を通常の文章(プロンプト)と同様に解釈してツールの利用可否を決めます。
- 3. 巡回用の反復処理(whileループ)
- モデルの返答を確認し、停止理由が
tool_useであれば関数を実行して結果を会話配列に追加し、通常のテキスト回答であればループを抜ける制御構造です。
通信プロトコルの仕様上、ツール実行結果(tool_result)は role: "user" として会話配列に送り返されます。人間が入力したメッセージではありませんが、プロトコル上「モデルのターン」に対して「それに応答する側のターン」として配置される規則になっています。
会話履歴を毎ターン全件送り直す構造がトークン課金を増やす?
AIモデルはリクエストをまたいで状態を保持しないステートレス(stateless)な性質を持っています。そのため、1ターン進むごとに「最初からのすべての会話履歴」を配列に入れて再送し直す必要があります。
資料に記載された試算例によると、5回のリクエストを重ねるエージェント処理では、送受信されるデータ量が累積していきます。
| ターン | 当該ターンの入力サイズ | 累積される入力トークン数 |
|---|---|---|
| リクエスト1 | 0.3k トークン | 0.3k トークン |
| リクエスト2 | 0.9k トークン | 1.2k トークン |
| リクエスト3 | 2.0k トークン | 3.2k トークン |
| リクエスト4 | 2.9k トークン | 6.1k トークン |
| リクエスト5 | 4.2k トークン | 10.3k トークン |
最終的な会話の長さが4.2kトークンであっても、課金対象となる入力トークンは合計で10.3kトークンに達し、約2.5倍の差が生じます。過去のターンで追加されたデータは、以降のリクエストすべてに同伴するためです。
ここで特に影響が大きいのが、ツールの戻り値のサイズです。例えば10ターンの処理において、2ターン目のファイル読み込みツールが8kトークンのソースコード全体を返した場合、残りの8回のリクエストすべてでその8kトークンが繰り返し送信されます。これだけで64kトークン(8回 × 8kトークン)の課金が上乗せされます。
プロンプト自体の文字数を削るよりも、ツールが返すデータ量を必要最小限(ファイル全体ではなく指定行範囲、テーブル全体ではなく抽出した数行など)に絞り込む設計が、実務上のコスト抑制において重要視されます。また、共通の履歴プレフィックスに対して割引が適用されるプロンプトキャッシュ機能の活用も有効な手段として挙げられています。
停止トラブルや並列呼び出しの阻害を回避する4つの実装基準
手書きでループを構築する際、エラーによる停止や意図しない動作を防ぐために、資料内では4つの基本ルールが整理されています。
- 1. 失敗した呼び出しにも必ず tool_result を返す
- 関数の実行時にファイル不在などの例外が発生しても、結果の返却を省略してはいけません。
is_error: trueを付与し、エラーメッセージを内容に含めて返却します。モデルはエラー内容を認識して別のアプローチを試みます。返却を怠ると、対応するIDが見つからず次のリクエストがプロトコル違反で拒否されます。 - 2. 複数のツール要求には1つのメッセージでまとめて回答する
- モデルが一度に複数のツール呼び出しを求めてきた場合、すべての結果を1つのユーザーメッセージ(配列内)にまとめて返します。メッセージを個別に分けて返信すると、モデルが以降のやり取りで並列呼び出しを行わなくなる傾向が生じ、処理効率が低下します。
- 3. モデルからの返信オブジェクトを加工せず完全な形で保存する
- テキスト部分だけを抽出して履歴に再構築するのではなく、
reply.content全体をそのまま配列に push します。モデルの返信には思考ブロックなどテキスト以外の情報が含まれる場合があり、それらを欠落させると次のターンの生成品質に悪影響を及ぼします。 - 4. ループに必ず上限回数を設定する
- ツールの説明文を誤解したモデルは、同じツールを意図せず何十回も連続で呼び出し続けることがあります。
while (true)のまま放置せず、ターン数のカウンタを設け、20回から40回程度で強制終了する安全装置が推奨されています。
自作ループ処理とSDKの自動実行ツールで分かれる制御の柔軟性
エージェントのループ処理には、自身で while ループを書く方法と、公式SDKが提供する自動実行機能(Anthropic SDKの tool_runner や toolRunner など)を利用する方法の2通りの選択肢があります。
SDKの自動実行機能を採用した場合、コード量は10行程度に抑えられ、並列呼び出しの集約やエラーハンドリングといった前述のルールも自動的に処理されます。一方で、ループ全体の制御権をSDK側に委ねることになります。
そのため、外部への書き込み処理の直前に人間の承認ステップを挟む、リクエストごとに独自のログを記録する、失敗時に引数をプログラム側で書き換えて再試行する、といった細かな介入を行う場合は、手書きのループ処理の方が制御を直接組み込みやすいという特性があります。
なお、外部ツールとの接続規格として注目されるMCP(Model Context Protocol)は、エージェントのループ構造そのものを置き換える仕組みではありません。外部のサーバーから利用可能なツール一覧を取得し、手元の tools 配列に供給するための接続インターフェースという位置づけになります。
最小限のコードで構成されたループの構造を把握しておくことで、SDKや上位フレームワークを利用する際にも内部の動作状況を正確に判断できるようになります。
あわせて読みたい関連記事
おすすめ Nvidiaが示した「ハーネス」とは?AIツール選びで見る場所
ツールコーリングの循環構造(モデルと外部ツールのやり取りを制御するループ)を理解した読者にとって、モデル単体ではなく外側の制御構造(ハーネス)が果たす役割を整理した本記事は、システム全体のアーキテクチャを体系的に把握する上で最も役立ちます。




コメント