1. モデルはあなたのツールをどのように「見る」か
まず確認しておきたいのは、モデルにとっての tool は「あなたの美しい TypeScript の関数」ではなく、次のような構造化された説明だということです:
- name: 技術的な名前。たとえば "search_gifts";
- description: そのツールをいつ・なぜ使うのかを説明する人間向けテキスト;
- inputSchema: 各フィールドごとに description、型、制約などを持てる JSON Schema。
大まかには、モデルは次のように動作します(GPT の頭の中の擬似コード):
1. ユーザーのリクエストを読む(言語は問わない)。
2. tools の一覧を読む: name + description + 引数の説明。
3. 各 tool が課題に適しているかを見積もる。
4. tool が必要なら、スキーマに従って引数入りの JSON を生成する。
5. そうでなければ、テキストで回答する。
ここで重要なポイントが 2 つあります。
第一に、ツールの description フィールドは開発者向けコメントではなく、モデルとあなたのバックエンドの間のインターフェースです。説明が曖昧・不完全・あるいはユーザーの言語と一致しない場合、モデルの失敗が増えます。たとえば不適切な tool の選択、引数の取り違え、本来ツールが必要な場面でツールを使わずテキストで答える、といった挙動です。
第二に、JSON Schema の各フィールドに付ける description は、ツール自体の説明と同じくらい重要です。モデルは各プロパティの description を実際に読み、「年齢」はどのフィールドで、「予算」はどこで、id はどこにあるべきかをそこで判断します。
GiftGenius のミニ例
ツール search_gifts を考えましょう。最初の「EN のみ」版は次のようになります:
// server/tools/searchGifts.ts
export const searchGiftsTool = {
name: "search_gifts",
description: "Search for gift ideas based on user preferences.",
inputSchema: {
type: "object",
properties: {
recipient_age: {
type: "integer",
description: "Age of the recipient in years.",
},
budget: {
type: "number",
description: "Maximum budget in user's currency.",
},
},
required: ["budget"],
},
};
ユーザーが「お母さんへのプレゼントが欲しい。60 歳。予算は 3000 ルーブルまで」と書いた場合、モデルは次を行う必要があります:
- search_gifts が適切なツールだと判断する。
- 「60 歳」を recipient_age に、「3000 ルーブル」を budget に入れると判断する。
説明が英語のみでも GPT は対処できますが、内部で余計な「翻訳」ステップが必要になります。言語が複数、あるいは弱めのモデルでは精度に悪影響が出ます。
2. 多言語 App における「英語の説明」問題
モジュール 9 では、UI ウィジェット、カタログ、エラー、コマース系テキストといったローカライズの全体像に触れました。ここでは絞り込んで、ツール説明が英語のみで、ユーザーがたとえばロシア語やスペイン語でやり取りする場合に何が起こるかを見ます——ここでちょっとした混乱が始まります。
典型的なシナリオ:
- ユーザー: 「IT 系の友人向けにプレゼントを選んで。予算は 50 ユーロまで」。
- モデルは tools の一覧を見て、説明が EN のみであることに気づく。
- リクエストも EN なら——問題なし。
- 別の言語なら、モデルは次を強いられる:
- まずリクエストの理解、
- それを英語の説明へと頭の中でマッピング、
- ツールの選択、
- さらに JSON の引数抽出。
強いモデルならまだ何とかなりますが、次の傾向が出ます:
- ツールを呼ばない回答の割合が増える——モデルが「自力で答えられる」と判断しがち;
- 引数の誤りが増える(とくに通貨・単位・地域制約が重要な場合);
- 複数ツール間のルーティングが不安定になる(的外れのツールを選ぶ)。
ありがちなミス: budget フィールドは「ユーザーの通貨」での値を期待しているのに、説明にその旨がない。モデルはデフォルトで USD と解釈し、ユーザーが明確に 50 ユーロと言ったのにバックエンドへ 50 ドルを送ってしまう。
ここで「説明のローカライズ」が効いてきます。
3. ローカライズ手法: 別ツール方式 vs 多言語説明方式
基本となるアーキテクチャには 2 つの方式があり、どちらにも存在理由があります。
言語ごとに別ツール
この方式では、説明言語の異なる複数の tools(それぞれ名前も別)を用意します。
GiftGenius の例:
export const searchGiftsEn = {
name: "search_gifts_en",
description: "Search for gift ideas based on user preferences.",
// ...
};
export const searchGiftsRu = {
name: "search_gifts_ru",
description: "ユーザーの好みに基づいてギフトを選定します。",
// ...
};
ChatGPT App では、利用可能な tools の一覧が locale に依存することが重要です。もし locale が = "ru-RU" なら、MCP サーバーは search_gifts_ru のみを返す必要があります。
一方、 locale が = "en-US" なら、search_gifts_en のみです。
利点は、description を完全に単一言語で「きれい」に保てることです。App を複数の単一言語版(それぞれ独自のプロンプトと説明を持つ)として考えることができます。対象言語が少なく、市場差が大きい場合に心地よい運用です。
欠点は、ロジックの重複と分析面の複雑さです。バックエンドの処理は結局同じでも、MCP/マニフェスト層では 2 つの別ツールになります。変更のたびに両方の説明を更新し忘れないようにする必要があります。
インサイト(2025-12-01 時点のデータ)
実験的には、ユーザー言語/locale と一致する description に顕著な優位は見られませんでした。説明の言語にかかわらず、ツールの選択頻度はほぼ同等でした。類似の説明を持つ 2 つのツール(言語違い)が並ぶと、かえって ChatGPT が混乱することもありました。
また、Store への登録時にはアプリのレビューも必要です。そのため、すべての tool description と argument description を英語で書くことを推奨します。
ただし、将来的に ChatGPT に何千ものアプリが並び、ツール選択の競争が激しくなれば、ユーザーの locale に一致する description が優位を得る可能性もあります。いわば Tool Search Optimization の登場を待つ段階です。
多言語の descriptions を持つ単一ツール
もう一つの方式では、name は 1 つ(例: search_gifts)に保ちつつ、description と JSON Schema のフィールド説明を多言語化します。
いくつかのスタイルがあります:
- 短文の 2 言語形式:
description: "Search gifts for a recipient. / 受取人の好みに基づいてギフトを検索します。", - 言語ごとのマーク付きブロック:
description: "[EN] Search for gifts based on user preferences. [RU] 受取人の好みに基づいてギフトを選定します。", - 個別フィールドを結合して 1 本の文字列にする(やや不便):
description: `EN: ${enDescription} RU: ${ruDescription}`,
利点——ツールは 1 つ、情報源は一元化(single source of truth)。MCP Gateway を伴うアーキテクチャでも展開しやすく、ユーザーの言語に関係なく ChatGPT へ常に同じインターフェースを提示できます。
欠点: 説明が長くなりがちです。言語を雑に「混ぜる」と、特に [EN] と [RU] のような明示的な区切りがない場合にモデルが少し混乱することがあります。
GiftGenius のような学習用プロジェクトでは、ハイブリッドを推奨します。説明は基本 EN にしつつ、ローカル言語での短い補足をそっと添える。実際の「意味」(どの言語で応対するか、ユーザーへの話しかけ方)は、引数(locale)と system プロンプトで通す、という設計です。
4. JSON Schema のローカライズ: フィールド説明
次は引数そのものに踏み込みます。
JSON Schema では各フィールドに description を(できれば)付けます。モデルはツール呼び出し用の JSON を生成する際にこの文字列を読みます。
GiftGenius なら次のようにできます:
export const searchGiftsTool = {
name: "search_gifts",
description:
"Search gifts based on user preferences (RU: 受取人の好みに基づいてギフトを選定する)。",
inputSchema: {
type: "object",
properties: {
recipient_age: {
type: "integer",
description:
"Recipient age in years. RU: 受取人の年齢(整数)。",
},
budget: {
type: "number",
description:
"Maximum budget in user's currency. RU: ユーザーの通貨での上限予算。",
},
locale: {
type: "string",
description:
"User locale (e.g. 'en-US', 'ru-RU'). RU: インターフェースと言語の指定。",
},
},
required: ["budget", "locale"],
},
};
いくつかの実践的な指針。
第一に、フィールド名(recipient_age、budget、locale)は通常英語のままにします。翻訳するのは description です。これは、JSON 形式を言語ごとに変えないために重要で、言語ごとの別契約を維持せずに済みます。
第二に、description には、通貨・単位・重要な制約を明示するのが有効です。「変な引数」を大きく減らせます。
第三に、MCP Gateway をすでに使っているなら、locale を自動的にツールの引数へ渡す取り決めも可能です。そうすればモデルが自分で補完する必要はありません。それでも locale の説明は残しましょう。モデルはそのパラメータの意味と用途をより正確に理解できます。
5. 説明言語の選び方: 実アプリのための戦略
実務的な問いはこれです: 説明の基本言語を何にするか、いつ完全ローカライズすべきか。
経験上、GPT モデルは依然として英語コンテキストで最良に動作し、多くの開発者が説明を EN のみにしています。ただし多言語アプリでは妥協的な選択になり得ます。
いくつかの戦略を見ていきます。
EN のみの説明
最も単純な方式——すべて英語。
利点: コードベースが一つ、維持すべき言語も一つ。正確で良い文章を書きやすい。モデルも英語で統一されていると嬉しい。
欠点: 他言語で書くユーザーに対しては、ツール選択や引数の品質が低下する恐れ。特に「怠惰」なモデルや、パラメータが多い複雑なツールで顕著。
EN + 短いローカル補足
折衷案: 基本説明は EN、最後にローカル言語の短いブロックを付け、モデルがユーザー語彙と引数を結び付けやすくする。
例:
description:
"Search for gifts based on user preferences. RU: 受取人の説明・年齢・予算に基づいてギフトを選定するツール。",
JSON Schema の例:
description:
"Age of the recipient in years. RU: 受取人の年齢(年)。",
利点: モデルは引き続き「英語の世界」にいられるが、ユーザー言語でのヒントも持てる。
欠点: 説明は多少長くなるが、たいていは許容範囲。
locale ごとに説明を完全ローカライズ
最も本格的な方式: ツールとフィールドの説明を、ChatGPT から得られる locale に応じて切り替える。en-US には英語、ru-RU にはロシア語、de-DE にはドイツ語。
これはもはや「固定の JSON Schema 1 本」ではなく、MCP/Gateway が都度選ぶスキーマの集合です。
MCP レベルでは次のようになります:
function getSearchGiftsToolDescription(locale: string) {
if (locale.startsWith("ru")) {
return {
name: "search_gifts",
description: "受取人の好みに基づいてギフトを選定します。",
// ru‑schema...
};
}
return {
name: "search_gifts",
description: "Search for gifts based on user preferences.",
// en‑schema...
};
}
利点: モデルがユーザーと同じ言語のインターフェースを見られる。最大限の使い勝手。
欠点: 運用とテストが複雑になる。ローカライズ版の説明が意味的に同期され続けるようにし、英語スキーマにフィールドを追加した際に他言語を更新し忘れないプロセスが必要。
6. 私たちのアプリ GiftGenius での実装
具体例に進みます。GiftGenius ではハイブリッド案を採用します。単一ツール search_gifts、説明は主に EN だがロシア語補足を少し、さらに引数に locale を含めます。
MCP サーバーは TypeScript で、MCP SDK 風に tools を定義しているとします。
// mcp/tools/searchGifts.ts
import { z } from "zod";
export const searchGiftsInputSchema = z.object({
recipient_age: z
.number()
.int()
.describe(
"Age of the recipient in years. RU: 受取人の年齢(整数)。"
),
budget: z
.number()
.describe(
"Maximum budget in user's currency. RU: ユーザーの通貨での上限予算。"
),
locale: z
.string()
.describe(
"User locale (e.g. 'en-US', 'ru-RU'). RU: インターフェースと言語の指定。"
),
});
export const searchGiftsTool = {
name: "search_gifts",
description:
"Search for gifts based on user preferences (RU: 受取人の好みに基づいてギフトを選定する)。",
inputSchema: searchGiftsInputSchema,
// execute(...) ...
};
重要なポイント:
- locale は必須です。ウィジェットがそれを知っている( _meta["openai/locale"] から取得)なら、callTool の呼び出しに自動で渡すか、MCP Gateway 側で自動付与できます。
- 説明にすでに「年齢」「予算」「インターフェースの言語」といったキーワードが含まれているため、モデルはユーザーの入力からどれをどこへ入れるべきかを理解しやすくなります。
Apps SDK 側では、たとえば widgetAccessible が有効なら、ウィジェットから直接このツールを叩き、ウィジェットが持つ locale を渡す関数を用意できます。
// widget/hooks/useSearchGifts.ts
export async function searchGiftsFromWidget(params: {
recipientAge: number;
budget: number;
locale: string;
}) {
const openai = (window as any).openai;
const result = await openai.callTool("search_gifts", {
recipient_age: params.recipientAge,
budget: params.budget,
locale: params.locale,
});
return result;
}
この接続により、locale が ChatGPT からツールへ渡り→ツールは正しいカタログや価格フォーマットを選び→最終的にフロントエンドで美しく描画できる、というアーキテクチャが強化されます。
7. 挙動の実験: ローカライズの影響をどう測るか
さて肝心の検証です。tools と説明のローカライズが本当にモデル挙動を改善しているのか、翻訳にかけた時間が無駄になっていないのかをどう確かめるか。
GiftGenius の Dev Mode だけでも、ちょっとした「ミニ実験」を行えます。
2 つの App 構成: base vs localized
次の 2 つの構成を用意します:
- base — ツールと JSON Schema の説明が EN のみ;
- localized — EN+RU の説明(あるいは完全な ru 版があればなお良い)。
その他(カタログ、UI、プロンプト)は効果の混在を避けるため同一に保ちます。
簡単にするなら:
- Dev Mode(や Store)では localized のみを運用;
- base はローカルの別ブランチで走らせ、あらかじめ用意したリクエストセットで比較。
何を計測するか
重要な指標は 3 つあります。
1 つ目——正しいツール選択の頻度。ロシア語(や他言語)のテストリクエスト集合に対して、モデルがどれだけの割合で:
- 必要な場面でツールを呼べているか;
- search_gifts を正しく選べているか(他ツールではなく)。
2 つ目——引数の正しさ。JSON が期待どおりかを検証します。フィールドの取り違えがないか、予算が正しい通貨か、年齢が明確に整数か、locale が欠落していないか。
3 つ目——奇妙・無意味な呼び出しの数。たとえば「今何時?」への質問に search_gifts を呼ぶ、あるいは recipient_age に 3000 を入れる(本来は予算)など。
手動でも、MCP/Agents のログでも検証可能です。ログ分析は今後必ず役立つので、今のうちから慣れておくと良いでしょう。
手動のテストセットをどう作るか
ローカライズ用の小さな「golden prompt set」を用意できます:
1. "10 歳の女の子向けに 30 ユーロ以内の手頃なプレゼントが欲しい。絵を描くのが好きです。"
2. "同僚のプログラマ向けのプレゼントを選んで。35 歳、予算は 100$。"
3. "祖母の記念日用に、70 歳、予算は 5000 ルーブルまでのプレゼントが欲しい。"
これらを両方の App(base と localized)で流し、次を観察します:
- モデルがどの tools を選ぶか;
- どの引数を設定するか;
- ツールを呼ばなかった場合、テキスト回答がどう変わるか。
半職人的ライフハック: ChatGPT API に対してこれらのリクエストを回す簡単なスクリプトの枠組みを作っても構いませんが、本講座の範囲では Dev Mode の手動で十分です。特に有用なのは、混合言語メッセージや奇妙な locale 組み合わせに関するカテゴリーのリクエストです。これについては別枠で扱います。
もし本格的な商用アプリを開発しており、数百万ドル規模の影響があり得るなら、必ずあなたのアプリでこれらの点を検証してください。モジュール 20 では「golden prompt set」をプロフェッショナルに扱う方法を解説しています——ぜひ習得を。
8. 混合言語と奇妙な locale の組み合わせ
LLM 開発者を一番楽しませるのは、2 言語を同時に使うユーザーです。たとえば:
"プレゼントが欲しい for my friend、彼は Star Wars が好き、budget 100€"
モデルは多言語対応なので多くの場合はこなしますが、混合言語 + 英語説明のときは誤り確率が上がります。
ありがちな状況がいくつかあります。
第一——ユーザーは RU で書き、ツール説明は EN。理解はできても、ときどき専門用語(カテゴリ名や珍しいフィールド名)で混乱します。
第二——locale = "ru-RU" だが、ユーザーはなぜか英語で書く。ChatGPT はインターフェースをロシア語で出すべしというシグナルをあなたに渡しているが、実際のテキストは EN。対応は 2 つ:
- locale を一次の真実としてロシア語の説明を返す;
- またはメッセージの言語検出を追加シグナルとして行い、実際の言語に説明を合わせる。
第三——locale = "en" だが、ユーザーがときどきロシア語の語彙を挟む。この場合は、英語の説明でも概して問題ありません。
実務では、明確な方針を 1 つ決めれば十分です。たとえば:
- locale が "ru" で始まるなら、説明にロシア語の文を加える;
- それ以外は、説明は英語のみ。
方針が明確なら、各分岐を意図的にテストできますし、なぜ今日はこの言語の説明になったのかと首をかしげずに済みます。
9. ドキュメント、プロセス、そして「正典」言語
説明のローカライズは一度きりではなくプロセスです。新機能はユーザーに喜ばれ、既存機能が壊れないとあなたは嬉しい。そのため、あらかじめ次を決めておきましょう:
- どの言語を「正典」として、そこから他言語へ翻訳するか;
- ローカライズ済み説明をどこに保存するか;
- 整合性をどう検査するか。
多くの場合、正典は英語です。新しいツールやフィールドはまず EN で説明を書いてレビューし、その後に他言語へローカライズ。コードベースでは次のように表現できます:
- tools.en.json に name/description/フィールドの完全版;
- tools.ru.json、tools.de.json は各言語向けの派生物;
- これらの辞書から MCP 用の最終 JSON Schema を組み立てる小さなジェネレータ。
簡易運用では、当面はコード内の文字列で済ませても構いませんが、後で辞書へ切り出しやすいよう構造化しておくと良いです。
説明はプロダクトテキストでもあります。UI テキスト同様に厳密にレビューしましょう。分かりやすさ、曖昧さの排除、不要な「水増し」をチェックします。特に多言語版では、ロシア語の補足が英語の主説明と矛盾しないことが重要です。
10. ビジュアル図: 言語がスタックをどう流れるか
全体像をつかむために、ツールのローカライズを加味したリクエストの簡略フローを見てみます。
flowchart TD
U[ユーザーはRUで書く] --> C[ChatGPT UI]
C -->|"_meta.openai/locale = 'ru-RU'"| W[GiftGenius ウィジェット]
W -->|"locale = 'ru-RU'"| T["Tool descriptions (EN+RU)"]
T --> M[GPT モデル]
M -->|callTool search_gifts| MCP[MCP / Gateway]
MCP -->|"locale = 'ru-RU'"| B[Backend / カタログ RU]
B --> MCP --> M2["GPT モデル(回答)"]
M2 --> C2[ChatGPT UI + ウィジェット RU]
ここでは、ユーザーの言語と locale が次を決定します:
- ウィジェットの UI をどの言語で表示するか;
- モデルが見るツールとフィールドの説明言語;
- バックエンドが選ぶカタログや通貨;
- (モデルおよびウィジェットの)回答の書式。
11. tools と説明のローカライズでありがちなミス
エラー No.1: description を「技術的」だと見なして一切ローカライズしない。
英語ユーザーだけなら動きますが、他言語ユーザーが来た途端、モデルはツールなし回答やスキーマに対する歪んだ引数を出しがち。UI を翻訳したのに、App が「英語のまま」振る舞うという事態になります。
エラー No.2: JSON のフィールド名を言語ごとに変える。
たとえば age を vozrast、budjet のようにしたくなるかもしれません。これはバックエンド側で悪夢を生みます。異なるスキーマ、形式、ログ解析の複雑化。フィールドの name は安定させ、説明だけをローカライズしましょう。
エラー No.3: 説明文で言語を無秩序に混ぜる。
「受取人の preferences に基づく gifts の search」のような文は、モデルにも人間にも役立ちません。多言語説明を行うなら、[EN] ... [RU] ... のように明確に区切りましょう。モデルは構造として認識できます。
エラー No.4: locale をツールへ渡さない。
説明をローカライズしても、locale をツールへ渡していなかったり(あるいは MCP Gateway が中継していない)すると、バックエンドはどのカタログ・書式を使うべきか分かりません。結果、モデルは「多言語対応」しようとするのに、サーバーは一市場向けのデータしか返さないというチグハグが起きます。
エラー No.5: 機械翻訳だけで説明を自動翻訳し、レビューしない。
自動翻訳に流すだけで十分に思えるかもしれませんが、実際には用語や引数の訳が不正確になりがちです。その結果、モデルがツールやフィールドの意味を誤解します。よく練られた EN 版 + 丁寧にローカライズした少数言語、のほうが、「自動」対応の 20 言語よりも良い成果を生みます。
エラー No.6: 各 locale に対するテスト/実験がない。
最低限のテストセットすら無いと、最初の実ユーザーが来るまで問題に気づかないことが何カ月も続き得ます。小さな golden セットと Dev Mode の手動テストだけでも、そのリスクは大きく減らせます。
エラー No.7: 正典とローカライズ版の説明が非同期になる。
英語スキーマに新しい occasion(プレゼントの「きっかけ」)フィールドを追加したのに、ロシア語版を更新し忘れた、というケース。結果として RU 側ではモデルがそのフィールドを知らず、埋めない。サーバーは occasion で絞り込みたいのに null を受け取り、一覧が広すぎる——EN では動くが RU でこっそり壊れる。このため、ツール説明の変更は必ずシンプルでも定期的なプロセスを通すべきです。EN を更新 → 他言語を更新 → 簡易テストを回す。
GO TO FULL VERSION