JavaRush /Курсы /ChatGPT Apps /Локализация tools и description: влияние на GPT и экспери...

Локализация tools и description: влияние на GPT и эксперименты с поведением

ChatGPT Apps
9 уровень , 3 лекция
Открыта

1. Как модель «видит» ваши инструменты

Начнём с того, что для модели tool — это вовсе не «ваша красивая функция на TypeScript», а структурное описание в стиле:

  • name: техническое имя, например "search_gifts";
  • description: текст на человеческом языке, который объясняет, когда и зачем этот инструмент использовать;
  • inputSchema: JSON Schema с полями, у каждого из которых тоже может быть description, тип, ограничения.

Очень упрощённо модель делает примерно так (псевдокод внутри головы GPT):


1. Прочитать пользовательский запрос (на любом языке).
2. Прочитать список tools: name + description + описания аргументов.
3. Для каждого tool прикинуть, подходит ли он к задаче.
4. Если нужен tool — сгенерировать JSON с аргументами по схеме.
5. Иначе — ответить текстом.

Здесь есть два важных вывода.

Во‑первых, поле description у инструмента — это не комментарий для разработчиков, а интерфейс между моделью и вашим backend’ом. Если описание инструмента туманное, неполное или на языке, который не совпадает с языком пользователя, модель будет чаще промахиваться: не тот tool, не те аргументы, ответ без инструментов там, где они были нужны.

Во‑вторых, description у полей в JSON Schema так же важен, как и описание самого инструмента. Модель реально читает 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 долларов», модель должна:

  1. Понять, что search_gifts — подходящий инструмент.
  2. Понять, что «60 лет» нужно класть в recipient_age, а «3000 долларов» — в budget.

Пока описания только на английском, GPT всё равно справится, но это уже требует от неё лишнего внутреннего «перевода». На нескольких языках и слабых моделях это бьёт по точности.

2. Проблема «английских описаний» в многоязычном App

В модуле 9 мы уже вскользь проговаривали общую «карту локализации»: UI‑виджет, каталоги, ошибки, commerce‑тексты. Теперь сузим фокус и посмотрим, что происходит, когда описания инструментов написаны только по‑английски, а пользователь общается, скажем, на русском или испанском — там и начинается лёгкий хаос.

Типичный сценарий:

  1. Пользователь: «Подбери подарок для друга‑айтишника, бюджет до 50 евро».
  2. Модель смотрит на список tools, видит описания только на EN.
  3. Если запрос тоже на EN — всё хорошо.
  4. Если запрос на другом языке, ей приходится:
    • сначала понять запрос,
    • мысленно сопоставить его с английскими описаниями,
    • подобрать инструмент,
    • потом ещё и извлечь аргументы в JSON.

В сильных моделях это ещё как‑то живёт, но:

  • чаще возрастает доля ответов без вызова tools — модель «думает», что сама справится;
  • выше шансы ошибок в аргументах (особенно когда важны валюта, единицы измерения, региональные ограничения);
  • логика маршрутизации между несколькими tools становится менее надёжной (инструмент «мимо кассы»).

Простой пример ошибки: в поле budget ожидается «в валюте пользователя», а описание про это не говорит вовсе. Модель решает, что это USD по дефолту, и отправляет в backend 50 долларов там, где пользователь явно хотел 50 евро.

И здесь вступает в игру локализация описаний.

3. Подходы к локализации: отдельные tools vs многоязычные описания

Есть два базовых архитектурных подхода, и оба имеют право на жизнь.

Отдельные инструменты для каждого языка

В этом варианте вы делаете несколько 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.

Плюсы такого подхода в том, что descriptions получаются максимально «чистыми» и одноязычными. Вы можете вообще думать об App как о нескольких одноязычных версиях, каждая с собственными prompts и описаниями. Это комфортно, когда языков мало, а рынки сильно различаются.

Минусы — дублирование логики и сложности с аналитикой. В backend‑коде, скорее всего, всё равно будет один и тот же обработчик, а вот на уровне MCP/манифестов — уже два разных инструмента. Нужно не забывать обновлять описания обоих при каждом изменении.

Insight (данные на 2025-12-01)

