Tutorial

Como dar tarefas ao agente de IA: uma funcionalidade por vez, lista de funcionalidades e critérios de pronto

Atualizado: · Skillaria.Top

Ao agente de IA damos as tarefas uma de cada vez: em andamento há sempre exatamente uma funcionalidade, ela tem um comportamento observável e um comando de verificação executável, e tudo o que for notado no caminho vai para o backlog, sem ser consertado na hora. O estado de todas as funcionalidades fica em uma lista legível por máquina (por exemplo, feature_list.json), e só o script de verificação pode marcar uma funcionalidade como concluída, não o próprio agente.

Por que o agente pega tudo de uma vez

Você pede ao agente um cadastro de usuários. Duas horas depois, o diff tem doze arquivos alterados e oitocentas linhas novas: modelo de usuário, verificação de e-mail, serviço de envio de correio conectado, hash de senhas, middleware refatorado e testes realocados. Tudo está fortemente acoplado, e nenhum cenário passa por inteiro. A sessão seguinte abre esse código e não entende por onde começar.

A causa não é um "modelo ruim". Uma pessoa tem a sensação de "por hoje basta" e o cansaço comum, que limitam o tamanho do trabalho. O agente não tem nem uma coisa nem outra. Acrescentar "e já aproveito para ajustar o tratamento de erros" custa a ele algumas dezenas de tokens, mas o custo para o projeto é real: a atenção se dispersa, o acoplamento cresce e nada fica pronto.

Há também uma conta simples. Seja C a capacidade útil do contexto e k o número de tarefas que o agente segura ao mesmo tempo. Para cada uma sobra cerca de C/k de recurso de raciocínio. Quando esse valor cai abaixo do necessário para concluir uma tarefa, nenhuma delas é concluída. Por isso a resposta para "como dar tarefas ao agente de IA" não começa pela redação do prompt, e sim por uma restrição: uma tarefa em andamento.

A regra de uma funcionalidade (WIP=1)

WIP é work in progress, o número de tarefas em andamento. Para o agente ele vale um: a cada momento está ativa exatamente uma funcionalidade, e a próxima só começa depois que a atual passa na verificação. A regra é registrada no arquivo de instruções do projeto (AGENTS.md ou CLAUDE.md), para que o agente a veja em cada sessão:

## Regras de trabalho
- A qualquer momento, EXATAMENTE UMA funcionalidade está no estado `active`.
- Começar a próxima funcionalidade só depois que a atual passar para `passing`.
- É proibido refatorar, renomear, reorganizar pastas e consertar bugs não relacionados
  "de passagem". Encontrou um problema? Registre-o em `BACKLOG.md` e siga em frente.
- Alterações fora do escopo da funcionalidade ativa são violação, mesmo que sejam úteis.

O último item é mais importante do que parece. O agente considera sinceramente os seus "de passagem" úteis, e muitas vezes eles são. Mas uma correção útil fora do escopo mistura duas tarefas no mesmo diff, e não é mais possível reverter ou verificar cada uma separadamente.

BACKLOG.md: para onde vai o "de passagem"

O impulso de "consertar já que estou aqui" não pode ser simplesmente proibido: ele precisa de uma saída legítima. Essa saída é o arquivo BACKLOG.md: o agente anota o problema notado em uma ou duas linhas e volta à funcionalidade atual. A informação não se perde, e o escopo não cresce. Sem o backlog, a regra de uma funcionalidade será contornada cedo ou tarde, porque deixar silenciosamente um bug lhe parece pior do que a infração.

# BACKLOG.md
- [ ] middleware/errors.py: formato de erro diferente em /auth e /orders
- [ ] tests/: fixtures do usuário duplicadas em três arquivos
- [ ] precisa de verificação de e-mail no cadastro? — perguntar

Critérios de pronto: "pronto" é um comando

A segunda metade do problema é que o agente decide sozinho que a tarefa terminou. "O código parece normal", "a função existe", "não há erros de compilação": tudo isso são impressões, não fatos. O critério de pronto de uma tarefa para o agente precisa ser um comando executável com resultado inequívoco: passou ou não passou.

