> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crewai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Flows Conversacionais

> Crie apps de chat multi-turno com handle_turn por turno, histórico de mensagens, roteamento de intenção, tracing e streaming estruturado.

## 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, roteamento opcional de intenção, tracing adiado, streaming estruturado de turnos e um REPL local `flow.chat()`.

| Conceito                | Implementação                                                                                                       |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Id de sessão            | `handle_turn(..., session_id=...)` → `kickoff(inputs={"id": ...})` → `state.id`                                     |
| Linha do usuário        | `handle_turn(message)` acrescenta em `state.messages` antes do grafo rodar                                          |
| Turno concluído         | `conversation_turn_completed`; com o adiamento padrão de traces, `FlowFinished` aguarda `finalize_session_traces()` |
| Trace da sessão inteira | `ConversationConfig(defer_trace_finalization=True)` + `finalize_session_traces()`                                   |

## 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 depois de redefinir o estado de execução do turno.

| API                                    | Uso                                                                  |
| -------------------------------------- | -------------------------------------------------------------------- |
| `handle_turn(message, session_id=...)` | Wrapper ergonômico de um turno para `Flow` conversacional            |
| `stream_turn(message, session_id=...)` | Transmite um turno conversacional como frames ordenados do runtime   |
| `chat()`                               | REPL local no terminal para `Flow` conversacional                    |
| `kickoff(inputs={...})`                | Execução avançada do flow sem tratamento de turno conversacional     |
| `ask()`                                | Prompt bloqueante **dentro** de um passo (wizard, esclarecimento)    |
| `@human_feedback`                      | Aprovar/rejeitar **saída de um passo** — não a próxima linha do chat |

`handle_turn()`, `stream_turn()` e `chat()` geram `ValueError` se o modo conversacional não estiver habilitado. Aplicar `@ConversationConfig(...)` o habilita automaticamente; caso contrário, defina `conversational = True`.

## Início rápido

```python theme={null}
from uuid import uuid4

from crewai import Flow
from crewai.flow import listen
from crewai.flow import (
    ConversationConfig,
    ConversationState,
)


@ConversationConfig(defer_trace_finalization=True)
class SupportFlow(Flow[ConversationState]):
    def route_turn(self, context):
        message = (self.state.current_user_message or "").lower()
        if "order" in message:
            return "order"
        if "bye" in message or "goodbye" in message:
            return "goodbye"
        return "help"

    @listen("order")
    def handle_order(self):
        reply = "Your order is on the way."
        self.append_assistant_message(reply)
        return reply

    @listen("help")
    def handle_help(self):
        reply = "How can I help?"
        self.append_assistant_message(reply)
        return reply

    @listen("goodbye")
    def handle_goodbye(self):
        reply = "Goodbye!"
        self.append_assistant_message(reply)
        return reply


session_id = str(uuid4())
flow = SupportFlow()

try:
    flow.handle_turn("Where is my order?", session_id=session_id)
    flow.handle_turn("What about returns?", session_id=session_id)
finally:
    flow.finalize_session_traces()  # one trace link for the whole chat
```

## Streaming de um turno

Use `stream_turn()` quando uma UI ou um runtime precisar de eventos estruturados para um turno de chat. Ele retorna uma sessão de stream com frames ordenados para roteamento do Flow, chunks do LLM, atividade de tools e mensagens da conversa.

```python theme={null}
stream = flow.stream_turn("Where is my order?", session_id=session_id)

with stream:
    for frame in stream.events:
        if frame.channel == "llm" and frame.type == "llm_stream_chunk":
            print(frame.content, end="", flush=True)

result = stream.result
```

Para o contrato completo dos frames e a lista de canais, consulte [Contrato do Runtime de Streaming](/edge/pt-BR/learn/streaming-runtime-contract).

## Ciclo de vida do turno

Cada `handle_turn` executa este pipeline:

