Skip to main content

Agent de Sinistro Automotivo

Esta jornada usa um case realista de atendimento para abertura de sinistro automotivo em formato menos guiado. A proposta é que você implemente partes importantes por conta própria e valide o resultado no Agent Builder.

Ao longo do conteúdo, cada desafio traz:

  • objetivo técnico
  • critérios de aceitação
  • dicas de implementação
  • solução expandível
Escopo desta jornada

Você vai construir um fluxo completo com Agent + Skills + Actions

Resultado esperado ao final

Ao concluir todos os desafios, você terá:

  • 1 Agent Conversacional configurado no Builder
  • Skills em YAML para triagem, coleta, fechamento e exceções
  • Actions em Python com ResponseToUser e ResponseToAgent
  • Uso de state_manager e memory_manager para controle de fluxo e contexto
  • Fluxo validado ponta a ponta em cenários de sucesso e exceção

Criar o Agent

Desafio 1: Definir instruções gerais de forma objetiva

Objetivo: escrever as instruções do agent para triar e conduzir abertura de sinistro auto com tom empático e limites claros.

Critérios de aceitação:

  • delimita escopo (somente sinistro automotivo)
  • define comportamento para fora de escopo
  • orienta linguagem clara e acolhedora
  • evita instruções longas e ambíguas

Dica: instruções curtas e diretas tendem a funcionar melhor do que textos muito extensos.

Ver solução sugerida
Você é uma assistente virtual especializada em abertura de sinistro automotivo.

Objetivo:
- Coletar os dados necessários da ocorrência com clareza e empatia.
- Conduzir o cliente até a confirmação final da abertura.

Regras:
- Atue apenas em assuntos relacionados a sinistro automotivo.
- Se o tema for fora de escopo, ofereça transferência para atendimento humano.
- Não invente informações e não deduza dados críticos sem confirmação.
- Faça uma pergunta por vez quando faltar informação obrigatória.
- Sempre mantenha linguagem simples, objetiva e acolhedora.

Criar Skills

Desafio 2: Modelar a skill de triagem e roteamento inicial

Objetivo: criar uma skill que classifique o tipo de atendimento inicial e direcione o próximo passo.

Critérios de aceitação:

  • skill identifica se é segurado, terceiro ou frota
  • usa ação customizada para validar perfil
  • prevê saída para atendimento humano quando necessário
  • cita system actions por colchetes quando aplicável
Ver solução sugerida
instructions: |
Realize a triagem inicial para abertura de sinistro automotivo.
Confirme o perfil de atendimento antes de avançar para coleta detalhada.

topics:
- Triagem Inicial:
description: Identifica o perfil e direciona o fluxo correto.
steps:
- Coletar Perfil: Pergunte se o atendimento é para segurado, terceiro ou frota.
- Validar Perfil: Use [validar_perfil_atendimento] para validar e classificar o perfil.
- Próximo Passo:
- Se perfil for segurado, redirecione para a skill "Coleta da Ocorrência" com [redirecionar_habilidade].
- Se perfil exigir atendimento especializado, ofereça [transferir_agente_humano].

Desafio 3: Modelar a skill de coleta progressiva da ocorrência

Objetivo: criar um fluxo menos engessado, mas com campos obrigatórios e progresso verificável.

Campos obrigatórios sugeridos:

  • data da ocorrência
  • hora aproximada
  • local da ocorrência
  • breve descrição do ocorrido
  • veículo envolvido (placa ou modelo)
  • existência de terceiros (sim/não)

Critérios de aceitação:

  • skill chama action para registrar cada dado
  • skill chama action para revisar pendências
  • skill evita perguntar houve_terceiros quando a descrição já indicar envolvimento de outro veículo/pessoa
  • avança para resumo quando não houver pendências
Ver solução sugerida
instructions: |
Conduza a coleta da ocorrência em pequenos passos.
Registre cada informação recebida e revise pendências com frequência.

topics:
- Coleta da Ocorrência:
description: Coleta e valida dados essenciais para abertura do sinistro.
steps:
- Coletar Dado: Solicite apenas um campo por vez.
- Registrar Dado: Sempre que o usuário informar um campo, use [registrar_dado_ocorrencia].
- Revisar Pendências: Use [revisar_pendencias_ocorrencia] para descobrir próximos campos obrigatórios.
- Regra de Terceiros: Se a revisão inferir `houve_terceiros=sim` pela descrição do acidente, não pergunte novamente sobre terceiros e siga para o próximo campo faltante.
- Transição para Resumo: Quando não houver pendências, redirecione para "Resumo e Conclusão" com [redirecionar_habilidade].