F01: cadastro de usuário
verification: pytest tests/e2e/test_auth.py::test_register_flow
state: active

A diferença entre um critério fraco e um forte aparece bem nos exemplos:

Critério fracoCritério forte
O código compilapytest tests/e2e/test_cart.py -q passa
Não há erros de sintaxecurl para /cart retorna código 200
A função addToCart existeDepois de adicionar um produto, GET /cart mostra o item e o total correto
Parece corretonpx playwright test cart.spec.ts passa

Um bom comando de verificação é determinístico (mesmo código, mesmo resultado), roda sem a participação de uma pessoa e verifica o comportamento de fora, do jeito que o usuário ou o cliente da API o vê. Se não é possível escrever esse comando, isso indica que a tarefa está mal formulada.

Decomposição: qual deve ser o tamanho de uma funcionalidade

Depois de "uma funcionalidade por vez", a pergunta mais comum é o que chamar de funcionalidade. Uma tarefa larga demais não termina em uma sessão, e uma estreita demais se perde em custos de inicialização, contexto e verificação.

  • Largo demais: "implementar o carrinho". É uma semana de trabalho e uma dezena de cenários.
  • Na medida: "o usuário pode adicionar um produto ao carrinho e vê-lo lá". Um comportamento observável, um cenário de verificação.
  • Estreito demais: "criar o campo quantity no modelo CartItem". É um passo de implementação, não um comportamento; verificá-lo isoladamente não faz sentido.

Uma verificação prática de tamanho: uma funcionalidade = uma sessão do agente = um commit com sentido = um cenário ponta a ponta (E2E). Se a funcionalidade não cabe em uma sessão, divida mais. Se não é possível escrever um cenário próprio para ela, é pequena demais e deve ser unida à vizinha.

Exemplo de decomposição da tarefa "carrinho"

O requisito amplo se divide em unidades atômicas, cada uma com um comportamento e um comando de verificação próprio:

  1. Adicionar um produto ao carrinho e vê-lo na lista.
  2. Alterar a quantidade do produto — o total é recalculado.
  3. Remover um produto do carrinho.
  4. O carrinho de visitante é preservado depois do login na conta.
  5. Não é possível adicionar um produto sem estoque — erro compreensível.

Teste final da decomposição: nenhuma unidade exige alterar "de passagem" outra. Se o item 4 precisa reescrever o item 1, a ordem ou os limites foram escolhidos mal.

Lista de funcionalidades: feature_list.json

A regra de uma funcionalidade mantém a ordem dentro da sessão. Mas quem lembra, entre as sessões, o que já está pronto? Uma anotação no chat como "auth e catálogo prontos, carrinho quase pronto, mas há bugs" não serve de nada para a sessão nova: o que significa "quase", quais testes passaram, o que bloqueia o pagamento, nada disso se sabe. Cada sessão nova é como um novo motorista do mesmo carro: se as anotações ficavam só na cabeça do anterior, o novo não sabe nada.

Por isso o estado é levado para fora, para um arquivo legível por máquina. No curso, esse arquivo é .agent/feature_list.json:

[
  {
    "id": "F01",
    "behavior": "O usuário se cadastra com e-mail e senha e entra logo em seguida",
    "verification": "pytest tests/e2e/test_auth.py::test_register_flow",
    "state": "passing",
    "evidence": "a3f9c12: 1 passed in 2.31s"
  },
  {
    "id": "F02",
    "behavior": "O usuário adiciona um produto ao carrinho e o vê na lista",
    "verification": "pytest tests/e2e/test_cart.py::test_add_item -q",
    "state": "active"
  },
  {
    "id": "F03",
    "behavior": "Pagamento do pedido com cartão",
    "verification": "pytest tests/e2e/test_payment.py -q",
    "state": "blocked",
    "blocker": "não há chaves de teste do gateway de pagamento"
  }
]

Cada registro tem três campos obrigatórios: a descrição do comportamento (observável de fora), o comando de verificação (executável, sem pessoa) e o estado. Um quarto campo, opcional, é evidence: o hash do commit e a saída da verificação, que comprovam que a passagem para passing realmente aconteceu.

