Pular para conteúdo

Replay de conversas

Uma fixture é um roteiro JSON com mensagens do cliente, respostas brutas do LLM e invariantes esperadas. O runner reproduz a aplicação com essas respostas, permitindo investigar regressões de orquestração sem custo de geração nem variabilidade do modelo.

Implementação e execução

tests/integration/fixture_replay.py descobre todos os *.json em tests/fixtures/conversations/, ordena arquivos e cria Fixture. replay() injeta MagicMock como agent_invoker e como LLM do resumo; CRM, eGestor e handoff usam stubs, WhatsApp usa mock e repositories usam a tabela moto. test_fixture_replay.py parametriza um teste por nome de fixture.

python -m pytest tests/integration/test_fixture_replay.py -v --no-cov
python -m pytest tests/integration/test_fixture_replay.py -k TC-14 -v --no-cov

A dashboard local também oferece Scenarios. _run_one_fixture() cria uma tabela única para a rodada e a remove em finally, usando o contexto moto já aberto por scripts/dashboard.py. O helper não abre moto sozinho: não exponha a UI de desenvolvimento diretamente em AWS real para executar cenários.

Formato mínimo fictício

{
  "name": "TC-doc-reclamacao",
  "description": "Reclamacao encaminhada ao atendimento humano.",
  "phone_number": "+5500000000000",
  "instance": "gogenetic",
  "turns": [
    {
      "cliente": "Tenho uma reclamacao sobre meu atendimento.",
      "llm_responses": [
        "{\"s\":3,\"intent\":\"reclamacao\",\"handoff_reason\":\"reclamacao\"}Vou encaminhar voce para a equipe."
      ]
    }
  ],
  "expected": {
    "state": "HANDOFF_PENDING",
    "handoff_reason": "reclamacao"
  }
}

llm_responses é uma lista por turno porque Triagem pode encadear Qualificação na mesma mensagem, s=99 pode solicitar contexto, e reparos podem exigir novas chamadas. O runner achata todas as respostas na ordem dos turnos e as entrega por side_effect.

Asserções implementadas

Chave em expected Verificação
state session.state.value da sessão ativa
service_identified Nome do serviço na sessão
segment Valor do segmento, exigindo que exista
handoff_reason Motivo, exigindo que exista
demands_recorded Quantidade de demandas retornadas por list_recent(limit=100)

Somente essas chaves são implementadas por assert_expected(). Chaves extras não criam novas asserções. O runner sempre exige uma sessão ativa após o replay; chave ausente em expected não é verificada.

Limitações encontradas no código

instance é carregado no dataclass, mas replay() não o passa a process_message(). O campo não garante uma sessão GoYou. A descrição de TC-99-context-enrichment.json menciona conferir collected_packages, porém seu expected confere apenas QUALIFYING. Para validar esses contratos, os testes unitários específicos continuam necessários.

Fixtures existentes

Arquivo Cobertura pretendida
TC-01-qpcr-agro-happy.json Roteiro de qPCR/Agro
TC-07-out-of-portfolio.json Demanda fora do portfólio
TC-08-empresa-grande-handoff.json Encaminhamento de empresa grande
TC-09-cadastral-confirm.json Coleta cadastral e confirmação
TC-14-reclamacao.json Reclamação e handoff
TC-99-context-enrichment.json Solicitação e retomada com Context Package

Adicionar e diagnosticar

Crie um JSON em UTF-8, use nome igual ao stem do arquivo, dados fictícios e o menor roteiro que preserve a regressão. Execute o arquivo de replay e depois os testes do componente alterado. scripts/chat.py --record e /save <nome> permitem gravar uma conversa de desenvolvimento, mas o chat usa OpenAI real; só grave em atividade autorizada e anonimize tudo antes de versionar.

Se faltarem respostas LLM, o side_effect pode terminar antes das chamadas da aplicação e produzir erro/fallback. Se o estado final divergir, confira quantidade de chamadas por turno, action codes e dados obrigatórios antes de mudar o esperado. Uma fixture passa apenas nas invariantes declaradas; ela não valida texto comercial, prompt real nem disponibilidade dos providers.