Ключевые выводы
- Около 100 строк Node.js позволяют вам работать с ИИ-распознаванием контента: отправить текст, дождаться отчета, прочитать оценку каждого предложения.
- Обнаружение не происходит мгновенно, поэтому вам понадобится опрос или фоновое задание.
- Покажите людям, какие предложения выглядят написанными с помощью искусственного интеллекта. Одинокий процент неправильно истолковывается.
- Следите за лимитом скорости и счетом за слово. Оба ловят людей.
- Не отклоняйте автоматически ничего на основании оценки. Детекторы ошибаются, и сильнее всего страдают писатели, не являющиеся носителями английского языка.
Запрос на функцию, который у вас, вероятно, уже был
Рано или поздно кто-то из вашей команды спросит об этом. Может быть, это менеджер по продукту, может быть, руководитель отдела контента, может быть, учитель, который только что отметил сорок почти одинаковых эссе: можем ли мы определить, когда люди вставляют выходные данные ChatGPT?
Короткий ответ: нет, не с уверенностью. Любой, кто обещает вам это, что-то продает.
Но ты может получите полезный сигнал и сможете показать его тем, кто на самом деле звонит: редакторам, преподавателям, модераторам. В этом руководстве вы создадите небольшое приложение Express, которое принимает текст, отправляет его в API-детектор AI, ожидает результата и выделяет предложения, которые выглядят машинно написанными.
Здесь я использую API Quetext AI Detector. Он оценивает каждое предложение, а не только весь документ, и вы можете попробовать его без подписки. Однако большинство API обнаружения следуют одному и тому же шаблону отправки, опроса и выборки. Если вы позже смените провайдера, вы перепишете один файл.
Что вам понадобится
- Node.js 18 или новее. Он имеет
fetchвстроен, поэтому для HTTP не нужно ничего дополнительно устанавливать. - Ключ API. Зарегистрируйтесь на Quetext и создайте его под Аккаунт › Ключи API. На момент написания этой статьи новые аккаунты начинаются с 5000 бесплатных слов, а после этого вы платите из предоплаченного кошелька по цене 0,10 доллара за 1000 слов.
- Некоторый опыт Экспресса. Ничего особенного.
Настройте проект:
mkdir ai-check && cd ai-check
npm init -y
npm pkg set type=module
npm install express
Поместите ключ в переменную среды. Не запрограммируйте его жестко и не позволяйте ему находиться рядом с браузером.
export QUETEXT_API_KEY="your-key-here"
Как работает API
Вы сделаете три звонка. Анализ занимает несколько секунд, поэтому API выдает вам билет и заставляет вернуться за результатом. Держите документацию Quetext API открытой на другой вкладке, пока вы выполняете дальнейшие действия; они перечисляют каждую конечную точку, поле и код ошибки.
Вот короткая версия:
- Отправить текст:
POST /api/v2/ai-detect-reportвозвращает отчетid - Проверьте прогресс:
GET /api/v2/report-progress/{id}возвращаетprogressот 0 до 1 - Получите результаты:
GET /api/v2/ai-detect-report/{id}возвращаетai_score,ai_summary,ai_matches[]
Каждый запрос отправляет ваш ключ в X-API-Key заголовок. Минимальный ввод — 50 слов, что звучит раздражающе, пока вы не поймете, что ни один детектор не может сказать ничего полезного о двухстрочном комментарии.
И запомните это число: 10 запросов каждые 5 секунд на аккаунт. Проверки прогресса учитываются. Если вы проигнорируете это, оно аукнется вам в производственном отделе.
Шаг 1. Миниатюрный API-клиент
Создавать quetext.js. Мне нравится хранить все, что касается конкретного поставщика, в одном файле, чтобы остальная часть приложения не знала, кто занимается обнаружением.
const BASE_URL = 'https://www.quetext.com/api/v2';
const API_KEY = process.env.QUETEXT_API_KEY;
if (!API_KEY) {
throw new Error('QUETEXT_API_KEY is not set');
}
async function request(path, options = {}) {
const res = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-API-Key': API_KEY,
},
});
const body = await res.json().catch(() => ({}));
if (!res.ok) {
const err = new Error(`Detector API error: HTTP ${res.status}`);
err.status = res.status;
err.data = body.data;
throw err;
}
return body.data;
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function submitAiCheck(text, title) {
const { id } = await request('/ai-detect-report', {
method: 'POST',
body: JSON.stringify({ text, title }),
});
return id;
}
export async function waitForReport(id, { intervalMs = 2500, timeoutMs = 60_000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const data = await request(`/report-progress/${id}`);
if (data.message) throw new Error(data.message);
if (data.progress >= 1) return;
await sleep(intervalMs);
}
throw new Error(`Report ${id} did not finish within ${timeoutMs / 1000}s`);
}
export async function getAiReport(id) {
return request(`/ai-detect-report/${id}`);
}
Там три решения, каждое из которых стоит приговора:
Ошибки сохраняют свой код состояния HTTP. Код 402 означает, что у вас закончился кредит, а код 429 означает, что вы проводите опрос слишком быстро, и вам следует обращаться с ними иначе, чем с «что-то сломалось».
Он опрашивает каждые 2,5 секунды, потому что в документах Quetext указано от 2 до 3. Более быстрый опрос не ускорит создание отчета. Вы просто перебьете лимит скорости.
И тут тяжелый тайм-аут. Оставьте это, и один застрявший отчет может навсегда заблокировать запрос. Это ужасная ошибка, которую нужно отследить в 23:00.
Шаг 2. Конечная точка Express
Следующий, server.js. Он проверяет ввод, выполняет три вызова и выдает интерфейсу простой ответ, в котором Quetext нигде не упоминается.
import express from 'express';
import { submitAiCheck, waitForReport, getAiReport } from './quetext.js';
const app = express();
app.use(express.json({ limit: '1mb' }));
app.use(express.static('public'));
const countWords = (text) => text.split(/\s+/).filter(Boolean).length;
app.post('/api/check', async (req, res) => {
const text = String(req.body.text ?? '').trim();
if (countWords(text) < 50) {
return res.status(400).json({ error: 'Please submit at least 50 words.' });
}
try {
const id = await submitAiCheck(text);
await waitForReport(id);
const report = await getAiReport(id);
res.json({
score: report.ai_score === null ? null : Number.parseFloat(report.ai_score),
summary: report.ai_summary,
sentences: report.ai_matches.map((m) => ({
text: m.sentence,
probability: m.generated_prob,
})),
});
} catch (err) {
if (err.status === 402) {
return res.status(402).json({ error: 'The detection service is out of credit.' });
}
if (err.status === 429) {
return res.status(429).json({ error: 'Too many checks right now. Try again shortly.' });
}
console.error(err);
res.status(500).json({ error: 'The check failed. Please try again.' });
}
});
app.listen(3000, () => console.log('Running on http://localhost:3000'));
Посмотрите на карту в конце. generated_prob становится probabilityи ai_score возвращается в виде строки типа "82.50"поэтому он анализируется в число. Это маленькая деталь. Но это означает, что ваш интерфейс никогда не узнает, с какими именами полей провайдера он имеет дело, и именно это делает замену провайдеров изменением одного файла.
Шаг 3: Выделите предложения
Покажите кому-нибудь «64% ИИ», и он сделает поспешный вывод. Покажите им абзац с тремя предложениями, выделенными розовым цветом, и они его прочитают.
Таким образом, на странице отображается каждое предложение, раскрашенное в зависимости от того, насколько вероятно, что оно было сгенерировано ИИ. Создавать public/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AI Content Check</title>
<style>
body { font: 16px/1.6 system-ui, sans-serif; max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
textarea { width: 100%; min-height: 200px; }
.high { background: #fde2e1; }
.medium { background: #fff4cc; }
</style>
</head>
<body>
<h1>AI Content Check</h1>
<textarea id="input" placeholder="Paste at least 50 words..."></textarea>
<button id="check">Check text</button>
<p id="summary"></p>
<div id="result"></div>
<script>
const button = document.querySelector('#check');
const summary = document.querySelector('#summary');
const result = document.querySelector('#result');
button.addEventListener('click', async () => {
button.disabled = true;
summary.textContent = 'Checking... this usually takes a few seconds.';
result.replaceChildren();
try {
const res = await fetch('/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: document.querySelector('#input').value }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
summary.textContent = `${data.score}% likely AI. ${data.summary}`;
for (const s of data.sentences) {
const span = document.createElement('span');
span.textContent = s.text + ' ';
if (s.probability >= 0.8) span.className = 'high';
else if (s.probability >= 0.5) span.className = 'medium';
span.title = `${Math.round(s.probability * 100)}% AI probability`;
result.append(span);
}
} catch (err) {
summary.textContent = err.message;
} finally {
button.disabled = false;
}
});
</script>
</body>
</html>
Бегать node server.js и открыть http://localhost:3000. Попробуйте два примера: что-то, что вы написали сами, а затем необработанный ответ чат-бота на ту же тему. Видя, какие предложения загораются, вы узнаете больше о том, как работают эти детекторы, чем любая диаграмма, которую я мог бы нарисовать.
Быстрый пункт охраны. Эти предложения исходили от ваших пользователей, а затем прошли через внешний сервис. Относитесь к ним как к ненадежным. Вот почему код использует textContent и никогда innerHTMLкак рекомендует шпаргалка по предотвращению XSS OWASP.
Прежде чем отправить его
Демо-версия оставляет HTTP-запрос открытым во время опроса. Хорошо на вашем ноутбуке. Не устраивает реальный трафик.
Поместите проверки в фоновое задание
Отправьте текст, сохраните отчет id с pending статус и сразу же ответить. Рабочий (BullMQ, задание cron, бессерверная очередь, все, что вы уже используете) выполняет опрос и обновляет запись после завершения. Пользовательский интерфейс говорит «Проверка…», пока не появится результат.
Ограничение скорости является общим
Вот как все идет не так. Сегодня утро понедельника, 20 редакторов открывают свои очереди на рецензирование и нажимают «Проверить» примерно в одно и то же время. Каждый запрос опрашивается сам по себе, и в течение нескольких секунд вы набираете 429 по всем направлениям, поскольку ограничение 10 на 5 секунд распространяется на всю вашу учетную запись, а не на каждого пользователя.
Используйте одного исполнителя, который циклически обрабатывает каждый ожидающий отчет с небольшой паузой между вызовами. Проблема решена.
Проверяйте то, что важно, кэшируйте остальное
Вы платите за слово, поэтому не проверяйте каждый комментарий в блоге. Представление статей, задания и оплачиваемая внештатная работа обычно оправдывают затраты. Кэшируйте результаты и против хеша текста. Тот, кто отправляет один и тот же черновик три раза, не должен стоить вам трижды.
Добавьте еще проверку на плагиат
Обнаружение ИИ не обнаруживает копирование. Кто-то может дословно передать пост в блоге 2019 года, и он получит 0% AI, потому что его написал человек. Просто не этот человек. Если оригинальность имеет значение для вашего продукта, у одного и того же API есть конечные точки плагиата, которые работают точно так же. Документация API охватывает оба варианта.
Пожалуйста, не позволяйте счету решить
Разработчики пропускают эту часть. Это также та часть, которая заканчивается гневными обращениями в службу поддержки.
В 2023 году Лян и его коллеги из Стэнфорда прогнали эссе, написанные людьми, для которых английский язык не является родным (все люди, написанные для TOEFL), через семь детекторов искусственного интеллекта. В среднем детекторы пометили более 61% из них как созданные ИИ. Простая лексика и грамматика из учебника воспринимаются как «предсказуемые», а предсказуемость — это именно то, что ищут детекторы.
Представьте себе, что иностранный студент получает автоматический ноль, потому что так написано в сценарии. Вы не хотите быть человеком, который это построил.
Итак, создайте для обзора:
- Используйте высокие баллы для перемещения элементов вверх по очереди модератора. Не используйте их, чтобы что-либо заблокировать.
- Покажите рецензентам выделенные предложения, чтобы они могли оценить сам текст.
- Позвольте пользователям отвечать, будь то загрузка черновиков или объяснение того, как они это написали.
- Зарегистрируйте каждую оценку с ее идентификатором отчета, чтобы любое решение можно было проверить позже.
Сделайте это, и детектор станет быстрым первым проходом, который укажет людям на то, что стоит присмотреться. Ваши люди все еще звонят.
Часто задаваемые вопросы
Как работают API-интерфейсы детектора искусственного интеллекта?
Вы отправляете текст, сервис проверяет, насколько предсказуемым будет каждое предложение для языковой модели, и вы получаете вероятность того, что его написала машина. Большинство этих API являются асинхронными. Вы отправляете отчет, получаете идентификатор отчета, проводите опрос, пока он не будет готов, а затем получаете результат. Лучшие из них оценивают отдельные предложения, а также весь текст, что значительно упрощает создание разумного экрана обзора.
- Планируйте отправку, опрос, выборку.
- Оценки на уровне предложений превышают одно число.
Сколько текста мне нужно для точного результата обнаружения ИИ?
Больше, чем вы думаете. API Quetext не принимает менее 50 слов, и на практике оценки стабилизируются, как только вы превысите около 300. Название продукта или однострочный комментарий просто не дают детектору достаточно информации для продолжения работы. Для короткого контента либо сгруппируйте связанные части вместе, либо не утруждайте себя проверкой.
- 50 слов — это минимум API.
- Стремитесь к 300+ словам для стабильных результатов.
Должен ли я автоматически отклонять контент с высоким показателем AI?
Нет. Детекторы совершают ошибки, и исследования показывают, что они замечают писателей, не являющихся носителями английского языка, гораздо чаще, чем носителей языка. Используйте оценку, чтобы решить, на что будет смотреть человек в первую очередь, а не блокировать что-либо. Дайте рецензентам выделенные предложения, дайте пользователям возможность объясниться и запишите свое решение. К вашим пользователям относятся справедливо, а ваша служба поддержки избегает кучи гневных заявок.
- Оценка начинает обзор. Это не заканчивается.
- Держите человека в курсе.
Как избежать ошибок ограничения скорости при опросе API обнаружения AI?
Опрашивайте со скоростью, рекомендованной провайдером (каждые 2–3 секунды для Quetext), и помните, что эти опросы также учитываются в вашем лимите. Когда вы приступите к работе, переместите проверки в фоновую очередь и позвольте одному работнику опрашивать каждый ожидающий отчет, а не опрашивать каждый запрос пользователя отдельно. Когда вы получите код 429, подождите немного и повторите попытку.
- Один работник должен выполнять весь опрос.
- Отступите и повторите попытку на 429.
Безопасно ли вызывать API обнаружения ИИ из браузера?
Нет. Если браузер напрямую вызывает API, ваш ключ попадает в код внешнего интерфейса, где любой может скопировать его и потратить ваш кредит. Сохраните ключ в переменной среды на своем сервере и отправляйте запросы через свою конечную точку, как показано в этом руководстве. Это также дает вам возможность проверить длину входных данных, применить ограничения для каждого пользователя и кэшировать результаты, прежде чем платить за что-либо.
- Храните ключ API на сервере.
- Проверяйте и ограничивайте запросы в своем собственном бэкэнде.