Pular para o conteúdo principal

Visão Geral

O decorador @human_feedback requer CrewAI versão 1.8.0 ou superior. Certifique-se de atualizar sua instalação antes de usar este recurso.
O decorador @human_feedback permite fluxos de trabalho human-in-the-loop (HITL) diretamente nos CrewAI Flows. Ele permite pausar a execução do flow, apresentar a saída para um humano revisar, coletar seu feedback e, opcionalmente, rotear para diferentes listeners com base no resultado do feedback. Isso é particularmente valioso para:
  • Garantia de qualidade: Revisar conteúdo gerado por IA antes de ser usado downstream
  • Portões de decisão: Deixar humanos tomarem decisões críticas em fluxos automatizados
  • Fluxos de aprovação: Implementar padrões de aprovar/rejeitar/revisar
  • Refinamento interativo: Coletar feedback para melhorar saídas iterativamente

Início Rápido

Aqui está a maneira mais simples de adicionar feedback humano a um flow:
Code
Quando este flow é executado, ele irá:
  1. Executar generate_content e retornar a string
  2. Exibir a saída para o usuário com a mensagem de solicitação
  3. Aguardar o usuário digitar o feedback (ou pressionar Enter para pular)
  4. Passar um objeto HumanFeedbackResult para process_feedback

O Decorador @human_feedback

Parâmetros

Uso Básico (Sem Roteamento)

Quando você não especifica emit, o decorador simplesmente coleta o feedback e passa um HumanFeedbackResult para o próximo listener:
Code

Roteamento com emit

Quando você especifica emit, o decorador se torna um roteador. O feedback livre do humano é interpretado por um LLM e mapeado para um dos outcomes especificados:
Code
Quando o humano diz algo como “precisa de mais detalhes”, o LLM mapeia para "needs_revision", que dispara review_content novamente via or_() — criando um loop de revisão. O loop continua até que o outcome seja "approved" ou "rejected".
O LLM usa saídas estruturadas (function calling) quando disponível para garantir que a resposta seja um dos seus outcomes especificados. Isso torna o roteamento confiável e previsível.
Um método @start() só executa uma vez no início do flow. Se você precisa de um loop de revisão, separe o método start do método de revisão e use @listen(or_("trigger", "revision_outcome")) no método de revisão para habilitar o self-loop.

HumanFeedbackResult

O dataclass HumanFeedbackResult contém todas as informações sobre uma interação de feedback humano:
Code

Acessando em Listeners

Quando um listener é disparado por um método @human_feedback com emit, ele recebe o HumanFeedbackResult:
Code

Acessando o Histórico de Feedback

A classe Flow fornece dois atributos para acessar o feedback humano:

last_human_feedback

Retorna o HumanFeedbackResult mais recente:
Code

human_feedback_history

Uma lista de todos os objetos HumanFeedbackResult coletados durante o flow:
Code
Cada HumanFeedbackResult é adicionado a human_feedback_history, então múltiplos passos de feedback não sobrescrevem uns aos outros. Use esta lista para acessar todo o feedback coletado durante o flow.

Exemplo Completo: Fluxo de Aprovação de Conteúdo

Aqui está um exemplo completo implementando um fluxo de revisão e aprovação de conteúdo:

Combinando com Outros Decoradores

O decorador @human_feedback funciona com @start(), @listen() e or_(). Ambas as ordens de decoradores funcionam — o framework propaga atributos em ambas as direções — mas os padrões recomendados são:
Code

Padrão de self-loop

Para criar um loop de revisão, o método de revisão deve escutar ambos um gatilho upstream e seu próprio outcome de revisão usando or_():
Code
Quando o outcome é "revise", o flow roteia de volta para review (porque ele escuta "revise" via or_()). Quando o outcome é "approved", o flow continua para publish. Isso funciona porque o engine de flow isenta roteadores da regra “fire once”, permitindo que eles re-executem em cada iteração do loop.

Roteadores encadeados

Um listener disparado pelo outcome de um roteador pode ser ele mesmo um roteador:
Code

Limitações

  • Métodos @start() executam uma vez: Um método @start() não pode fazer self-loop. Se você precisa de um ciclo de revisão, use um método @start() separado como ponto de entrada e coloque o @human_feedback em um método @listen().
  • Sem @start() + @listen() no mesmo método: Esta é uma restrição do framework de Flow. Um método é ou um ponto de início ou um listener, não ambos.

Melhores Práticas

1. Escreva Mensagens de Solicitação Claras

O parâmetro message é o que o humano vê. Torne-o acionável:
Code

2. Escolha Outcomes Significativos

Ao usar emit, escolha outcomes que mapeiem naturalmente para respostas humanas:
Code

