Último artigo da série de 6 partes sobre os cenários do CCA-F — código-fonte completo de cada cenário: labs.
O cenário, como está no exam guide
Um sistema extrai informação de documentos não estruturados, valida a saída com schemas JSON e mantém alta precisão, tratando casos extremos e integrando com sistemas downstream.
| Domínio | Peso | Onde aparece neste cenário |
|---|---|---|
| Prompt Engineering & Structured Output | 20% | Um JSON Schema de tool_use, mais uma segunda camada de validação que pega o que o schema não consegue |
| Context Management & Reliability | 15% | O que acontece a jusante quando um campo não pode ser extraído com confiança |
A leitura óbvia deste cenário é incompleta
"Usar tool_use pra tirar JSON estruturado do Claude" é o básico — necessário, e também não é o que separa um passe de uma reprovação aqui. A própria frase do exam guide, mantendo alta precisão ao tratar casos extremos, é o teste real: o que o sistema faz quando um documento é ambíguo, não quando ele está limpo?
Um JSON Schema garante formato. Não consegue garantir honestidade.
O EXTRACTION_TOOL_INPUT_SCHEMA do labs é um JSON Schema simples passado ao Claude via tool_choice: vendor é string | null, fieldsNeedingReview é um array de nomes de campo. Isso garante que a resposta tenha o formato certo. Não garante — e estruturalmente não consegue garantir — que a resposta seja internamente consistente. Nada em JSON Schema consegue expressar "se vendor é null, "vendor" também precisa aparecer em fieldsNeedingReview". Isso é uma relação entre dois campos, e JSON Schema valida campos individualmente.
O ExtractionSchema (um schema zod, checado depois que a resposta de tool_use volta) adiciona exatamente essa checagem via superRefine: qualquer campo anulável que seja null mas não esteja listado em fieldsNeedingReview falha na validação. Esse é o cenário inteiro numa frase: um schema sozinho não impede um modelo de retornar um null com aparência confiante que ele nunca sinalizou como incerto — uma segunda camada, checando uma relação que a primeira estruturalmente não consegue expressar, impede.
Dois modos de falha, um único destino seguro
O route.ts só permite auto_process quando a validação passou e fieldsNeedingReview está vazio. Tanto um resultado honesto de "alguns campos são ambíguos" quanto um resultado que viola a invariante ("isso alega confiança que não conquistou") caem em human_review — e, criticamente, o caso de violação de invariante retorna result: null pro chamador, em vez de tentar salvar parte de uma extração autocontraditória. Adivinhar qual metade de um resultado quebrado confiar é pior do que admitir que nenhuma das duas é confiável. Mesmo instinto de "degradar pro estado seguro, não propagar um valor quebrado" do tratamento de achados malformados do Cenário 5 — aqui o custo de adivinhar errado é um sistema downstream agindo silenciosamente sobre dados ruins, não um comentário de PR perdido.
Três fixtures, três formas diferentes de ser ambíguo
- Limpo — fornecedor, data, moeda e total inequívocos. Deve ir pro auto_process.
- Ambíguo — uma data que pode ser dois dias diferentes (
03/04/2026), um fornecedor ilegível, nenhum símbolo de moeda em lugar nenhum. - Multi-moeda — itens de linha em moedas inconsistentes e um "ver fatura de acerto final" explícito no lugar de um total, testando se o modelo sinaliza
totalAmountem vez de somar unidades incompatíveis num número que parece válido mas não significa nada.
Rode você mesmo
git clone https://github.com/aicertstudy/labs
cd labs/ccar-f/scenarios/scenario-6-structured-data-extraction
npm install
npm test # 8 testes passando — a invariante e o roteamento fail-safe, sem precisar de chave de API
cp .env.example .env # adicione sua ANTHROPIC_API_KEY
npm run extract -- fixtures/invoice-ambiguous.txt
Só o npm test já exercita a parte que vale a pena entender primeiro — a invariante e o fail-safe de roteamento — com zero chamadas de API.
Teste seus conhecimentos
1. Por que o JSON Schema de tool_use sozinho não consegue garantir que um vendor nulo sempre esteja listado em fieldsNeedingReview?
Resposta: JSON Schema valida o formato de cada campo independentemente; não tem construção pra uma relação condicional entre valores de dois campos diferentes.
"Se o campo A é X, o campo B precisa conter Y" é uma restrição entre campos, não uma restrição de tipo num único campo. JSON Schema puro (e por extensão um input_schema de tool_use) consegue descrever a cara de cada campo individualmente, não uma regra que abrange dois deles.
2. O que route() retorna quando o input bruto de tool_use viola a invariante null/fieldsNeedingReview — um resultado parcial de melhor esforço, ou nada?
Resposta: nada — result: null, roteado pra human_review, sem nenhuma tentativa de salvar ou confiar parcialmente nos dados autocontraditórios.
Uma extração internamente inconsistente não diz qual das suas afirmações acreditar. Retornar um resultado parcial ou de "melhor palpite" exigiria decidir qual metade de uma afirmação quebrada é a metade confiável — uma decisão pra qual o sistema não tem base de princípio nenhuma.
3. Em invoice-ambiguous.txt, por que a data 03/04/2026 merece uma sinalização em fieldsNeedingReview mesmo com uma data sendo extraída?
Resposta: porque o formato é genuinamente ambíguo (dia-primeiro vs. mês-primeiro), então qualquer valor único escolhido é um chute disfarçado de extração, não uma leitura confiante.
A invariante só pega mecanicamente os casos de null-não-sinalizado; pegar "extraiu um valor, mas o valor em si é cara-ou-coroa" depende do prompt pedir exatamente isso (veja o system prompt do extract.ts: sinalizar um campo mesmo não-nulo se não tiver confiança de que está correto) — o schema consegue garantir o caso nulo, mas um caso genuinamente ambíguo e não-nulo depende do modelo seguir essa instrução.
4. Por que invoice-multi-currency.txt testa algo significativamente diferente de invoice-ambiguous.txt?
Resposta: testa se o modelo evita combinar silenciosamente unidades incompatíveis num número que parece válido mas não tem significado, em vez de testar se ele consegue notar um único campo ausente/ilegível.
Dado ambíguo-mas-presente (um fornecedor ilegível) e dado inconsistente-mas-presente (moedas misturadas somadas como se fossem uma só) são formas de falha diferentes. A primeira é sobre reconhecer ausência; a segunda é sobre reconhecer que um valor calculado pode ser válido pro schema (um número bem formado) e ainda assim estar substancialmente errado.
5. Se você apagasse a checagem superRefine de ExtractionSchema, qual teste específico em route.test.ts começaria a falhar, e por quê?
Resposta: o teste "fails safe to human review... when the raw extraction violates the schema invariant" — porque sem o superRefine, um vendor nulo com fieldsNeedingReview vazio passaria na validação e seria roteado pra auto_process em vez de human_review.
Esse teste constrói exatamente o caso que a invariante existe pra pegar. Remova a checagem que pega isso, e o schema passa a aceitar silenciosamente o que antes era uma falha de validação — que é precisamente o modo de falha sobre o qual todo o cenário trata.
Simulado gratuito do CCA-F, cobrindo os 5 domínios: aicert.study/certifications/ccaf.