Desafio 4: Criar a skill de resumo e conclusão

Objetivo: consolidar dados, confirmar com o usuário e encerrar com ação de sistema.

Critérios de aceitação:

  • skill chama action de resumo
  • skill pede confirmação explícita
  • em confirmação positiva, usa [finalizar_conversa]
  • em negativa, retorna para coleta
Ver solução sugerida
instructions: |
Apresente resumo final de forma clara, confirme com o usuário e conclua o atendimento.

topics:
- Resumo e Conclusão:
description: Consolida os dados da ocorrência e finaliza o atendimento.
steps:
- Gerar Resumo: Use [gerar_resumo_ocorrencia].
- Confirmar Dados: Pergunte se os dados estão corretos para concluir a abertura.
- Se Confirmado: Use [finalizar_conversa] para encerrar cordialmente.
- Se Não Confirmado: Redirecione para "Coleta da Ocorrência" com [redirecionar_habilidade].

Criar Actions

Desafio 5: Implementar validação de perfil de atendimento

Nome da action: validar_perfil_atendimento

Parâmetros sugeridos:

NomeTipoObrigatórioDescrição
perfilTextoSimPerfil informado pelo usuário na triagem

Critérios de aceitação:

  • normaliza entradas equivalentes (ex: "cliente", "segurado")
  • define próximo passo no instruction
  • salva perfil no state_manager
Ver solução sugerida
def validar_perfil_atendimento(perfil: str):
perfil_normalizado = (perfil or "").strip().lower()

mapa = {
"segurado": "segurado",
"cliente": "segurado",
"terceiro": "terceiro",
"frota": "frota",
}

perfil_classificado = mapa.get(perfil_normalizado)

if not perfil_classificado:
return ResponseToAgent(
instruction=(
"Perfil não reconhecido. Solicite que o usuário informe "
"uma das opções: segurado, terceiro ou frota."
)
)

state_manager.update("sinistro.perfil", perfil_classificado)

if perfil_classificado == "segurado":
return ResponseToAgent(
instruction=(
"Perfil segurado validado. Redirecione para a skill "
"'Coleta da Ocorrência' e inicie a coleta dos campos obrigatórios."
)
)

if perfil_classificado == "terceiro":
return ResponseToUser(
instruction=(
"Perfil terceiro identificado. Informado canal genérico "
"de abertura para terceiros."
),
message=(
"Para abertura de sinistro por terceiro, utilize o canal digital "
"de terceiros da sua operação. Se preferir, posso transferir você "
"para um especialista."
)
)

return ResponseToUser(
instruction="Perfil frota identificado e orientação de canal genérico enviada.",
message=(
"Para abertura por frota, utilize o canal digital de frota "
"da sua operação. Se preferir, posso transferir para um especialista."
)
)

Desafio 6: Implementar registro de dados da ocorrência

Nome da action: registrar_dado_ocorrencia

Parâmetros sugeridos:

NomeTipoObrigatórioDescrição
campoTextoSimNome do campo coletado (data_ocorrencia ou hora_ocorrencia, local_ocorrencia ou descricao_ocorrencia ou veiculo_envolvido ou houve_terceiros)
valorTextoSimValor informado pelo usuário

Critérios de aceitação:

  • valida presença de campo e valor
  • normaliza nome de campo
  • persiste em memory_manager
  • registra progresso em state_manager
Ver solução sugerida
from datetime import datetime


def registrar_dado_ocorrencia(campo: str, valor: str):
campo_input = (campo or "").strip().lower()
valor_input = (valor or "").strip()

if not campo_input or not valor_input:
return ResponseToAgent(
instruction=(
"Campo ou valor ausente. Solicite novamente um campo válido "
"e o respectivo valor."
)
)

aliases = {
"data": "data_ocorrencia",
"data_ocorrido": "data_ocorrencia",
"hora": "hora_ocorrencia",
"local": "local_ocorrencia",
"descricao": "descricao_ocorrencia",
"veiculo": "veiculo_envolvido",
"terceiros": "houve_terceiros",
}
campo_normalizado = aliases.get(campo_input, campo_input)

memory_manager.update(f"sinistro.dados.{campo_normalizado}", valor_input)
state_manager.update("sinistro.ultima_atualizacao", datetime.utcnow().isoformat())

