Pular para o conteúdo principal

Visão geral

Apps conversacionais tratam cada linha do usuário como uma nova execução do flow com o mesmo id de sessão. A CrewAI oferece helpers para histórico de mensagens, classificação opcional de intenção, tracing adiado, pontes para UI e um REPL local flow.chat() para flows conversacionais.

APIs de turno

Use flow.handle_turn(message, session_id=...) para cada mensagem de usuário em REST, WebSocket, testes e UIs customizadas. Use flow.chat() quando quiser um loop de chat local no terminal para um Flow conversacional. Flow.kickoff() não aceita os argumentos nomeados user_message= ou session_id=. Para flows conversacionais, handle_turn() guarda a mensagem pendente e chama kickoff(inputs={"id": session_id}) internamente.

Início rápido

Ciclo de vida do turno

Cada handle_turn executa este pipeline:
  1. _configure_conversational_kickoff — mescla session_id / user_message em inputs, aplica ConversationalConfig, habilita tracing adiado quando configurado.
  2. Restauração de estado — se inputs["id"] existe e @persist está configurado, carrega o snapshot mais recente.
  3. FlowStarted — emitido apenas no primeiro turno da sessão adiada.
  4. prepare_conversational_turn — acrescenta a mensagem do usuário em state.messages, define last_user_message, limpa last_intent, classifica opcionalmente quando intents / default_intents + intent_llm estão definidos.
  5. Execução do grafo@start@router → handlers @listen.
  6. Fim da execuçãoflow_finished por turno e finalização de trace são ignorados com adiamento; Agent.kickoff() / crews aninhados também não fecham o batch pai.
Os handlers devem chamar append_assistant_message(reply) para que o próximo turno inclua a resposta do assistente. A linha do usuário já é salva por handle_turn — não acrescente de novo nos handlers.

ConversationalConfig (padrões em nível de classe)

Defina na subclasse de Flow como conversational_config: ClassVar[ConversationalConfig | None]. Sobrescreva por kickoff com intents= e intent_llm=.

ChatState (formato persistido recomendado)

ConversationalInputs é um TypedDict para kickoff(inputs={...}): id, user_message, last_intent.

API conversacional em Flow

Parâmetros de kickoff / kickoff_async

Atributos de instância

Métodos e propriedades

Helpers do módulo (crewai.flow.conversation)

Importáveis para testes ou orquestração customizada:

Padrões de roteamento de intenção

A. Pré-classificar via ConversationalConfig (mais simples)

Defina default_intents e intent_llm. Cada kickoff classifica antes do @router; leia self.state.last_intent em route().

B. Classificar dentro do @router (prompts mais ricos)

Defina default_intents=None para o kickoff só acrescentar a mensagem. Em route(), chame classify_intent com prompt ou descrições customizadas:
Use @listen("RESEARCH") (ou similar) para passos com Agent.kickoff() e ferramentas — não LLM.call() puro — quando precisar de pesquisa web ou uso multi-etapa de tools.

Quando o flow termina mas o usuário continua conversando

FlowFinished significa que esta execução do grafo terminou. A conversa segue com outro kickoff e o mesmo session_id. @persist restaura messages, flags e contexto. Padrão de persistência: prefira @persist em um único passo terminal (por exemplo finalize) em vez de na classe Flow inteira. Persist em nível de classe salva após cada método; load_state usa a linha mais recente, que pode ser snapshot no meio da execução e perder atualizações dos handlers no mesmo turno. Não use @human_feedback para linhas de chat de follow-up, a menos que um humano precise aprovar uma saída específica antes de exibi-la.

Flow conversacional (experimental)

Funcionalidade experimental. A superfície do Flow conversacional (conversational = True, handle_turn, ConversationConfig, RouterConfig, ConversationState, o grafo embutido + helpers) vive em crewai.experimental e pode mudar de formato antes de graduar. Fixe a versão do CrewAI se depende de comportamento específico e acompanhe o changelog para mudanças quebradoras. Feedback / issues bem-vindos.
Habilite o grafo conversacional definindo conversational = True em uma subclasse de Flow. O Flow base passa a expor um grafo embutido @start / @router / converse_turn / end_conversation, gerencia state.messages, dirige o LLM de roteamento e mantém o batch de trace aberto entre os turnos. Você escreve as rotas customizadas; o framework cuida do resto. Use isto quando quiser um chat multi-turno com router LLM e handlers por rota sem cablar o ciclo de vida na mão. Use Flow[ChatState] (o padrão de mais baixo nível acima) quando precisar de controle total.

Exemplo rápido

Para um chat local no terminal, use chat():
chat() envolve handle_turn() em um REPL, sai com exit / quit, ignora linhas em branco por padrão e chama finalize_session_traces() quando a sessão termina.

ConversationConfig

Decorador de classe que anexa os defaults de chat por classe.

