Problemas comuns
Esta unidade acrescenta uma peça que a Unidade 1 não tinha: a rede. Com ela vêm modos de falha novos.
Todos os erros abaixo foram provocados de propósito na máquina do curso, e as mensagens são as que o CNJ devolveu de fato.
HTTP 401 — chave rejeitada
HTTP 401: {"error":{"root_cause":[{"type":"security_exception",
"reason":"unable to authenticate with provided credentials and anonymous
access is not allowed for this request",
"additional_unsuccessful_credentials":"API key: Illegal base64 character 2d", ...
Causa. A chave está errada, truncada, ou você definiu DATAJUD_API_KEY com um valor inválido.
Repare no detalhe: Illegal base64 character 2d. O caractere 2d é o hífen — no teste, a chave falsa era chave-invalida-de-proposito. A mensagem diz até qual caractere estragou.
Solução. Limpe a variável de ambiente e deixe o padrão do cnj.py valer:
set DATAJUD_API_KEY=
Se ainda assim falhar, confira a chave na documentação oficial do CNJ. Ela é pública e muda muito raramente.
HTTP 404 — índice não existe
HTTP 404: {"error":{"root_cause":[{"type":"index_not_found_exception",
"reason":"no such index [api_publica_tjxx]", ...
Causa. A sigla do tribunal está errada. Cada tribunal é um índice separado, e não existe api_publica_tjxx.
Solução. Confira a sigla. O padrão é tjrj, tjsp, tjmg, trf2, tst. Para voltar ao TJRJ:
set DATAJUD_TRIBUNAL=tjrj
Aproveite para experimentar. Trocando a variável para tjsp, a mesma consulta responde normalmente:
URL: https://api-publica.datajud.cnj.jus.br/api_publica_tjsp/_search
OK: {'value': 10000, 'relation': 'gte'}
Nada no código do agente é específico do TJRJ. O que muda é uma sigla numa URL — e as Tools, o modelo e o ciclo continuam iguais.
HTTP 400 — consulta malformada
HTTP 400: {"error":{"root_cause":[{"type":"illegal_argument_exception",
"reason":"Fielddata is disabled on [grau] in [api_publica_tjrj]. ...
Please use a keyword field instead. ...
Causa. Quase sempre: agregação ou sort num campo de texto analisado.
Solução. Acrescente .keyword ao nome do campo. Ver Fazendo perguntas em Query DSL.
O 400 é o erro mais frequente enquanto você estiver montando consulta, e é também o mais fácil — a mensagem do Elasticsearch costuma dizer exatamente o que fazer.
"Não foi possível alcançar a API do CNJ"
Não foi possível alcançar a API do CNJ ([Errno 11001] getaddrinfo failed).
Verifique sua conexão e se a rede do Tribunal permite a saída.
Causa. O nome não resolveu, ou a conexão foi recusada. Numa máquina do Tribunal, o suspeito principal é a política de rede.
Diagnóstico, em ordem:
ping api-publica.datajud.cnj.jus.br
Se o nome não resolve, é DNS ou bloqueio de domínio.
curl -I https://api-publica.datajud.cnj.jus.br
Se o ping responde e o curl trava, é firewall na saída HTTPS.
Solução. Se estiver bloqueado, o pedido à TI é a liberação do domínio api-publica.datajud.cnj.jus.br (porta 443).
Vale como argumento no pedido: é um endereço público do CNJ, com dados públicos, sem credencial nominal e sem tráfego de entrada. Não é um pedido de acesso a base sigilosa.
Se houver proxy corporativo, o Python o respeita por variável de ambiente:
set HTTPS_PROXY=http://proxy.tjrj.jus.br:8080
Acentos saem como ?? no terminal
"nome": "Distribui??o"
Causa. O console do Windows não usa UTF-8 por padrão. O dado chegou certo; a tela é que não sabe desenhá-lo.
Solução. É a primeira linha executável de todos os exemplos:
import sys
sys.stdout.reconfigure(encoding="utf-8")
Se preferir resolver no terminal, antes de rodar:
chcp 65001
O agente demora muito
Cada passo do ciclo é uma geração completa do qwen2:7b na sua CPU. Três passos podem levar vários minutos numa máquina sem GPU — e é normal.
O que ajuda, em ordem de efeito:
| Medida | Efeito |
|---|---|
Reduzir max_steps |
Corta o pior caso |
Manter _source enxuto |
Menos texto no contexto a cada volta |
| Manter o cache do catálogo | Evita repetir a agregação |
Modelo menor (qwen2.5:3b) |
Bem mais rápido, e erra mais |
O que não ajuda é aumentar num_ctx. Janela maior não deixa o modelo mais rápido — deixa mais lento, e ainda permite que o histórico cresça mais antes de doer.
O agente pega os dados e não para
O log mostra a Tool devolvendo tudo certo num passo, e o agente continua trabalhando até bater no max_steps.
Causa. O CodeAgent só encerra quando o código gerado chama final_answer(...). Modelos pequenos frequentemente não chamam: reconhecer que a tarefa terminou é um julgamento, e é o que eles fazem pior.
Solução. Peça explicitamente, no fim do enunciado:
agente.run(
"Traga os 3 processos mais recentes sobre violência doméstica. "
"Use somente os dados devolvidos pelas Tools. "
"Assim que tiver os processos, chame final_answer com eles."
)
Não é garantia — é orientação. A garantia é o max_steps, que já está lá.
Não aumente o max_steps para "dar mais chance". Um agente que não converge em 6 passos raramente converge em 20, e cada passo extra é uma geração completa do modelo mais um contexto maior — mais lento que o anterior.
O agente processou tudo e devolveu lixo, sem erro nenhum
Sintoma: a execução termina no tempo normal, sem exceção, e a saída são nomes de campo soltos, caracteres avulsos ou uma lista vazia.
Causa. Quase sempre o tipo de retorno de uma Tool. Em Python quase tudo é iterável, e iterar a coisa errada não dá erro — dá outro resultado:
| A Tool devolve | O modelo escreve | O que acontece |
|---|---|---|
str (JSON serializado) |
for x in tool() |
percorre caracteres |
dict (envelope) |
lista.extend(tool()) |
acrescenta as chaves |
list de dicionários |
ambos | funciona |
Diagnóstico. Antes de suspeitar do modelo, teste a Tool sozinha no terminal:
r = buscar_processos("Violência Doméstica Contra a Mulher", 3)
print(type(r), len(r))
print(type(r[0]))
Se type(r) for str, o problema é seu, não do agente.
Solução. Devolva objetos Python, e devolva-os na forma que o chamador vai compor — normalmente uma lista de dicionários. Ver Alfred no CNJ.
O agente respondeu com processos que não existem
Aconteceu na Unidade 1 e pode acontecer aqui.
Teste antes de acreditar:
import sys
sys.path.insert(0, "../../unidade1/exemplos")
from validar_cnj import validar_cnj
validar_cnj("0029098-20.2026.8.19.0000") # True
Um número inventado por um LLM quase sempre reprova no dígito verificador. Um número que veio da API sempre passa.
Se reprovou, o problema não é o número — é que houve um passo em que o modelo escreveu no lugar de consultar. Olhe o log do agente e localize onde:
- Uma Tool devolveu erro e ele "resolveu" o assunto sozinho?
- Ele parafraseou a Observation em vez de usar o valor?
- Alguma Tool devolveu texto onde deveria devolver dado estruturado?