Экспериментально не выявлено заметных преимуществ у description на языке пользователя/locale. Инструменты выбирались с практически одинаковой частотой, назависимо от языка описания. Если было 2 инструмента с похожими описаниями (на разных языках), то они сбивали ChatGPT с толку.

Кроме того, вашему приложения нужно будет пройти review при регистрации в Store. Так что рекомендую просто писать все tool description и argument description на английском.

Однако, если в будущем в ChatGPT будут тысячи приложений, и конкуренция за "выбор инструмента" вырастет, возможно description на locale пользователя будет получать преимущество. Ждем появления Tool Search Optimization.

Один инструмент с многоязычными descriptions

Во втором варианте вы оставляете один name (например, search_gifts), но его description и описания полей JSON Schema делаете многоязычными.

Есть разные стили:

  1. Краткая двуязычная форма:
    description: "Search gifts for a recipient. / Поиск подарков по предпочтениям получателя.",
  2. Маркированные блоки по языкам:
    description: "[EN] Search for gifts based on user preferences. [RU] Подбор подарков по предпочтениям получателя.",
  3. Отдельные поля, объединённые в строку (менее удобно):
    description: `EN: ${enDescription} RU: ${ruDescription}`,

Плюсы — один tool, единый источник правды (single source of truth), проще разворачивать архитектуру с MCP Gateway: вы всегда экспонируете один и тот же интерфейс для ChatGPT, вне зависимости от того, на каком языке общается пользователь.

Минусы: описания становятся длиннее. Если «мешать» языки неаккуратно, модель может немного путаться — особенно когда английский и локальный текст идут вперемешку без явных пометок [EN], [RU].

Для учебного проекта вроде GiftGenius мы рекомендуем гибридный вариант: оставить описания в основном на английском, но аккуратно добавить короткое пояснение на локальном языке, а всю настоящую «семантику» (какой язык использовать, как обращаться к пользователю) прокладывать через аргументы (locale) и system‑prompt.

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. Как выбирать язык описаний: стратегии для реального App

Теперь главный практический вопрос: какой язык сделать основным для описаний, и когда стоит локализовывать их полностью?

Рекомендации и реальный опыт показывают, что GPT‑модели всё ещё лучше всего работают в английском контексте, и многие разработчики оставляют описания только на EN. Но для многоязычного App это может быть компромиссным решением.

Разберём несколько стратегий.

Только EN‑описания

Самый простой вариант — всё на английском.

Плюсы: одна кодовая база, один язык для поддержания, проще писать хорошие, точные формулировки. Модель счастлива, когда всё вокруг по‑английски.

Минусы: для пользователей, которые пишут на других языках, качество выбора инструментов и аргументов может быть ниже. Особенно для «ленивых» моделей или сложных инструментов с большим числом параметров.

EN + краткий локальный хвост

Компромиссный подход: основное описание на EN, а в конце — короткий блок для локального языка, который помогает модели сопоставлять слова пользователя с аргументами.

Пример:

description:
  "Search for gifts based on user preferences. RU: инструмент подбирает подарки по описанию получателя, его возрасту и бюджету.",

Для JSON Schema:

description:
  "Age of the recipient in years. RU: возраст получателя (в годах).",

Плюсы: модель по‑прежнему в «английском» мире, но имеет подсказку на языке пользователя.

Минусы: описания становятся длиннее, но обычно это не критично.

Полная локализация описаний по locale

Самый серьёзный подход: описания инструментов и полей меняются в зависимости от locale, которую вы знаете из ChatGPT. Для en-US вы отдаёте чисто английские описания, для ru-RU — чисто русские, а для de-DE — немецкие.

Это уже не «одна JSON Schema навсегда», а набор схем, которые 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, который описывает tools в стиле MCP SDK.

// 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 вы можете, например, иметь функцию, которая дергает этот tool напрямую (если включён 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 → попадёт в tool → тот выберет правильный каталог и форматы цен, которые потом вы красиво отрисуете во фронтенде.

7. Эксперименты с поведением: как измерять влияние локализации

Теперь самое интересное: как понять, что локализация tools и описаний действительно улучшает поведение модели, а вы не зря тратили время на переводы?

Можно сделать небольшой «научный эксперимент» прямо в Dev Mode на GiftGenius.

