Projeto do Agent Contas ORACLE

This commit is contained in:
2026-08-19 09:35:50 -03:00
commit 950a2bcd33
1366 changed files with 177217 additions and 0 deletions

295
docs/VALIDACAO_MIGRACAO.md Normal file
View File

@@ -0,0 +1,295 @@
# Validação da reconstrução
## Resultado
- Sintaxe Python (`compileall`): **PASS**
- YAML de `config/`: **PASS**
- Testes de domínio mock: **4 PASS**
- Smoke MCP: **PASS** para faturas, invoice explanation, VAS, histórico, cancelamento e contestação
- Diretório do pacote anterior presente: **NÃO**
- Imports do namespace anterior em `app/`, `mcp/`, `config/`: **0**
- `.env`: **preservado byte a byte**
## Limitação do ambiente de construção
O runtime usado para montar o pacote não possui `langgraph` instalado globalmente. Por isso o teste de import/execução do `StateGraph` completo não foi executado aqui. O projeto declara `langgraph` em `pyproject.toml`; rode `uv sync` antes de subir o backend.
## Aceite recomendado em ambiente do projeto
```bash
uv sync
pytest -q tests/migration
uv run uvicorn contas_mcp.servers.contas_mcp_server.main:app --port 8400
uv run uvicorn app.main:app --port 8000
```
Depois execute os cenários descritos em `MANUAL_AGENT_CONTAS_MIGRADO.md`.
## Atualização de paridade - 2026-08-18
Gate local atual: **338 passed / 3 skipped** em `tests/migration`.
Os skips dependem de bibliotecas não instaladas no runtime de construção (principalmente LangGraph/jellyfish) e permanecem habilitados para execução após `uv sync`.
Contratos adicionais corrigidos nesta rodada:
- VAS History: `GET ?msisdn=...`, `clientId`, `messageId` e `Authorization`.
- Contract Information: `clientId` (não `client_id`) e Basic Auth.
- Profile/Line Info: header `ClientID` conforme contrato original.
- Cancelamento VAS: `messageId` sempre não vazio, além de `channel=AIAGENTCR` e `interactionProtocol`.
Gates estruturais mantidos:
- zero imports de `agente_contas_tim` em `app/`, `mcp/` e no código-fonte do framework;
- zero imports diretos de `langgraph.graph` no domínio Contas;
- workflows do domínio executados por `agent_framework.workflows.WorkflowRuntime`;
- `FrameworkStateGraph` usado para composição do grafo principal;
- `.env` preservado como arquivo principal de configuração.
## Atualização de paridade - continuação 2026-08-18
Gate local atual: **400 passed / 2 skipped** em `tests/migration`.
Skips restantes:
- `test_original_item_matcher_transcription.py`: requer `jellyfish`, dependência declarada no projeto e instalada por `uv sync`.
- `test_original_workflow_cases.py`: requer `langgraph`, dependência declarada no projeto e instalada por `uv sync`.
Novas coberturas e correções comprovadas nesta rodada:
- cenário real `cy0001` voltou a integrar a regressão de `vas_variation`; o skip causado por caminho incorreto do harness foi removido;
- replay pós-finalização preserva `terminal_status` e possui fallback seguro quando a sessão é restaurada sem a última fala;
- transformação de transcrição `Fim/Mim -> Sim` foi validada contra a matriz original de fronteira de fala inteira;
- `processing_interruption` interrompível voltou a usar classificador LLM leve do **framework**; sem classificador/erro/resultado negativo o comportamento é replay fail-safe;
- os dois templates oficiais do framework receberam o mesmo fluxo de classificação de interrupção;
- SMS recuperou o default canônico `senderName=TIM Brasil`;
- Service Request Status prioriza `messageId`/`ura_call_id` antes de `session_id`;
- o compositor determinístico de mensagem de cancelamento VAS foi portado e os 19 testes originais passam;
- `cancelar_vas_avulso` encadeia `cancelamento_vas_avulso -> contestacao_tool` usando **dois WorkflowRuntime do framework**;
- o MCP inicializa WorkflowRuntime/checkpointer/idempotência de forma lazy, evitando abrir Oracle durante import/health/tools-list;
- o `IdempotencyStore` selecionado pelo framework é agora realmente injetado nos actions do Contas, eliminando o fallback local involuntário para memória;
- cancelamento usa VAS History como fallback e bloqueia recancelamento quando `canCancel=false`;
- resultado em lote expõe `cancelados`, `nao_encontrados`, `nao_cancelados` e `itens_para_contestacao`, preservando sucesso parcial;
- finalização normaliza status/aliases e summary conforme o original;
- finalização informacional cria protocolo fechado somente quando necessário e evita duplicidade quando já existe protocolo;
- combinações canônicas de notas `VAS Bundle`, `VAS Estratégico` e `VAS Avulso` foram portadas e testadas.
### Gates estruturais desta versão
```text
agente_contas_tim em app/ 0
agente_contas_tim em mcp/ 0
agente_contas_tim em agent_framework/src 0
langgraph.graph em app/ 0
langgraph.graph em mcp/ 0
```
O LangGraph continua interno ao `agent_framework_oci` por `FrameworkStateGraph` e `WorkflowRuntime`.
## Atualização de paridade - continuação 2026-08-18 (baseline 420)
Gate local atual: **420 passed / 2 skipped** em `tests/migration`.
Novas correções comprovadas desde a baseline 400:
- plano família mantém o MSISDN do titular na contestação e o MSISDN real do dependente no cancelamento;
- `invoice_detail` corrige deterministicamente a linha do item antes do side effect;
- `CVAL` encerra `contestacao_tool` imediatamente quando bloqueia a operação, sem seguir para SMS/contrato/SR;
- o MCP propaga `success=false`, mensagem e estado sistêmico quando a contestação é bloqueada;
- finalização de `invoice_explanation` cria protocolo informacional apenas quando o workflow realmente executou, não por mero prefetch;
- protocolo VEB já fechado é reutilizado sem nova abertura/fechamento; `force_rt15_finalization_protocol` força um RT-15 novo quando solicitado;
- `suppress_cvn_protocol_ic` preserva a semântica de VAS estratégico deferido;
- `WorkflowRuntime` preserva snapshot parcial, nodes e trace quando uma action posterior falha;
- Billing Analysis agora carrega metadata de tentativas e gera RCT.079-084 por tentativa;
- corrigido bug de `msisdn` duplicado em `preparar_invoice_explanation`;
- corrigido bug de inicialização de `AgentWorkflow`: router/agentes/grafo estavam em código inalcançável após `return`;
- `InvoiceContextService` foi reconstruído sobre `agent_framework.cache.Cache`, com isolamento por sessão, TTL, prefetch e reaproveitamento de CompleteInvoices/Billing Analysis/detalhe.
Os dois skips continuam sendo exclusivamente dependências deste runtime de construção:
- `jellyfish` para regressão fonética completa;
- `langgraph` para os 18 casos reais de WorkflowRuntime.
## Baseline 435 testes — continuação de paridade
Nesta baseline foram adicionadas as seguintes garantias:
- `InvoiceContextService` com single-flight por sessão/fatura, cache incompleto sensível a `include_detail`, CVN.002/CVN.006/CVN.007 como `business_events` deduplicados por sessão e sem republicação em cache hit.
- Metadados de prefetch: `fetch_elapsed_ms`, `task_timings`, `cache_age_ms` e erros por subconsulta.
- Latch genérico `business_workflows_executed` no `AgentRuntimeMixin`; workflows em `PAUSED` já contam como executados e o latch é persistido no patch transacional.
- Cancelamento em lote com concorrência máxima 5 (configurável por `TIM_CANCELAMENTO_BATCH_CONCURRENCY`) e protocolo por linha antes do side effect; falha de protocolo bloqueia o cancelamento daquela linha.
- `WorkflowRunResult.error_details` preserva fatos estruturados de exceções externas sem acoplamento do framework a TIM.
- Contestação FAILED mapeia mensagem do provider/protocolo parcial quando disponíveis e diferencia erro de negócio de falha sistêmica.
Resultado local: **435 passed / 2 skipped** em `tests/migration`.
Os dois skips continuam dependentes de `langgraph`/`jellyfish` indisponíveis neste runtime de construção.
## Baseline 440 testes - continuação
- 440 testes de migração passando; 2 skipped por dependências indisponíveis neste runtime (`langgraph`/`jellyfish`).
- Cancelamento VAS: falha de bloqueio/cancelamento pode permanecer candidata à contestação sem mascarar sucesso.
- Resposta composta preserva protocolos de cancelamento por linha + protocolo de contestação e sinaliza finalização sugerida.
- Plano família: protocolo de cancelamento em dependente resolve `socialSecNo` via LineInfo da própria linha.
- Registro de protocolo aceita aliases de resposta `interactionProtocol`, `protocolNumber`, `protocol` e `protocolo`.
- Finalização informacional recalcula Bundle/Estratégico/Avulso pela evidência de `invoice_detail`/Billing Analysis quando disponível.
## Baseline 533 - dependências de regressão e finalização
- `tests/migration`: **533 passed / 4 xfailed / 0 skipped**.
- Os quatro `xfail` são limitações históricas documentadas do ranking fonético (`gueimiloft`, `tim miusic`, `apou`, `agebeo max`), não testes ignorados por dependência.
- `jellyfish` deixou de ser requisito obrigatório: o domínio possui fallback puro-Python para Jaro-Winkler, Levenshtein e chave fonética. A biblioteca externa pode ser usada como aceleração, mas o projeto e a regressão não dependem dela.
- Os 18 casos históricos de workflow não usam mais `importorskip(langgraph)`. Em builders offline executam pelo backend determinístico **explicitamente opt-in** do `WorkflowRuntime`; em produção o backend padrão continua sendo LangGraph e a ausência de `langgraph` continua sendo erro de configuração.
- Finalização validada adicionalmente para: CVN.008/CVN.009, MPI.006/MPI.005, RCT.085/RCT.086, CVN.010/CVN.011, SAD.001 e árvore SAD opcional; protocolo informacional canônico de invoice explanation; supressão em handoff; ausência de aceite informacional após workflows transacionais; classificação de VAS estratégico/avulso sem invoice detail.
### Lockfile
O `uv.lock` herdado de snapshots anteriores foi removido porque ainda descrevia o pacote legado (`agente-contas-tim`) e dependências que já não pertencem ao projeto (`jellyfish` obrigatório, NeMoGuardrails/LangChain extras, entre outras). O primeiro `uv sync` deve regenerar o lock a partir do `pyproject.toml` atual.
## Baseline 550 - finalização e matcher sem skips/xfails
Validação consolidada desta etapa:
```text
550 passed
0 skipped
0 xfailed
```
Coberturas adicionadas nesta etapa:
- finalização conversacional diferencia encerramento genérico de contexto real de fatura;
- prefetch/invoice context pode garantir CVN.002/CVN.006 e RT-15 conforme semântica histórica;
- `conversation_unresolved_transition_emitted` impede reemissão indevida de CVN/MPI positivos;
- VEB terminal com protocolo fechado não cria RT-15 duplicado nem reemite CVN/MPI;
- evidência da fatura prevalece sobre tipo informacional salvo em match total;
- match parcial mescla inferência da fatura com tipo salvo ainda não representado;
- sem match na fatura, tipos salvos conflitantes são descartados;
- protocolo já existente impede RT-15 duplicado;
- alias `cpf` é propagado como `socialSecNo` no protocolo informacional;
- matcher de transcrição resolveu os quatro casos históricos antes marcados como xfail.
### Matcher sem dívida conhecida no catálogo de regressão
O `SimilarityItemMatcher` passou a combinar:
- similaridade de frase;
- similaridade fonética;
- alinhamento token-a-token;
- dupla evidência grafia + fonética por token.
Isso corrigiu explicitamente:
- `gueimiloft` -> `Gameloft`;
- `tim miusic` -> `TIM Music`;
- `apou` -> `Apple Music`;
- `agebeo max` -> `HBO Max`.
## Baseline 556 — paridade VAA (2026-08-18)
A regressão de migração passou a executar 556 testes sem skip/xfail.
Nesta etapa os testes históricos do projeto original foram usados diretamente como catálogo para restaurar a família VAA sem trazer o publisher legado:
- cancelamento VAS: VAA.001/VAA.002/VAA.003 no caminho feliz e VAA.004 em falha operacional;
- contestação: VAA.005 + VAA.006/VAA.007 e VAA.008/VAA.009 conforme sucesso e elegibilidade do código de barras;
- SMS: VAA.012/VAA.014 em sucesso e VAA.013/VAA.015 em falha, mantendo o workflow ativo;
- atualização/fechamento de SR: VAA.016/VAA.017.
Os events são retornados como `business_events`; transporte, sequence e fan-out permanecem responsabilidade exclusiva do `AgentObserver`/analytics do agent_framework_oci.
## Baseline 560 testes - metadata corporativa TIM
- 560 testes de migração passando, sem skips/xfails.
- Business events preservam contexto corporativo: `agentProtocolId`, `adjustedProtocol`, `billingId`, `uraCallId`, `channelId`, `sessionId`, `messageId` e `agentSpecificData`.
- Contestação VAA.005-009 preserva itens/valores ajustados e protocolo.
- Finalização CVN.010/011 preserva protocolo TIM/URA e billing context.
- RCTs derivados de retry HTTP carregam `apiUrl`, `apiStatusCode`, `apiResponsePayload` e `latencyMs`.
- SMS e Service Request Status carregam metadata de transporte e contexto de protocolo.
- `tim_payload_mapper` aceita o formato histórico `agentSpecificData` JSON-string e o converte para o objeto canônico TIM no payload final.
## Baseline 565 — metadata MPI/VEB/SAD e VAS estratégico
- Regressão: **565 passed / 0 skipped / 0 xfailed**.
- `VEB.*`, `MPI.*`, `CVN.*` e `SAD.*` passam a carregar metadata conversacional TIM via `_event_context`: `customerMessage`, `llmResponse`, `messageId`, `sessionId`, `channelId`, `uraCallId`, `billingId` e `sessionEndAt` quando aplicável.
- `SAD.001` normaliza `session_end_at` ISO-8601 para epoch milliseconds.
- VAS Estratégico recuperou a semântica histórica: `NAO` estratégico -> `VEB.004 -> VEB.006 -> VEB.007`; Bundle + `NAO` -> `VEB.004 -> VEB.005 -> VEB.007` e protocolo deferido para finalização; `SIM` -> `VEB.003` e registro de atendimento.
- Invoice Explanation (`MPI.005/006`), Pró-Rata (`MPI.010`) e cancelamento (`MPI.011`) usam o mesmo enriquecimento de contexto.
## Baseline 570 testes
- `pytest -q`: **570 passed**, 0 skipped, 0 xfailed.
- `pyproject.toml` limita o gate padrão a `tests/`, evitando coleta acidental dos scripts de teste duplicados existentes dentro dos templates do framework.
- `cancelar_vas_avulso` redireciona automaticamente para `vas_estrategico` quando o `InvoiceResolver` classifica a cobrança como estratégico/bundle.
- `invoice_explanation` recuperou `recomenda_finalizacao/status_finalizacao_sugerido` por branch do `WorkflowRuntime`.
- `pro_rata` recuperou recomendação de finalização nos branches `registrar_aceitou` e `registrar_nao_controle`.
- VAS Estratégico recuperou a busca de orientação do parceiro sem RAG próprio: a action declara `requires_rag/rag_queries`, e o `AgentRuntimeMixin` executa `RagService` do framework.
## Baseline 578 - wrapper cancelamento + LLM composition
- 593 testes passando; zero skipped/xfail.
- Cancelamento preserva `auto_finalize_on_failure` em falha técnica de contestação.
- Item já contestado não mascara cancelamento concluído como falha sistêmica.
- `cancelamento_vas_protocol` e todos os protocolos de resposta são preservados.
- Plano família contesta titular + dependentes em uma única `contestacao_tool`.
- Lote com muitos no-match continua contestando exatamente os candidatos elegíveis.
- Pró-rata usa `requires_llm_composition` do framework em vez de gateway LLM de domínio.
## Continuação — paridade explícita dos wrappers (baseline 593)
- `contestacao_tool` volta a garantir `tipo_atendimento=contestacao` e preserva o contexto do turno (`message_id`, `customer_message`, `ura_call_id`, `channel_id`, `ani`, `invoice_id`).
- Falhas de contestação são diferenciadas entre erro técnico (`erro_falha_sistema`) e conflito/item já contestado com mensagem/protocolo do provider.
- Cancelamento composto aceita tanto `contestation_candidates` quanto o alias histórico `itens_para_contestacao`.
- Resultado agregado em `cancelados/nao_cancelados` é aceito mesmo quando `results[]` não repete a flag `success`.
- `cpf` volta a ser alias de `social_sec_no` e é normalizado para dígitos antes de protocolo/contestação.
- Em fluxo misto Bundle + Estratégico no branch NÃO, o protocolo segue deferido para finalização, porém as `rag_queries` dos serviços estratégicos são preservadas.
- Falha parcial com candidato continua encadeando `cancelamento_vas_avulso -> contestacao_tool`; falha de SMS não transforma o cancelamento em falha.
- `pytest -q`: **593 passed, 0 skipped, 0 xfailed**.
## Baseline 599 testes — paridade explícita de wrappers
Validação consolidada: `599 passed`, `0 skipped`, `0 xfailed`.
A rodada adicionou regressões 1:1 baseadas nos wrappers históricos para:
- `itens_ja_contestados` preservados desde a action de contestação até o compositor de resposta;
- classificação de `contested_items`, `not_contested_items` e itens já contestados feita no domínio, não reconstruída no MCP;
- totais de contestação calculados somente sobre itens efetivamente aceitos;
- valor `0`/`0,00` retornado pela contestação tratado como ausência de valor útil para composição, usando o total cancelado;
- valores monetários normalizados em pt-BR na borda do MCP (`14,99`);
- `next_subject` não interfere na composição determinística de cancelamento;
- falha de serviço em `invoice_explanation` preserva a fraseologia canônica e não ativa auto-finalização.
Gates: `compileall` PASS, zero imports do namespace legado e zero imports diretos de LangGraph em `app/`/`mcp/`.
## Baseline 615 — contratos 1:1 dos wrappers históricos
A regressão foi ampliada para 615 testes executados, sem skips e sem xfails.
Nesta etapa foram portados como contratos explícitos do MCP novo os seguintes
outcomes do `backend_cancelar_vas_single` e `finalize_support` históricos:
- sucesso implícito quando o workflow reporta `cancelados[]` sem `success=true`;
- lista explícita vazia de candidatos não cria fallback indevido para contestação;
- no-match não chama contestação nem vocaliza valor como se houvesse ajuste;
- `protocol_closed` da contestação é preservado;
- flags internas `sms_sent` não vazam no contrato externo e falha de SMS é propagada por `sms_not_send_error`;
- cancelamento com protocolo e sem item efetivamente contestado continua resolvido;
- candidato explícito pode seguir para RT-02 mesmo após falha/no-match em RT-01;
- falha parcial de bloqueio/cancelamento continua elegível à contestação quando marcada pelo domínio;
- `holder_msisdn` não pode ser sobrescrito por linha dependente;
- item do titular não é marcado como dependente;
- titular + múltiplos dependentes são agregados em uma única contestação do titular;
- VAS estratégico após invoice explanation prioriza nota estratégica na finalização;
- Bundle + Estratégico deferidos geram RT-15 combinado na finalização;
- invoice explanation não abre protocolo informacional antes da finalização.
Gate executado:
```text
pytest -q: 615 passed
compileall app/mcp/framework: PASS
agente_contas_tim em app/mcp/framework runtime: 0
import direto langgraph.graph em app/mcp: 0
```