Como adicionar detecção de conteúdo de IA ao seu aplicativo Node.js


Principais conclusões

  • Cerca de 100 linhas de Node.js permitem que você trabalhe na detecção de conteúdo de IA: envie o texto, aguarde o relatório, leia a pontuação de cada frase.
  • A detecção não é instantânea, então você precisará de uma pesquisa ou de um trabalho em segundo plano.
  • Mostre às pessoas quais frases parecem escritas por IA. Uma única porcentagem é mal interpretada.
  • Fique atento ao limite da taxa e à fatura por palavra. Ambos pegam as pessoas.
  • Não rejeite automaticamente nada com base na pontuação. Os detectores erram e os escritores ingleses não-nativos são os mais atingidos.

A solicitação de recurso que você provavelmente já recebeu

Mais cedo ou mais tarde, alguém da sua equipe pergunta isso. Talvez seja um gerente de produto, talvez o chefe de conteúdo, talvez um professor que acabou de corrigir quarenta redações quase idênticas: podemos saber quando as pessoas estão colando a saída do ChatGPT?

A resposta curta é não, não com certeza. Qualquer pessoa que lhe prometa isso está vendendo alguma coisa.

Mas você pode obtenha um sinal útil e você poderá mostrá-lo às pessoas que realmente fazem a ligação: editores, professores, moderadores. Neste tutorial, você criará um pequeno aplicativo Express que pega algum texto, o envia para uma API detectora de IA, aguarda o resultado e destaca as frases que parecem escritas por máquina.

Estou usando o Detector de IA Quetext API aqui. Ele pontua cada frase, não apenas o documento inteiro, e você pode experimentá-lo sem assinatura. A maioria das APIs de detecção segue o mesmo padrão de envio, pesquisa e busca. Se você trocar de provedor posteriormente, reescreverá um arquivo.

O que você precisa

  • Node.js 18 ou mais recente. Tem fetch integrado, portanto não há nada extra para instalar para HTTP.
  • Uma chave de API. Cadastre-se no Quetext e gere um abaixo Conta › Chaves API. Enquanto escrevo isto, novas contas começam com 5.000 palavras grátis e, depois disso, você paga com uma carteira pré-paga a US$ 0,10 por 1.000 palavras.
  • Alguma experiência expressa. Nada sofisticado.

Configure o projeto:

mkdir ai-check && cd ai-check
npm init -y
npm pkg set type=module
npm install express

Coloque a chave em uma variável de ambiente. Não o codifique e não o deixe chegar perto do navegador.

export QUETEXT_API_KEY="your-key-here"

Como funciona a API

Você fará três ligações. A análise leva alguns segundos, então a API entrega um ticket e faz você voltar para ver o resultado. Mantenha o Documentos da API Quetext abra em outra aba enquanto acompanha; eles listam cada endpoint, campo e código de erro.

Aqui está a versão curta:

  • Enviar texto: POST /api/v2/ai-detect-reportretorna um relatório id
  • Verifique o progresso: GET /api/v2/report-progress/{id}retorna progress de 0 a 1
  • Obtenha resultados: GET /api/v2/ai-detect-report/{id}retorna ai_score, ai_summary, ai_matches()

Cada solicitação envia sua chave em um X-API-Key cabeçalho. A entrada mínima é de 50 palavras, o que parece irritante até você perceber que nenhum detector pode dizer algo útil sobre um comentário de duas linhas.

E lembre-se deste número: 10 solicitações a cada 5 segundos, por conta. Contagem de verificações de progresso. Ele voltará para te incomodar na seção de produção se você ignorá-lo.

Etapa 1: um pequeno cliente API