Por que JSON, e não uma lista em Markdown? Quem lê o arquivo não é só o agente, mas também os scripts: o planejador escolhe a próxima funcionalidade em not_started, o verificador pega o comando de verificação, o relatório da sessão é montado automaticamente e a distribuição dos estados dá as métricas de saúde do projeto. A lista de funcionalidades é a espinha dorsal do harness, não um lembrete.

Quatro estados e as transições permitidas

TransiçãoQuando
not_started → activeA funcionalidade é retomada para trabalho; não há outra ativa
active → passingO script de verificação terminou com código 0
active → blockedBloqueio externo; a causa é registrada em blocker
blocked → activeO bloqueio foi removido

Não há outras transições. Em particular, não existe caminho para passing que contorne a verificação, nem um estado de "quase pronto".

A trava passing: o agente pode pedir, mas não declarar

A regra central de todo o esquema: o agente não edita o campo state à mão. Ele pode rodar a verificação, mas não pode declarar o resultado por conta própria: isso é feito pelo script. A diferença entre "o agente informou" e "o script confirmou" é a diferença entre texto e fato. A regra também é registrada no arquivo de instruções:

## Regras da lista de funcionalidades
- Arquivo: `.agent/feature_list.json`. É a única fonte de verdade sobre o escopo.
- Exatamente uma funcionalidade no estado `active`.
- Você NÃO edita o campo `state` manualmente. Ele é alterado por `scripts/verify.sh`.
- Para passar uma funcionalidade para `passing`, rode `./scripts/verify.sh F02` e obtenha código 0.
- Se a funcionalidade estiver bloqueada, passe para `blocked` e preencha obrigatoriamente `blocker`.
- Novas funcionalidades são adicionadas apenas em `not_started` e apenas com comando de verificação.

O próprio script verifica em dois níveis: primeiro uma execução geral do projeto (a funcionalidade não pode quebrar o restante), depois o comando da funcionalidade específica. Sucesso local com regressão global não conta como sucesso.

#!/usr/bin/env bash
# Único componente com permissão para marcar passing.
set -uo pipefail
FID="$1"
FILE=".agent/feature_list.json"

CMD=$(jq -r --arg id "$FID" '.[] | select(.id==$id) | .verification' "$FILE")
if [ -z "$CMD" ] || [ "$CMD" = "null" ]; then
  echo "FAIL: não há comando de verificação para $FID"; exit 2
fi

echo "== verificação geral =="
make check || { echo "FAIL: verificação geral vermelha, $FID não pode ser passing"; exit 1; }

echo "== verificação da funcionalidade: $CMD =="
if OUT=$(eval "$CMD" 2>&1); then
  SHA=$(git rev-parse --short HEAD)
  LAST=$(printf '%s\n' "$OUT" | tail -n 1)
  TMP=$(mktemp)
  jq --arg id "$FID" --arg ev "$SHA: $LAST" \
    'map(if .id==$id then .state="passing" | .evidence=$ev else . end)' \
    "$FILE" > "$TMP" && mv "$TMP" "$FILE"
  echo "PASS: $FID"
else
  printf '%s\n' "$OUT"
  echo "FAIL: $FID"; exit 1
fi

A regra "não edite state" escrita em texto ainda é um pedido. Para que vire restrição, a escrita no arquivo pode ser bloqueada mecanicamente: com uma verificação no CI que compara evidence com o histórico de commits, ou com as permissões do agente na sua ferramenta. O princípio é o mesmo do resto do harness: uma regra importante é reforçada por mecanismo.

Como medir se o agente leva as tarefas até o fim

Uma métrica tentadora é o volume de código por sessão. Na prática ela funciona ao contrário: um diff grande costuma indicar que o agente se espalhou por várias direções. Conte as funcionalidades no estado passing, não as linhas.

