Com a explosão de conteúdo gerado por IA em 2026, saber se um texto saiu de um modelo — e, mais ainda, de qual modelo — virou um problema de infraestrutura, não só de curiosidade acadêmica. A União Europeia já cobra transparência sobre conteúdo sintético, plataformas começam a rotular mídia gerada, e a detecção post-hoc (tentar adivinhar depois) se mostra frágil demais. A resposta técnica madura é a marca d’água: embutir um sinal no próprio texto, no momento em que ele é produzido.
Não existe uma marca d’água universal. Tudo depende de uma única pergunta: qual etapa do pipeline de texto você controla? Se você controla a geração, consegue uma marca estatística no nível dos tokens. Se controla só o texto final, pode esconder bits em edições. Se não controla nenhum dos dois, sobra a detecção posterior ou metadados assinados. Este guia testa, com Python executável e sem API, as três famílias mais importantes.
Prós e contras de marcar texto de IA
✅ O que você ganha:
- Proveniência verificável: prova criptográfica de que o texto veio do seu sistema, sem depender de estilo de escrita.
- Detecção estatística confiável: a verificação é um teste de hipótese contra uma chave secreta, não um palpite.
- Resistência seletiva a ataques: escolha a família certa conforme sua ameaça (edição, paráfrase, cópia).
- Custo baixíssimo: as técnicas de lista verde rodam em Python puro, sem API.
- Conformidade e auditoria: facilita atender exigências de rotulagem e rastrear vazamentos.
⚠️ O que você NÃO ganha:
- Imunidade a parafraseadores agressivos: nenhuma família resiste a tudo; a maioria cai sob reescrita forte.
- Proteção contra quem reescreve tokens: se o sinal vive em tokens exatos, reescrever os destrói.
- Robustez universal: o que sobrevive a edição pode morrer com uma normalização Unicode simples.
Tabela de requisitos
| Componente | Mínimo | Recomendado | Ideal |
|---|---|---|---|
| Python | 3.8 | 3.10+ | 3.12 |
| Bibliotecas | hashlib, math, re (stdlib) | + unicodedata, random | + um LLM p/ parafrasear |
| Conhecimento | Python básico | Noções de probabilidade | Familiaridade com amostragem de LLM |
| Hardware | Qualquer notebook | 4 GB de RAM | GPU (só p/ testes de LLM) |
| Tempo estimado | 30 min | 1-2 horas | Meio dia com testes sérios |
Família 1 — Lista verde estatística (marca na geração)
Esta é a família usada pelos sistemas industriais. A ideia: uma função de hash com chave decide, token a token, se ele é “verde” ou “vermelho”. Na geração, você adiciona um viés (delta) aos logits dos tokens verdes. Na verificação, conta quantos tokens verdes sobreviveram e calcula um z-score — se estiver muito acima do esperado ao acaso, o texto foi marcado.
import hashlib, math
def is_green(key, prev, tok, gamma=0.5):
# hash determinístico: o mesmo token é sempre verde ou vermelho
h = hashlib.sha256(f"{key}|{prev}|{tok}".encode()).digest()
return int.from_bytes(h[:8], "big") / 2**64 < gamma
def bias_logits(logits, key, prev, delta=2.0, gamma=0.5):
# soma delta ao logit dos tokens verdes antes do softmax
return {t: lp + (delta if is_green(key, prev, t, gamma) else 0.0)
for t, lp in logits.items()}
def detect(tokens, key, gamma=0.5):
hits = sum(is_green(key, tokens[i-1] if i else "", tokens[i], gamma)
for i in range(len(tokens)))
n = len(tokens)
# z-score: desvio do esperado (gamma*n) em desvios padrão
return (hits - gamma * n) / math.sqrt(n * gamma * (1 - gamma))Um |z| entre 4 e 6 em algumas centenas de tokens costuma ser o limiar de detecção. Os dois botões trocam diretamente entre si: gamma controla a fração de vocabulário marcada, delta controla a força do viés. Aumentar delta detecta com menos tokens, mas degrada a qualidade, porque a amostragem passa a ser guiada por hash e não pelas preferências do modelo. O ponto fraco é direto: como o sinal vive em escolhas exatas de token, qualquer coisa que reescreva tokens o ataca.
Família 2 — Marcas pós-edição e payloads de largura zero
Se você não toca no amostrador, o texto é seu único canal. Marcas pós-edição fazem uma passada de reescrita no texto pronto e guardam bits nas escolhas de reescrita. Os esquemas de sinônimo trocam palavras selecionadas por equivalentes com chave — o bit é qual sinônimo sobreviveu. Esquemas sintáticos (como o EXPEDITO) codificam a mensagem na estrutura da frase, então apagar palavras individuais não apaga os bits.
O truque mais simples — e mais frágil — é o payload de largura zero: esconder bits em caracteres Unicode invisíveis.
def zw_write(text, message):
# cada byte vira bits; 0 vira ZERO WIDTH SPACE, 1 vira ZERO WIDTH NON-JOINER
bits = "".join(format(b, "08b") for b in message.encode())
payload = "".join("\u200b" if b == "0" else "\u200c" for b in bits)
i = text.find(" ")
return text[:i] + payload + text[i:] if i > 0 else text + payload
def zw_read(text):
return "".join("0" if c == "\u200b" else "1"
for c in text if c in "\u200b\u200c")O que esse código não aguenta: uma passada de normalização Unicode (NFKC) ou qualquer filtro que remova caracteres de categoria “format” apaga o payload inteiro. Muitas plataformas sanitizam ao colar, e leitores de tela enxergam ruído que você não vê mais. Trate marcas de largura zero como rastreio de vazamento para cópias intactas, não como prova de autoria.
Família 3 — Marcas invariantes semânticas (sobrevivem a paráfrase)
A paráfrase é o ataque que decide tudo. A abordagem SIR (semantic invariant robust) move o sinal dos tokens de superfície para escolhas entre entidades semanticamente equivalentes. Para cada posição do texto existe um conjunto canônico de formas intercambiáveis: um cargo pode ser “o CEO” ou “o diretor-executivo”; um plano pode ser “forte” ou “sólido”. Uma permutação com chave decide qual forma codifica 0 e qual codifica 1.
import hashlib
SLOTS = {"quality": ["strong", "solid"], # só variantes intercambiáveis
"cost": ["cheap", "low-cost"]}
def order_for(key, slot):
h = int(hashlib.sha256(f"{key}|{slot}".encode()).hexdigest(), 16)
return sorted(SLOTS[slot]), h % 2
def encode(text, bits, key):
for (slot, _), bit in zip(SLOTS.items(), bits):
order, off = order_for(key, slot)
text = text.replace(f"<{slot}>", order[off ^ bit])
return text
def decode(text, key):
out = []
for slot in SLOTS:
order, off = order_for(key, slot)
idx = next((i for i, v in enumerate(order) if v in text), None)
out.append(None if idx is None else idx ^ off)
return outUma paráfrase reescreve o tecido conectivo do texto, mas os fatos persistem — e o bit vive nos fatos. O custo é real: a geração precisa de uma etapa extra (frequentemente uma chamada a um LLM) para produzir variantes que de fato caibam no contexto, e a verificação precisa mapear o texto de volta às entidades canônicas. Equivalência é uma suposição: duas variantes que você declarou intercambiáveis podem não ser, em contexto. O que você compra é a propriedade que nenhuma outra família oferece — resistência ao ataque que quebra as demais.
Um harness de robustez executável
O harness abaixo define três ataques e mede a sobrevivência de uma marca antes e depois de cada um. Sem API, sem chaves.
import re, random, unicodedata
def attack_copy_paste(t):
t = unicodedata.normalize("NFKC", t) # o assassino dos bits zero-width
return re.sub(r"\s+", " ", t).strip()
def attack_edits(t, rate=0.15, seed=7):
rng, swap = random.Random(seed), {"strong": "robust",
"cheap": "frugal", "quick": "swift"}
return " ".join(swap.get(w, w) if rng.random() < rate else w
for w in t.split())
def attack_paraphrase(t):
# substituto por regras; troque por um parafraseador LLM real em teste sério
rules = [("a strong and cheap plan", "the plan was strong yet cheap")]
for a, b in rules:
t = t.replace(a, b)
return t
def survival(text, mark, verify, attacks):
marked = mark(text)
print(f"{'baseline':<10} {verify(marked)}")
for name, fn in attacks.items():
print(f"{name:<10} {verify(fn(marked))}")Duas ressalvas mantêm os resultados honestos: o ataque de copiar-colar já inclui normalização NFKC, porque é isso que pipelines reais fazem com texto colado; e o substituto de paráfrase é deliberadamente fraco — trate o resultado como teto de sobrevivência e plugue um parafraseador real antes de confiar uma decisão de implantação ao harness.
O que os testes realmente mostram
| Família | Cópia/colar | Edições de palavra | Paráfrase |
|---|---|---|---|
| Lista verde estatística | Sobrevive (z cai) | Declínio gradual com a taxa de edição | Pode tender ao acaso |
| Payload zero-width | Só em canais não sanitizados | Sobrevive | Apagado por normalização |
| Sinônimo pós-edição | Sobrevive | Bits erodem por troca | Quase todo perdido |
| Sintático pós-edição | Sobrevive | Segura melhor que sinônimo | Degrada |
| Invariante semântica | Sobrevive | Sobrevive se variantes mantidas | Segura enquanto os fatos sobrevivem |
Três leituras importam. Primeiro, marcas estatísticas degradam com graça: cada token sobrevivente continua votando verde ou vermelho, então o z cai junto com a contagem de tokens, e edições leves mantêm a detecção de pé. Segundo, paráfrase forte é outra história — trabalho de Sadasivan et al. (2023) mostra pressão de paráfrase degradando acentuadamente tanto a confiabilidade do detector quanto a detectabilidade da marca, às vezes rumo ao acaso. Terceiro, a escolha certa é sempre relativa à ameaça: pergunte primeiro qual ataque você precisa sobreviver, depois escolha a família.
Casos de uso reais
- Plataforma de conteúdo: rotule textos gerados para o usuário final e consiga provar a origem em disputas de moderação.
- Detecção de vazamento: distribua versões com marca zero-width diferente por cliente e descubra quem copiou para fora.
- Integridade acadêmica: uma instituição marca a saída dos seus próprios assistentes para auditar uso em trabalhos.
- Conformidade regulatória: atenda exigências de transparência (ex.: AI Act europeu) com prova verificável em vez de declaração.
- Treinamento de modelos: marque dados sintéticos para evitar contaminação por conteúdo gerado em corridas futuras.
Comparação de custo
| Abordagem | Custo de implantação | Custo por verificação | Dependências |
|---|---|---|---|
| Lista verde (local) | Zero (stdlib) | Desprezível (hash) | Nenhuma |
| Zero-width (local) | Zero (stdlib) | Desprezível | Nenhuma |
| Invariante semântica | Chamadas a LLM (geração/verificação) | Baixo a médio | API de LLM |
| Detecção post-hoc (terceiros) | Assinatura mensal | Por documento | Serviço externo |
Troubleshooting — erros comuns
- ❌ Detecção cai a zero após colar no editor → Causa: normalização NFKC apagou os bits zero-width. Solução: use lista verde ou invariante semântica para canais que sanitizam.
- ❌
zbaixo mesmo com texto marcado → Causa: poucos tokens sobreviventes ougammamal calibrado. Solução: aumente a amostra ou reduzagamma; confira odelta. - ❌ Qualidade do texto degrada com a marca → Causa:
deltaalto demais. Solução: baixedeltae aceite precisar de mais tokens para detectar. - ❌ Bits de sinônimo somem após edição manual → Causa: cada troca de palavra sobrescreve um bit. Solução: migre para esquema sintático ou invariante semântica.
- ❌ Verificação da SIR retorna
None→ Causa: variante canônica não encontrada após reescrita agressiva. Solução: use um LLM para mapear o texto reescrito de volta às entidades.
FAQ
- Preciso de uma GPU para testar? Não. As três famílias e o harness rodam em Python puro; GPU só ajuda se você plugar um parafraseador LLM real.
- Qual família escolher para produção? Depende da ameaça: sem reescrita, lista verde; canais não sanitizados, zero-width; sob paráfrase, invariante semântica.
- Marca d'água substitui detector de IA? Não. Ela é prova de origem para texto que você marcou; não detecta texto de terceiros.
- Um parafraseador forte destrói tudo? A família semântica resiste enquanto os fatos sobrevivem; as demais tendem a cair.
- Posso marcar texto já publicado? Só com pós-edição (sinônimo, sintático ou zero-width); a lista verde exige controle da geração.
Para onde isso vai
Em 2027, espere marcas d'água se tornarem parte do padrão de saída dos grandes modelos — não como opção, mas como configuração padrão de transparência —, impulsionadas por regulação e pela demanda de plataformas por proveniência verificável. A fronteira de pesquisa está em tornar a marca robusta a parafraseadores cada vez melhores, e em fechar a lacuna entre marcar e provar para um auditor externo sem revelar a chave.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