Два варианта App: base vs localized

Подготовьте две конфигурации вашего App:

  • base — описания инструментов и JSON Schema только на EN;
  • localized — описания с EN+RU (или полноценные ru‑версии, если вы готовы).

Остальное (каталоги, UI, промпты) оставьте одинаковым, чтобы не путать эффекты.

Для простоты можно:

  • в Dev Mode (и тем более в Store) держать только localized‑версию;
  • а base запускать локально в отдельной ветке и сравнивать результаты на заранее заготовленном наборе запросов.

Что измерять

Есть три ключевых метрики.

Первая — частота правильных выборов инструментов. Для набора тестовых запросов на русском (и/или другом языке) вы смотрите, сколько раз модель:

  • вообще решила вызвать инструмент, когда это нужно;
  • выбрала именно search_gifts, а не другой tool.

Вторая — корректность аргументов. Проверяете, насколько часто JSON вызова соответствует ожиданиям: не перепутаны поля, бюджет в правильной валюте, возраст явно целое число, locale не потерян.

Третья — количество странных или бессмысленных вызовов. Например, модель вызывает search_gifts для вопроса «а сколько сейчас времени?» или подставляет recipient_age: 3000 вместо бюджета.

Тестировать можно и руками, и через логи MCP/Agents — всё равно логи вам пригодятся в будущем, поэтому лучше привыкать к такой аналитике уже сейчас.

Как организовать ручной тест‑набор

Можно завести небольшой «golden prompt set» для локализации:

1. "Мне нужен недорогой подарок до 30 евро для девочки 10 лет, она любит рисовать."
2. "Подбери подарок для коллеги‑программиста, 35 лет, бюджет 100$."
3. "Нужен подарок бабушке на юбилей, 70 лет, бюджет до 5000 лев."

И прогнать их через обе версии App (base и localized), наблюдая:

  • какие tools модель выбирает;
  • какие аргументы подставляет;
  • как меняется текст ответа, если инструмент не был вызван.

Полупрофессиональный лайфхак: можно сделать простую скриптовую обвязку, которая будет крутить эти запросы через ChatGPT API, но в рамках курса достаточно ручного режима в Dev Mode. Отдельная категория запросов, которые особенно полезно включить в такой набор, — смешанные по языку сообщения и странные комбинации locale. Им посвятим отдельный блок.

Если вы разрабатываете серьезное коммерческое приложение и речь идет о милионах долларов, то обязательно протестируйте эти моменты именно для вашего приложения. Модуль 20 посвящен профессионально работе с «golden prompt set» - обязательно разберитесь в этой теме.

8. Смешанные языки и странные комбинации locale

Ничто так не веселит LLM‑разработчика, как пользователь, который пишет на двух языках сразу. Например:

"Нужен подарок for my friend, он любит Star Wars, budget 100€"

Мы уже знаем, что модель мультиязычная и чаще всего справится. Но при смешанных языках и «английских» описаниях вероятность ошибки растёт.

Есть несколько типичных ситуаций.

Первая — пользователь пишет на RU, а описания инструментов на EN. Модель может понять, но иногда путается, особенно в специфической терминологии (названия категорий, редкие метки полей).

Вторая — locale = "ru-RU", но пользователь почему‑то пишет на английском. ChatGPT прислал вам сигналы, что интерфейс лучше строить на русском, но фактический язык текста — EN. Можно:

  • всё равно отдавать русские описания, считая locale первичной правдой;
  • либо реализовать детект языка сообщения как дополнительный сигнал и подстраивать описания под фактический язык.

Третья — locale = "en", а пользователь иногда вставляет русские слова. В этом случае, как правило, англоязычные описания себя отлично чувствуют.

На практике достаточно выбрать одну чёткую политику. Например:

  • если locale начинается с "ru" — вы добавляете русские куски в описания;
  • если нет — описания — чисто английские.

Чёткое правило полезно тем, что вы сможете специально тестировать каждую ветку, а не гадать, почему сегодня описания оказались на том или ином языке.

9. Документация, процесс и «канонический» язык