1. **Preparação do turno** — armazena a mensagem pendente do usuário, resolve o id da sessão, redefine o acompanhamento de execução por turno e chama `kickoff(inputs={"id": session_id})`.
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. **Hidratação do turno pendente** — acrescenta a mensagem do usuário em `state.messages`, define `current_user_message` / `last_user_message` e classifica opcionalmente quando `intents` / `default_intents` + `intent_llm` estão definidos.
5. **Execução do grafo** — métodos `@start` definidos pelo usuário (se houver) → `route_conversation` (o start/router embutido) → o handler `@listen` selecionado. `route_conversation` também chama o helper sobrescrevível `conversation_start()`.
6. **Fim da execução** — `flow_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)`** quando a resposta visível não for o valor de retorno, ou ao recortar o histórico. Um retorno de string pública também é gravado como assistente e entra no snapshot `@persist`, então uma nova instância de Flow o restaura. A linha do usuário já é salva por `handle_turn` — não acrescente de novo nos handlers.

## Visão geral da configuração

Decorar uma subclasse de `Flow` com `ConversationConfig` anexa os padrões de chat e habilita o modo conversacional. Consulte a [referência completa de campos](#conversationconfig) abaixo. Sobrescreva a pré-classificação por turno com `handle_turn(..., intents=..., intent_llm=...)`.

## Helpers `ChatState` de mais baixo nível

`ChatState`, o `ConversationalConfig` legado e os helpers de `crewai.flow.conversation` continuam disponíveis para importação em orquestração avançada, testes ou wrappers customizados. Eles são separados da API `ConversationState` / `ConversationConfig` e não adicionam os argumentos nomeados `user_message=` ou `session_id=` a `Flow.kickoff()`.

```python theme={null}
from crewai.flow import ChatState


class MyChatState(ChatState):
    # Herdados: id, messages, last_user_message, last_intent, session_ready
    research_turn_count: int = 0
    custom_flag: bool = False
```

| Campo               | Função                                             |
| ------------------- | -------------------------------------------------- |
| `id`                | UUID da sessão (igual a `inputs["id"]`)            |
| `messages`          | `list` de `{role, content}` para histórico de LLM  |
| `last_user_message` | Última linha do usuário neste turno                |
| `last_intent`       | Rótulo de rota após classificação (se usado)       |
| `session_ready`     | Flag de bootstrap único (permissões, caches, etc.) |

`ConversationalInputs` é um `TypedDict` para as chaves convencionais de `kickoff(inputs={...})`: `id`, `user_message`, `last_intent`.

O `ConversationState` armazena `messages` como objetos `ConversationMessage` e também fornece `current_user_message`, `ended`, `events` e `agent_threads`. Use `conversation_messages` ao passar seu histórico canônico para um LLM.

## API conversacional em `Flow`

### Parâmetros de `handle_turn`

| Parâmetro          | Propósito                                                                                                 |
| ------------------ | --------------------------------------------------------------------------------------------------------- |
| `message`          | Texto deste turno                                                                                         |
| `session_id`       | UUID da conversa → `inputs["id"]` / `state.id`                                                            |
| `intents`          | Rótulos de outcome para `classify_intent` antes do kickoff                                                |
| `intent_llm`       | LLM para classificação (obrigatório com `intents`)                                                        |
| `**kickoff_kwargs` | Encaminhados para `kickoff()` para opções como `input_files`, `from_checkpoint` e `restore_from_state_id` |

### Parâmetros de `kickoff`

`Flow.kickoff()` aceita `inputs`, `input_files`, `from_checkpoint` e `restore_from_state_id`. Passe `inputs={"id": session_id}` quando precisar executar o flow diretamente, mas use `handle_turn()` quando a chamada representar uma mensagem de chat.

### Atributos de instância

| Atributo                   | Propósito                                                                                                                                 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `conversational`           | Defina como `True` para habilitar o grafo conversacional e `handle_turn()`                                                                |
| `defer_trace_finalization` | Sobrescrita opcional na instância. Caso contrário, `_should_defer_trace_finalization()` lê `ConversationConfig.defer_trace_finalization`. |
| `suppress_flow_events`     | Oculta painéis do flow no console e suprime eventos de execução de métodos; os eventos de início/fim do flow continuam sendo emitidos     |
| `stream`                   | Flag genérica de streaming do Flow. Para turnos conversacionais, use `stream_turn()` em vez de combinar esta flag com `handle_turn()`.    |

### Métodos e propriedades

| Nome                                                     | Descrição                                                                      |
| -------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `append_assistant_message(content)`                      | Acrescenta uma resposta visível ao usuário em `state.messages`                 |
| `append_message(role, content, **extra)`                 | Acréscimo de mais baixo nível em `state.messages`                              |
| `conversation_messages`                                  | Histórico somente leitura para chamadas LLM                                    |
| `classify_intent(text, outcomes, *, llm, context=None)`  | Mapeia texto a um outcome (mesma lógica de `@human_feedback`)                  |
| `receive_user_message(text, *, outcomes=None, llm=None)` | Acrescenta mensagem do usuário; opcionalmente define `last_intent`             |
| `finalize_session_traces()`                              | Emite `flow_finished` adiado e finaliza o batch de trace da sessão             |
| `_should_defer_trace_finalization()`                     | Hook avançado/interno que resolve se a finalização de trace por turno é adiada |
| `input_history`                                          | Trilha de auditoria de prompts e respostas de `ask()`                          |

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

Importáveis de `crewai.flow.conversation` para testes ou orquestração customizada. Esses helpers usam o formato legado de `ConversationalConfig`; `prepare_conversational_turn()` também limpa `last_intent`, ao contrário do `handle_turn()`, que o preserva como contexto do router.

| Função                                                                                         | Descrição                                                          |
| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `normalize_kickoff_inputs(inputs, user_message=..., session_id=...)`                           | Mescla kwargs conversacionais em `inputs`                          |
| `get_conversation_messages(flow)`                                                              | Lê mensagens do estado ou buffer interno                           |
| `append_message(flow, role, content, **extra)`                                                 | Igual ao método de instância                                       |
| `prepare_conversational_turn(flow, user_message=..., intents=..., intent_llm=..., config=...)` | Hidratação de turno de mais baixo nível para wrappers customizados |
| `receive_user_message(flow, text, ...)`                                                        | Igual ao método de instância                                       |
| `set_state_field(flow, name, value)`                                                           | Define campo em estado dict ou Pydantic                            |
| `get_conversational_config(flow)`                                                              | Lê `conversational_config` da classe                               |
| `input_history_to_messages(entries)`                                                           | Converte `input_history` para formato de mensagens LLM             |

## Padrões de roteamento de intenção

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

Defina `default_intents` e `intent_llm`. Cada `handle_turn()` pré-classifica a mensagem atual. Um resultado não vazio retornado por um `route_turn()` customizado tem precedência; caso contrário, `route_conversation` usa a intenção classificada do turno atual.

### B. Classificar dentro de `route_turn` (prompts mais ricos)

Defina `default_intents=None` para `handle_turn()` apenas acrescentar a mensagem do usuário. Em `route_turn()`, chame `classify_intent` com um prompt ou descrições customizadas:

```python theme={null}
def route_turn(self, context):
    intent = self.classify_intent(
        self._routing_prompt(self.state.current_user_message),
        ("GREETING", "ORDER", "RESEARCH", "GOODBYE"),
        llm="gpt-4o-mini",
    )
    self.state.last_intent = intent
    return intent
```

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

Cada `handle_turn()` conclui uma execução do grafo, e a conversa continua com outro `handle_turn()` usando o mesmo `session_id`. Com o ciclo de vida de trace adiado padrão, essa execução emite `conversation_turn_completed`, enquanto `FlowFinished` é emitido uma vez quando `finalize_session_traces()` encerra a sessão. `@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

Habilite o grafo de chat conversacional definindo `conversational = True` em uma subclasse de `Flow` ou aplicando `@ConversationConfig(...)`. O `Flow` base passa a fornecer `route_conversation` como start/router embutido, além dos listeners `converse_turn` e `end_conversation`. O listener descontinuado `answer_from_history_turn` permanece disponível para compatibilidade. O framework gerencia `state.messages`, pode acionar um LLM de roteamento e mantém o batch de trace aberto entre turnos. Você escreve as **rotas customizadas**; o framework cuida do resto.

Use isto quando quiser um chat multi-turno com router 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

```python theme={null}
from crewai import Flow
from crewai.flow import listen
from crewai.flow import (
    ConversationConfig,
    ConversationState,
)


@ConversationConfig(defer_trace_finalization=True)
class SupportFlow(Flow[ConversationState]):
    def route_turn(self, context: dict) -> str | None:
        message = (self.state.current_user_message or "").lower()
        if "search" in message or "news" in message:
            return "INTERNET_SEARCH"
        if "docs" in message or "crewai" in message:
            return "CREWAI_DOCS"
        return "converse"

    @listen("INTERNET_SEARCH")
    def handle_internet_search(self) -> str:
        """Fresh web research, current news, real-time lookups."""
        reply = "I would run the web research route here."
        self.append_assistant_message(reply)
        return reply

    @listen("CREWAI_DOCS")
    def handle_crewai_docs(self) -> str:
        """Look up the CrewAI documentation for framework/API questions."""
        reply = "I would look up the CrewAI docs here."
        self.append_assistant_message(reply)
        return reply


flow = SupportFlow()
try:
    flow.handle_turn("What can you do?")              # routes to converse
    flow.handle_turn("Search the web for AI news.")   # routes to INTERNET_SEARCH
    flow.handle_turn("Check the CrewAI docs.")         # routes to CREWAI_DOCS
finally:
    flow.finalize_session_traces()
```

Para um chat local no terminal, use `chat()`:

```python theme={null}
def kickoff() -> None:
    SupportFlow().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.

| Campo                        | Padrão                                       | Propósito                                                                                                                                                                           |
| ---------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `system_prompt`              | `slices.conversational_system_prompt` (i18n) | System message usado pelo `converse_turn` embutido. Passe `""` para desativar totalmente.                                                                                           |
| `llm`                        | `None`                                       | LLM de conversa (usado pelo `converse_turn` e como fallback do router).                                                                                                             |
| `router`                     | `None`                                       | Sobrescritas opcionais de `RouterConfig`. Com listeners customizados e um LLM que possa ser resolvido, o roteamento é habilitado automaticamente mesmo quando este campo é omitido. |
| `answer_from_history_prompt` | padrão do framework                          | **Descontinuado.** Use o system prompt de `converse` ou sobrescreva `converse_turn()`.                                                                                              |
| `answer_from_history_llm`    | `None`                                       | **Descontinuado.** Use `llm`; `converse` já recebe o histórico canônico.                                                                                                            |
| `intent_llm`                 | `None`                                       | LLM para o caminho legado `intents=`/`default_intents`.                                                                                                                             |
| `default_intents`            | `None`                                       | Labels de outcome para pré-classificação legada.                                                                                                                                    |
| `visible_agent_outputs`      | `None`                                       | `"all"` ou lista de nomes de agentes cujos `append_agent_result()` devem virar mensagens públicas.                                                                                  |
| `defer_trace_finalization`   | `True`                                       | Mantém um único batch de trace aberto entre chamadas de `handle_turn()`.                                                                                                            |

<Warning>
  `answer_from_history_prompt`, `answer_from_history_llm` e a rota
  `answer_from_history` estão descontinuados e serão removidos em uma versão
  futura. Eles duplicam `converse`, que já recebe o histórico canônico,
  adicionam uma chamada de LLM para verificar elegibilidade e são ignorados
  quando o auto-router normal retorna uma rota. As configurações existentes
  continuam funcionando e emitem `DeprecationWarning`.
</Warning>

Sem rotas customizadas, os turnos caem em `converse`. Com rotas customizadas e um LLM de conversa/router, o framework sintetiza um `RouterConfig` padrão; forneça um explicitamente apenas para customizar seu prompt, lista de rotas, descrições ou comportamento de fallback. Definir `default_intents` usa o caminho legado de pré-classificação.

Se nenhum LLM de conversa estiver configurado, o `converse_turn` embutido retorna um placeholder de configuração em vez de gerar uma resposta.

### `RouterConfig` e o catálogo de rotas auto-gerado

```python theme={null}
from typing import Literal

from pydantic import BaseModel

from crewai import LLM
from crewai.flow import RouterConfig


class MyRoute(BaseModel):
    intent: Literal["INTERNET_SEARCH", "CREWAI_DOCS", "converse"]


ROUTER_LLM = LLM(model="gpt-4o-mini")


router_config = RouterConfig(
    prompt="Optional domain framing (policy, voice, persona).",
    response_format=MyRoute,        # optional; auto-generated otherwise
    llm=ROUTER_LLM,                  # falls back to ConversationConfig.llm
    routes=["INTERNET_SEARCH", "CREWAI_DOCS"],   # optional; inferred from listeners
    route_descriptions={
        "INTERNET_SEARCH": "Override the docstring for this one route.",
    },
    default_intent="converse",       # used when LLM call fails or no LLM available
    fallback_intent="converse",      # used when LLM returns an invalid route
    intent_field="intent",
)
```

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` e a rota de compatibilidade descontinuada `answer_from_history` (otimizado para o LLM de routing).
3. O `description` declarado do método (usado por flows declarativos e projeções da DSL).
4. Primeira linha não vazia da docstring do handler `@listen(label)`.
5. Vazio (a rota aparece no catálogo sem descrição).

Na prática, **adicionar uma rota é `@listen("X")` + uma docstring de uma linha**:

```python theme={null}
from crewai.flow import listen


@listen("INTERNET_SEARCH")
def handle_internet_search(self) -> str:
    """Fresh web research, current news, real-time lookups."""
    ...
```

…e o LLM de routing vê:

```
Routes:
- CREWAI_DOCS: Look up the CrewAI documentation for framework/API questions.
- INTERNET_SEARCH: Fresh web research, current news, real-time lookups.
- converse: Ordinary chat, follow-ups, summaries, clarifications…
- end: User signals the conversation is finished (goodbye, exit, done).
```

`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.

### Nomeando handlers

A string em `@listen("…")` é um **rótulo de rota do router** (um nome de evento), e não o nome do método Python. Rótulos de rota e eventos de conclusão de métodos compartilham o mesmo namespace de gatilhos; portanto, dar ao handler o mesmo nome de sua rota faria o handler acionar a si próprio em loop.

Use um nome de método diferente — os exemplos da documentação usam o prefixo `handle_*`:

```python theme={null}
@listen("create_video")
def handle_create_video(self) -> str:
    """User wants a new video."""
    ...
```

**Não** replique o rótulo da rota no método:

```python theme={null}
@listen("create_video")
def create_video(self) -> str:  # rejected at flow instantiation
    ...
```

### Rotas embutidas

| Rota                  | Handler                    | Propósito                                                                                        |
| --------------------- | -------------------------- | ------------------------------------------------------------------------------------------------ |
| `converse`            | `converse_turn`            | Handler de chat padrão. Chama `ConversationConfig.llm` com o system prompt + histórico canônico. |
| `end`                 | `end_conversation`         | Define `state.ended = True` e emite uma resposta de encerramento.                                |
| `answer_from_history` | `answer_from_history_turn` | **Rota de compatibilidade descontinuada.** Use `converse`, que já recebe o histórico canônico.   |

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. Executa métodos `@start` definidos pelo usuário (se houver), depois `route_conversation` como start/router embutido e, por fim, o handler `@listen` escolhido. `route_conversation` invoca o helper sobrescrevível `conversation_start()`.
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ê e persiste o `state.messages` atualizado para que a restauração `@persist` inclua o turno do assistente.

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()`:

```python theme={null}
flow = SupportFlow()
flow.chat()
```

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`.

`chat(defer_trace_finalization=True)` habilita temporariamente a flag de adiamento na instância durante o REPL e restaura o valor anterior ao sair.

Customize o comportamento do terminal com I/O injetável:

```python theme={null}
flow.chat(
    session_id="demo-session",
    prompt="You: ",
    assistant_prefix="Assistant: ",
    exit_commands=("exit", "quit", "bye"),
)
```

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`:

```python theme={null}
from typing import Any

from crewai import Flow
from crewai.flow import ConversationState


class SupportFlow(Flow[ConversationState]):
    conversational = True

    def route_turn(self, context: dict[str, Any]) -> str | None:
        self.event_bus = MyBus(self)
        return super().route_turn(context)
```

Para ignorar completamente o router LLM e escolher uma rota programaticamente, retorne uma string não vazia de `route_turn`. Um retorno falsy **não** invoca `_route_with_config()` a partir da sua sobrescrita; o roteamento segue para a intenção pré-classificada deste turno, depois para o caminho de compatibilidade descontinuado `answer_from_history` quando configurado e, por fim, para `converse`. O `last_intent` do turno anterior fica disponível no contexto do router, mas nunca é repetido como fallback.

### `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).

