Ответственное управление продуктом чат-бота для взрослых: чек-лист разработчика
Чат-бот для взрослых — это реальный продукт, а реальные продукты несут ответственность. API предоставляет модель, которая не отказывает в законном контенте для взрослых, а значит, проверки возраста, внутренние правила, система жалоб и контроль злоупотреблений должны находиться в вашем приложении, а не в модели. Этот чек-лист поможет пройти по каждому уровню с рабочим кодом, чтобы вы могли запустить продукт с уверенностью.
Обновлено
Ключевые моменты
- Платформа только для взрослых; проверяйте возраст на входе и храните проверку на сервере.
- Напишите собственную политику контента и путь для жалоб, так как открытость модели не заменяет ваши правила.
- Сексуальный контент с участием несовершеннолетних всегда блокируется с кодом 403; продумайте для этого ясный и спокойный интерфейс.
- Ограничьте использование на пользователя и храните API-ключ на своем сервере; один ключ используется для всего вашего трафика.
Разделение ответственности
Думайте о стеке как о двух слоях. API обслуживает законный контент для взрослых, художественную литературу и спорные темы без отказов для пользователей старше 18 лет. Все остальное вокруг модели, людей, которые могут к ней обратиться, правил, с которыми они соглашаются, и того, что происходит при ошибках, — ваше. Это разделение дает свободу, но только если вы серьезно отнесетесь к своей части.
Вот краткая таблица обязанностей, которую стоит разместить на видном месте.
| Вопрос | Платформа | Ваше приложение |
|---|---|---|
| Сексуальный контент с участием несовершеннолетних | Всегда блокируется (403) | Объясните это, завершите сцену, предотвратите обходные решения |
| Возраст ваших пользователей | Только взрослые по условиям | Ворота и проверка на входе |
| Внутренние правила, выходящие за рамки закона | Не применяются | Напишите, опубликуйте и применяйте их |
| Злоупотребления со стороны пользователей | 300 запросов в минуту на ключ | Лимиты на пользователя и в день |
| Секретный ключ | Выдает его | Храните его на своем сервере |
Это инженерное руководство, а не юридическая консультация; правила различаются в разных регионах, поэтому проверьте требования в местах проживания ваших пользователей.
Проверка возраста, которая действительно работает
Баннер с кнопкой «Мне 18» — это минимум, и целеустремленный несовершеннолетний может нажать на нее. Это все еще важно, потому что это фиксирует утвердительное заявление и поддерживает честность вашей политики. Ключ в том, где вы его применяете. Если проверка только скрывает чат в браузере, любой может напрямую вызвать ваш эндпоинт. Заставьте сервер отказаться от разговора, пока не появится подписанный маркер.
import crypto from "node:crypto";
// Minimal 18+ gate: the user must confirm before the chat routes work.
// Replace the confirmation with a real verification step when your market requires it.
const SECRET = process.env.GATE_SECRET; // random string kept on the server
function sign(value) {
return crypto.createHmac("sha256", SECRET).update(value).digest("hex");
}
export function issueAdultCookie(res, userId) {
const stamp = String(Date.now());
const payload = `${userId}.${stamp}`;
res.cookie("adult_ok", `${payload}.${sign(payload)}`, {
httpOnly: true, sameSite: "strict", secure: true, maxAge: 30 * 24 * 3600 * 1000,
});
}
export function requireAdult(req, res, next) {
const raw = req.cookies?.adult_ok || "";
const [userId, stamp, sig] = raw.split(".");
if (!sig || sign(`${userId}.${stamp}`) !== sig) {
return res.status(403).json({ state: "age_required" });
}
req.userId = userId;
next();
}Подключите requireAdult перед каждым маршрутом чата. Добавьте более строгую проверку, когда этого требуют ваш рынок или уровень риска, например, проверку возраста третьей стороной, и сделайте маркер достаточно короткоживущим, чтобы вы могли запросить согласие снова. Никогда не ориентируйтесь на студентов или школы в своем маркетинге и размещайте ворота перед показом любого контента для взрослых, включая превью и примеры диалогов.
Написание собственной политики контента
Модель не отказывает в законном взрослом материале, поэтому вы решаете, что разрешает ваш продукт. Одной страницы политики достаточно. Охватите, что приветствуется, что вне пределов в вашем сообществе, даже если это законно (например, преследование реальных людей или доксинг), как пользователи могут сообщить о проблемах и какие действия вы предпринимаете: предупреждения, временные муты, баны.
- Опубликуйте ее там, где пользователи регистрируются, понятным языком.
- Отразите это в системном промпте. Строка вроде «Все персонажи — взрослые; завершите любую сцену, которая становится несогласной или включает реальных известных людей» удерживает бота в соответствии с вашими правилами.
- Сделайте применение правил скучным. Определите пороги заранее, чтобы модераторы не действовали наугад.
- Пересматривайте политику каждый квартал по мере развития вашего продукта.
Путь для жалоб, который пользователи могут найти
Дайте каждому сообщению небольшую кнопку отчета и заставьте ее работать без обхода входа. Когда кто-то сообщает об ответе, захватите минимум, необходимый для действий: идентификатор пользователя, идентификатор сообщения, краткую причину и временную метку. Направьте это в очередь, которую наблюдают ваши модераторы.
app.post("/api/report", requireAdult, express.json(), (req, res) => {
const { messageId, reason } = req.body;
// Store only what you need to act on: who, which message, why, when.
queueForModerator({ userId: req.userId, messageId, reason: String(reason).slice(0, 500), at: Date.now() });
res.json({ ok: true });
});Держите свои обещания узкими. Сообщите пользователям, именно что запускает отчет, и храните только то, что требуют ваша собственная политика и применимые правила. Пересмотрите, как долго вы храните отчеты, и удаляйте их по расписанию. Подтверждайте каждый отчет человеку, который его подал, даже однострочным благодарственным сообщением, потому что пользователи перестают сообщать, когда кажется, что ничего не происходит. Отслеживайте, как быстро ваши модераторы закрывают элементы, и установите цель, которую вы действительно можете достичь.
Проектирование для блокировки несовершеннолетних 403
Сексуальный контент с участием несовершеннолетних всегда блокируется, в художественной литературе и ролевых играх тоже, и API отвечает с кодом 403 и кодом content_blocked. Не повторяйте попытку и не пытайтесь перефразировать от имени пользователя. Вместо этого относитесь к этому как к моменту продукта, который заслуживает ясного дизайна.
- Переведите это на сервере в состояние, которое знает ваш интерфейс, чтобы сырой текст от поставщика никогда не достигал экрана.
- Скажите, что произошло одной спокойной фразой и назовите правило.
- Завершите или сбросьте сцену. Предложение «продолжить в любом случае» противоречит смыслу.
- Запишите событие с идентификатором пользователя, чтобы повторные попытки могли запустить ваше собственное принуждение.
- Не догадывайтесь о намерении. Некоторые блокировки вызваны неоднозначной формулировкой; нейтральное сообщение подходит для обоих случаев.
// server: translate upstream outcomes into states your UI understands
if (upstream.status === 403) {
return res.status(403).json({
state: "content_blocked",
message: "That request breaks our rules, so we can't continue this scene.",
});
}
if (upstream.status === 402) return res.status(503).json({ state: "paused" });
if (upstream.status === 503) return res.status(503).json({ state: "busy" });// browser: show a calm, specific message for each state
const MESSAGES = {
content_blocked: "This scene can't continue. Sexual content involving minors is never allowed. Start a new chat any time.",
age_required: "Please confirm you are 18 or older to use the chat.",
slow_down: "You're sending messages quickly. Give it a few seconds.",
daily_limit: "You've reached today's message allowance. Come back tomorrow.",
paused: "Chat is briefly unavailable. We're on it.",
busy: "The service is busy. Try again in a moment.",
};
async function send(messages) {
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages }),
});
if (!res.ok) {
const info = await res.json().catch(() => ({}));
showBanner(MESSAGES[info.state] || "Something went wrong.");
if (info.state === "content_blocked") endCurrentScene();
return null;
}
return res;
}
Контроль злоупотреблений и лимитов для каждого пользователя
Ваш единственный API-ключ позволяет 300 запросов в минуту в общей сложности, разделяемых всеми пользователями. Один скриптованный аккаунт может лишить доступа всех остальных, поэтому самостоятельно соблюдайте лимиты справедливого использования, значительно ниже этого потолка. Пример ограничивает каждого пользователя 12 сообщениями в минуту и дает каждому дневной бюджет токенов, измеряемый по фигуре usage в последнем потоковом чанке.
const WINDOW_MS = 60_000;
const PER_USER_PER_MIN = 12; // your product rule, far below the key's 300
const DAILY_TOKEN_BUDGET = 60_000; // per user, tracked from the usage chunk
const hits = new Map(); // userId -> array of timestamps
const spent = new Map(); // userId -> tokens used today
export function userLimit(req, res, next) {
const now = Date.now();
const recent = (hits.get(req.userId) || []).filter((t) => now - t < WINDOW_MS);
if (recent.length >= PER_USER_PER_MIN) {
return res.status(429).json({ state: "slow_down" });
}
if ((spent.get(req.userId) || 0) >= DAILY_TOKEN_BUDGET) {
return res.status(429).json({ state: "daily_limit" });
}
recent.push(now);
hits.set(req.userId, recent);
next();
}
export function recordUsage(userId, totalTokens) {
spent.set(userId, (spent.get(userId) || 0) + totalTokens);
}Для производства храните счетчики в общем хранилище, а не в памяти процесса, чтобы лимиты переживали перезагрузки и работали на нескольких серверах. Добавьте умеренный max_tokens в каждый запрос, чтобы ограничить худший случай, и следите за 402 на своем балансе. Бесплатный пробный баланс $0.50 на 7 дней — это хороший способ протестировать эти меры контроля до появления реального трафика.
Храните ключ на сервере
Никогда не отправляйте ключ в сборках фронтенда, мобильных приложениях или публичных репозиториях. Храните его как переменную окружения или в менеджере секретов, загружайте только в своем бэкенде и исключайте его из журналов и отчетов об ошибках. Если вы подозреваете раскрытие, сгенерируйте новый из своей учетной записи; старый ключ перестает работать немедленно, поэтому имейте готовый деплой. Помните, что на аккаунт приходится один ключ, что делает чистый прокси-слой еще более ценным. Руководство по веб-чату показывает шаблон от начала до конца, а документация перечисляет коды ошибок, которые должен сопоставить ваш обертка.
Чек-лист перед запуском
Пройдитесь по этому списку за день до открытия дверей. Каждый пункт занимает несколько минут и предотвращает класс проблем, который болезненно исправлять после прихода пользователей.
- Попробуйте сломать ворота. Вызовите маршрут чата с помощью curl и без cookie. Он должен ответить 403 с
age_required, никогда ответом модели. - Активируйте каждое состояние. Подставьте upstream, чтобы он возвращал 402, 403, 429 и 503 по очереди, и убедитесь, что каждое показывает правильный баннер и никогда не выдает сырой текст ошибки.
- Прочитайте свою политику вслух. Если модератор не может принять решение по делу из нее за минуту, уточните формулировки.
- Проверьте кнопку репорта с экрана телефона. Элемент, до которого трудно добраться, не считается путем для отчетности.
- Ищите ключ в своем репозитории. Ищите в папках фронтенда, результатах сборки, примерах конфигураций и истории коммитов. Сгенерируйте новый, если найдете его где-либо публично.
- Настройте оповещения. Уведомляйте себя при 402, при всплеске блокировок 403 от одного пользователя и при скачке ежедневного использования токенов.
- Подготовьте переключатель паузы. Один флаг, который плавно отключает чат, бесценен во время инцидента.
Безопасность — это непрерывный процесс, и список тоже; добавляйте пункты каждый раз, когда инцидент учит вас чему-то новому, и делитесь ими с теми, кто присоединится к вашей команде. Пересматривайте список, когда добавляете такие функции, как загрузка изображений в вашем собственном интерфейсе, групповые комнаты или публичный обмен персонажами, потому что каждая из них открывает новые пути для злоупотреблений. Чтобы помочь с созданием самих персонажей, прочитайте руководство по дизайну персонажей, и держите под рукой основы заметок по интеграции в документации по мере роста вашей команды.
Вопросы и ответы
Достаточно ли флажка для проверки возраста?
Это минимальный уровень, фиксирующий заявление, но его легко обойти. Обеспечьте проверку на сервере и добавьте более строгую верификацию там, где этого требует ваш уровень риска или местные правила.
Что должно делать приложение при получении 403 content_blocked?
Не повторяйте запрос. Переведите это в спокойное сообщение в приложении, завершите или сбросьте сцену и зафиксируйте событие для вашей собственной модерации.
Могу ли я справедливо разделить ёмкость моего API-ключа между пользователями?
Да, добавив лимиты запросов и дневные лимиты токенов для каждого пользователя на вашем сервере. Сам ключ ограничен 300 запросами в минуту в общем.
Обеспечивает ли модель соблюдение моих внутренних правил?
Нет. Он не отказывает в законном контенте для взрослых, поэтому эту работу должны выполнить ваша политика, системный промпт и поток модерации.
Ваш ключ — в одной форме от вас
Создайте аккаунт, скопируйте ключ, измените базовый URL. Это вся настройка.