Criar quetext.js. Gosto de manter tudo específico do provedor em um arquivo, para que o restante do aplicativo não tenha ideia de quem está fazendo a detecção.


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}`);
}

Três decisões aí que valem uma sentença cada:

Os erros mantêm seu código de status HTTP. Um 402 significa que você ficou sem crédito e um 429 significa que você está pesquisando muito rápido e você desejará lidar com isso de forma diferente de “algo quebrou”.

Ele faz pesquisas a cada 2,5 segundos porque os documentos do Quetext sugerem 2 a 3. Pesquisas mais rápidas não aceleram o relatório. Você simplesmente ultrapassará o limite de taxa.

E há um tempo limite difícil. Deixe-o de fora e um relatório travado poderá travar uma solicitação para sempre. Esse é um bug miserável de rastrear às 23h.

Etapa 2: o ponto final expresso

Próximo, server.js. Ele verifica a entrada, executa as três chamadas e dá ao frontend uma resposta simples que não menciona o Quetext em lugar nenhum.


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'));

Veja o mapeamento no final. generated_prob torna-se probabilitye ai_score volta como uma string como "82.50"então ele é analisado em um número. É um pequeno detalhe. Mas isso significa que seu front-end nunca aprende os nomes dos campos do provedor com os quais está lidando, e é isso que torna a troca de provedores uma alteração em um único arquivo.

Etapa 3: destaque as frases

Mostre a alguém “64% IA” e eles tirarão conclusões precipitadas. Mostre-lhes um parágrafo com três frases sombreadas em rosa e eles realmente o lerão.

Portanto, a página mostra cada frase, colorida de acordo com a probabilidade de ser gerada por IA. Criar 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>

Correr node server.js e aberto http://localhost:3000. Experimente dois exemplos: algo que você mesmo escreveu e, em seguida, uma resposta bruta do chatbot sobre o mesmo tópico. Ver quais frases se acendem ensina mais sobre como esses detectores funcionam do que qualquer diagrama que eu pudesse desenhar.

Um ponto de segurança rápido. Essas frases vieram de seus usuários e depois passaram por um serviço externo. Trate-os como não confiáveis. É por isso que o código usa textContent e nunca innerHTMLcomo o Folha de dicas de prevenção OWASP XSS recomenda.

Antes de enviá-lo

A demonstração mantém uma solicitação HTTP aberta enquanto faz a pesquisa. Tudo bem no seu laptop. Não está bem com tráfego real.

Coloque as verificações em um trabalho em segundo plano

Envie o texto, salve o relatório id com um pending status e responda imediatamente. Um trabalhador (BullMQ, um cron job, uma fila sem servidor, o que você já usa) faz a pesquisa e atualiza o registro assim que terminar. A IU diz “Verificando…” até que o resultado apareça.

O limite da taxa é compartilhado

Veja como isso dá errado. É segunda-feira de manhã, 20 editores abrem suas filas de revisão e clicam em “Verificar” aproximadamente ao mesmo tempo. Cada solicitação pesquisa por conta própria e, em poucos segundos, você atingiu 429 segundos, porque o limite de 10 por 5 segundos cobre toda a sua conta, não cada usuário.

Use um trabalhador que percorre todos os relatórios pendentes, com uma pequena pausa entre as chamadas. Problema resolvido.

Verifique o que é importante, armazene em cache o resto

Você paga por palavra, então não verifique todos os comentários do blog. Envios de artigos, trabalhos e trabalhos freelance pagos geralmente justificam o custo. Armazene os resultados em cache também em um hash do texto. Alguém que envia o mesmo rascunho três vezes não deveria custar três vezes.

Adicione uma verificação de plágio também

A detecção de IA não detecta cópias. Alguém pode levantar uma postagem de blog de 2019 palavra por palavra e obterá 0% de IA, porque foi escrita por um humano. Só não esse humano. Se a originalidade é importante para o seu produto, a mesma API possui endpoints de plágio que funcionam exatamente da mesma maneira. O Documentação da API cobre ambos.

Por favor, não deixe a pontuação decidir

Os desenvolvedores pulam esta parte. É também a parte que resulta em tickets de suporte irritados.

Em 2023, Liang e colegas em Stanford publicou ensaios escritos por falantes não nativos de inglês (todos humanos, escritos para o TOEFL) por meio de sete detectores de IA. Em média, os detectores rotularam mais de 61% deles como gerados por IA. Vocabulário simples e gramática de livro didático são considerados “previsíveis”, e previsibilidade é exatamente o que os detectores procuram.

Imagine um estudante internacional recebendo um zero automático porque um script dizia isso. Você não quer ser a pessoa que construiu isso.

Portanto, construa para revisão:

  • Use pontuações mais altas para mover itens para cima na fila do moderador. Não os use para bloquear nada.
  • Mostre aos revisores as frases destacadas para que eles julguem o texto em si.
  • Deixe os usuários responderem, seja enviando rascunhos ou explicando como os escreveram.
  • Registre cada pontuação com seu ID de relatório, para que qualquer decisão possa ser verificada posteriormente.

Faça isso e o detector será uma primeira passagem rápida que aponta às pessoas o que vale a pena olhar mais de perto. Seus humanos ainda fazem a ligação.

Perguntas frequentes

Como funcionam as APIs do detector de IA?

Você envia um texto, o serviço verifica o quão previsível cada frase seria para um modelo de linguagem e você recebe de volta a probabilidade de que uma máquina a tenha escrito. A maioria dessas APIs é assíncrona. Você envia, obtém um ID do relatório, pesquisa até que esteja pronto e depois busca o resultado. Os melhores pontuam frases individuais e também o texto inteiro, o que torna muito mais fácil construir uma tela de revisão sensata.

  • Planeje enviar, pesquisar, buscar.
  • As pontuações em nível de frase superam um único número.

Quanto texto preciso para obter um resultado preciso de detecção de IA?

Mais do que você imagina. A API do Quetext não aceita menos de 50 palavras e, na prática, as pontuações diminuem quando você passa das 300. Um título de produto ou um comentário de uma linha simplesmente não fornece ao detector o suficiente para continuar. Para conteúdo curto, agrupe peças relacionadas ou não se preocupe em verificá-las.

  • 50 palavras é o mínimo da API.
  • Procure usar mais de 300 palavras para obter pontuações estáveis.

Devo rejeitar automaticamente conteúdo com alta pontuação de IA?

Não. Os detectores cometem erros, e pesquisas mostram que eles sinalizam escritores não-nativos de inglês com muito mais frequência do que falantes nativos. Use a pontuação para decidir o que um humano olha primeiro, e não para bloquear nada. Dê aos revisores as frases destacadas, dê aos usuários uma maneira de se explicarem e registre o que você decidiu. Seus usuários são tratados de forma justa e sua equipe de suporte evita uma pilha de tickets irritados.

  • A pontuação inicia uma revisão. Não termina um.
  • Mantenha uma pessoa informada.

Como evito erros de limite de taxa ao pesquisar uma API de detecção de IA?

Faça pesquisas na velocidade recomendada pelo provedor (a cada 2 a 3 segundos para Quetext) e lembre-se de que essas pesquisas também contam para o seu limite. Quando estiver em produção, mova as verificações para uma fila em segundo plano e deixe que um trabalhador pesquise cada relatório pendente, em vez de fazer com que cada usuário solicite uma pesquisa por si só. Quando você obtiver um 429, espere um momento e tente novamente.

  • Um trabalhador deve fazer todas as pesquisas.
  • Recue e tente novamente em um 429.

É seguro chamar uma API de detecção de IA no navegador?

Não. Se o navegador chamar a API diretamente, sua chave terminará no código de front-end, onde qualquer pessoa poderá copiá-la e gastar seu crédito. Mantenha a chave em uma variável de ambiente no seu servidor e envie solicitações através do seu próprio endpoint, como o deste tutorial. Isso também oferece um lugar para verificar o comprimento da entrada, aplicar limites por usuário e armazenar resultados em cache antes de pagar por qualquer coisa.

  • Mantenha a chave API no servidor.
  • Valide e limite solicitações em seu próprio back-end.



Source link