## Declarando um flow conversacional em JSON/YAML

Um [Flow declarativo](/edge/pt-BR/concepts/cli) também pode ser conversacional. Adicione um bloco `conversational` no nível raiz e declare suas próprias rotas como métodos que fazem `listen` em um rótulo de rota:

```yaml theme={null}
schema: crewai.flow/v1
name: SupportFlow

conversational:
  system_prompt: You are a terse support assistant.
  llm: gpt-4o-mini
  router:
    llm: gpt-4o-mini

methods:
  handle_order:
    description: Order status, shipping and delivery questions.
    listen: order
    do:
      call: agent
      with:
        role: Support specialist
        goal: Answer order questions accurately
        backstory: Knows the fulfilment pipeline.
        input: "${state.current_user_message}"
```

Declarar o bloco já é o opt-in — `enabled` tem valor padrão `true`. Use `enabled: false` para manter a configuração e desligar o chat. Isso também desabilita a síntese de métodos embutidos, portanto a declaração deve fornecer um grafo não conversacional normal.

Três coisas são fornecidas para você:

| Fornecido           | Detalhe                                                                                                                                                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| O grafo interno     | `route_conversation`, `converse_turn` e `end_conversation` são adicionados automaticamente. O `answer_from_history_turn` descontinuado é mantido para compatibilidade. Declare um método com um desses nomes para sobrescrevê-lo. |
| Estado da conversa  | `ConversationState` é usado quando não há bloco `state`. Um estado Pydantic definido por `ref` ou `json_schema` é composto automaticamente com os campos conversacionais; ele não precisa estender `ConversationState`.           |
| O catálogo de rotas | Inferido de métodos que não são routers e têm rótulos `listen`, excluindo rotas internas. As descrições seguem a precedência acima, e `router.routes` explícito pode limitar as opções.                                           |