return ResponseToAgent(
instruction=(
f"Campo '{campo_normalizado}' registrado com sucesso no contexto do sinistro. "
"Use revisar_pendencias_ocorrencia para decidir o próximo passo."
)
)

Desafio 7: Implementar revisão de pendências e resumo final

Nome das actions:

  • revisar_pendencias_ocorrencia
  • gerar_resumo_ocorrencia

Critérios de aceitação:

  • lista campos faltantes de forma determinística
  • atualiza etapa do workflow
  • resumo final legível para confirmação do usuário
Ver solução sugerida
CAMPOS_OBRIGATORIOS = [
"data_ocorrencia",
"hora_ocorrencia",
"local_ocorrencia",
"descricao_ocorrencia",
"veiculo_envolvido",
"houve_terceiros",
]


def inferir_terceiros_por_descricao(descricao: str) -> bool:
descricao_normalizada = (descricao or "").strip().lower()
if not descricao_normalizada:
return False

gatilhos_terceiros = [
"outro veiculo",
"outro veículo",
"veiculo terceiro",
"veículo terceiro",
"terceiro",
"terceiros",
"foi atingido",
"atingido na traseira",
"colidiu com",
"colisao com",
"colisão com",
"bateu em",
]
return any(gatilho in descricao_normalizada for gatilho in gatilhos_terceiros)


def revisar_pendencias_ocorrencia():
dados = memory_manager.get("sinistro.dados", default={}) or {}

if not dados.get("houve_terceiros") and inferir_terceiros_por_descricao(
str(dados.get("descricao_ocorrencia", ""))
):
memory_manager.update("sinistro.dados.houve_terceiros", "sim")
state_manager.update("sinistro.houve_terceiros_inferido", True)
dados["houve_terceiros"] = "sim"

faltantes = [campo for campo in CAMPOS_OBRIGATORIOS if not dados.get(campo)]
state_manager.update("sinistro.campos_faltantes", faltantes)

if faltantes:
memory_manager.store_workflow_progress(
"abertura_sinistro_auto",
"coleta_em_andamento",
{"faltantes": faltantes}
)
return ResponseToAgent(
instruction=(
"Ainda existem campos pendentes para conclusão da abertura: "
+ ", ".join(faltantes)
+ ". Solicite o próximo campo faltante ao usuário."
)
)

memory_manager.store_workflow_progress(
"abertura_sinistro_auto",
"coleta_concluida",
{"faltantes": []}
)
return ResponseToAgent(
instruction=(
"Todos os campos obrigatórios foram coletados. "
"Agora gere o resumo com a action gerar_resumo_ocorrencia."
)
)


def gerar_resumo_ocorrencia():
dados = memory_manager.get("sinistro.dados", default={}) or {}

if not dados:
return ResponseToAgent(
instruction=(
"Não há dados suficientes para resumo. "
"Retome a coleta de dados da ocorrência."
)
)

resumo = (
"Resumo da ocorrência:\n"
f"- Data: {dados.get('data_ocorrencia', 'não informado')}\n"
f"- Hora: {dados.get('hora_ocorrencia', 'não informado')}\n"
f"- Local: {dados.get('local_ocorrencia', 'não informado')}\n"
f"- Veículo: {dados.get('veiculo_envolvido', 'não informado')}\n"
f"- Houve terceiros: {dados.get('houve_terceiros', 'não informado')}\n"
f"- Descrição: {dados.get('descricao_ocorrencia', 'não informado')}"
)

memory_manager.update("sinistro.resumo_final", resumo)

return ResponseToUser(
instruction=(
"Resumo final gerado e apresentado ao usuário. "
"Solicitar confirmação para concluir abertura."
),
message=(
f"{resumo}\n\n"
"Os dados estão corretos para concluir a abertura do sinistro?"
)
)

Kit final de implementação

Use este kit quando quiser partir de uma base funcional completa.

1) Instruções gerais do Agent

Você é uma assistente virtual especializada em abertura de sinistro automotivo.
Atenda com empatia, objetividade e foco na coleta correta de dados.

Escopo:
- Abrir sinistro automotivo para segurado.
- Coletar, validar e resumir dados da ocorrência.

Regras:
- Não invente informações.
- Solicite um dado por vez quando houver pendências.
- Em assuntos fora de escopo, ofereça transferência para atendimento humano.
- Após resumo e confirmação positiva, conclua o atendimento com [finalizar_conversa].

