Guardrails de LLM em streaming: não dá para desdizer um token
Guardrails de LLM em streaming: token enviado não volta. Como NeMo, Guardrails AI e Bedrock trocam velocidade por segurança e como uma trava valida cada frase.
Um token que um LLM já transmitiu não pode ser retirado. Depois que o texto chega ao leitor, um guardrail que o rejeite só consegue impedir o que vem depois. Por isso, um guardrail num stream toma uma única decisão de fato: verificar antes de liberar e fazer o leitor esperar, ou liberar primeiro e aceitar que uma resposta bloqueada já foi lida. Há três formatos viáveis: verificar depois de liberar, aplicar a trava a chunks de tamanho fixo ou a frases inteiras. O assistente deste site, que eu construí, aplica a trava por frase. O custo é esperar uma frase. O limite: a trava verifica se uma afirmação tem respaldo, não se ela é verdadeira.
Este artigo trata da última verificação antes de uma resposta fundamentada ser liberada. As camadas anteriores, desde a recusa de perguntas fora do escopo até um piso de pontuação de recuperação, estão em Guardrails contra alucinação em RAG: um design em cinco camadas.
Por que não dá para desdizer um token transmitido
Um validador post-hoc só vê a resposta completa depois que o leitor já a leu. Se a terceira frase inventa uma data, apagá-la depois muda o registro, não o que foi lido. Um preprint recente de Christopher M. Frost no arXiv descreve esse limite com precisão: “um controle que detecta um padrão proibido depois de liberar o chunk que o completa não consegue retirá-lo”.
Os fornecedores documentam o mesmo limite. O filtro assíncrono do Azure OpenAI, que precisa ser ativado explicitamente, devolve o texto na hora e só garante o sinal de violação dentro de cerca de 1.000 caracteres do conteúdo que viola a política. Na seção de solução de problemas, a orientação é “implementar a ocultação de conteúdo no seu aplicativo para remover o conteúdo já exibido”. A entrada LLM09:2025 da OWASP recomenda ferramentas e processos “para validar automaticamente as saídas principais”. A questão em aberto é em que momento a validação roda em relação à liberação.
Três formas de proteger um stream: post-hoc, por chunk e por frase
Abordagem | Quando o leitor vê o texto | Uma resposta bloqueada | Exemplos |
|---|---|---|---|
Post-hoc (libera primeiro) | Antes da verificação (NeMo: cada chunk assim que é acumulado no buffer; Azure e Bedrock no modo assíncrono: à medida que é gerado) | Lida em parte antes da interrupção | NeMo com |
Trava por chunk | Depois que cada chunk passa | O chunk bloqueado nunca aparece | Padrões do Azure e do Bedrock; NeMo com |
Trava por frase | Depois que cada frase passa | A frase bloqueada nunca aparece | O assistente deste site |
O trade-off do stream_first no NeMo Guardrails
No NeMo Guardrails, o streaming de output rails vem desativado por padrão. Depois de ativado, ele executa os output rails, as verificações do NeMo sobre o texto gerado, em chunks de 200 tokens por padrão, levando 50 tokens de contexto para o chunk seguinte. A flag stream_first tem True como padrão: “O cliente recebe cada chunk de tokens antes que os output rails processem esse chunk. Isso reduz o tempo até o primeiro token, mas, se um rail bloquear o conteúdo, o usuário já terá recebido os tokens.” Em seguida, o stream termina com um erro em JSON.
Com False, “o usuário nunca vê conteúdo bloqueado, mas o tempo até o primeiro token aumenta o equivalente ao tempo de execução do rail por chunk”. Esse é o trade-off: tempo até o primeiro token (TTFT) contra texto bloqueado chegando ao leitor. Com o streaming de output rails ativado, o NeMo libera primeiro por padrão; os dois filtros de nuvem descritos abaixo verificam primeiro. Se um rail existe para barrar algo que nunca pode ser visto, mude o padrão.
O trade-off dos validadores por frase do Guardrails AI
O Guardrails AI escolhe a frase: “Por padrão, os validadores esperam até acumular o equivalente a uma frase de conteúdo do LLM antes de executar a validação.” Validar frases isoladas, e não a resposta inteira, “ajuda a economizar custos em validadores que exigem inferência cara”.
Nem todos os tipos de on_fail são compatíveis com streaming, e as correções de vários validadores esperam “que todos os validadores acumulem chunks suficientes” antes de serem mescladas. A documentação não diz com clareza se os tokens brutos chegam ao usuário antes da validação; por isso, confira esse ponto na sua própria integração.
Com buffer por padrão: Azure OpenAI e Amazon Bedrock
Por padrão, o Azure OpenAI coloca o texto da completion em buffer, filtra esse buffer e devolve “chunks de conteúdo” em vez de tokens individuais. O Amazon Bedrock Guardrails, no modo síncrono padrão, analisa os chunks antes de enviá-los, o que “introduz alguma latência”, mas “oferece maior precisão”. No modo assíncrono do Bedrock, os chunks “podem conter conteúdo inadequado até que a verificação dos guardrails termine”, e não é possível mascarar informações sensíveis.
Como uma trava de frases valida um stream
O assistente deste site responde apenas com base no conteúdo do próprio site, e uma pergunta que não é sobre mim e que nenhum trecho respalda nunca chega ao modelo. Ele fala de mim na terceira pessoa. Num site que leva o meu nome, a pior falha é uma credencial inventada, e uma credencial é uma afirmação. A frase é a menor unidade que carrega uma afirmação; por isso, a trava acumula o texto até um limite de frase, verifica e só então libera. A demo RAG vs. sem RAG do laboratório chama o mesmo endpoint de streaming.
Onde uma frase termina enquanto os tokens ainda estão chegando
Terminadores:
.!?…, além do ponto de interrogação؟e do ponto final۔da escrita árabe e, para chinês e híndi,。!?, o danda।e o danda duplo॥. Uma sequência como?!conta como um único limite. O ponto e vírgula؛da escrita árabe une orações, por isso fica de fora de propósito.Depois precisa vir um espaço em branco. Só essa regra já mantém
3.14eexample.cominteiros. A exceção são o ponto final, o ponto de exclamação e o ponto de interrogação do chinês, que não são seguidos de espaço. Dentro de uma citação, eles encerram a frase onde a citação se fecha, e não a encerram se vier uma vírgula depois.Nem todo ponto encerra uma frase. Seguido de letra minúscula, não encerra (
et al. e), e o ponto de uma abreviatura de uma lista curta, comoe.g.,p. ex.ou o alemãobzw., e o que vem depois do dia numa data em alemão (am 17. September) também não.O caractere seguinte decide. Um limite só é definitivo quando chega um caractere que não seja espaço nem marcador de citação, porque os marcadores depois do ponto pertencem à frase anterior: todos eles,
[S1][S2]ou[S1] [S2], não só o primeiro.Um marcador de lista depois de um limite não é uma frase. Do contrário, um
1.abrindo a resposta ou uma nova frase seria julgado como afirmação sem citação e interromperia uma resposta fundamentada logo no primeiro item.Um teto. Passados mil caracteres sem limite, o buffer é cortado no último espaço e verificado mesmo assim.
Uma linha em branco é preservada. Uma frase que vinha depois de uma linha em branco é liberada também depois de uma, então uma resposta em dois parágrafos continua com dois parágrafos.
Esperar o próximo caractere costuma custar um caractere de atraso, um pouco mais quando vêm marcadores depois do ponto final. A última frase é verificada quando o stream termina. As comparações normalizam os dígitos e separadores persas (۳٫۱۴ corresponde a 3.14) e as formas das letras árabes, removem os sinais de vogais breves do árabe e tratam o zero-width non-joiner como espaço: são as mesmas variações que a busca híbrida para RAG em persa resolve no lado da recuperação.
As quatro verificações determinísticas: citações, valores e pessoa gramatical
Faixa de citação. Um marcador como
[S7], quando seis fontes foram fornecidas, aponta para uma fonte inventada.Citação obrigatória. Uma frase com oito palavras ou mais, ou com qualquer número, URL ou endereço de e-mail, precisa de marcador. Frases curtas de ligação passam.
Respaldo literal. Todo número, URL e endereço de e-mail precisa aparecer numa fonte que a frase cita. A correspondência respeita os limites dos dígitos, então um ano numa fonte não pode servir de respaldo a uma porcentagem que termina com os mesmos dígitos, e decimais que só diferem por zeros no fim são um mesmo valor:
20.00é20. Uma vírgula que não agrupa milhares é uma vírgula decimal:2,5é2.5, não25.Pessoa gramatical. Uma palavra em primeira pessoa interrompe o stream. A lista em inglês é curta e fixa: pronomes de primeira pessoa e suas contrações. A lista em persa é curada: pronomes e algumas formas verbais de alto risco, como o equivalente persa de “eu sou” e “eu tenho”. O persa marca a pessoa em terminações verbais que substantivos comuns também podem ter, então uma regra ampla o bastante para pegar todas essas terminações pegaria substantivos também. As listas dos demais idiomas do site trazem pronomes pessoais e possessivos, e as de espanhol, português, russo, árabe e híndi acrescentam alguns verbos, porque esses idiomas costumam dizer “eu” só com o verbo; em chinês, a verificação procura 我 em qualquer ponto da frase, menos num composto de 自我, “auto-”, como 自我修正 (“autocorreção”). Quais listas se aplicam depende do idioma em que a resposta foi pedida. As de inglês, persa, árabe, russo, híndi e chinês se aplicam a toda resposta. O inglês é o idioma das instruções do modelo e, portanto, aquele para o qual uma resposta tem mais chance de escorregar; os outros cinco usam escritas em que nenhum nome ou sigla latina consegue formar uma de suas palavras. A exceção é uma resposta em árabe, que não é conferida quanto ao “eu” e ao “nós” persas, porque em árabe essas mesmas palavras significam “de” e “o que”. As listas de alemão, francês, espanhol e português só se aplicam a respostas pedidas no próprio idioma: suas palavras curtas também são nomes e palavras comuns de outros idiomas, como o “Ma” de “Ma et al.”. Siglas em maiúsculas, um sobrenome antes de “et al.” e o numeral romano de “Anexo I” são deixados de lado antes da leitura de qualquer lista.
O que acontece quando uma frase é reprovada
Toda frase aprovada antes da falha é liberada, inclusive as anteriores que chegaram no mesmo lote.
A trava interrompe o stream e continua fechada: uma única frase sem respaldo desacredita o restante.
Um evento
guardleva os motivos da violação e um texto fixo e localizado avisando que o restante foi retido, e o evento finaldoneregistra a interrupção. A frase bloqueada nunca é enviada; o servidor registra em log apenas o tamanho dela.O stream do provedor é fechado, e é assim que o design cancela a requisição upstream. O que foi gerado antes da interrupção continua na fatura e, por isso, também é contabilizado no limite mensal de gastos. Os dois protocolos de provedor informam o uso de saída num evento final que um stream cortado nunca alcança, então o serviço guarda o texto que recebeu e preenche o que o provedor não informou a um token por byte em UTF-8, uma contagem que, quando erra, erra para mais. Os tokens que um modelo de raciocínio gasta pensando nunca chegam como texto, então essa estimativa os deixa de fora. O artigo das cinco camadas trata do limite de gastos.
O painel de chat marca a resposta como retida e mantém o texto já liberado no histórico da conversa, porque esse texto tem respaldo até o ponto da interrupção.
Truncamento é outro tipo de falha, e uma versão anterior deste assistente confundia os dois. Quando o modelo ficava sem espaço de saída, a trava julgava o fragmento cortado como uma frase concluída e o retinha por falta de citação. O leitor era avisado de que uma resposta apenas interrompida não tinha respaldo nas fontes. Quando nenhum texto voltava, o turno sumia sem nenhuma mensagem. Agora o fragmento inacabado é descartado sem julgamento e aparece um aviso de resposta cortada. Uma frase interrompida não é uma frase sem respaldo. Se as duas coisas acontecerem, prevalece a interrupção da trava.
Os marcadores de citação são internos
O prompt pede um marcador [Sn] em toda frase que afirma um fato, e a trava aplica a regra de citação a toda frase substantiva. Quando uma frase passa, os marcadores são removidos e o espaçamento é corrigido, inclusive antes da pontuação persa, de modo que o leitor vê texto corrido, não marcadores. Tirar os marcadores do prompt, em vez disso, faria toda frase substantiva ser reprovada na regra de citação.
Quando o modelo não tem nada a dizer
Quando as fontes não cobrem a pergunta, o prompt pede ao modelo um token fixo, não uma frase. Uma recusa escrita pelo modelo seria uma afirmação como qualquer outra, e sem citação: a trava a interromperia com um aviso que culpa as fontes por uma pergunta que elas nunca deveriam responder.
A trava nunca libera o token. Enquanto o texto recebido ainda puder virar o token, ele fica retido; o primeiro caractere que descarta o token manda o texto pelo caminho normal, então uma resposta comum não espera nada a mais. Se uma resposta começa com o token, o serviço a substitui pela recusa fixa do site, o mesmo texto de qualquer outra recusa. Se o token vem depois de frases já liberadas, essas frases ficam e a resposta termina ali.
Verificar respaldo não é verificar a verdade
Num estudo liderado por Stanford, Magesh e colegas fizeram o que descrevem como a primeira avaliação pré-registrada de ferramentas de pesquisa jurídica com IA. As ferramentas de pesquisa com IA da LexisNexis e da Thomson Reuters alucinaram, cada uma, entre 17% e 33% das vezes. Uma resposta contava como alucinada se fosse “incorreta ou mal fundamentada” (misgrounded), e mal fundamentada significa que “proposições factuais-chave são citadas, mas interpretam mal a fonte ou remetem a uma fonte inaplicável”. Respostas mal fundamentadas trazem citações. O que isso significa para produtos de IA jurídica é tema de outro artigo.
Uma trava baseada em strings não enxerga a má fundamentação, e esta trava tem pontos cegos conhecidos:
Uma frase que cita duas fontes pode tirar um valor de qualquer uma delas. A verificação é feita por frase, não por oração.
O ponto decimal conta como limite.
14.52numa fonte pode respaldar52%ou14, e o separador de milhar faz o mesmo:1,200respalda200.Palavras não são verificadas. Uma frase sem números que troca uma função ou uma organização passa com qualquer marcador dentro da faixa.
Frases curtas sem citação passam. Uma frase com menos de oito palavras e sem número, URL ou endereço não precisa de marcador. O chinês não usa espaços entre palavras, então nele se contam os caracteres, três para cada duas palavras: uma frase feita só de caracteres chineses não precisa de marcador abaixo de doze.
Listas fixas de palavras têm lacunas. Em inglês,
we'delet'spassam, assim como um pronome escrito todo em maiúsculas, comoWEouUS, que é lido como sigla. A cobertura da primeira pessoa em persa depende do prompt: um verbo em primeira pessoa que não está na lista curada passa. As listas dos demais idiomas também são curadas, e a primeira pessoa em alemão, francês, espanhol ou português numa resposta pedida em outro idioma fica a cargo do prompt.A pontuação de fechamento pode juntar frases. Depois de aspas que se fecham sobre um terminador, como em
?»ou.", só uma letra maiúscula ou uma quebra de linha encerra a frase; depois de um parêntese, como em.), só uma quebra de linha ou um marcador de citação. Assim, duas frases podem ser julgadas como uma só: depois de.)diante de maiúscula, depois de؟»em árabe, persa e hindi, que não têm maiúsculas, e depois das aspas alemãs„…?“e»…?«, que o segmentador nem lê como aspas de fechamento. Os sinais chineses mantêm a própria regra, descrita acima.
O erro também acontece no sentido contrário. Uma abreviatura que não está na lista, seguida de maiúscula, encerra uma “frase” antes da hora, como Mr. diante de um nome, assim como uma pergunta entre aspas seguida de um nome ou, em alemão, de qualquer substantivo, e, se esse fragmento tiver oito palavras ou mais e nenhum marcador, uma resposta fundamentada é retida. Uma lista introduzida por uma linha que termina em dois-pontos não tem limite antes do primeiro marcador de lista. O segmentador corta logo depois de 1., e esse fragmento é reprovado na regra de citação. Nos dois casos, a trava falha fechada (fail-closed): retém uma resposta fundamentada em vez de liberar uma que não foi verificada.
O corpus também muda. Os artigos publicados são indexados para o assistente, então os números deles, incluindo os 17% e 33% deste artigo, viram valores que a trava vai aceitar como respaldados. Ter respaldo significa “está em algum lugar do que a frase cita”, não “é sobre isto”.
A trava é a última verificação antes da liberação, não a única. Joren e colegas constataram que modelos maiores “frequentemente dão respostas incorretas em vez de se abster” quando o contexto não é suficiente, e o Google Research observa que, embora o RAG costume melhorar o desempenho geral, ele “paradoxalmente reduz a capacidade do modelo de se abster de responder quando apropriado”. Por isso, a recusa e o piso de pontuação vêm antes, como descreve o design em cinco camadas. O preprint de Frost define o limite do próprio resultado com palavras que também servem para esta trava: “conformidade com a política para uma rede de segurança determinística, não evidência de segurança semântica nem de completude da política”.
SSE para streaming de LLM: fetch, proxies e cancelamento
Uma trava de frases só ajuda se cada frase liberada chegar ao leitor na hora. Quatro detalhes de transporte decidem isso.
Por que fetch e ReadableStream, e não EventSource
A especificação do WHATWG dá ao construtor de EventSource uma URL e uma única opção, withCredentials: nada de método, nada de corpo. Uma pergunta, junto com o locale e o histórico, é um corpo de requisição, então o endpoint é um POST que devolve Server-Sent Events. O navegador lê a resposta com fetch, um reader de ReadableStream e um AbortController, cujo abort(), nas palavras da MDN, “consegue abortar requisições fetch, o consumo de quaisquer corpos de resposta e streams”.
Um parser escrito à mão precisa seguir estas regras:
Mantenha um buffer entre leituras. Um chunk não é uma mensagem.
Despache o evento numa linha em branco. Até ela chegar, o evento está incompleto.
Ignore as linhas de comentário, as que começam com
:.Junte dados de várias linhas: todas as linhas
data:de um mesmo evento se unem com um line feed.Aceite CRLF, LF e CR como fim de linha.
Remova um espaço depois dos dois-pontos, e só um.
Decodifique com TextDecoder e {stream: true}, a configuração que a MDN indica “se os dados forem processados em chunks”. Caso contrário, um caractere persa dividido entre dois chunks é decodificado como caracteres de substituição.
Buffering em proxies: nginx e Caddy
O nginx faz buffer das respostas de proxy por padrão, e um header de resposta X-Accel-Buffering pode desligar isso. O stream do assistente envia X-Accel-Buffering: no e Cache-Control: no-cache, no-store, no-transform; o no-transform está ali porque um proxy que recomprime o corpo pode juntar chunks. O Caddy faz o flush de respostas text/event-stream imediatamente, e a configuração do Caddy deste site também define flush_interval -1 na rota do chat. O service worker não registra nenhuma rota para o stream, porque uma estratégia de cache seguraria a resposta até o fim.
Keep-alive e erros dentro de um stream aberto
A especificação de SSE sugere uma linha de comentário “a cada 15 segundos, mais ou menos” para se proteger de proxies legados que derrubam conexões depois de um timeout curto, e o servidor envia pings nesse intervalo. O status já é 200 quando o primeiro byte sai, então as falhas viajam como um evento error dentro do stream.
Cancelamento que também interrompe a cobrança
Parar no navegador aborta o fetch. O servidor verifica se houve desconexão a cada token que chega. Quando encontra uma, sai do loop e fecha o stream do provedor, o que, neste design, cancela a requisição upstream. Duas coisas merecem um teste, não uma suposição. Primeiro, que cada proxy e CDN entre o navegador e a sua API repasse a desconexão do cliente. Teste esse caminho; não confie na configuração. Segundo, quando o seu provedor reporta o uso: se o uso de saída só chega com o chunk final, um stream que você fecha antes nunca o recebe, e um limite de gastos que soma o uso reportado vai contabilizar menos do que foi gerado.
Como testar um guardrail em streaming
Os testes cobrem a trava em três níveis:
Testes unitários da trava com fontes fixas. Valores inventados, marcadores fora da faixa, afirmações sem citação e palavras em primeira pessoa (em cada um dos idiomas do site) interrompem o stream; valores com respaldo e frases curtas de ligação passam; frases boas anteriores sobrevivem a uma frase ruim posterior.
Testes do segmentador. Decimais e domínios não são limites, marcadores no fim ficam com a sua frase e
؟encerra uma frase. Texto em chinês é dividido em frases sem espaços. O que vale copiar: um texto alimentado em pedaços de três caracteres precisa ser dividido exatamente como o texto inteiro.Testes de stream com um provedor simulado que segue um roteiro e reproduz uma resposta em pedaços pequenos: um valor inventado nunca chega à rede, uma interrupção da trava ou uma desconexão fecha o stream upstream, e o truncamento é reportado como truncamento.
Um esboço dos dois testes centrais, escrito para este artigo e não tirado do site:
# Sketch only: SentenceSplitter and SentenceGate stand for your own classes.
import pytest
ANSWER = "The reading room opens early [S1]. Is the café open? Yes, on weekdays [S2]."
def pieces(text, size):
return [text[i:i + size] for i in range(0, len(text), size)]
def split_streamed(text, size):
splitter = SentenceSplitter()
sentences = [s for piece in pieces(text, size) for s in splitter.feed(piece)]
tail = splitter.flush()
return sentences + ([tail] if tail else [])
@pytest.mark.parametrize("size", [1, 2, 3, 7])
def test_piece_size_does_not_change_sentences(size):
assert split_streamed(ANSWER, size) == split_streamed(ANSWER, len(ANSWER))
def test_unsupported_value_never_reaches_the_reader():
gate = SentenceGate(sources=["The library opens at nine."])
released = ""
for piece in pieces("It opens at nine [S1]. Late fees ended in 2019 [S1]. More", 7):
outcome = gate.feed(piece)
released += outcome.text
if outcome.stopped:
break
assert "2019" not in released and gate.stoppedNada disso faz red teaming de um modelo real em funcionamento. O que medi foram os erros da própria trava: respostas gravadas em produção e reproduzidas offline, para achar onde ela barrou uma resposta fundamentada. Isso conta bloqueios indevidos com um modelo e um conjunto de perguntas, e não a frequência com que um modelo de verdade inventa; por isso não o publico como taxa.
Checklist para colocar em produção um guardrail de resposta em streaming
Decida o que nunca pode ser visto. Isso define se você verifica antes ou libera primeiro, seja qual for o padrão do seu framework.
Escolha a unidade e deixe explícita a espera: um chunk fixo ou uma frase.
Use verificações determinísticas onde strings bastam, normalizando antes os dígitos e as formas das letras. Um rail baseado em modelo soma o próprio tempo de execução a cada unidade.
Só considere um limite definitivo quando o próximo caractere chegar, e ponha um teto no buffer.
Diante de uma violação, libere o que passou, interrompa, mostre um texto fixo, nunca repita o texto bloqueado e feche o stream upstream.
Separe truncamento de violação, e contabilize o que foi gerado, não o que foi exibido.
Transporte: POST com
fetch, um reader de stream eAbortController; um decodificador em modo streaming; pings de comentário; erros como eventos.Proxies:
X-Accel-Buffering: no,no-transform, flush de event-stream, um caminho de desconexão testado e nenhum service worker no stream.Testes: reproduza respostas em pedaços pequenos e verifique que um valor bloqueado nunca chega à rede.
Se você está projetando um assistente fundamentado em fontes e quer um segundo olhar sobre o caminho de liberação das respostas, o Diagnóstico de projeto é onde essa conversa começa.
Perguntas frequentes
Dá para validar a saída de um LLM em streaming sem abrir mão do streaming?
Sim. Acumule o texto até uma unidade que dê para verificar e libere cada unidade assim que ela passar. O leitor vê o texto chegar frase a frase ou chunk a chunk, em vez de token a token.
Fazer buffer até o fim da frase piora o tempo até o primeiro token?
Atrasa a saída visível em uma frase: o texto aparece quando a primeira frase está completa e verificada. Verificações de strings são baratas perto da geração, mas não medi a diferença neste site, então não dou nenhum número.
Devo desativar o stream_first no NeMo Guardrails?
Desative se um chunk bloqueado nunca puder ser visto. O NeMo documenta o custo: o tempo até o primeiro token aumenta o equivalente ao tempo de execução do rail por chunk. Mantenha o padrão só se o seu produto aceitar que o leitor veja tokens que um rail bloqueia depois.
Por que não usar EventSource para streaming de LLM?
EventSource recebe uma URL e uma flag de credenciais, então não consegue enviar um corpo de requisição. Uma requisição de chat com histórico é um POST; use fetch com um reader de stream, que também oferece cancelamento via AbortController.
Uma verificação de citações impede alucinações?
Não. Ela barra números de fonte inventados, frases substantivas sem citação e valores ausentes das fontes que a frase cita. Não consegue dizer se a fonte citada sustenta a afirmação, que é a falha de má fundamentação. Trate-a como uma rede de segurança atrás da recusa e de um piso de pontuação de recuperação.