3. Sempre Forneça um Outcome Padrão

Use default_outcome para lidar com casos onde usuários pressionam Enter sem digitar:
Code

4. Use o Histórico de Feedback para Trilhas de Auditoria

Acesse human_feedback_history para criar logs de auditoria:
Code

5. Trate Feedback Roteado e Não Roteado

Ao projetar flows, considere se você precisa de roteamento:

Feedback Humano Assíncrono (Não-Bloqueante - Human in the loop)

Por padrão, @human_feedback bloqueia a execução aguardando entrada no console. Para aplicações de produção, você pode precisar de feedback assíncrono/não-bloqueante que se integre com sistemas externos como Slack, email, webhooks ou APIs.

A Abstração de Provider

Use o parâmetro provider para especificar uma estratégia customizada de coleta de feedback:
Code
O framework de flow persiste automaticamente o estado quando HumanFeedbackPending é lançado. Seu provider só precisa notificar o sistema externo e lançar a exceção—não são necessárias chamadas manuais de persistência.

Tratando Flows Pausados

Ao usar um provider assíncrono, kickoff() retorna um objeto HumanFeedbackPending em vez de lançar uma exceção:
Code

Retomando um Flow Pausado

Quando o feedback chega (ex: via webhook), retome o flow:
Code

Tipos Principais

PendingFeedbackContext

O contexto contém tudo necessário para retomar:
Code

Exemplo Completo de Flow Assíncrono

Code
Se você está usando um framework web assíncrono (FastAPI, aiohttp, Slack Bolt modo async), use await flow.resume_async() em vez de flow.resume(). Chamar resume() de dentro de um event loop em execução vai lançar um RuntimeError.

Melhores Práticas para Feedback Assíncrono

  1. Verifique o tipo de retorno: kickoff() retorna HumanFeedbackPending quando pausado—não precisa de try/except
  2. Use o método resume correto: Use resume() em código síncrono, await resume_async() em código assíncrono
  3. Armazene informações de callback: Use callback_info para armazenar URLs de webhook, IDs de tickets, etc.
  4. Implemente idempotência: Seu handler de resume deve ser idempotente por segurança
  5. Persistência automática: O estado é automaticamente salvo quando HumanFeedbackPending é lançado e usa SQLiteFlowPersistence por padrão
  6. Persistência customizada: Passe uma instância de persistência customizada para from_pending() se necessário

Aprendendo com Feedback

O parâmetro learn=True habilita um ciclo de feedback entre revisores humanos e o sistema de memória. Quando habilitado, o sistema melhora progressivamente suas saídas aprendendo com correções humanas anteriores.

Como Funciona

  1. Após o feedback: O LLM extrai lições generalizáveis da saída + feedback e as armazena na memória com source="hitl". Se o feedback for apenas aprovação (ex: “parece bom”), nada é armazenado.
  2. Antes da próxima revisão: Lições HITL passadas são recuperadas da memória e aplicadas pelo LLM para melhorar a saída antes que o humano a veja.
Com o tempo, o humano vê saídas pré-revisadas progressivamente melhores porque cada correção informa revisões futuras.

Exemplo

Code
Primeira execução: O humano vê a saída bruta e diz “Sempre inclua citações para afirmações factuais.” A lição é destilada e armazenada na memória. Segunda execução: O sistema recupera a lição sobre citações, pré-revisa a saída para adicionar citações e então mostra a versão melhorada. O trabalho do humano muda de “corrigir tudo” para “identificar o que o sistema deixou passar.”

Configuração

Decisões de Design Principais

  • Mesmo LLM para tudo: O parâmetro llm no decorador é compartilhado pelo mapeamento de outcome, destilação de lições e pré-revisão. Não é necessário configurar múltiplos modelos.
  • Saída estruturada: Tanto a destilação quanto a pré-revisão usam function calling com modelos Pydantic quando o LLM suporta, com fallback para parsing de texto caso contrário.
  • Armazenamento não-bloqueante: Lições são armazenadas via remember_many() que executa em uma thread em segundo plano — o flow continua imediatamente.
  • Degradação graciosa: Se o LLM falhar durante a destilação, nada é armazenado. Se falhar durante a pré-revisão, a saída bruta é mostrada. Nenhuma falha bloqueia o flow.
  • Sem escopo/categorias necessários: Ao armazenar lições, apenas source é passado. O pipeline de codificação infere escopo, categorias e importância automaticamente.
learn=True requer que o Flow tenha memória disponível. Flows obtêm memória automaticamente por padrão, mas se você a desabilitou com _skip_auto_memory, o aprendizado HITL será silenciosamente ignorado.

Documentação Relacionada