2) Skills (YAML)

instructions: |
Gerencie abertura de sinistro automotivo em três etapas: triagem, coleta e conclusão.

topics:
- Triagem Inicial:
description: Identifica perfil de atendimento e direciona o fluxo.
steps:
- Coletar Perfil: Pergunte se o atendimento é para segurado, terceiro ou frota.
- Validar Perfil: Use [validar_perfil_atendimento].
- Direcionar:
- Se segurado: redirecione para "Coleta da Ocorrência" com [redirecionar_habilidade].
- Se necessário: ofereça [transferir_agente_humano].

- Coleta da Ocorrência:
description: Coleta e valida dados obrigatórios da ocorrência.
steps:
- Coletar Campo: Solicite um campo por vez.
- Registrar Campo: Use [registrar_dado_ocorrencia] para salvar cada informação.
- Revisar Pendências: Use [revisar_pendencias_ocorrencia] para decidir o próximo campo.
- Regra de Terceiros: Se a action inferir terceiros pela descrição, não repita a pergunta e continue para o próximo campo faltante.
- Concluir Coleta: Quando não houver pendências, redirecione para "Resumo e Conclusão" com [redirecionar_habilidade].

- Resumo e Conclusão:
description: Consolida o resumo, confirma com o usuário e finaliza.
steps:
- Gerar Resumo: Use [gerar_resumo_ocorrencia].
- Confirmar: Pergunte se os dados estão corretos para concluir a abertura.
- Encerrar:
- Se confirmado: use [finalizar_conversa].
- Se não confirmado: redirecione para "Coleta da Ocorrência" com [redirecionar_habilidade].

3) Actions (Python)

from datetime import datetime


CAMPOS_OBRIGATORIOS = [
"data_ocorrencia",
"hora_ocorrencia",
"local_ocorrencia",
"descricao_ocorrencia",
"veiculo_envolvido",
"houve_terceiros",
]


def inferir_terceiros_por_descricao(descricao: str) -> bool:
descricao_normalizada = (descricao or "").strip().lower()
if not descricao_normalizada:
return False

gatilhos_terceiros = [
"outro veiculo",
"outro veículo",
"veiculo terceiro",
"veículo terceiro",
"terceiro",
"terceiros",
"foi atingido",
"atingido na traseira",
"colidiu com",
"colisao com",
"colisão com",
"bateu em",
]
return any(gatilho in descricao_normalizada for gatilho in gatilhos_terceiros)


def validar_perfil_atendimento(perfil: str):
perfil_normalizado = (perfil or "").strip().lower()
mapa = {
"segurado": "segurado",
"cliente": "segurado",
"terceiro": "terceiro",
"frota": "frota",
}
perfil_classificado = mapa.get(perfil_normalizado)

if not perfil_classificado:
return ResponseToAgent(
instruction="Perfil não reconhecido. Solicite: segurado, terceiro ou frota."
)

state_manager.update("sinistro.perfil", perfil_classificado)

if perfil_classificado == "segurado":
return ResponseToAgent(
instruction=(
"Perfil segurado validado. Redirecione para a skill "
"'Coleta da Ocorrência'."
)
)

if perfil_classificado == "terceiro":
return ResponseToUser(
instruction="Perfil terceiro identificado e orientação de canal genérico enviada.",
message=(
"Para abertura por terceiro, utilize o canal digital de terceiros "
"da sua operação. Se preferir, posso transferir para um especialista."
)
)

return ResponseToUser(
instruction="Perfil frota identificado e orientação de canal genérico enviada.",
message=(
"Para abertura por frota, utilize o canal digital de frota "
"da sua operação. Se preferir, posso transferir para um especialista."
)
)


def registrar_dado_ocorrencia(campo: str, valor: str):
campo_input = (campo or "").strip().lower()
valor_input = (valor or "").strip()

if not campo_input or not valor_input:
return ResponseToAgent(
instruction="Campo ou valor ausente. Solicite novamente o dado."
)

aliases = {
"data": "data_ocorrencia",
"hora": "hora_ocorrencia",
"local": "local_ocorrencia",
"descricao": "descricao_ocorrencia",
"veiculo": "veiculo_envolvido",
"terceiros": "houve_terceiros",
}
campo_normalizado = aliases.get(campo_input, campo_input)

memory_manager.update(f"sinistro.dados.{campo_normalizado}", valor_input)
state_manager.update("sinistro.ultima_atualizacao", datetime.utcnow().isoformat())

