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.