1. Зачем вообще нужен Product Feed
Если сравнивать с классическим e‑commerce, Product Feed — это нечто среднее между:
- «витриной» товаров (каталог с ценами, наличием, ссылками и медиа);
- и техническим контрактом, который описывает, какие именно SKU мерчант готов показать и/или продать через ChatGPT.
OpenAI в своей спецификации прямо говорит, что feed — это единый источник правды о товарах, на который опираются поиск, рекомендации и подготовка данных для чекаута.
В обычном интернет‑магазине пользователь сам ходит по страницам, листает категории, фильтрует и т.д. В AI‑commerce всё наоборот: пользователь просто говорит модели «подбери мне цифровой подарок до 30 долларов другу‑разработчику, который любит настолки» — а ChatGPT уже сам думает, какие именно SKU из вашего Product Feed подходят, в каком порядке их показать и как всё это оформить в виде карточек и последующего Instant Checkout.
Поэтому Product Feed решает сразу несколько задач.
Во‑первых, он даёт ChatGPT структурированные данные для поиска. Модель опирается не только на описание и название товара, но и на категории, теги, цену, доступность, локаль, ограничения по странам.
Во‑вторых, он является источником данных для чекаута. Когда ChatGPT начинает готовить checkout_session, именно из фида берутся ID SKU, цена, валюта, seller URL и другая коммерческая информация.
И, наконец, Product Feed — это формализованный контракт между вами и платформой. Вы явно говорите: «вот список SKU, вот какие из них можно просто искать (discovery), а какие можно и оформлять через Instant Checkout».
Чтобы это увидеть, удобно нарисовать простую схему.
flowchart TD A[GiftGenius DB] --> B[Feed Builder] B --> C["Product Feed (CSV/JSON/...)"] C --> D[OpenAI Ingestion] D --> E[Поисковый индекс + ранжирование] E --> F[ChatGPT/Agent подбирает подарки] F --> G["Instant Checkout (ACP)"]
Слева — ваша внутренняя база, где уже живёт «настоящий» каталог. Справа — ChatGPT, который показывается пользователю. Посередине — Product Feed и механизмы его приёма и индексирования. Всё, что мы делаем в этой лекции, находится ровно между A и D.
2. Форматы и «физика» Product Feed
Спецификация OpenAI довольно гибко относится к формату файлов: поддерживаются TSV, CSV, XML и JSON. Это сделано специально, чтобы большинство существующих систем (от самописных монолитов до Shopify) могли экспортировать фид без шаманства.
В типичном варианте:
- вы размещаете файл или endpoint с Product Feed на своём HTTPS‑сервере;
- этот адрес регистрируете в портале ChatGPT Merchants;
- OpenAI периодически забирает этот фид, валидирует и индексирует товары.
Документация подчёркивает, что фид нужно обновлять регулярно (можно даже каждые 10–15 минут), чтобы пользователи видели актуальные цены и наличие, особенно в распродажи и пиковые периоды.
В учебном примере с GiftGenius мы будем работать с форматом JSON, потому что он хорошо заходит TypeScript‑разработчикам. Но важно понимать: на уровне спецификации OpenAI не привязан к JSON, просто для нас это удобнее.
Простейший JSON‑фид мог бы выглядеть так:
[
{
"id": "gg-coffee-sub-1m-usd",
"title": "Подписка на кофе на 1 месяц",
"description": "Ежемесячная коробка зернового кофе для разработчика.",
"price": 2900,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/coffee-subscription-1m",
"image_link": "https://cdn.giftgenius.app/images/coffee-1m.png",
"enable_search": true,
"enable_checkout": true
}
]
В реальности полей будет больше, и некоторые из них являются обязательными, а другие — рекомендованными или опциональными. Как это устроено — разберёмся дальше.
3. Продукт vs вариант (SKU): как это моделировать
Одна из самых частых вопросов: «А как в Product Feed отразить размеры, пакеты, длительности подписки и другие варианты одного товара?»
Спецификация Product Feed оперирует строками/записями, каждая из которых описывает одну продаваемую конфигурацию. Архитектурный паттерн, который рекомендует индустрия (и который хорошо состыкован с OpenAI‑фидом), выглядит так: каждая отдельная конфигурация (размер, вариант подписки, тариф, регион) — это отдельная запись фида, то есть отдельный SKU.
Базовый продукт живёт у вас во внутренней модели, а в фиде вы работаете на уровне SKU.
Например у вас есть сервис, и подписки на него на 1 месяц, 3 месяца, 6 месяцев. С точки зрения product feed это все разные SKU. Если один сервис можно купить на 20 разных условиях, то у вас в product feed должно быть 20 SKU.
В TypeScript это можно выразить примерно так:
// Домашняя модель GiftGenius
export interface GiftProduct {
id: string; // product_123
name: string;
description: string;
baseImageUrl: string;
}
// SKU, который пойдёт в Product Feed
export interface GiftSkuFeedItem {
id: string; // product_123_usd_1m
productId: string; // ссылка на GiftProduct.id
title: string;
description: string;
price: number; // в минимальных единицах (центах)
currency: string; // "usd"
}
Внутри GiftGenius у вас может быть связь один‑ко‑многим между GiftProduct и GiftSkuFeedItem. А в фиде вы отдаёте уже «плоский» список SKU.
Чтобы ChatGPT мог понимать, какие SKU относятся к одному базовому продукту (например, подписка на 1, 3 и 12 месяцев), часто используется группирующее поле вроде item_group_id. Однако это уже архитектурный паттерн, а не жёсткое требование стандарта.
Например:
{
"id": "gg-coffee-sub-1m-usd", // SKU подписки на 1 месяц
"item_group_id": "gg-coffee-sub", // Ваш продукт
"title": "Подписка на кофе — 1 месяц",
"price": 2900,
"currency": "usd",
"enable_search": true,
"enable_checkout": true
}
А для 3‑месячной подписки:
{
"id": "gg-coffee-sub-3m-usd", // SKU подписки на 3 месяца
"item_group_id": "gg-coffee-sub", // Тот же id-продукта
"title": "Подписка на кофе — 3 месяца",
"price": 7900,
"currency": "usd",
"enable_search": true,
"enable_checkout": true
}
Такой подход облегчает и жизнь модели, и жизнь вашего бэкенда при создании заказов: ID SKU становится уникальным ключом, по которому вы всегда можете найти точную конфигурацию, которую купил пользователь.
4. Обязательные поля Product Feed и их влияние на UX
В спецификации Product Feed OpenAI делит поля примерно на три группы: обязательные (required), рекомендуемые (recommended) и опциональные (optional).
Конкретные названия и списки всегда нужно смотреть в актуальной документации, но для учебных целей можно опираться на такой «минимальный» набор.
| Поле | Для чего нужно | Что будет, если его нет |
|---|---|---|
|
Уникальный идентификатор SKU в рамках мерчанта | Товар нельзя однозначно идентифицировать |
|
Короткое название для карточки | Модели сложнее понять, что это за товар |
|
Расширенное описание | Ответы будут более общими, хуже персонализация |
|
Цена в минимальных единицах | Невозможно подготовить чекаут |
|
Код валюты ISO 4217, обычно в нижнем регистре | Платформа не поймёт, в чём считать |
|
URL страницы товара у мерчанта | Пользователь не сможет уйти на ваш сайт |
|
Статус наличия (in_stock, out_of_stock и др.) | Могут показываться недоступные товары |
|
Можно ли использовать товар в поиске | Без true товар не попадёт в выдачу |
|
Можно ли покупать через Instant Checkout | Будет только discovery/link‑out |
Важный нюанс: enable_search и enable_checkout логически разделяют режимы работы.
Если enable_search = true, enable_checkout = false, товар может участвовать в выдаче, но при попытке купить пользователь уйдёт по вашей ссылке (link) на ваш сайт, а не в Instant Checkout внутри ChatGPT (где уже привязана карта).
Если же enable_checkout = true, то при соблюдении остальных условий (поддерживаемый регион, валюта, валидный ACP backend) товар может быть куплен прямо в ChatGPT одним‑двумя кликами (что сильно повышает конверсию).
Пример «минимально пригодного» объекта GiftGenius для checkout:
{
"id": "gg-dev-notebook-plain-usd",
"title": "Минималистичный блокнот для разработчика",
"description": "Чёрный, без линий, 120 страниц. Для тех, кто пишет спецификации от руки.",
"price": 1500,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/dev-notebook",
"image_link": "https://cdn.giftgenius.app/images/dev-notebook.png",
"enable_search": true,
"enable_checkout": true
}
Обратите внимание: даже в примере мы добавляем картинку (image_link) — формально она может быть рекомендованным, а не обязательным полем, но без неё UX будет сильно хуже.
5. Рекомендованные и опциональные поля: как сделать фид «вкуснее»
Обязательные поля — это «чтобы вообще работало». Но если останавливаться только на них, вы получите что‑то вроде минимально валидного CSV для бухгалтерии, а не крутую AI‑витрину.
Рекомендованные поля обычно включают:
- основной и дополнительные URL изображений;
- категорию товара (часто на основе таксономии, вроде «gifts > experiences > online courses»);
- бренд/мерчант‑нейм;
- атрибуты вроде цвета, размера, материала;
- флаги adult‑контента, возрастные ограничения и т.п.
Чем богаче вы опишете товар, тем более осмысленные ответы сможет генерировать модель. Например, если вы явно прописываете, что блокнот сделан из переработанной бумаги и поддерживает «разработчиков, переживающих за планету», ChatGPT может осознанно рекомендовать его пользователю, который просил эко‑дружественные подарки.
В GiftGenius мы могли бы расширить описание такого SKU:
{
"id": "gg-dev-notebook-plain-usd",
"title": "Эко-блокнот для разработчика",
"description": "Минималистичный блокнот без линовки, 120 страниц из переработанной бумаги.",
"price": 1500,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/eco-dev-notebook",
"image_link": "https://cdn.giftgenius.app/images/eco-dev-notebook.png",
"category": "gifts > office > notebooks",
"brand": "GiftGenius Originals",
"enable_search": true,
"enable_checkout": true
}
Дополнительные атрибуты вроде category и brand не только улучшают выдачу, но и помогают в аналитике: вы можете смотреть, какие категории лучше конвертируют через ChatGPT, а какие — хуже.
Опциональные поля часто связаны с очень специфическими сценариями (например, геопараметры цены, о которых поговорим отдельно, или кастомные метаданные). Их нужно добавлять по мере взросления проекта, а не ради галочки.
6. Коммерческие флаги и режим discovery‑only
Давайте ещё раз чётко зафиксируем логику enable_search и enable_checkout, потому что это критичный мостик к следующим лекциям про ACP и Instant Checkout.
Представим, что вы только начинаете путь как ChatGPT‑мерчант. У вас есть каталог подарков, но ACP‑backend и Delegated Payment ещё в разработке. Вы хотите уже сейчас, чтобы ChatGPT мог находить ваши SKU и отправлять пользователей на ваш сайт для оплаты.
В этом случае вы:
- публикуете Product Feed с enable_search = true для нужных SKU;
- оставляете enable_checkout = false до тех пор, пока не завернёте и не сертифицируете ACP‑интеграцию.
ChatGPT тогда сможет включать ваши подарки в ответы для пользователей, показывать карточки и предлагать ссылку «Перейти на сайт GiftGenius», но не будет строить внутренний UI Instant Checkout.
Когда же вы реализуете Agentic Checkout и Delegated Payment, определённые товары можно перевести в режим «готовы для Instant Checkout» — просто установив enable_checkout = true и дополнительно выполнив все требования по данным (наличие цены, валюты, seller URL и т.п.).
На уровне спецификаций именно поля из Product Feed будут использоваться для заполнения полей line_items и суммы в checkout_session.
Таким образом, фид становится рычагом тонкой настройки: какие именно SKU и в каком виде ChatGPT вообще имеет право продавать от вашего имени.
7. Локали, валюты, регионы и мульти‑региональные цены
Думаю вы в курсе, что мир не ограничивается en-US и долларами. В модулях про локализацию мы уже обсуждали, как locale и userLocation влияют на бизнес‑логику. Здесь это выходит на передний план: товары в Германии могут стоить иначе, чем в США, и какие‑то подарки в принципе не могут продаваться в некоторых странах.
Product Feed спецификация учитывает это через несколько механизмов.
Во‑первых, валюта: currency должен быть валидным кодом ISO 4217 (например, usd, eur, gbp).
Во‑вторых, могут использоваться поля, описывающие геозависимую цену и доступность. В документации приводится пример атрибутов вроде geo_price и связанных с ним региональных кодов, основанных на ISO 3166.
Есть два базовых архитектурных подхода.
Подход один: один фид на один регион.
- product-feed-us-en.json для США;
- product-feed-de-de.json для Германии;
- product-feed-br-pt.json для Бразилии.
В каждом фиде все SKU уже приведены к нужной валюте и локали. Простота для ChatGPT, больше работы для вас по поддержанию нескольких фидов.
Подход два: единый фид c geo‑полями.
Внутри каждой записи вы храните либо массив цен, либо дополнительные атрибуты:
{
"id": "gg-dev-notebook-multi",
"title": "Эко-блокнот для разработчика",
"description": "Поддерживает вашу любовь к чистому коду и к планете.",
"prices": [
{ "region": "US", "currency": "usd", "price": 1500 },
{ "region": "DE", "currency": "eur", "price": 1400 }
],
"availability_by_region": [
{ "region": "US", "availability": "in_stock" },
{ "region": "DE", "availability": "out_of_stock" }
],
"enable_search": true,
"enable_checkout": true
}
Конкретная структура мульти‑региональных полей зависит от версии спецификации, но идея одна: фид должен позволять платформе понять, в каких странах SKU существует и сколько он там стоит.
С точки зрения GiftGenius, важно продумать маппинг между:
- locale и userLocation, которые ChatGPT знает;
- и той частью фида, из которой нужно брать цены и тексты.
Чаще всего в коммерческих сценариях вы не отдаёте одну запись «на весь мир», а делаете разные SKU на разные страны, чтобы проще было соблюдать налоги, политику и ограничения на товары.
8. Качество данных и политика: без этого Instant Checkout не взлетит
Product Feed — это не только про формат, но и про качество данных и соблюдение политики OpenAI.
В части качества OpenAI явно требует:
- корректные, стабильные идентификаторы;
- валидные URL с HTTPS и кодом ответа 200;
- согласованность цены и валюты;
- актуальное наличие (не должно быть in_stock товаров, которых на самом деле уже нет).
Также в спецификации есть требования к длине текстов: например, title не должен быть слишком длинным (сотни символов), а description имеет разумный лимит (тысячи символов), чтобы карточки выглядели опрятно и не превращались в роман в трёх томах.
Отдельный блок — Prohibited Products Policy. Это список категорий товаров и услуг, которые нельзя продавать через Instant Checkout и/или ChatGPT вообще: очевидные вещи вроде незаконных товаров, оружия, некоторых медицинских услуг и т.п. Конкретику всегда нужно проверять в актуальной политике, но для нас важно осознать: Product Feed будет проверяться не только на формат, но и на допустимость содержания.
Если ваш каталог содержит неоднозначные категории (например, алкоголь, азартные игры или что‑то, связанное с детьми), к этим разделам стоит относиться с особым вниманием. Часто их проще оставить в режиме enable_checkout = false и продавать только через собственный сайт с полноценной юридической обвязкой.
9. Практика: собираем минимальный Product Feed для GiftGenius
Давайте теперь применим всё это на практике и соберём простой фид для трёх SKU GiftGenius. Представим, что у нас есть:
- Эко‑блокнот для разработчика.
- Подписка на кофе на 1 месяц.
- Подарочный сертификат на курс «TypeScript для взрослых».
Сначала опишем TypeScript‑тип, который будем использовать для генерации фида:
export interface GiftGeniusFeedItem {
id: string;
title: string;
description: string;
price: number; // в центах
currency: "usd" | "eur";
availability: "in_stock" | "out_of_stock";
link: string;
image_link?: string;
enable_search: boolean;
enable_checkout: boolean;
}
Теперь создадим в коде массива с несколькими элементами и потом сериализуем его в JSON:
export const giftGeniusFeed: GiftGeniusFeedItem[] = [
{
id: "gg-eco-notebook-usd",
title: "Эко-блокнот для разработчика",
description: "Минималистичный блокнот без линовки из переработанной бумаги.",
price: 1500,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/eco-dev-notebook",
image_link: "https://cdn.giftgenius.app/images/eco-dev-notebook.png",
enable_search: true,
enable_checkout: true
},
{
id: "gg-coffee-sub-1m-usd",
title: "Подписка на кофе для разработчика — 1 месяц",
description: "Ежемесячная коробка зернового кофе. Совместимо с дедлайнами.",
price: 2900,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/coffee-subscription-1m",
image_link: "https://cdn.giftgenius.app/images/coffee-1m.png",
enable_search: true,
enable_checkout: true
},
{
id: "gg-ts-course-gift-usd",
title: "Подарочный сертификат на курс TypeScript",
description: "Онлайн-курс для разработчиков, которые наконец хотят понять generics.",
price: 9900,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/ts-course",
image_link: "https://cdn.giftgenius.app/images/ts-course.png",
enable_search: true,
enable_checkout: false // пока только discovery
}
];
Дальше можно сделать простую утилиту, которая будет раз в N минут генерировать файл product-feed.json из этой структуры и выкладывать его на ваш HTTPS‑сервер.
import { writeFile } from "node:fs/promises";
import { giftGeniusFeed } from "./feed-data";
// Простейший генератор JSON-фида
async function buildProductFeed() {
const json = JSON.stringify(giftGeniusFeed, null, 2);
await writeFile("public/product-feed.json", json, "utf8");
}
buildProductFeed().catch(console.error);
Понятно, что в реальном проекте вы не будете хранить весь фид в коде; вместо этого данные будут браться из БД. Но для начала полезно собрать хотя бы такой учебный пример, чтобы протестировать пайплайн: генерация → выкладка → валидация.
10. Анти‑пример: как выглядит «плохой» Product Feed
Чтобы лучше почувствовать требования спеки и UX, полезно посмотреть на пример фида, который формально почти работает, но на практике приведёт к проблемам:
{
"id": "1",
"title": "Подарок",
"description": "Классный подарок",
"price": 12.333333,
"currency": "usdollars",
"availability": "yes",
"link": "http://giftgenius.local/gift/1",
"enable_search": "true",
"enable_checkout": "maybe"
}
Здесь можно насчитать сразу несколько проблем.
Во‑первых, id = "1" — это нестабильный и небогатый идентификатор. Если вы когда‑нибудь мигрируете БД или введёте шардирование, такие идентификаторы становятся хрупкими. Лучше использовать осмысленные и достаточно длинные ID, которые уникальны в рамках мерчанта.
Во‑вторых, price указан как десятичное число с бесконечным хвостом. Спецификации и платёжные системы обычно ожидают цену в минимальных единицах (центы, копейки) целым числом, чтобы избежать проблем с плавающей точкой и округлением.
В‑третьих, currency = "usdollars" и availability = "yes" не соответствуют ожидаемым форматам (ISO 4217 и перечень допустимых статусов).
В‑четвёртых, link ведёт на http и локальный домен — ни то ни другое неприемлемо для реального продакшена; спецификация прямо требует HTTPS и публичную доступность.
В‑пятых, флаги enable_search и enable_checkout должны быть булевыми, а не строками. В противном случае парсер OpenAI либо не примет фид, либо приведёт к значению по умолчанию, которое может вас неприятно удивить.
Такие проблемы могут привести как к жёсткой ошибке валидации (фид отклонён), так и к более неприятной ситуации: фид формально принят, но часть SKU игнорируется или работает не так, как вы ожидали. Именно поэтому стоит вкладываться во внутреннюю валидацию ещё на вашей стороне.
11. Типичные ошибки при работе с Product Feed
Ошибка №1: думать о фиде как о "разовом CSV" для импорта.
Иногда команды воспринимают Product Feed как файл, который они один раз сгенерируют «для интеграции» и забудут. В AI‑commerce это не так: фид — живой источник правды, который должен регулярно обновляться. Если вы меняете цены, снимаете товары с продажи, запускаете промо‑акции — всё это должно своевременно попадать в фид. Иначе ChatGPT будет рекомендовать то, чего уже нет, или по старой цене, и пользователи вполне справедливо будут злиться.
Ошибка №2: смешивать модель продукта и SKU.
Популярный анти‑паттерн — пытаться отразить один базовый продукт с кучей опций в одной записи фида с множеством полей «size1/size2/size3» или «duration1/duration2». В результате модель не понимает, что именно продаётся, а ваш ACP‑backend начинает страдать от распаковки этих полей в момент чекаута. Гораздо проще и надёжнее: один SKU — одна запись фида, даже если это вариант в рамках одного продукта.
Ошибка №3: игнорировать локали и регионы.
Разработчики, которые делают первый MVP, часто ставят currency = "usd" и enable_checkout = true для всего подряд, не задумываясь о том, что Instant Checkout в вашем регионе может быть недоступен или какие‑то товары нельзя продавать в отдельных странах по политике или закону. Потом, когда вы выходите на новый рынок, всё начинает ломаться: цены не сходятся, налоги не учитываются. Лучше с самого начала привязывать SKU к регионам и валютам, даже если у вас пока только один рынок.
Ошибка №4: относиться к описаниям как к SEO‑текстам из прошлого.
Часть команд копирует в Product Feed старые описания из своих сайтов, иногда написанные под «ключевики» и роботов. Для ChatGPT это скорее вредно, чем полезно: модель и так умеет писать текст, ей гораздо важнее структурированные, честные и точные факты. Лучше кратко и по делу описывать, чем забивать description маркетинговой водой на пол‑экрана.
Ошибка №5: не валидировать фид самостоятельно.
Полагаться только на валидацию со стороны OpenAI — путь к больным ночам перед дедлайнами. Стоит сделать простой валидатор на своём бекенде или в CI, который проверяет схемы полей, допустимые значения, форматы URL и валют. Это можно сделать даже на TypeScript, используя, например, Zod или собственные проверки. Тогда вы будете ловить проблемы ещё до того, как залили фид в прод.
Ошибка №6: включать в Product Feed «всё подряд».
Иногда хочется заодно засунуть в фид тысячи SKU, чтобы «ну пусть будут, вдруг пригодятся». На практике это затрудняет и отладку, и аналитику, и контроль качества. Гораздо разумнее начать с ограниченного поднабора: только те категории и SKU, за которыми вы готовы следить и которые реально хотите продавать через ChatGPT. Остальное можно держать в discovery‑режиме или вообще обойтись без интеграции.
Ошибка №7: не синхронизировать Product Feed и ACP‑backend.
Фид и ACP‑API — две стороны одной медали. Если в фиде появился новый SKU, а ваш backend ещё не умеет его продавать (или наоборот, SKU убрали из фида, но backend всё ещё верит, что он существует), вы получите рассинхронизацию, сложные баги и сложные тикеты в саппорт. Хороший тон — иметь единую доменную модель каталога и использовать её и для генерации фида, и для обработки чекаута.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