return ResponseToAgent(
instruction=(
f"Campo '{campo_normalizado}' registrado com sucesso. "
"Use revisar_pendencias_ocorrencia para decidir o próximo passo."
)
)


def revisar_pendencias_ocorrencia():
dados = memory_manager.get("sinistro.dados", default={}) or {}

if not dados.get("houve_terceiros") and inferir_terceiros_por_descricao(
str(dados.get("descricao_ocorrencia", ""))
):
memory_manager.update("sinistro.dados.houve_terceiros", "sim")
state_manager.update("sinistro.houve_terceiros_inferido", True)
dados["houve_terceiros"] = "sim"

faltantes = [campo for campo in CAMPOS_OBRIGATORIOS if not dados.get(campo)]

state_manager.update("sinistro.campos_faltantes", faltantes)

if faltantes:
memory_manager.store_workflow_progress(
"abertura_sinistro_auto",
"coleta_em_andamento",
{"faltantes": faltantes}
)
return ResponseToAgent(
instruction=(
"Há pendências na coleta: "
+ ", ".join(faltantes)
+ ". Solicite o próximo campo faltante."
)
)

memory_manager.store_workflow_progress(
"abertura_sinistro_auto",
"coleta_concluida",
{"faltantes": []}
)
return ResponseToAgent(
instruction=(
"Todos os campos obrigatórios foram coletados. "
"Agora execute gerar_resumo_ocorrencia."
)
)


def gerar_resumo_ocorrencia():
dados = memory_manager.get("sinistro.dados", default={}) or {}

if not dados:
return ResponseToAgent(
instruction="Sem dados suficientes para resumo. Retome a coleta."
)

resumo = (
"Resumo da ocorrência:\n"
f"- Data: {dados.get('data_ocorrencia', 'não informado')}\n"
f"- Hora: {dados.get('hora_ocorrencia', 'não informado')}\n"
f"- Local: {dados.get('local_ocorrencia', 'não informado')}\n"
f"- Veículo: {dados.get('veiculo_envolvido', 'não informado')}\n"
f"- Houve terceiros: {dados.get('houve_terceiros', 'não informado')}\n"
f"- Descrição: {dados.get('descricao_ocorrencia', 'não informado')}"
)

memory_manager.update("sinistro.resumo_final", resumo)

return ResponseToUser(
instruction="Resumo final exibido ao usuário para confirmação.",
message=(
f"{resumo}\n\n"
"Os dados estão corretos para concluir a abertura do sinistro?"
)
)

4) Mapa de vínculos skill-action

  • Skill Triagem Inicial:
    • validar_perfil_atendimento
    • redirecionar_habilidade
    • transferir_agente_humano
  • Skill Coleta da Ocorrência:
    • registrar_dado_ocorrencia
    • revisar_pendencias_ocorrencia
    • redirecionar_habilidade
  • Skill Resumo e Conclusão:
    • gerar_resumo_ocorrencia
    • finalizar_conversa
    • redirecionar_habilidade

Casos de teste para validação

Use os casos abaixo para validar o comportamento do agent no Builder ao final da implementação.

Case 1: Segurado (fluxo completo com conclusão)

Objetivo: validar triagem de segurado, coleta progressiva, inferência de terceiros pela descrição, resumo e conclusão.

Resultado esperado:

  • o agent inicia coleta de dados obrigatórios em pequenos passos
  • houve_terceiros é assumido como sim a partir da descrição (sem nova pergunta específica sobre terceiros)
  • o resumo final apresenta os campos coletados e pede confirmação
  • após confirmação, o agent conclui a abertura do sinistro com sucesso

Case 2: Frota (roteamento para canal especializado)

Objetivo: validar triagem com perfil frota e encaminhamento correto sem iniciar coleta detalhada.

Resultado esperado:

  • o agent identifica perfil de frota na triagem
  • responde com orientação de uso do canal digital de frota
  • oferece possibilidade de transferência para especialista
  • encerra de forma cordial, sem coletar campos da ocorrência

Case 3: Terceiro (roteamento para canal especializado)

Objetivo: validar triagem com perfil terceiro e encaminhamento correto sem iniciar coleta detalhada.

Resultado esperado:

  • o agent identifica perfil de terceiro na triagem
  • responde com orientação de uso do canal digital de terceiros
  • oferece possibilidade de transferência para especialista
  • encerra de forma cordial, sem coletar campos da ocorrência