Como reconstruímos o nosso dataset de injeção de prompts depois de nos ter mentido em silêncio

zn6 min de leitura

No dia antes de publicarmos a zn como código aberto, divulgámos um resultado de que gostámos: a nossa porta de injeção fundida aguentou-se contra um modelo da Meta muito maior, no nosso próprio benchmark de 217 vetores. Depois voltámos a treinar a revisão seguinte da cabeça do modelo e descobrimos que os nossos dados de treino nos andavam a mentir há meses. Este artigo é a autópsia e a reconstrução.

O diagnóstico

No checkpoint v13d treinámos uma cabeça de deteção com o corpus que tínhamos na altura. As métricas de treino pareciam bem. A avaliação contra qualquer coisa real não. Entradas benignas genuínas (prompts normais de utilizadores, recusas de LLM, rastos de geração automática de ataques que nunca contêm uma carga) obtiveram um logit médio de 4,26. Os ataques simples ficaram em média em 2,83. A nossa classe benigna estava acima da nossa classe de ataque.

A cabeça não tinha aprendido "injeção versus benigno". Tinha aprendido a fronteira de um corpus sintético: cadeias curtas geradas a partir de modelos contra tudo o resto. Cada texto real estava fora da distribuição, e o que está fora da distribuição era mapeado para cima. Os documentos reais pareciam mais um "ataque" do que os próprios ataques, porque os ataques saíam da mesma fábrica de modelos que os benignos.

Podíamos recalibrar os limiares e seguir em frente? Não. Antes do treino tínhamos registado uma regra de paragem: se um conjunto externo de prompts do mundo real se separasse do nosso conjunto abaixo de AUROC 0,75, o dataset estava partido e nenhuma quantidade de ajuste de limiares o podia salvar. A separabilidade medida foi de 0,33, abaixo até de 0,5, o que significa que o ranking estava efetivamente invertido. A regra de paragem disparou exatamente como foi desenhada.

Para sermos precisos quanto à culpa: não era um problema de arquitetura. Nenhuma função de perda, amostrador ou truque de calibração corrige um espaço de etiquetas em que a classe positiva é definida como "parece o nosso corpus sintético".

O que construímos

Reconstruímos o corpus a partir de um blueprint de famílias em vez de um monte de modelos:

  • Famílias de ataque: simples, red-team, paráfrase e multilingue.
  • Famílias benignas: prompts curtos, prosa real longa, documentação, transcrições conversacionais e meta-texto.
  • Regra de desenho: o texto benigno tem de cobrir a distribuição do texto real, não a distribuição do nosso gerador.

A família de meta-texto merece destaque porque é uma vacina direta para o modo de falha exato descrito acima. São documentos que falam de ataques de injeção (considerações de segurança em RFCs, secções de segurança de documentação) sem conterem qualquer carga. São benignos segundo a verdade de referência, são 100% de fontes naturais e vivem precisamente na região onde a cabeça antiga morreu: texto que discute ataques em prosa simples. Se um classificador se deixa convencer a sinalizar um modelo de ameaças, não aprendeu ameaças; aprendeu palavras-chave.

As paráfrases são deliberadamente aborrecidas. Nenhum LLM as reescreve. Uma tabela fixa de 53 pares de sinónimos comuns na formulação de injeções, reordenação segura de cláusulas por correspondência de expressões regulares, prefixos e sufixos contextuais tirados de listas fixas, capitalização e pontuação controladas: uma passagem, as substituições nunca são reanalisadas, para que as transformações não encadeiem em algo não intencional. Cada item regista a proveniência como id da semente mais a lista de transformações, com a data de proveniência congelada para que as reexecuções sejam idênticas byte a byte. Máximo de 3 variantes por semente. Diremos com clareza: a substituição mecânica é variação de superfície, não diversidade semântica. Ancora a superfície, e não afirmamos mais do que isso.

A família multilingue é nova na v15, e a honestidade exige dizer o que é e o que não é. São 1.812 ataques em 8 locales (francês, espanhol, português, italiano, neerlandês, polaco, alemão e russo), produzidos por tradução automática de sementes de ataque em inglês com o Qwen3-32B do Amazon Bedrock, com um custo total de 0,07 $, por trás de uma cache de respostas congelada e determinística para que as reexecuções sejam idênticas byte a byte. Cada item regista a proveniência como id da semente mais locale mais id do modelo. Uma injeção traduzida continua a ser uma injeção que a porta tem de apanhar, por isso é uma âncora real de capacidade interlinguística, mas é cobertura ao nível da superfície: inglês traduzido automaticamente, não padrões de ataque escritos nativamente em cada locale. As sementes mantêm hashes estáveis, e o divisor sensível a grupos coloca cada semente juntamente com as suas traduções, de modo que uma tradução nunca pode atravessar treino e teste.

As divisões são sensíveis a grupos através de agrupamento de quase-duplicados por MinHash com k=128, para que um texto e os seus quase-gémeos nunca atravessem treino e teste. Obcecámo-nos com isto porque auditámos o corpus antigo e encontrámos fugas documentadas contra os nossos próprios conjuntos de avaliação: 3 em 217 vetores do benchmark e 1 em 60 itens de outro conjunto externo sobrepunham-se a dados de treino. A nova auditoria pós-divisão correu sobre todos os 23.699 textos: 0 violações.

Os números

Apenas o que é rastreável até aos artefactos em evidence/v15/:

Métrica Valor
Textos 23.699
Famílias de ataque simples, red-team, paráfrase, multilingue
Paráfrases 2.500, máximo de 3 variantes por semente
Ataques multilingues 1.812 em 8 locales (traduzidos automaticamente, divulgado)
Família de meta-texto 966 itens, 100% de fontes naturais
Auditorias de divisão 7 verificações a passar, 0 violações

Ainda não há números de exatidão, porque o objetivo deste artigo são os dados, não um modelo treinado com eles.

O corpus é público: zn-prompt-injection-bench no Hugging Face, publicado sob CC-BY-4.0, com as licenças de cada fonte documentadas na ficha do dataset.

O que ainda falta

Três lacunas, ditas em vez de escondidas:

  1. A cobertura multilingue é inglês traduzido automaticamente. Os oito locales contêm sementes traduzidas, não ataques escritos nativamente por falantes de cada língua. Os hábitos de formulação específicos de cada locale e os padrões de ofuscação estão sub-representados, por isso tratamos esta família como uma âncora de capacidade interlinguística, não como profundidade.
  2. A paráfrase mecânica é variação de superfície. Não pode substituir formulações de ataque semanticamente distintas, e tratamo-la assim ao interpretar resultados.
  3. 1.416 linhas sintéticas continuam privadas. Derivam do nosso corpus interno de produção (a fatia sb4_mutants), por isso as mantemos reservadas em vez de as redistribuir. A sua existência está declarada na secção de limitações da ficha do dataset; os dados em si não são públicos.

Seguinte passo

Congelar o corpus, correr o varrimento de GPU sob as portas pré-registadas G1 a G7 e publicar o que sair, mesmo que seja feio. Esse é o acordo que fizemos connosco quando a regra de paragem da v13d disparou.

Se construiu corpora de injeção e carrega as cicatrizes, queremos a sua revisão. Ataque a metodologia, as quotas por família e a pipeline de paráfrases. O detetor em si é distribuído como um único binário Rust, a documentação de integração está em /docs, a especificação completa da API em /openapi.json, e a metodologia de benchmark da comparação anterior está no artigo anterior. Encontre o que fizemos mal antes de voltarmos a treinar com isto.

Share