Skip to content

O que é automático

Todo framework esconde alguma coisa. O problema não é esconder — é você não saber o quê, e descobrir na hora errada.

Esta página é a lista completa. Se algo acontece sem você mandar, está aqui.

Feito por você

Acontece sozinhoOnde você entra
montar as mensagens (system, user, assistant, tool)o hook beforePrompt altera o system
chamar o modelo com as tools declaradassampling no provider ou no @Agent
detectar que o modelo pediu uma tool(fechado — veja abaixo)
validar os argumentos contra o schema Zodvocê escreve o schema
executar a tool e devolver o resultadoos hooks beforeTool / afterTool
anexar cada turno ao históricoctx.state é público e editável
repetir enquanto o until do laço não for verdadeirovocê escreve o until
criar o estado do workflow, um por execuçãovocê declara a classe em @Workflow({ state })
tentar de novo numa falha transitória de rederetry no provider
mascarar segredos conhecidos no conteúdo capturadoredact no config
gravar a árvore da execuçãoreport e log no config

A parte deliberadamente fechada

Três linhas estão em negrito. Elas são o núcleo do framework e não têm gancho de customização — de propósito.

Quando o modelo responde, alguém precisa decidir: isso é resposta final ou pedido de tool? Feito à mão, esse código vira uma pilha de condicionais que muda a cada modelo:

ts
// o que você NÃO escreve
if (resposta.tool_calls?.length) { … }
else if (resposta.content?.startsWith("{")) { …tentar parsear… }
else if (resposta.content?.includes("<tool_call>")) { …outro formato… }

É ali que moram os bugs mais chatos de agente: um modelo pequeno emite a chamada como texto em vez de usar o campo estruturado, e o agente encerra achando que respondeu. O framework trata isso — inclusive resgatando chamadas escritas como texto, em vários formatos — e você nunca vê.

Por que isso é bom

Trocar qwen2.5-coder por gpt-4o-mini não deveria exigir mudar o seu código. Como essa camada é do framework, não exige.

Se você precisa mesmo interferir aí, o ponto de extensão certo é escrever um provider — a resposta para "meu backend fala diferente".

O que não é automático

Igualmente importante: o framework não decide por você.

  • Quando parar. O until do laço é seu. Não há heurística escondida de "acho que terminou".
  • Quando buscar na memória. Nada é injetado no prompt sozinho. Se você quer contexto vetorial, você chama recall onde fizer sentido.
  • Quanto gastar. Sem budget, nada é medido nem limitado.
  • Como formatar contexto recuperado. Você monta a string.
  • Quando uma falha de tool deve ser fatal. Tudo é recuperável, a menos que você lance FatalToolError.

A regra que seguimos: o framework entrega o mecanismo, você escolhe a política.

O que muda de comportamento por padrão

Uma exceção honesta à regra acima. O retry vem ligado: um 429 ou um 503 momentâneo são reexecutados até 3 vezes, com espera crescente.

Foi decisão deliberada — falha transitória de rede derrubando uma execução inteira é quase sempre indesejado. Para desligar:

ts
super({ host, model, retry: false });

O timeout, esse sim, não tem valor padrão: um teto arbitrário abortaria um modelo local lento que hoje funciona. Ligue quando quiser (Providers).

O segundo é o mascaramento, também ligado: padrões conhecidos de segredo são escondidos em tudo que é capturado, antes de chegar ao report, ao log ou a um plugin. Um arquivo em disco, sem retenção nem controle de acesso, é o pior lugar para um segredo aparecer por descuido.

O que é condicionalmente automático

A observação. Uma execução monta a árvore, emite eventos e pede streaming ao provider só quando alguém está olhandoreport, log, um plugin com onEvent, ou um observe: true explícito.

Sem nenhum deles, a execução segue o caminho de custo zero: sem árvore, sem eventos, sem streaming. Vale cerca de 2× em tempo de CPU por execução, e é o motivo de o onEvent poder parecer não fazer nada.

Onde você pode entrar

Do mais leve ao mais invasivo:

PrecisãoFerramenta
ajustar o prompt finalhook beforePrompt
inspecionar ou barrar uma toolhook beforeTool (um throw cancela)
transformar o resultado de uma toolhook afterTool
transformar a resposta do agentehook afterResponse
tratar erro sem derrubarhook onError
receber o contexto ou o estado numa tool@context(), @state() nos parâmetros
escolher qual memória vetorial@memory(Store) no construtor
mexer no histórico e no contextoctx.state, público
controlar o laçoo until do loop
envolver cada tool ou cada chamada ao modeloapp.use({ tool, chat })
falar com outro backendescrever um provider
assumir o turno inteirométodo run(input, ctx) na classe do agente

Esse último é o escape hatch total: se a classe do agente define run, ela toma conta do passo e nenhum hook é chamado.

Relacionado