Os campos declarativos `llm`, `router.llm` e `intent_llm` aceitam um id de modelo ou um mapping de configuração, como `{model: openai/gpt-4o-mini, max_tokens: 512}`. O bloco `conversational` também aceita `default_intents`, `visible_agent_outputs`, `defer_trace_finalization` e os campos de `RouterConfig` mostrados acima. As declarações descontinuadas `answer_from_history_prompt` / `answer_from_history_llm` continuam sendo aceitas para compatibilidade.

Execute a partir do Python com as mesmas APIs de turno de um Flow conversacional baseado em classe:

```python theme={null}
from crewai.flow import Flow

flow = Flow.from_declaration(path="flow.yaml")

try:
    flow.handle_turn("Where is my order?", session_id="session-1")
finally:
    flow.finalize_session_traces()
```

### Nomeando rotas

Rótulos de rota e nomes de métodos compartilham um único namespace de gatilhos, então um handler não pode ter o nome da rota que escuta — `create_video` escutando `create_video` é rejeitado na construção do flow. Use o prefixo `handle_*`.

### O que uma declaração não consegue expressar

| Não expressável                                      | Use no lugar                                                                                                                            |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Uma instância `LLM` viva ou um `BaseLLM` customizado | Um id de modelo em string ou mapping estático de configuração                                                                           |
| `router.response_format` como classe de modelo viva  | Nomeie a classe com um ref python: `response_format: {python: my_project.schemas.ConversationRoute}`. Omita e o framework sintetiza uma |
| Uma sobrescrita de `route_turn()`                    | Escreva o Flow em Python ou substitua o método declarativo `route_conversation` por uma ação `call: code` / expressão                   |
| Uma sobrescrita de `can_answer_from_history()`       | Descontinuado. Use `converse` ou sobrescreva `converse_turn()` no Python.                                                               |

