Agent Mentor Learn
Colaboração Multiagente · Lição 6 de 6

Lição 6: Mão na massa: construindo um pipeline de revisão de dois agentes

Objetivos de aprendizado:

  • Escrever um pipeline de dois agentes produtor-revisor genuinamente executável com a API do Claude
  • Fazer o revisor devolver um resultado de revisão estruturado e verificável em vez de um genérico “parece bom”
  • Colocar uma válvula de segurança no loop para que o produtor e o revisor não fiquem polindo um para o outro para sempre

Pré-requisitos: concluir as Lições 1 a 5, ser capaz de ler JavaScript/Node.js básico e ter uma chave da API do Claude funcionando | Anterior: Lição 5 <<

Primeiro, o resultado: uma execução completa

Isto é o que você terá rodando ao final da lição. Você entrega uma tarefa ao terminal, e dois agentes se revezam até a revisão passar ou você atingir o limite de rodadas:

$ node review-pipeline.js "Write an API change announcement for developers: the v2 endpoint changes the user_id field from a number to a string"
[Producer v1]The v2 endpoint is here! Hugely improved experience — please switch to the new version soon.
[Reviewer round 1] Rejected. Issues:- Doesn't spell out the specific field this change affects (never mentions that user_id goes from number to string)- Gives no migration advice; developers don't know how to update their code- "Hugely improved experience" is an unverifiable, exaggerated claim with no concrete basis
[Producer v2]v2 API change notice: the user_id field type changes from number to string.Check every piece of code that parses this field and switch the read logic from numeric to string,to avoid parse failures caused by the type mismatch. This change takes effect in v2.1.0.
[Reviewer round 2] Approved
Final draft (approved in round 2):v2 API change notice: the user_id field type changes from number to string.Check every piece of code that parses this field and switch the read logic from numeric to string,to avoid parse failures caused by the type mismatch. This change takes effect in v2.1.0.

A primeira versão é rejeitada pelo revisor, com razões atreladas a cada critério específico; o produtor revisa em uma segunda versão, o revisor olha de novo e, desta vez, ela passa. Este é o padrão produtor-revisor da Lição 4 transformado em código: "one LLM call generates a response while another provides evaluation and feedback in a loop."1

A forma geral: o mesmo esqueleto de um loop de execução

Se você fez o curso Tool calling de agentes: fazendo agentes agirem de verdade desta série, o esqueleto deste pipeline vai parecer familiar: um loop, um julgamento por rodada, um resultado que decide se continua, mais uma válvula de segurança contra o loop infinito. A única diferença é o que o julgamento julga — o loop de execução de ferramentas daquele curso julga “o modelo ainda quer chamar uma ferramenta” (a semântica do loop está na lição A ida e volta completa de uma chamada de ferramenta daquele curso e em suas fontes oficiais), enquanto aqui ele julga “o revisor disse que passou”. Mesmo esqueleto, conteúdos diferentes no corpo do loop.

O pipeline inteiro são três funções costuradas juntas: runProducer gera ou revisa o texto, runReviewer o pontua em relação a critérios e dá notas específicas, e runPipeline liga as duas em um loop com um teto de rodadas como válvula de segurança.

Passo 1: O produtor — receber a tarefa, produzir o texto

Na primeira execução, o produtor tem apenas a própria tarefa; em uma segunda execução após uma rejeição, ele também carrega a versão anterior completa e as notas de revisão, de modo que o produtor revisa em cima da última versão de acordo com as notas em vez de improvisar do zero:

O prompt do produtor é autocontido. Como a Lição 3 cobriu, um subagente não consegue ver o que aconteceu do lado do orquestrador, e também não consegue ver como foi revisado da última vez2. Então cada chamada escreve “qual é a tarefa”, “o que a versão anterior dizia” e “(se houver) quais foram os problemas da última rodada” no prompt desta chamada, literalmente. Note que até o próprio rascunho anterior do produtor precisa ser repassado explicitamente — esta é a metade do princípio do autocontido que é mais fácil de esquecer: a Messages API é sem estado, cada requisição precisa carregar todo o histórico de que precisa, e o servidor não guarda nada entre requisições3. “Revise sua versão anterior” só significa algo quando a versão anterior foi de fato escrita neste prompt.

Passo 2: O revisor — pontuar em relação a critérios concretos, sem veredictos vagos

O revisor não apenas pergunta ao modelo “isso é bom”. Como a Lição 5 cobriu, a verificação precisa pousar em critérios concretos e verificáveis em vez de uma nota baseada em impressão4. Aqui o revisor recebe uma lista de verificação explícita e é obrigado a responder em um formato JSON fixo:

Juntos, os campos approved e issues formam um resultado de revisão estruturado: não um único “está ok”, mas “passou ou falhou” mais “o problema específico por trás de cada critério que falhou”. Uma vez que o produtor tem issues, ele revisa esses problemas específicos em vez de adivinhar para onde ir a partir de um veredicto vago.

Passo 3: Não confie cegamente no resultado da revisão — trate uma falha de parse como uma rejeição

runReviewer devolve uma string, não um objeto JSON de verdade, então ainda precisa ser feito o parse. Mesmo que o revisor seja instruído a “responder estritamente em JSON”, sem uma restrição de saída estruturada o modelo ainda pode produzir JSON sintaticamente inválido, omitir campos ou embrulhar o JSON em um bloco de código com algumas linhas de explicação ao redor5. A armadilha aqui é: o que acontece quando o parse falha? Tomar o caminho preguiçoso — deixar passar por padrão numa falha de parse — silenciosamente transforma uma falha de “o revisor não fez seu trabalho” em “revisão passou”. Esse é exatamente o ponto que a Lição 5 fez: uma saída que “parece” pronta não é o mesmo que uma saída que de fato está correta, e o que você não consegue verificar não deveria entregar6. Aqui fazemos o oposto: uma falha de parse sempre conta como uma rejeição, nunca como um passe:

As linhas typeof parsed.approved !== "boolean" e !Array.isArray(parsed.issues) estendem a mesma ideia — mesmo quando JSON.parse tem sucesso, você ainda confirma que os campos parseados têm a forma certa, e um tipo de campo errado também conta como uma rejeição. Não baixe a guarda só porque é “pelo menos JSON válido”.

Um à parte: existe um recurso oficial de saídas estruturadas que garante, no nível da amostragem, que a resposta corresponde estritamente a um schema5. Esta lição usa deliberadamente o estilo “chamada crua mais seu próprio parse defensivo” para você sentir na pele que a saída do modelo não pode ser confiada cegamente; em produção, você pode usar saídas estruturadas para remover essa cova por completo.

Passo 4: Ligue tudo em um loop, adicione a válvula de segurança

Com runProducer, runReviewer e parseReview em mãos, runPipeline liga os três, e MAX_ROUNDS é a única válvula de segurança aqui — o produtor e o revisor poderiam, em teoria, polir para sempre, então precisa haver um teto:

Quando atinge MAX_ROUNDS ainda sem passar, runPipeline não força um veredicto de “passou”. Ele honestamente entrega o último rascunho e os problemas ainda não resolvidos para revisão humana — este também é o ponto da Lição 5 aplicado na etapa de fechamento: quando a etapa de integração de resultados esbarra em algo que não consegue julgar, ela não deveria disfarçar isso decidindo por si mesma no código.

Recapitulação

  • O esqueleto do pipeline produtor-revisor é a mesma coisa que um loop de execução: um loop, um julgamento por rodada, um resultado que decide se continua, mais uma válvula de segurança contra o loop infinito. A definição oficial deste padrão é exatamente "one LLM call generates a response while another provides evaluation and feedback in a loop"1 — aqui o julgamento muda de “uma ferramenta deveria ser chamada” para “o revisor disse que passou”.
  • O prompt do produtor é autocontido: cada chamada escreve a tarefa, a versão anterior completa e (se houver) os problemas específicos da última rodada no prompt, literalmente — a Messages API é sem estado, cada requisição precisa carregar todo o histórico, e nada é guardado entre requisições3, então você não pode contar com o modelo lembrando por conta própria o que aconteceu na última rodada2.
  • O revisor pontua em relação a critérios concretos e verificáveis, item a item, e devolve um {approved, issues} estruturado em vez de um veredicto genérico4.
  • O que o revisor devolve também não pode ser confiado cegamente — uma falha de parse ou uma forma de campo errada deveria contar como uma rejeição, não um passe silencioso6; esse princípio se aplica não só a “confiar no que um subagente diz”, mas também a “confiar no formato de dados que um subagente devolve”.
  • Quando atinge a contagem máxima de rodadas ainda sem passar, o pipeline deveria honestamente entregar o último rascunho e os problemas não resolvidos para revisão humana, em vez de decidir um passe por si mesmo no código.

E é isso, as seis lições deste curso: de “por que vários agentes”, passando por como o orquestrador e os subagentes dividem o trabalho, como escrever prompts de delegação, qual padrão de colaboração se encaixa em qual cenário e como lidar com falhas, terminando com a construção à mão de um pipeline produtor-revisor funcionando. A coisa mais valiosa a fazer em seguida não é reler as explicações — é pegar uma tarefa pequena e real que você tem em mãos, jogá-la neste esqueleto de pipeline, ajustar os critérios de revisão e rodá-lo para ver se ele rejeita o rascunho e quantas vezes. Ajustar você mesmo os critérios de revisão uma vez vale mais que reler a teoria dez vezes.

Footnotes

  1. Building effective agents (Anthropic Engineering) — https://www.anthropic.com/engineering/building-effective-agents 2

  2. Create custom subagents (Claude Code Docs) — https://code.claude.com/docs/en/sub-agents 2

  3. Using the Messages API (Claude API) — https://platform.claude.com/docs/en/build-with-claude/working-with-messages 2

  4. How we built our multi-agent research system (Anthropic Engineering) — https://www.anthropic.com/engineering/multi-agent-research-system 2

  5. Structured outputs (Claude API) — https://platform.claude.com/docs/en/build-with-claude/structured-outputs 2

  6. Best practices for Claude Code (Claude Code Docs) — https://code.claude.com/docs/en/best-practices 2

Exercícios

01

Monte o código desta lição em um review-pipeline.js, rode npm install @anthropic-ai/sdk, npm pkg set type=module, defina ANTHROPIC_API_KEY e rode a tarefa de exemplo desta lição uma vez. Confirme que você vê pelo menos uma rodada “Rejected” antes de ver “Approved”. (Se a primeira versão do produtor passar direto, troque por uma tarefa mais fácil de tropeçar — por exemplo, peça deliberadamente “um anúncio bem curto” sem dizer quão curto.)

Nível 1: Coloque para rodar, depois adicione um critério de revisão

Uma vez que rode, adicione um novo critério a REVIEW_CRITERIA: “Does the text mention the specific version number where the change takes effect?” Rode de novo e confirme que os issues do revisor agora incluem uma nota atrelada a esse novo critério.

Critérios de conclusão · marcado localmente
02

A versão de parseReview abaixo tem um problema. Primeiro explique a situação em que ela deixaria passar como “approved” um rascunho que nunca foi de fato revisado, depois dê o código corrigido.

Nível 2: Quebre algo de propósito, depois conserte
Critérios de conclusão · marcado localmente