Локализация описаний — это не однократный акт, а процесс. Пользователи любят, когда фичи появляются, а вы — когда не ломается ничего старое. Поэтому нужно заранее договориться с самим собой:

  • какой язык считать «каноническим», относительно которого делать все переводы;
  • где хранить локализованные описания;
  • как проверять консистентность.

Обычно в роли канонического языка выступает английский. Все новые инструменты и поля сначала описываются на EN, проходят ревью, и только потом локализуются на остальные языки. В кодовой базе это можно выразить так:

  • файл tools.en.json с полным описанием name/description/полей;
  • файлы tools.ru.json, tools.de.json как «деривативы» для конкретных языков;
  • небольшой генератор, который собирает финальные JSON Schema для MCP на базе этих словарей.

В простом варианте можно пока обойтись строками в коде, но структурировать их так, чтобы потом легко вынести в отдельные словари.

Важно помнить, что описания — это тоже продуктовый текст. Их стоит ревьюить так же строго, как тексты в UI: проверять на понятность, отсутствие двусмысленностей и лишней «воды». Особенно в многоязычном варианте мы не хотим, чтобы русский хвост противоречил английской части описаний.

10. Визуальная схема: как язык проходит сквозь стэк

Чтобы собрать всё в голове, посмотрим на упрощённую схему потока запроса с учётом локализации tools.

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;
  • какие описания инструментов и полей видит модель;
  • какие каталоги и валюты выбирает backend;
  • как форматируются ответы (и моделью, и виджетом).

11. Типичные ошибки при локализации tools и описаний

Ошибка №1: считать поле description «техническим» и не локализовать его вовсе.
Такой подход работает, пока у вас только англоязычные пользователи. Как только появляются другие языки, модель начинает чаще отвечать без tools или передавать кривые аргументы по схемам. Вы вроде перевели UI, а App ведёт себя «по‑английски».

Ошибка №2: менять имена полей в JSON по языку.
Иногда возникает соблазн сделать agevozrast, budjet и т.д. Это приводит к кошмару на стороне backend’а: разные схемы, разные форматы, сложный анализ логов. Лучше оставить name полей стабильными и локализовать только описания.

Ошибка №3: хаотично смешивать языки в описаниях.
Фразы в стиле «Поиск gifts по предпочтениям user» не помогают ни модели, ни человеку. Если вы делаете многоязычные описания, разделяйте блоки явно: [EN] ... [RU] .... Тогда модель видит структуру, а не кашу.

Ошибка №4: не передавать locale в инструменты.
Даже если вы локализовали описания, но не передаёте locale в tool (или MCP Gateway её не прокидывает), backend не знает, какие каталоги и форматы использовать. В итоге модель старается быть «многоязычной», а сервер возвращает данные только для одного рынка.

Ошибка №5: автоперевод описаний через машинный перевод без ревью.
Кажется, что можно прогнать описания через автоматический переводчик и радоваться жизни. На практике такие переводы часто неточны, особенно в части терминов и аргументов. В результате модель может неверно интерпретировать смысл инструмента или поля. Лучше иметь один хорошо продуманный EN‑вариант и аккуратно локализованные версии, чем двадцать «автоматических» языков.

Ошибка №6: отсутствие тестов/экспериментов для разных локалей.
Если вы не проверяете поведение App хотя бы на базовом наборе запросов для каждой локали, всё может «ломаться» месяцами, пока к вам не придёт первый реальный пользователь с этим языком. Небольшой golden‑набор запросов и ручные тесты Dev Mode значительно снижают этот риск.

Ошибка №7: рассинхрон между каноническими и локализованными описаниями.
Вы добавили новое поле occasion («повод» подарка) в английскую схему, но забыли обновить русскую. В результате на RU‑локали модель вообще не знает об этом поле и не заполняет его, хотя backend уже ожидает. Например, сервер пытается отфильтровать подарки по поводу, получает null и показывает слишком общий список — на EN всё работает, а на RU поведение «ломается» незаметно. Поэтому любые изменения описаний инструментов должны проходить через простой, но регулярный процесс: обновить EN → обновить локали → коротко прогнать тесты.

1
Задача
ChatGPT Apps, 9 уровень, 3 лекция
Недоступна
FormattingDemo — локализация чисел, валюты и дат через Intl
FormattingDemo — локализация чисел, валюты и дат через Intl
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