Uma métrica prática do curso é o VCR (Verified Completion Rate): o número de tarefas verificadas dividido pelo número de tarefas ativadas. A regra é simples: enquanto o VCR for menor que um, não se ativam novas tarefas. É uma pressão de retorno que o harness aplica ao agente, e ao mesmo tempo uma forma de tornar o inacabado um número visível, e não uma sensação.

Erros típicos ao dar tarefas ao agente

  • Tarefa sem comando de verificação. "Faça o cadastro" sem um cenário: o agente decide sozinho onde parar, e decide errado.
  • Várias funcionalidades em um pedido. "Faça o carrinho e o pagamento" são duas tarefas ativas, e nenhuma será concluída.
  • Estado só no chat. O progresso registrado na conversa desaparece quando a sessão termina. Ele precisa viver em um arquivo.
  • Autorrelato no lugar da verificação. "O agente disse que os testes passaram" não é o mesmo que o código de retorno do script.
  • Sem backlog. A proibição de "fazer de passagem" sem um lugar para anotar o que foi notado será contornada pelo agente.

Quando uma funcionalidade por vez já não basta

O esquema "uma funcionalidade, uma sessão, uma verificação" funciona bem para um agente em um ciclo. Quando há muitas tarefas e surgem ramos independentes, ele se expande para um sistema de vários ciclos: um nó de planejamento, nós de implementação, um nó de verificação e as arestas entre eles, paralelas, condicionais, com retorno à implementação quando a verificação falha. Mas também ali cada nó de implementação segue a mesma regra: uma tarefa, um critério de pronto executável, estado fora da cabeça do agente. O grafo não cancela a disciplina de uma funcionalidade, ele é construído sobre ela.

Um alerta importante da mesma análise: o ciclo otimiza honestamente a métrica que lhe deram. Se o critério de pronto estiver mal escolhido, por exemplo "o ticket foi fechado" em vez de "o problema foi resolvido", o agente aprende a cumprir o critério, e não a tarefa. Por isso, a formulação do comportamento e do comando de verificação merece mais tempo do que parece.

Resumindo

Em resumo, como dar tarefas ao agente de IA: uma de cada vez, com comportamento observável e comando de verificação executável, com um backlog para tudo o que for notado pelo caminho, e com uma lista de funcionalidades em arquivo em que a passagem para "pronto" é feita pelo script, não pelo agente. Quem põe o freio no agente é o harness: ele mesmo não vai acioná-lo. A regra WIP=1, o feature_list.json e o verify.sh são explicados na prática no curso de harness engineering, com um projeto completo como exemplo.

Perguntas frequentes

Posso dar ao agente várias tarefas de uma vez, se forem pequenas?

Pode colocá-las em uma lista de funcionalidades, mas só uma pode estar em andamento ao mesmo tempo. O agente pega a próxima apenas depois que a atual passa na verificação, e assim cada tarefa é levada até o fim.

O que fazer quando não é possível escrever uma verificação automática para a tarefa?

Normalmente isso significa que a tarefa foi formulada como uma ação, e não como um comportamento. Reformule-a pelo que o usuário ou o cliente da API vai ver; para a parte visual, testes E2E no navegador funcionam bem.

Quem escreve o feature_list.json, a pessoa ou o agente?

A lista inicial é melhor montada em conjunto: o agente propõe a decomposição e a pessoa revisa. Depois disso, o agente pode incluir novas funcionalidades, mas apenas no estado not_started e com comando de verificação.

Em que o feature_list.json difere das tarefas em um rastreador?

O rastreador é feito para pessoas; a lista de funcionalidades é feita para leitura por máquina: cada registro tem um comando executável e um estado rigoroso, que só o script altera. Dá para sincronizar os dois, mas o agente precisa do arquivo no repositório.

A regra de uma funcionalidade não deixa o trabalho mais lento?

As sessões ficam mais curtas, mas a proporção de tarefas concluídas aumenta. O tempo que antes ia para revisar código meio pronto, misturado com três funcionalidades, é economizado.

Aprenda este tema em vídeo

Este tema é abordado em profundidade, com prática, no curso “Harness Engineering: инженерия надёжности AI-агентов”.

Ir para o curso