RouterConfig e o catálogo de rotas auto-gerado

O prompt do router é montado automaticamente. Para cada rota o framework escolhe a descrição nesta precedência:
  1. RouterConfig.route_descriptions[label] — override explícito.
  2. Flow.builtin_route_descriptions[label] — texto canônico do framework para converse, end, answer_from_history (otimizado para o LLM de routing).
  3. Primeira linha não vazia da docstring do handler @listen(label).
  4. Vazio (a rota aparece no catálogo sem descrição).
Na prática, adicionar uma rota é @listen("X") + uma docstring de uma linha:
…e o LLM de routing vê:
RouterConfig.prompt é para enquadramento de domínio (persona do assistente, regras de negócio, voz). O catálogo de rotas é auto-gerado — não liste rotas em prompt; elas vão sair de sincronia assim que você adicionar um handler.

Rotas embutidas

Você pode sobrescrever qualquer uma definindo um handler com o mesmo nome na subclasse.

Semântica de handle_turn()

flow.handle_turn(message) roda um turno:
  1. Reseta o tracking por execução (_completed_methods, _method_outputs) para o grafo re-rodar — sem isso, chamadas repetidas de kickoff na mesma instância dariam curto-circuito no turno 2+ porque Flow.kickoff_async trata inputs={"id": ...} como restauração de checkpoint.
  2. Anexa a mensagem do usuário em state.messages, define current_user_message / last_user_message. last_intent é preservado do turno anterior para que o LLM de routing possa usá-lo como sinal.
  3. Roda conversation_startroute_conversation → o handler @listen escolhido.
  4. O router grava sua decisão em state.last_intent (visível para o contexto de routing do próximo turno).
  5. Se seu handler retornou uma string e ainda não chamou append_assistant_message, handle_turn anexa para você.
Chame handle_turn() para mensagens de chat. Chamar kickoff(inputs={"id": ...}) diretamente executa o grafo sem aplicar o wrapper de turno conversacional.

chat() para REPLs locais

flow.chat() é o wrapper de terminal pronto para uso em cima de handle_turn():
Ele cobre o loop local comum:
  1. Solicita uma mensagem do usuário.
  2. Para com exit / quit, EOFError ou KeyboardInterrupt.
  3. Chama handle_turn(message, session_id=...).
  4. Imprime o resultado do assistente.
  5. Finaliza traces de sessão adiados em um bloco finally.
Customize o comportamento do terminal com I/O injetável:
Para apps web, workers em background, testes e transportes customizados, continue usando handle_turn() diretamente.

Comportamento customizado do router

Para rodar efeitos colaterais (setup de event bus, telemetria) em toda decisão de routing, sobrescreva route_turn:
Para ignorar o router LLM e escolher uma rota programaticamente, retorne uma string de route_turn; retornar None cai no _route_with_config(...).

append_assistant_message e append_agent_result

Dentro de um handler @listen(label), escolha:
  • self.append_assistant_message(text) — adiciona um turno de assistente visível ao usuário em state.messages. O converse_turn do próximo turno vai vê-lo.
  • self.append_agent_result(agent_name, result, visibility="private") — registra um evento estruturado em state.events e uma thread em state.agent_threads[agent_name]. Visibilidade pública também chama append_assistant_message automaticamente. Use resultados privados para trabalho de bastidor que não deve poluir o histórico canônico.
ConversationConfig.visible_agent_outputs pode promover globalmente os resultados privados de agentes específicos para públicos ("all" ou lista de nomes).

Tracing entre turnos

Com defer_trace_finalization=True (padrão em ConversationalConfig):
  • Um batch de trace para toda a sessão de chat.
  • flow_started só no primeiro turno; flow_finished uma vez em finalize_session_traces().
  • kickoff por turno não exibe “Trace batch finalized”.
  • Trabalho aninhado (Agent.kickoff(), crews, tools Exa) acrescenta ao batch pai; flows internos de AgentExecutor não fecham o batch da sessão cedo.
flow.chat() chama finalize_session_traces() para você. Quando você controla o loop com handle_turn() ou kickoff(...), chame finalize_session_traces() quando a sessão terminar. suppress_flow_events=True só oculta painéis do console; eventos de trace e método ainda são emitidos.

Ciclo de vida de trace do Flow conversacional

O Flow conversacional experimental usa o mesmo ciclo de vida de tracing: defer_trace_finalization é True por padrão, então cada handle_turn() mantém o trace da sessão aberto. Sempre finalize ao fim da sessão — envolva seu loop em try/finally e chame flow.finalize_session_traces() na saída. Sem isso, o batch fica aberto e a última conversa pode nunca ser exportada.

Streaming

Defina stream = True na classe Flow. kickoff(...) então emitirá assistant_delta (e eventos relacionados) pelo event bus padrão.

Imports

Veja também