O `crewai run` abre a TUI de chat para um flow conversacional declarativo — a mesma que um Flow conversacional em Python recebe. Um loop de chat precisa de um terminal, então uma execução headless encerra com código diferente de zero e orientações, em vez de rodar um único turno; ali, conduza pelo Python com `handle_turn()` ou `stream_turn()`. Um método declarativo com um bloco `human_feedback:` (Python: `@human_feedback`) roda em um REPL de terminal, porque o runtime coleta feedback com um prompt bloqueante que a TUI não consegue atender. O `--inputs` não é aceito em um flow conversacional — a entrada de cada turno é a mensagem que você digita — e retomar uma sessão por id ainda não está ligado à CLI; use `flow.handle_turn(message, session_id=...)` no Python para isso.

## Tracing entre turnos

Com `defer_trace_finalization=True` (padrão em `ConversationConfig`):

* **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.

```python theme={null}
flow.chat(session_id=session_id)
```

`flow.chat()` chama `finalize_session_traces()` para você. Quando você controla o loop com `handle_turn()`, chame `finalize_session_traces()` quando a sessão terminar.

`suppress_flow_events=True` oculta painéis Rich no console e suprime eventos de execução de métodos. Os eventos de início/fim do Flow continuam sendo emitidos, portanto o ciclo de vida externo do Flow permanece rastreável, mas os spans de métodos individuais são omitidos.

