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 localflow.chat() para flows conversacionais.
APIs de turno
Useflow.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
Cadahandle_turn executa este pipeline:
_configure_conversational_kickoff— mesclasession_id/user_messageeminputs, aplicaConversationalConfig, habilita tracing adiado quando configurado.- Restauração de estado — se
inputs["id"]existe e@persistestá configurado, carrega o snapshot mais recente. FlowStarted— emitido apenas no primeiro turno da sessão adiada.prepare_conversational_turn— acrescenta a mensagem do usuário emstate.messages, definelast_user_message, limpalast_intent, classifica opcionalmente quandointents/default_intents+intent_llmestão definidos.- Execução do grafo —
@start→@router→ handlers@listen. - Fim da execução —
flow_finishedpor turno e finalização de trace são ignorados com adiamento;Agent.kickoff()/ crews aninhados também não fecham o batch pai.
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:
@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)
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
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
RouterConfig.route_descriptions[label]— override explícito.Flow.builtin_route_descriptions[label]— texto canônico do framework paraconverse,end,answer_from_history(otimizado para o LLM de routing).- Primeira linha não vazia da docstring do handler
@listen(label). - Vazio (a rota aparece no catálogo sem descrição).
@listen("X") + uma docstring de uma linha:
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:
- Reseta o tracking por execução (
_completed_methods,_method_outputs) para o grafo re-rodar — sem isso, chamadas repetidas dekickoffna mesma instância dariam curto-circuito no turno 2+ porqueFlow.kickoff_asynctratainputs={"id": ...}como restauração de checkpoint. - Anexa a mensagem do usuário em
state.messages, definecurrent_user_message/last_user_message.last_intenté preservado do turno anterior para que o LLM de routing possa usá-lo como sinal. - Roda
conversation_start→route_conversation→ o handler@listenescolhido. - O router grava sua decisão em
state.last_intent(visível para o contexto de routing do próximo turno). - Se seu handler retornou uma string e ainda não chamou
append_assistant_message,handle_turnanexa para você.
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():
- Solicita uma mensagem do usuário.
- Para com
exit/quit,EOFErrorouKeyboardInterrupt. - Chama
handle_turn(message, session_id=...). - Imprime o resultado do assistente.
- Finaliza traces de sessão adiados em um bloco
finally.
handle_turn() diretamente.
Comportamento customizado do router
Para rodar efeitos colaterais (setup de event bus, telemetria) em toda decisão de routing, sobrescrevaroute_turn:
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 emstate.messages. Oconverse_turndo próximo turno vai vê-lo.self.append_agent_result(agent_name, result, visibility="private")— registra um evento estruturado emstate.eventse uma thread emstate.agent_threads[agent_name]. Visibilidade pública também chamaappend_assistant_messageautomaticamente. 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
Comdefer_trace_finalization=True (padrão em ConversationalConfig):
- Um batch de trace para toda a sessão de chat.
flow_startedsó no primeiro turno;flow_finisheduma vez emfinalize_session_traces().kickoffpor turno não exibe “Trace batch finalized”.- Trabalho aninhado (
Agent.kickoff(), crews, tools Exa) acrescenta ao batch pai; flows internos deAgentExecutornã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
Definastream = True na classe Flow. kickoff(...) então emitirá assistant_delta (e eventos relacionados) pelo event bus padrão.
Imports
Veja também
- Dominando o Gerenciamento de Estado em Flows — persistência, estado Pydantic,
@persist - Construa Seu Primeiro Flow — fundamentos de flow
- Demo:
lib/crewai/runner_conversational_flow_simple.py— REPL mínimo comRESEARCH+ agente Exa
