O que é Passagem em Texto Livre?
Passagem em Texto Livre (Free-Text Handoff) é um smell de IA, ou seja, um sinal de problema em sistemas com LLM, do grupo Acopladores. Uma etapa entrega texto solto para a próxima, que precisa adivinhar o formato.
Agora que você sabe medir, a gente começa a consertar. Primeiro, um smell que quase todo time que constrói com modelos tem, e quase ninguém percebe até o fechamento do mês.
Uma empresa extrai dados de contratos de fornecedores em duas etapas. A primeira lê o contrato e lista os dados principais. A segunda pega esse texto e preenche o cadastro no ERP da empresa.
E entre as duas etapas, tem isto. Uma expressão regular que procura a palavra Total, dois pontos e um número.
Funcionou por três meses. Aí um dia, o modelo começou a escrever valor total do contrato, em vez de só total. A expressão regular não achou nada. O total ficou vazio. E cento e quarenta contratos entraram no sistema sem valor, antes de alguém perceber.
Nada quebrou. Nenhuma exceção, nenhum alerta. O dado só estava vazio. Nesta aula, você vai aprender a reconhecer esse smell, por que ele é tão tentador, e a refatoração que elimina essa classe inteira de erros.
O formato é este. A primeira etapa produz um texto feito para uma pessoa ler.
E esse texto é a única interface entre as duas etapas.
O código, ou às vezes o próximo modelo, tenta adivinhar onde está cada dado.
Quando o palpite acerta, tudo funciona.
Quando o modelo muda o jeito de escrever, o dado some, sem alarme.
Como identificar? Uma etapa devolve prosa, e a próxima usa uma expressão regular, um split, ou procura a linha que começa com.
O código tolera vários nomes para o mesmo campo. Total, valor total, total geral.
A segunda etapa é outra chamada ao modelo, cujo único trabalho é entender o formato da primeira.
Mudar uma palavra num prompt quebra um parser em outro arquivo.
E campos que faltam viram vazio em silêncio, em vez de gerar um erro.
Normalmente começa com um protótipo numa janela de chat. Alguém testou o prompt, gostou da resposta em texto e colou no código. No chat, texto é o produto. Num pipeline, texto é um fio desencapado.
Texto parece flexível. Você não precisa decidir nomes, tipos ou o que é obrigatório antes de começar. Um schema obriga você a decidir.
E funciona, por um tempo. Os modelos costumam repetir o formato, até o dia em que não repetem. O modelo mudou, o prompt mudou, ou só a entrada mudou. Um contrato que diz montante em vez de total.
E aqui está a armadilha de quem está começando. Pedir JSON no prompt, responda só com JSON, e achar que resolveu. Isso continua sendo texto. O modelo pode embrulhar em formatação, acrescentar uma vírgula ou esquecer uma chave.
O pior custo é dado ruim em silêncio. Nada quebra. Cento e quarenta contratos sem total não geram exceção nenhuma. O financeiro só vê o buraco no fechamento do mês.
Depois, acoplamento escondido. O prompt da primeira etapa vira parte do contrato da segunda, e ninguém sabe disso. O parser mora em outro arquivo, outro repositório, talvez outro time.
Depois, medo. Qualquer ajuste de prompt ou troca de modelo vira risco. O time para de melhorar o extrator, porque a expressão regular do financeiro vai quebrar.
E tokens desperdiçados. Prosa feita para um parser gasta saída com palavras que ninguém lê. O valor total do contrato, impostos inclusos, é. Dezenas de tokens que um campo só não precisa.
Uma pergunta para você. O time pediu JSON no prompt, com as chaves total e data, e o código faz o parse. Uma vez por semana, a execução quebra por uma vírgula a mais, ou uma marcação de formatação em volta do objeto. Qual próximo passo elimina essa classe inteira de erro?
Usar a saída estruturada da API, com um schema, e validar os campos no código. O formato deixa de ser um pedido ao modelo e vira uma garantia da API.
Tentar de novo até o parse funcionar esconde a instabilidade. E no dia em que a variação for um texto onde devia haver um número, o parse funciona, e o dado ruim entra. Colocar três exemplos de JSON válido baixa a taxa, mas nunca zera. Você ainda dependeria do modelo obedecer uma instrução de formato.
O tratamento é uma refatoração chamada replace free text with structured output. O contrato entre as etapas fica explícito.
O CNPJ do fornecedor, a chave de acesso da nota, que pode faltar, o total, com tipo de dinheiro, e a lista de vencimentos.
A API garante a forma. O código lê o campo total, não uma frase. Valor total do contrato simplesmente não tem mais como existir. E um campo obrigatório que falta vira erro na hora. O documento vai para revisão humana, em vez de entrar no sistema como zero.
E muitas vezes tem um bônus. Se a segunda etapa só existia para transformar o texto em campos, ela pode sumir. A primeira chamada já devolve os campos.
Mas schema não substitui eval. Uma forma correta ainda pode trazer o CNPJ de uma empresa que não é a emissora. O eval mede se o valor está certo. O schema garante que o valor chegou como valor.
Quando dá para ignorar? Quando o destino é uma pessoa. Uma resposta de chat ou um e-mail para um cliente deve ser texto. O smell é texto entre máquinas.
Quando o texto é o produto. Uma etapa escreve o rascunho de um artigo, e outra revisa. Elas trocam texto de forma legítima. Estruture o que o código usa de fato, como a lista de problemas encontrados, ou se foi aprovado. Não o artigo em si.
E um protótipo descartável tudo bem. No dia em que o parser for para o sistema de verdade, o schema vai junto.
Um último caso, para afiar o olho. Um classificador de tickets responde com uma palavra só, cobrança, pedido ou outro, e o código compara o texto. Às vezes volta Cobrança com maiúscula e ponto final, e o ticket cai na fila errada. Isso é esse smell? É. Mesmo uma palavra é texto livre, se o seu código depende da forma exata dela. Um schema com uma lista fechada de valores resolve, com um campo só.
Então, a ideia para levar. Texto é para pessoas. Quando uma etapa entrega dado para o código, ou para outra etapa, o contrato precisa ser um schema, não uma frase. Senão o seu sistema funciona até o dia em que o modelo achar um jeito mais bonito de dizer total.
Na próxima aula, a gente faz a refatoração em si, passo a passo, com eval antes e depois: replace free text with structured output. Até lá.
Free-Text Handoff
Passagem em Texto Livre (Free-Text Handoff) é um smell de IA, ou seja, um sinal de problema em sistemas com LLM, do grupo Acopladores. Uma etapa entrega texto solto para a próxima, que precisa adivinhar o formato.
Uma chain extrai dados de contratos de fornecedor e de DANFEs (a representação em PDF da NF-e brasileira). O primeiro passo lê o PDF e “lista os dados principais”. O segundo passo recebe esse texto e preenche o cadastro no ERP. O código entre os dois (anonimizado):
summary = llm("Liste os dados principais do contrato.", pdf_text)
# passo 2: tirar o total do texto
match = re.search(r"Total:\s*R\$\s*([\d\.,]+)", summary)
total = parse_brl(match.group(1)) if match else None
Funcionou por três meses. Um dia, o modelo começou a escrever “Valor total do contrato: R$ 48.200,00” em vez de “Total: R$ 48.200,00”. O regex não achou nada, total virou None, e 140 contratos entraram no ERP sem valor antes de alguém notar.
O mesmo padrão aparece na NF-e: o modelo descreve “CNPJ do emitente 12.345.678/0001-90” e o código procura a linha CNPJ:. A chave de acesso de 44 dígitos some no meio de um parágrafo. O financeiro só vê o furo no fechamento.
Sinais de Passagem em Texto Livre:
split ou “procure a linha que começa com”.None em silêncio, em vez de erro.flowchart LR
A(["1. Passo A escreve texto"]):::llm --> T["2. Texto livre"]:::tool
T --> P["3. Regex adivinha o formato"]:::tool
P --> B(["4. Passo B"]):::llm
P -->|"formato mudou"| X(["5. Campo vazio, sem erro"]):::io
A armadilha de quem está começando: pedir JSON no prompt (“responda só com JSON”) e achar que resolveu. Isso ainda é texto. O modelo pode envolver o JSON em markdown, acrescentar uma vírgula, omitir uma chave. json.loads quebra, ou pior, passa e o campo vem com o tipo errado.
total não precisa.No cenário de extração de NF-e e contratos, esse smell é o primeiro a aparecer: o modelo descreve a nota e o ERP espera campos.
| Refatoração | Escolha quando |
|---|---|
| Trocar Texto Livre por Saída Estruturada | Sempre que um passo entrega dados para outro passo ou para código. O destino é o padrão Saída Estruturada. |
Se o passo seguinte só existe para converter o texto em campos, ele provavelmente pode sumir: a primeira chamada já devolve os campos.
O contrato fica explícito. Exemplo de schema, no mesmo caso do total:
schema Invoice {
vendor_tax_id: string # CNPJ
access_key: string | null # chave da NF-e, 44 dígitos
total: money
due_dates: list[date]
}
A API garante o formato (JSON com essas chaves, tipos checados). O código do ERP lê invoice.total, não uma frase. Campo obrigatório ausente vira erro na hora, e o documento vai para revisão humana em vez de entrar zerado.
Isso não substitui eval. Um schema correto ainda pode vir com o CNPJ de quem não é o emitente. O eval mede se o valor está certo; o schema garante que o valor chegou como número.
Responda as 6 perguntas para concluir a aula. Só assistir não conclui: a checagem é o que conta para a trilha e para o certificado.
Passagem em Texto Livre (Free-Text Handoff) é um smell de IA, ou seja, um sinal de problema em sistemas com LLM, do grupo Acopladores. Uma etapa entrega texto solto para a próxima, que precisa adivinhar o formato.
Com Trocar Texto Livre por Saída Estruturada. Cada refatoração traz o passo a passo seguro, com eval antes e depois.
Nos cenários reais Extração de NF-e e contratos.