### Ciclo de vida de trace do `Flow` conversacional

O [`Flow` conversacional](#flow-conversacional) 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. Turnos adiados também suprimem `flow_failed` por turno; em caso de erro em um turno ou encerramento antecipado da sessão, finalize a sessão explicitamente. Isso fecha o batch com o evento `FlowFinished` no nível da sessão, em vez de um evento `FlowFailed` por turno. Sempre envolva seu REPL/loop em `try/finally` e chame `flow.finalize_session_traces()` na saída. Sem isso, o batch fica aberto e a conversa final pode nunca ser exportada.

## Streaming

Para UIs conversacionais, use `stream_turn()` e itere sobre seus objetos `StreamFrame` ordenados:

```python theme={null}
stream = flow.stream_turn("Where is my order?", session_id=session_id)

with stream:
    for frame in stream.events:
        if frame.channel == "llm" and frame.type == "llm_stream_chunk":
            print(frame.content, end="", flush=True)

reply = stream.result
```

Para um Flow não conversacional, definir `stream = True` faz `kickoff()` retornar uma `StreamSession`. Não defina `flow.stream = True` ao usar `handle_turn()`; `stream_turn()` controla o ciclo de vida do streaming conversacional.

## Imports

```python theme={null}
from crewai.flow import (
    ChatState,
    ConversationalConfig,
    ConversationalInputs,
    Flow,
    listen,
    persist,
    router,
    start,
)
from crewai.flow.conversation import prepare_conversational_turn
from crewai.flow import (
    ConversationConfig,
    ConversationState,
    RouterConfig,
)
```

## Veja também

* [Dominando o Gerenciamento de Estado em Flows](/pt-BR/guides/flows/mastering-flow-state) — persistência, estado Pydantic, `@persist`
* [Construa Seu Primeiro Flow](/pt-BR/guides/flows/first-flow) — fundamentos de flow
