関数からToolへ:Vercel AI SDKでツール呼び出しの扉を開く

Next.js で API Route を書き、サードパーティの API を呼び出すことに慣れているなら、あとは「自分で API を呼び出せる大規模言語モデルアシスタント」を作るまであと一歩です。これらの関数を Tool としてラップし、モデルにいつ使うかを決めさせるだけです。
この記事では、MCP や Agent フレームワークといった「大きな言葉」には触れず、次のことだけを行います:
「普通の関数を書く」ところから「大規模言語モデルから呼び出せる Tool を書く」ところまでをサポートし、その背後にある考え方を明確にします。
読み終わった後、次のことができるようになるはずです:
Vercel AI SDK の Tool が何であるか、普通の関数とどう違うかを理解する。
最小限の機能する Tool を作成し、Next.js で実行する方法を知る。
Tool のパラメータと戻り値をどのように設計すれば、モデルがコードをより使いやすくなるかを理解する。
大まかに理解する:今日はまず Tool だけで多くのアプリケーションを実現でき、MCP のようなプロトコルは後回しにしてよいということ。
1. なぜ「ツール」というレイヤーが必要なのか?
まず単純な要件を想像してみてください:「天気を調べるチャットボット」を作りたいとします。
最もナイーブな方法は次の通りです:
ユーザーが「北京の今日の天気を調べて」と入力する。
サーバー上でこの自然言語を取得する。
自分で if/else や正規表現を書いて都市名を抽出する。
天気 API を呼び出して結果を取得する。
結果をプロンプトに戻し、モデルに一文にまとめさせてユーザーに送信する。
この方法は使えますが、明らかな問題があります:
ロジックがコードにハードコードされ、モデルは単なる「高度なテンプレートエンジン」になっている。
機能を追加するたび(為替レートの確認、スケジュールの確認など)に、解析と呼び出しのロジックを追加で書かなければならない。
モデルは「どのような能力が使えるか」を全く知らないため、手取り足取り「道を示す」必要がある。
Tool というレイヤーは、「あなたの if/else + API 呼び出し」をモデルから見える機能に昇格させます。
言い換えれば:
関数を書き、「何ができるか」「どんなパラメータが必要か」「何を返すか」を定義する。
モデルに「これらはあなたが呼び出せるツールです」と伝える。
モデルはユーザーの入力に基づいて、どのツールを呼び出すか、どんなパラメータを渡すかを自分で判断する。
この抽象化こそ、Vercel AI SDK の Tool が行っていることです。
2. Vercel AI SDK の Tool とは何か?
Tool は次のように理解できます:
「取扱説明書付きの関数」。説明書はモデル向けで、関数本体はあなた自身のコード向けです。
Vercel AI SDK では、Tool は通常次の3つの情報を含みます:
description:このツールが何をするかを一文で説明します。parameters:JSON Schema(または Zod)を使って、必要な入力パラメータを記述します。execute:実際に実行される関数。HTTP API の呼び出しやデータベースのクエリなどを行います。
おおよそ次のような外見です(疑似コードで、定義を先に感じ取ってください):
import { tool } from 'ai';
const getWeather = tool({
description: '查询指定城市的实时天气',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名称,比如 Beijing' },
},
required: ['city'],
},
execute: async ({ city }) => {
// 这里就是你平时会写的业务代码
const res = await fetch(`https://api.example.com/weather?city=${city}`);
return await res.json();
},
});その後、モデルを呼び出す際に、この getWeather を AI SDK に渡します:
SDK が
descriptionとparametersをモデルが理解できる「ツールリスト」に変換します。モデルはユーザーの意図を理解する際に、「getWeather を使うべきか?使うならどんな
cityを渡すべきか?」を考えます。モデルが呼び出しを決定すると、SDK はパラメータを
executeに渡し、結果を取得して、モデルが最終応答を生成し続けるのを可能にします。
「都市名を解析する」「いつ API を呼び出すかを決定する」といったロジックを自分で書く必要はなくなり、「主導権」をモデルに委ねます。
3. 最小限の機能する Tool の例
実際に動作する最小限の例を書いてみましょう:現在時刻を調べる。
3.1 シンプルな Tool を定義する
Next.js プロジェクトに ai パッケージ(Vercel AI SDK)がインストールされていると仮定します。サーバー側でシンプルな Tool を作成します:
// app/api/chat/tools.ts
import { tool } from 'ai';
export const getCurrentTime = tool({
description: '获取当前服务器时间(ISO 字符串)',
parameters: {
type: 'object',
properties: {},
},
async execute() {
return {
now: new Date().toISOString(),
};
},
});ここで注目すべき点がいくつかあります:
parametersが空オブジェクト:このツールは入力パラメータを必要としないことを意味します。executeがオブジェクト{ now: string }を返す(文字列を直接返さない)。これにより、後で構造化された処理(フロントエンド表示、後続の計算など)が容易になります。
3.2 API Route で Tool を使用する
次に、最もシンプルなチャット API を書き、モデルが会話中にこの Tool を呼び出せるようにします。具体的な呼び出しの詳細はバージョンによって若干異なりますが、ここでは「構造」と「考え方」に焦点を当てます。疑似コードは次のようになります:
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import { streamText } from 'ai'; // 假设使用 AI SDK 的某个调用方法
import { getCurrentTime } from './tools';
import { openai } from '@ai-sdk/openai';
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const response = await streamText({
model: openai('gpt-4o'), // 你选的模型
messages,
tools: {
getCurrentTime,
},
});
return response.toAIStreamResponse();
}ここでの主要なポイント:
toolsフィールドで、定義した Tool をモデルに公開します。モデルがユーザーメッセージを受信した後、現在時刻が必要と判断した場合、「getCurrentTime に対する呼び出しを開始」し、SDK が自動的に
executeを実行します。Tool の呼び出し結果はモデルにフィードバックされ、モデルはその時間に基づいて最終応答を生成します。
3.3 シンプルなフロントエンドチャットインターフェース
フロントエンドでは useChat のような Hook を使って最小限のチャット UI を作成できますが、この部分はこの記事の主要トピックではないため、詳しくは説明しません。重要なのは、フロントエンドから見ると通常のチャット API と変わらず、モデルが「自分で時刻を調べる」ようになったという点です。
4. Tool と通常の関数の本質的な違い
ここで疑問に思うかもしれません:ルート内で直接 new Date().toISOString() と書けば時刻を調べられるのでは?なぜ Tool を使う必要があるのか?
重要な違いは 誰が「決定」を下すか にあります:
通常の関数:ビジネスロジック内で明示的に呼び出し、完全にあなたが「いつ呼び出すか」を決定します。
Tool:単に「この能力」を公開し、モデルに「使い方」を伝えるだけで、「使うかどうか」「どう使うか」はモデルの判断に委ねます。
これにより、いくつかの実用的な利点が生まれます:
if/else を減らす
自然言語を解析したり、キーワードをマッチングしたり、分岐を選択したりする必要がなくなります。
モデルがコンテキストに基づいて、特定の Tool を使うべきかどうかを自分で判断します。
複数の Tool の自動連携をサポート
複数のツール(時刻確認、天気確認、スケジュール確認)を定義し、モデルが一回の会話の中で必要に応じて複数のツールを呼び出すことができます。
ツールが増えても、大量の「ビジネスルーティングテーブル」を維持する必要はなく、モデル自身が「なんとかする」ようになります。
モデル間の統一性
モデルベンダーごとに Tool / Function Calling のインターフェースは異なりますが、AI SDK がこれらの詳細を統一してくれます。
Tool 定義を1セット維持するだけで、異なるモデル間を切り替えることができます。
Tool は次のように捉えることができます:
「モデル ↔ あなたのコード」の間に「構造化されたメタデータ付きの関数」というレイヤーを追加し、この連携をインテリジェントでありながら制御可能にします。
5. 使いやすい Tool の設計:パラメータと戻り値
Tool を書く際は、「動けばいい」だけでは不十分です。パラメータと戻り値を適切に設計することで、モデルがより安定して制御可能にツールを使えるようになります。
5.1 パラメータ:モデルが物事を明確に表現できるようにする
パラメータ定義にはいくつかの小さな原則をお勧めします:
列挙できるものは列挙する:例:
unit: 'celsius' | 'fahrenheit'。フィールドに分割できるものは分割する:「都市+日付」を1つの文字列にまとめず、モデルに自分で分解させる。
descriptionに例を含める:モデルは実際にそれを読みます(または、トレーニング中に同様のパターンを見たことがあります)。
例:
const getWeather = tool({
description: '查询指定城市在指定日期的天气',
parameters: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称,比如 Beijing、Shanghai',
},
date: {
type: 'string',
description: '日期,格式为 YYYY-MM-DD,比如 2025-05-17',
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: '温度单位,默认 celsius',
},
},
required: ['city', 'date'],
},
async execute({ city, date, unit = 'celsius' }) {
// ...
},
});5.2 戻り値:できるだけ構造化し、文字列の一塊にしない
execute 内で直接「東京、今日25度、晴れ」のような文字列を返すこともできますが、これにより柔軟性が大きく損なわれます:
フロントエンドはその結果をそのまま表示や計算に利用できません。
モデルも後続の呼び出しで構造化情報を再利用するのが難しくなります。
推奨される方法は、明確なオブジェクトを返すことです:
async execute({ city, date, unit = 'celsius' }) {
const data = await fetchWeather(city, date, unit);
return {
city,
date,
unit,
temperature: data.temp,
condition: data.condition, // sunny, cloudy, etc.
};
}こうすることで、モデルは自分で言葉を組み立ててユーザーに応答できるだけでなく、後続のステップでこれらのフィールドを引き続き利用できます(例えば、別の Tool を呼び出すなど)。
6. 複数の Tool を一度に接続する:モデル自身に選ばせる
これまでは getCurrentTime だけを使っていましたが、ここに getWeather を追加して、まとめてモデルに渡してみましょう。
// app/api/chat/tools.ts
export const tools = {
getCurrentTime,
getWeather,
};モデルを呼び出す際:
const response = await streamText({
model: openai('gpt-4o'),
messages,
tools,
});このとき、ユーザーが次のように言ったとします:
「明日の上海の天気を教えて。もし晴れなら、ついでに今の時刻も教えて。」
モデルは次のようなことを行う可能性があります(論理レベル):
まず
getWeatherを呼び出して、「晴れかどうか」を判断する。次に
getCurrentTimeを呼び出して、現在時刻を取得する。2つの呼び出し結果を統合した最終応答を生成する。
この一連のロジックのために「ビジネスフローチャート」を書く必要は一切ありません。必要なのは:
ツールを適切にリストアップし、パラメータを明確に定義すること。
システムプロンプトでモデルに「このような要件にはツールを活用しなさい」と大まかに伝えること。
これこそが「ツール呼び出し」の真の力です:あなたは「フローを書く」ことから「能力を定義する」ことへと移行します。
7. この記事のまとめ:まず Tool を使いこなしてから、プロトコル層を考える
この記事では、次の3つのことだけを行いました:
Tool を概念的に分解:それは「取扱説明書付きの関数」であり、説明書はモデル向けです。
最小限の Tool の例を作成し、Next.js API Route でそれを使用する方法を示しました。
パラメータと戻り値の設計方法について説明し、モデルがツールをより使いやすくする方法を述べました。
Vercel AI SDK の Tool だけで、次のようなものを作成できます:
内部のカスタマーサポートアシスタント/チケットアシスタント。
SaaS 製品内のインテリジェントクエリ/インテリジェント生成。
様々な「調べて」「計算して」「記録して」といった業務アシスタント。
そしてこの段階では、MCP などのプロトコル層を全く気にする必要はありません。これらは主に「ホストやプロジェクトをまたいでツールを再利用する」問題を解決するものであり、ツールの数や呼び出し環境が複雑になってからゆっくり導入するのに適しています。
次の記事では、「Tool だけを使う」ことをベースに、ツールが増え、呼び出し環境が複雑になったときに、Tool だけではどのような限界や課題に直面するか、そしてなぜ MCP のようなプロトコルが登場するのかについて、具体的に掘り下げます。
Google でフォロー
HeyBinyang を Google の優先ソースに追加
Google から更新を見つけやすくしたい場合は、このサイトを優先ソースとして追加できます。
共有
共有
この記事を共有します。