diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index f71d1b6..d3fa4e7 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 7cfe855..3dbd1ac 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index 04e56ea..67e644d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index 69e3d34..b0e4dda 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index bd17d05..0a90f41 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 247d900..ddedee0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index 8681308..3706435 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index c9f0a6e..8936cb0 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 2a37530..892395c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index 0796c12..13d5a9c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index 6ffb379..41e348e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index 34b0e57..fa80334 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index 9bf0bb1..2dbd728 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc index d640def..21efb85 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 2616304..4708cfe 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index fe11483..bce6081 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 7e2e6f2..9316b89 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc index a5e5649..9a27145 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index 73ac151..fd4d59d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index b708063..5044c00 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index e3c5e17..156e566 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index d3285e7..1646570 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc index 6503e22..d3208e5 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 15d4e82..f2432c2 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index a1d2c6a..8110ec9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index d7186ef..a4b8461 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index 3e3772f..131939a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index 7d10414..a91ffe5 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index 2329824..57a065b 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index f288519..b290cb9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc index d231d22..885cc2c 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index c69bef6..2ca4865 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index a0659fa..5d759df 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc index eb043c1..3f2d1a9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc index bb86381..1bf5cbc 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index 4949e31..09c1849 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc index 08db33c..f49141f 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py index b6acd9e..05c8138 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py @@ -1,148 +1,78 @@ -"""Prompt do rail COER (coerência do input do cliente). +"""Prompt do rail COER (coerência semântica do input do cliente). -Roda no INPUT, em paralelo com PINJ (mesmo pool), num 20b. Decide se a fala do -cliente é aproveitável. Saída BINÁRIA (`1` passa / `0` descarta) — o `reason` é -texto fixo; pedir motivo antes do dígito foi medido e não paga (+170 ms, empate). +O COER responde uma pergunta estreita: existe significado conversacional recuperável +na fala do cliente? Ele não decide intenção, completude de parâmetros, executabilidade, +escopo de negócio ou se uma solicitação deve ser aceita. Essas decisões pertencem ao +router, aos contratos transacionais, aos validadores e ao mecanismo de clarification. -Descarta SÓ por três motivos: - -(a) incompreensível — transcrição quebrada, palavra solta, conversa paralela; -(b) negação ambígua — "não" colado num pedido de AÇÃO do atendente, sem a vírgula - que decidiria a leitura ("não quero cancelar" × "não, quero cancelar"); -(c) idioma (2026-08-10) — frase INTEIRA em inglês é STT quebrado, não cliente - bilíngue: descarta mesmo se ela se entende ou responde à pergunta pendente. - Ressalva: passa quando o agente pediu o NOME do item — nome de serviço É em - inglês (`coer_ok_0023`). ⚠️ A regra só funciona no ENQUADRAMENTO, acima do - gate de histórico (dentro de (a): 0/9 nos casos de inglês; no topo: 9/9), - porque o gate concede 1 a quem responde e o catch-all a quem pede algo - legível. Travado em `tests/guardrails/test_coerencia.py`. - -O resto passa e é tratado adiante (matcher, TOX, OOS, orquestrador): referência -vaga, nome deformado, xingamento, assunto fora de fatura, resposta curta. O -histórico entra no prompt porque é ele que resolve fala curta e negação sem vírgula. - -Dois bugs de produção fechados, ambos com a mesma assinatura — o modelo reconhece -a fala e escapa por uma regra de allow antes de aplicar (b): - - 2026-08-07, "não" seco no degrau 2 da retenção: (b) disparava só por começar - com "não" e o modelo COMPLETAVA a elipse com a ação que o AGENTE ofereceu. - Conserto: (b) exige que a fala PEÇA algo, e o teste da subtração proíbe - completar com a oferta do agente (`coer_ok_0027`: 161/220 → 340/340); - - 2026-08-10, "não gostaria de falar com a atendente" (`coer_ambig_0014`, 2/9): - a causa é o VERBO, não o gate nem o histórico (sonda 2×2 — condicional + - histórico curto 2/10 × "não quero" + o histórico longo do trace 10/10). - Conserto: gate vale só para a fala que "SÓ responde a ela"; (b) diz que - entender o pedido não dispensa o teste; a glosa do 1º exemplo cobre o - condicional. Alvo → 7/9, suíte 176,0 → 180,7/189. - -⚠️ Protocolo: decida por BATCH (3 amostras de `--repeat 3` da suíte inteira, banda -de ruído ±4). `--repeat` focado engana nos dois sentidos — a mesma variante deu -7/10 focado × 0/9 batch, e o prompt atual dá 7/9 batch × 3/9 focado. - -Variantes medidas e REJEITADAS (não retentar sem motivo novo) — a suíte está numa -fronteira zero-soma, cada cláusula compra um caso e vende outro: - - "a recusa soar clara não fecha" → CONTRADIZ a exceção "a fala segue dizendo - qual leitura vale": mata `coer_ok_0003` (7/9 → 0-1/9) em 3 variantes; - - exceção no GATE ("fala com 'não' ainda passa por (b)") → mata `coer_ruido_0011` - (9/9 → 0/9): exceção explícita REFORÇA o gate para todo o resto; - - "gostaria" na lista de modais de (b) → 169,7/189; - - few-shot NÃO é mais alavanca (era em 2026-08-05, +3,4 p.p.): +3 exemplos = empate - exato por +132 tokens; só o do NOME em inglês = 189,7/201 (arrasta a regra (c)); - tirar exemplos custa mais do que os tokens que ocupam — inclusive o "não quero - entender porque…", que o controle FOCADO media como "sem efeito" e em batch vale - `coer_ok_0010` inteiro (9/9 → 1/9). - -Tamanho: 1289 → 1334 (2026-08-07) → **1451 tokens** (cl100k). Suíte: **191,7/201 -(95,4%)**, 67 casos. Detalhe por caso e histórico: `tests/llm_tests/README.md`. - -Remedido em 2026-08-12 ao desfazer o revert (41979c4d): 193,7/204 (95,0%), 68 casos -— o novo `coer_ruido_0022` ("um" respondendo "sanei sua dúvida?", STT que não pegou -o "sim" → golden 0, reperguntar) sai de 3/10 no prompt antigo para 9/9 em batch só -com o gate "SÓ responde a ela", sem mudança extra de prompt. +A classificação continua totalmente delegada ao LLM. Não há listas de frases, +regexes ou exceções de domínio para liberar/bloquear entradas específicas. """ from __future__ import annotations def build_coer_prompt(text: str, context: str = "") -> str: - """Monta o prompt do rail COER. + """Monta o prompt semântico do rail COER. Args: text: fala do cliente a classificar. - context: bloco de histórico já formatado por - ``prompts._context.format_context_block`` (para este rail a última - fala do agente é PRESERVADA — é a pergunta pendente). + context: histórico já formatado, incluindo a pergunta pendente do agente + quando disponível. Returns: Prompt cuja resposta esperada é um único caractere: ``1`` ou ``0``. """ - return f"""Você filtra a fala do CLIENTE no atendimento de fatura do provedor. A fala vem de -transcrição de voz e pode chegar truncada ou trocada. O atendimento é em português: -frase inteira em INGLÊS é STT quebrado, não cliente bilíngue — responda 0 mesmo que -ela se entenda ou responda à pergunta do agente; só não vale quando o agente pediu o -NOME do item, que é em inglês. + return f"""Você é o guardrail de COERÊNCIA SEMÂNTICA da fala do CLIENTE em uma conversa. -PRIMEIRO olhe o histórico. Se o agente terminou com uma pergunta e a fala SÓ responde a ela -(sim/não, "ainda não", nome de serviço, valor, uma das opções oferecidas), responda 1 -— mesmo curta, estranha ou com o nome deformado pelo STT. Se não há pergunta pendente, -julgue a fala sozinha pelos casos abaixo, sem dar desconto. +Sua única responsabilidade é decidir se a fala contém significado conversacional +recuperável o suficiente para que as próximas camadas do sistema possam trabalhar. -Responda 0 (descartar) SÓ nestes dois casos: +NÃO tente decidir aqui: +- qual é a intenção do cliente; +- se a intenção mudou em relação ao turno anterior; +- se uma transação deve continuar, ser abandonada ou encerrada; +- se faltam parâmetros para executar uma ação; +- se um valor, nome, data ou outro parâmetro é válido; +- se a solicitação pertence ao escopo do atendimento; +- se uma ação é permitida por regra de negócio; +- se a fala precisa de clarification ou desambiguação posterior. -(a) NÃO DÁ PARA ENTENDER — você não conseguiria dizer em uma frase, SEM INVENTAR, o - que o cliente quer, responde ou reclama: transcrição quebrada, frase cortada no - meio, palavra ou letra solta, frase que soa completa mas cujo pedido não faz - sentido, ou fala dirigida a OUTRA PESSOA (o cliente conversando com quem está do - lado, sem falar com o atendimento). Palavra do domínio (plano, fatura, valor, - cpf) dentro de frase sem sentido não salva a fala. Fala VAGA não é - incompreensível: se ela aponta para o que está na tela ("esse aí", "isso aqui", - "esse negócio", "os valores"), responda 1 — perguntar qual item é do fluxo. - E se a última fala do agente pediu um NOME de item/serviço, nenhuma fala curta - é incompreensível: ela é a tentativa de dizer o nome, por mais estranha que - soe → 1 (reconhecê-lo é da etapa seguinte, que tem a fatura). +Essas responsabilidades pertencem ao router, ao estado transacional, aos validadores +e ao mecanismo de clarification. Portanto, uma fala pode ser compreensível mesmo +sendo incompleta para execução, contendo negação, discordância, reclamação, múltiplas +intenções, informalidade, erro gramatical ou referência que precise ser resolvida pelo +contexto. -(b) NEGAÇÃO AMBÍGUA — a fala começa com "não" E PEDE ALGO depois; entender o que ela - pede não a salva, quem decide é o teste. Faça o teste: tire - esse "não" do início e olhe SÓ o que sobra na fala — nunca complete com a ação - que o agente ofereceu. Se não sobra pedido nenhum ("não", "não sanou"), é - resposta ao agente → 1, seja qual for a pergunta pendente. Se o que sobra é - pedido de ação do atendente (cancelar, tirar cobrança, - ajustar/diminuir a fatura, transferir para atendente, encerrar a conta, - parcelar), sobram duas leituras opostas — recusa ("não quero cancelar") ou - pedido ("não, quero cancelar") — e a vírgula que decidiria não veio na - transcrição: responda 0. Vale para qualquer verbo ("não quero/preciso/posso", - "não quero que vocês...", "não cancela"). - Responda 1 se: vem vírgula, "porque" ou "mas" depois do "não"; há sujeito antes - do "não" ("eu não quero cancelar"); a fala segue dizendo qual leitura vale; ou o - que sobra sem o "não" não é ação do atendente (pagar, reconhecer, entender, - mudar de plano). +Use o histórico somente para interpretar elipses, respostas curtas e referências ao +turno anterior. Nunca complete a fala inventando uma intenção que não esteja apoiada +pela própria fala ou pelo contexto imediato. -Responda 1 em TODO o resto, inclusive: -- pedido, queixa, dúvida ou desabafo que você entende, mesmo com erro de transcrição, - gíria, xingamento, número solto ou assunto fora de fatura (outros filtros cuidam); -- nome de serviço estranho ou deformado, inclusive quando o agente pediu para repetir - o nome do serviço; -- pedido de tempo, "alô?", agradecimento, despedida. +Responda 1 quando for possível identificar, sem inventar, pelo menos um conteúdo +conversacional útil: uma intenção, pergunta, resposta, afirmação, negação, reclamação, +referência, escolha, valor, nome, pedido de esclarecimento, encerramento ou mudança de +assunto. Não exija que esse conteúdo já seja suficiente para executar uma ferramenta. -Dúvida se entendeu a fala → 1. Pergunta ou pedido claro dirigido ao atendimento, mesmo -fora do assunto de fatura → 1. Dúvida entre as duas leituras da negação → 0. +Responda 0 somente quando, mesmo considerando o contexto imediato, não houver +significado conversacional recuperável com segurança — por exemplo, transcrição +fragmentada, palavras desconexas, fala cortada antes de formar qualquer relação +semântica, ou conversa paralela sem solicitação dirigida ao atendimento. -Exemplos (ilustram a regra, não são lista de falas): -- "não quero parcelar a fatura" → 0 (sem a vírgula, pode ser "não, quero parcelar"); - idem no condicional, "não gostaria de parcelar a fatura" -- "eu não quero parcelar a fatura" → 1 (o "eu" antes do "não" fecha a leitura) -- "não quero parcelar, quero só entender o valor" → 1 (a fala diz qual leitura vale) -- "não vou pagar essa multa" → 1 (pagar não é ação do atendente: a queixa é a mesma) -- "não", depois de "sanou sua dúvida?" → 1 (responde a pergunta pendente) -- "deixe zero", depois de "qual o nome do serviço?" → 1 (pode ser o nome que o STT - deformou — "Deezer"; reconhecer o nome é da etapa seguinte, que tem a fatura) -- "não quero entender porque a conta subiu tanto" → 1 (entender é dúvida, não ação) -- "olha o menino ali pegando o negócio lá" → 0 (não dá para dizer o que o cliente quer) -- "bota dois planos um em cima do outro pra cá" → 0 (soa ordem, não quer dizer nada) -- "está cobrando um" → 0 (cortada no meio: não dá para saber de quê) +Critério decisivo: +- compreensível mas incompleto/ambíguo para a regra de negócio -> 1; +- compreensível mas com possível mudança de intenção -> 1; +- compreensível mas sem todos os parâmetros -> 1; +- compreensível e contendo negação/discordância -> 1; +- impossível determinar qualquer conteúdo conversacional sem inventar -> 0. + +Na dúvida entre "há significado, mas outra camada precisa esclarecer" e "não há +significado recuperável", escolha 1. O COER deve bloquear apenas incompreensibilidade +semântica real, não incerteza de negócio. ------------------------------------{context} Fala do cliente: {text} ------------------------------------ -Responda APENAS um caractere: 1 (aproveitável) ou 0 (descartar). +Responda APENAS um caractere: 1 (semanticamente compreensível) ou 0 +(semanticamente incompreensível). """ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index fc0d11b..b332bab 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 3f1f38b..0d98db3 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index e7f40e1..31ea4b6 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index b9f0d6a..6b3a60e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index e3e19b2..99a2d6d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index 291c324..1697f9a 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index 9d4c5d2..e62ed42 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index 8c92df8..0de4a46 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index 4bbce8a..5af6e2e 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 3503902..2eb99a9 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index 84e8436..6a456ac 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc index 83b9ffb..414c3b8 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc index aa63f53..34abe3d 100644 Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ diff --git a/docs/FIX_COER_SEMANTIC_LLM_20260831.md b/docs/FIX_COER_SEMANTIC_LLM_20260831.md new file mode 100644 index 0000000..ee161cb --- /dev/null +++ b/docs/FIX_COER_SEMANTIC_LLM_20260831.md @@ -0,0 +1,27 @@ +# Ajuste semântico do guardrail COER + +## Objetivo + +Evitar que o COER confunda negação, reclamação, mudança de intenção ou falta de parâmetros com fala incompreensível. + +## Alteração + +O prompt de `agent_framework.guardrails.calibrated.prompts.coerencia` foi simplificado para uma responsabilidade única: decidir se existe significado conversacional recuperável. + +Foram removidas heurísticas textuais específicas de negação, listas de ações e exemplos de frases usados como regras de decisão. O julgamento continua sendo feito pelo LLM com o perfil `guardrail`. + +O COER agora não decide intenção, mudança de intenção, continuidade de transação, completude de parâmetros, validade de parâmetros, escopo ou executabilidade. Essas responsabilidades permanecem no router, runtime transacional, validators e clarification. + +## Contrato + +- compreensível, mas incompleto/ambíguo para negócio: ALLOW; +- compreensível com possível mudança de intenção: ALLOW; +- compreensível faltando parâmetros: ALLOW; +- compreensível com negação/discordância: ALLOW; +- sem significado semântico recuperável: BLOCK. + +## Testes + +- regressão dirigida do COER: 22 PASS; +- `tests/migration`: 824 PASS / 2 FAIL; +- os 2 FAIL são preexistentes e não relacionados ao COER. diff --git a/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md b/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md new file mode 100644 index 0000000..a484181 --- /dev/null +++ b/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md @@ -0,0 +1,71 @@ +# Guardrails e Judges externos — Contas + +## Objetivo +O `agent_framework_oci` mantém apenas mecanismos e políticas realmente genéricos. O agente Contas mantém políticas, exemplos e prompts que conhecem TIM, Contas, VAS, fatura, cancelamento ou nomenclaturas comerciais. + +## Regra arquitetural +- **Framework:** engine, contratos, execução paralela, fail-fast, telemetria, carregamento YAML e implementações genéricas. +- **Agente:** prompts/policies de domínio e classes externas. +- Um componente externo só é importado quando o YAML do agente declara `type: external`. +- Guardrails/judges nativos continuam funcionando sem qualquer alteração de configuração. + +## Configuração de guardrail externo +```yaml +output: + - code: TIM_AOFERTA + type: external + class: app.extensions.tim_guardrails:TimProactiveOfferRail + enabled: true +``` +O código `TIM_AOFERTA` deixa explícito que esta política é do agente Contas. O framework continua podendo oferecer `AOFERTA` como rail genérico para outros agentes. + +## Configuração de judge externo +```yaml +judges: + - name: tim_groundedness + type: external + class: app.extensions.tim_judges:TimGroundednessJudge + enabled: true + threshold: 0.60 +``` + +## Concorrência e threads +O `ParallelRailExecutor` executa todos os rails concorrentemente. `evaluate()` assíncrono roda no event loop; plugin síncrono roda por `asyncio.to_thread`, portanto não bloqueia o loop. Judges nativos e externos são disparados com `asyncio.gather`; judges síncronos também são deslocados para `asyncio.to_thread`. A ordem da lista de resultados permanece a ordem do YAML. + +## Compatibilidade +A extensão é aditiva. Entradas antigas como `{code: PINJ}` e `{name: groundedness}` seguem nativas. Somente itens com `type: external` usam import dinâmico. Isso evita dependência reversa do framework para `app.*`. + +## Mapeamento nesta versão do Contas +| Genérico no framework | Específico no Contas | Motivo | +|---|---|---| +| OOS | TIM_OOS | escopo do Contas/TIM | +| AOFERTA | TIM_AOFERTA | política de oferta do atendimento TIM | +| REVPREC | TIM_REVPREC | exemplos e ações transacionais TIM | +| FRASEOLOGIA | TIM_FRASEOLOGIA | fraseologia própria (mantido desabilitado como antes) | +| response_quality | tim_response_quality | prompt original do auditor Contas | +| groundedness | tim_groundedness | prompt original de alucinação/grounding do Contas | + +Os prompts originais foram preservados em `app/extensions/tim_prompts/`. As versões sob `agent_framework/.../calibrated/prompts` foram generalizadas e não devem conter nomes comerciais TIM. + +## Como criar um novo componente +1. Implemente uma classe no agente com `evaluate(...)`. +2. Para guardrail, retorne `RailDecision`/`RailResult`; para judge, retorne `JudgeResult`. +3. Declare `type: external` e o caminho `module:Class` no YAML. +4. Não crie cliente LLM próprio: use o `llm` fornecido pelo framework/contexto para manter `llm_profiles.yaml`, Langfuse e contabilização. +5. Teste convivência com os rails/judges nativos e o comportamento fail-closed. + +## Hardcodes de integração do Contas +Os valores legados de `clientId`, `channel`, `cspId`, sender e URLs que antes apareciam como fallback em Python foram movidos para `config/tim_integration_defaults.yaml`. A precedência é: + +1. variável de ambiente; +2. `config/tim_integration_defaults.yaml`; +3. default explícito somente quando a chamada realmente define um default técnico. + +Isso preserva contratos diferentes por operação (`TIM_CANCELAMENTO_CHANNEL`, `TIM_DIVERGENCIA_CHANNEL`, etc.) sem usar um `TIM_DEFAULT_*` que altere silenciosamente o legado. + +## Validação de contestação +`validate_contestation_items` é regra do domínio Contas e agora vive em `app/domain/contas/contestation_validation.py`. O módulo antigo no framework existe apenas como shim de compatibilidade/depreciação; código novo do Contas importa a implementação do agente diretamente. + +## Observabilidade dos códigos externos + +O código semântico de um guardrail/judge externo não deve ser alterado para atender um código numérico de um cliente. Use `config/observability_mapping.yaml` para o contrato de telemetria. Exemplo: a extensão pode continuar emitindo `GRL.TOXOUT`, enquanto o contrato publica `GRL.004`. Isso mantém a política do agente separada do catálogo externo de observabilidade. diff --git a/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md b/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md new file mode 100644 index 0000000..ee161cb --- /dev/null +++ b/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md @@ -0,0 +1,27 @@ +# Ajuste semântico do guardrail COER + +## Objetivo + +Evitar que o COER confunda negação, reclamação, mudança de intenção ou falta de parâmetros com fala incompreensível. + +## Alteração + +O prompt de `agent_framework.guardrails.calibrated.prompts.coerencia` foi simplificado para uma responsabilidade única: decidir se existe significado conversacional recuperável. + +Foram removidas heurísticas textuais específicas de negação, listas de ações e exemplos de frases usados como regras de decisão. O julgamento continua sendo feito pelo LLM com o perfil `guardrail`. + +O COER agora não decide intenção, mudança de intenção, continuidade de transação, completude de parâmetros, validade de parâmetros, escopo ou executabilidade. Essas responsabilidades permanecem no router, runtime transacional, validators e clarification. + +## Contrato + +- compreensível, mas incompleto/ambíguo para negócio: ALLOW; +- compreensível com possível mudança de intenção: ALLOW; +- compreensível faltando parâmetros: ALLOW; +- compreensível com negação/discordância: ALLOW; +- sem significado semântico recuperável: BLOCK. + +## Testes + +- regressão dirigida do COER: 22 PASS; +- `tests/migration`: 824 PASS / 2 FAIL; +- os 2 FAIL são preexistentes e não relacionados ao COER. diff --git a/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md b/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md new file mode 100644 index 0000000..58be391 --- /dev/null +++ b/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md @@ -0,0 +1,27 @@ +# Correções após regressão de 31/08/2026 + +Esta rodada corrige quatro comportamentos observados no relatório de regressão sem reintroduzir o runtime conversacional legado. + +## 1. Cancelamento múltiplo após confirmação + +O snapshot transacional do framework já preservava corretamente os argumentos confirmados. O defeito estava no preflight de execução do MCP do Contas, que tentava resolver novamente o `subject` de apresentação (por exemplo, `Tamboro Mensal, Paramount+`) mesmo quando `items[]` já continha múltiplas entidades canônicas pré-validadas. Agora `items[]` é a fonte de verdade nessa condição e não há segunda resolução textual. + +## 2. Contestação com valor incompatível + +Uma divergência de valor comprovada pelo CVAL passa a ser recuperável: o item permanece preservado, apenas `valor` volta para coleta e a resposta informa o valor autoritativo encontrado na fatura. O contrato de auditoria mantém `reason=CVAL` e acrescenta `recoverable_reason=amount_not_supported_by_invoice`. + +O runtime genérico ganhou suporte opcional a `parameter_message` emitido por um pre-validator de domínio. O framework apenas apresenta essa mensagem enquanto permanece em `COLLECTING_PARAMETERS`; ele não interpreta a regra de negócio. + +## 3. Encerramento explícito + +Expressões inequívocas como `entendi, obrigado, era só isso` encerram a sessão como `resolvido`, desde que não exista transação ou workflow ativo. Isso evita que uma despedida caia em fallback/guardrail sem consumir confirmações pendentes. + +## 4. Continuação plural após explicação de fatura + +Frases como `as duas mesmo, pode seguir` imediatamente após `contas_invoice_explanation` permanecem no contexto de explicação e não são confundidas com finalização genérica. + +## Regressão + +- Testes novos e direcionados: 58/58 no Contas e 30/30 no runtime transacional do framework. +- `tests/migration`: 818 PASS / 2 FAIL. +- Os 2 FAIL restantes são preexistentes nesta base: caso histórico de `validar_contestacao` com TIM Fashion/R$50 e fraseologia de `termino_desconto`. diff --git a/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md b/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md new file mode 100644 index 0000000..c86dc15 --- /dev/null +++ b/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md @@ -0,0 +1,63 @@ +# Correção de paridade conversacional residual do Contas — 2026-08-31 + +## Escopo + +Correções pontuais extraídas do comportamento útil do Contas anterior sem restaurar o runtime legado: + +- retenção antes de handoff humano; +- jurídico/Anatel/Procon com dependência de entidade previamente em foco; +- três falas consecutivas realmente incompreensíveis; +- preservação do pós-finalização no framework, com status terminal de erro respeitado; +- cancelamento múltiplo por entidades nomeadas/contextuais e por "todos os VAS avulsos". + +## Arquitetura + +Foi adicionada `app/domain/contas/conversation_policy.py`, executada depois do `EnterpriseRouter` e antes do agente de domínio. A policy não executa side effects: apenas reprompta, enriquece contexto ou altera o roteamento. Toda operação transacional continua passando pelo `AgentRuntimeMixin`, pré-validação MCP, confirmação explícita e workflow do Contas. + +## Retenção + +Quando o router solicita handoff humano, a policy procura evidência autoritativa de VAS avulso que participou da variação da conta usando `varied_avulso_items`. Sem evidência, o handoff segue normalmente. Com evidência, a policy oferece dois degraus: explicação da variação e, em seguida, tratamento dos VAS identificados. Recusa do segundo degrau leva ao handoff humano normal. + +O aceite do tratamento cria reentrada contextual com os nomes já comprovados; o cliente não precisa repeti-los e a transação continua sujeita à pré-validação e confirmação. + +## Jurídico / Anatel / Procon + +A mera ameaça regulatória não cria uma transação. Sem entidade concreta em foco, a fala é encaminhada como reclamação ampla ao suporte, sem MCP transacional. Quando já existe `subject/items` em estado transacional, o foco é preservado e a fala pode continuar no fluxo correspondente. A fatura inteira nunca é usada para inventar o alvo. + +## Três falas incompreensíveis + +Foi criada a intent semântica `contas_no_match`, exclusiva para fala sem conteúdo recuperável. `fallback` genérico não conta como incompreensão. O contador é consecutivo e reinicia em qualquer turno compreendido. + +- 1ª: pede reformulação; +- 2ª: pede reformulação; +- 3ª: encerra pelo nó global `end_session`, chamando `finalizar_atendimento` com `status=erro_no_match`. + +O limite pode ser configurado por `CONTAS_NO_MATCH_MAX_CONSECUTIVE`, default 3. + +## Cancelamento múltiplo + +`validar_vas_subject` agora resolve múltiplas entidades exclusivamente contra catálogo autorizado VAS/fatura. Exemplos suportados após extração semântica contextual: + +- `cancela Netflix e HBO`; +- `os dois` / `ambos`, quando o extrator LLM consegue resolver os nomes pelo contexto imediato; +- `todos os VAS avulsos`. + +`todos` genérico não expande em massa. Para "todos os VAS avulsos", itens estratégicos/bundle são filtrados pela política de domínio. Os itens resolvidos são enviados como `items[]` para o workflow batch existente, com uma única confirmação explícita antes da execução. + +## Pós-finalização + +Não foi criado runtime duplicado. O lifecycle continua no framework. O nó `end_session` passou apenas a respeitar um `terminal_status` já definido pela policy (por exemplo `erro_no_match`) e uma mensagem terminal específica, mantendo o mecanismo atual de replay/soft reset. + +## Testes + +Novos testes: + +- `tests/migration/test_contas_conversation_policy_residuals.py`; +- `tests/migration/test_multiple_vas_subject_resolution.py`. + +Regressão direcionada: **61 PASS**. + +Regressão completa `tests/migration`: **810 PASS / 2 FAIL**. Os mesmos dois FAIL foram reproduzidos no ZIP original sem estas alterações, portanto são falhas preexistentes e fora do escopo desta correção: + +1. `test_validar_contestacao_aprova_quando_item_e_valor_sao_comprovados`; +2. `test_termino_desconto_e_valor_divergente_preservam_semantica_do_original`. diff --git a/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md b/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md new file mode 100644 index 0000000..5750b08 --- /dev/null +++ b/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md @@ -0,0 +1,24 @@ +# Correção CVAL: valores monetários e itens homônimos + +## Problema + +O CVAL removia todo ponto de valores textuais antes da conversão decimal. Assim, valores vindos de JSON/backend como `19.99` eram interpretados como `1999`, permitindo indevidamente ajustes como `29.98`. + +Além disso, quando o mesmo `subject` aparecia mais de uma vez na fatura, a validação escolhia a primeira ocorrência após a ordenação estrutural, sem usar o valor solicitado para desambiguar a cobrança correta. + +## Correção + +1. `_parse_amount()` agora reconhece formatos decimais e de agrupamento comuns, incluindo `19.99`, `R$ 19,99`, `1.999,99` e `1,999.99`. +2. Quando existem múltiplos candidatos com o mesmo nome, o CVAL usa o valor solicitado como evidência: + - prefere correspondência exata; + - para ajuste parcial, escolhe a menor ocorrência que comporte o valor solicitado; + - se nenhuma ocorrência comportar o valor, usa a maior ocorrência para que a regra genérica `validated > item_amount` bloqueie a solicitação. + +Nenhuma regra específica para "dobro", "triplo" ou percentual foi adicionada. + +## Regressões cobertas + +- `19.99` permanece `19.99`; +- `R$ 19,99` vira `19.99`; +- `Tamboro Mensal` em `14.99` e `19.99` + solicitação `19.99` resolve a ocorrência correta; +- `Tamboro Mensal` em `14.99` e `19.99` + solicitação `29.98` é bloqueada com `valor_ajuste_maior_que_item`. diff --git a/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md b/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md new file mode 100644 index 0000000..ecbf7ee --- /dev/null +++ b/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md @@ -0,0 +1,29 @@ +# Correção do cenário 23 — saldo de internet em tempo real + +## Problema + +O FaturasAgent respondia que não havia informação nos "dados consultados" sobre o saldo de internet restante mesmo quando nenhuma tool de consumo em tempo real havia sido executada. Isso criava uma alegação de consulta sem evidência funcional. + +## Solução + +A correção foi mantida no agente, sem alteração do framework. Para perguntas explícitas de saldo/franquia restante em tempo real, o FaturasAgent: + +1. verifica se alguma tool bem-sucedida trouxe evidência de saldo/consumo atual; +2. se houver evidência, deixa o fluxo normal responder com os dados reais; +3. se não houver evidência, não afirma que consultou dados e orienta o cliente ao Meu TIM. + +Resposta de fallback: + +> Não consigo consultar o saldo de internet em tempo real por aqui. Para ver quanto ainda resta neste mês, consulte o app Meu TIM, onde você acompanha o consumo atual da sua franquia. + +A regra não intercepta perguntas genéricas sobre internet/plano e não bloqueia uma integração futura que passe a devolver saldo real. + +## Arquivo alterado + +- `app/agents/faturas_agent.py` + +## Testes + +- `tests/migration/test_scenario_23_live_internet_balance_guidance.py` +- 3/3 testes novos PASS +- regressão `tests/migration`: 821 PASS / 2 FAIL preexistentes diff --git a/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md b/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md new file mode 100644 index 0000000..26db861 --- /dev/null +++ b/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md @@ -0,0 +1,46 @@ +# Correção: precedência de parâmetros sobre semantic intent shift + +## Problema + +Durante uma transação ativa em `COLLECTING_PARAMETERS`, o roteador executava o +`semantic_classifier` de mudança de intenção **antes** da extração dos parâmetros +quando `ENABLE_LLM_ROUTER=true`. Com isso, respostas referenciais válidas, como +`"a de quatorze e noventa e nove"`, podiam ser roubadas por outra intent +semanticamente plausível antes de o contrato da transação tentar consumi-las. + +## Regra restaurada + +A ordem agora é: + +1. `AWAITING_CONFIRMATION`: confirmação explícita continua com precedência absoluta. +2. `COLLECTING_PARAMETERS`: tentar primeiro extrair pelo menos um parâmetro pendente. +3. Se algum parâmetro for consumido, manter a transação e **não** executar intent shift. +4. Somente quando nenhum parâmetro for consumido, avaliar `semantic_classifier` para + `CONTINUE`/`SHIFT`. +5. Um novo objetivo explícito continua podendo mudar a intenção, desde que o extrator + corretamente não o converta em parâmetro da transação anterior. + +## Resolução contextual + +O extrator do roteador agora recebe um contexto conversacional recente e limitado, +apenas como auxílio não-autoritativo para resolver referências. Exemplo: se o histórico +recente contém `Tamboro Mensal = R$ 14,99`, a fala `"a de 14,99"` pode produzir o +candidato `subject=Tamboro Mensal`. A validação/pre-validation da transação continua +sendo responsável por provar a entidade contra evidência de backend/MCP antes da +confirmação ou execução. + +## Arquivo principal alterado + +- `agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py` + +## Testes + +Foram atualizados/adicionados testes em: + +- `agent_framework_oci/tests/test_transaction_parameter_llm_precedence.py` + +Validação executada: + +- 8/8 testes do arquivo de precedência passaram. +- 81/81 testes combinados de transaction routing, state interruption, contextual reentry, + expected input semantic classifier e route stickiness passaram. diff --git a/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md b/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md new file mode 100644 index 0000000..45d8e43 --- /dev/null +++ b/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md @@ -0,0 +1,51 @@ +# Correção: valor já coletado pode ser corrigido durante COLLECTING_PARAMETERS + +## Problema + +Uma transação podia estar em `COLLECTING_PARAMETERS` com um campo obrigatório já preenchido em turno anterior (por exemplo `valor=19.99`) e outro ainda pendente (`subject`). Se o cliente corrigisse o valor no mesmo turno em que identificava o item — por exemplo `desculpa, é a de quatorze e noventa e nove` — o runtime enviava ao extrator LLM apenas os parâmetros ainda ausentes. Assim, `valor` ficava fora do contrato editável do turno e permanecia congelado em `19.99`. + +Isso gerava estados inconsistentes como `resolved_value=14.99` e `valor=19.99`, fazendo a contestação executar com o valor antigo. + +## Regra corrigida + +Enquanto a transação estiver em `COLLECTING_PARAMETERS`, o extrator transacional recebe o conjunto completo de `policy.requires` como campos editáveis do turno. A LLM continua autorizada a devolver somente valores realmente presentes/inequívocos na fala atual. O merge mantém os valores antigos para campos não citados e sobrescreve apenas as chaves efetivamente extraídas. + +Precedência resultante: + +1. fala atual explicitamente corrige/preenche required field; +2. valor previamente coletado é preservado apenas se a fala atual não o alterar; +3. parâmetros ainda ausentes continuam sendo coletados; +4. somente depois disso é avaliada mudança de intenção. + +## Caso de regressão coberto + +Estado anterior: + +- `valor=19.99` +- `subject` pendente + +Mensagem atual: + +- `desculpa, é a de quatorze e noventa e nove` + +Router/contexto resolve: + +- `subject=Tamboro Mensal` + +Extrator do runtime corrige: + +- `valor=14.99` + +Resultado esperado antes da confirmação: + +- `subject=Tamboro Mensal` +- `valor=14.99` + +## Testes + +Foram executados: + +- 46 testes de runtime/roteamento/parâmetros transacionais; +- 27 testes de migração ligados a contestação/CVAL/paridade. + +Todos passaram. diff --git a/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md b/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md new file mode 100644 index 0000000..ab174e6 --- /dev/null +++ b/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md @@ -0,0 +1,104 @@ +# Paridade de Integração Contas — Mock x Sistemas Reais + +Data: 2026-08-22 + +## Objetivo + +Validar o agente Contas migrado contra o código original, usando o legado como fonte de verdade para contratos HTTP, autenticação, headers, payloads, URLs, timeouts e comportamento de integração. O objetivo é manter o funcionamento com mocks locais sem impedir a execução contra sistemas reais quando `TIM_GATEWAY_MODE`/`TIM_USE_MOCK_GATEWAY` forem configurados para modo real. + +## Correção crítica — identidade do item transacional + +Foi corrigido o caso em que um pedido explícito para `TIM CTRL Redes Sociais 8.0` podia ser reinterpretado pelo matcher fuzzy como `TIM Fashion Mensal`. + +### Causa + +O `InvoiceResolver` eliminava seções não transacionáveis (por exemplo, planos) antes da resolução de identidade e, em seguida, aplicava similaridade somente sobre VAS. Como `TIM Fashion Mensal` ultrapassava o threshold do matcher, o `subject` era substituído antes da execução. + +### Correção + +- A resolução exata de identidade agora acontece antes de qualquer fuzzy matching. +- A busca exata considera também itens fora do escopo transacional, como planos. +- Um plano encontrado exatamente é classificado como `out_of_scope` para a operação VAS. +- O fuzzy matching continua restrito aos candidatos realmente tratáveis. +- `resolve_items()` preserva o comportamento anterior onde necessário para compatibilidade; o fluxo operacional usa a proteção de identidade. + +Resultado esperado para o caso: + +`TIM CTRL Redes Sociais 8.0` -> exact match -> `plano` -> `out_of_scope` -> não substituir por outro VAS -> não executar cancelamento. + +## Comparação com o código original + +Foram comparados os comandos do projeto original (`agente_contas_tim/commands`), `factory.py`, `config.py` e o gateway HTTP com o adaptador atual `app/domain/contas/client.py`. + +| Serviço/Integração | Contrato encontrado no original | Situação no migrado após revisão | +|---|---|---| +| Consulta VAS | GET, URL com `{msisdn}` ou append `/msisdn`, normalização para prefixo 55, clientId | Corrigido: aliases originais, timeout, append e prefixo 55 | +| Histórico VAS | GET com `?msisdn=`, clientId/messageId/auth | Corrigido default `clientId=AIAGENTCR` | +| Bloqueio VAS | POST, contratos de payload `pmid`/`input`/`vasBlock`, headers extras | Corrigidos aliases de URL/auth/timeout/clientId/operation/payload/encoding | +| Cancelamento VAS | DELETE, body com channel/msisdn/appId/cspId/interactionProtocol, OAM/CN/type opcionais | Corrigidos aliases `TIM_CANCELLATION_*` e `TIM_CANCELAMENTO_*` | +| Divergência / explicação de fatura | GET `/?channel=AIAGENTCR`, Basic opcional user/password, clientID | Corrigidos aliases, Basic auth e timeout | +| CompleteInvoices | POST `{"msisdn": ...}`, `ClientID=AIAGENTCR` | Compatível; timeout respeitado | +| Profile bill | Factory original usa configuração de CompleteInvoices | Corrigido para priorizar contrato/config de CompleteInvoices | +| Profile full | GET com placeholder ou append `/msisdn`, `ClientID=AIAGENTCR` | Corrigido append e timeout | +| Line info | Mesmo padrão de URL do profile full | Corrigido append e timeout | +| Contrato | GET `/`, clientId do legado | Corrigido default `AIAGENTCR` | +| Protocolo V2 | POST serviceRequest/interaction, headers opcionais OAM/CN/type | Mantido no adaptador atual | +| Contestação do cliente | POST, clientId/messageId/X-Agent-Id, user configurável | Corrigido default via `TIM_CUSTOMER_CONTESTATION_USER_ID` | +| Atualização de Service Request | POST, channel/serviceRequest, headers de integração | Mantido | +| Tracking Activities | POST com customer/protocol/invoice/activity/user | Mantido | +| SMS | POST com msisdn/sender/message/URL e receipt opcional | Mantido | +| Bill PDF detalhada | POST com invoiceId/customerId e invoiceType `DETALHADA`, retorno PDF | Aliases ampliados | +| Secure PDF / invoice recover | GET com invoiceId/msisdn/customerId | Aliases/header ajustados | + +## Configurações presentes no legado sem uso operacional comprovado + +- `status_customer`: configuração encontrada, mas sem comando/runtime consumidor localizado na revisão. +- configuração OAuth específica de SMS: declarada no config original, mas sem consumidor runtime localizado. + +Esses itens não foram tratados como requisito ativo sem evidência de uso no código original. + +## Mock x modo real + +O mock continua suportado. Em mock, o cliente retorna fixtures locais para as operações previstas. Em modo real, o mesmo adaptador segue os contratos HTTP reconstruídos a partir do código original. + +A principal diferença de risco é que um mock tende a responder `200/OK` para cenários preparados. Por isso, a validação de identidade deve ocorrer antes do gateway — como agora ocorre — para impedir que um erro de resolução de entidade seja mascarado pelo mock e, principalmente, que chegue a um backend real. + +## Testes executados + +### Regressão + contratos existentes + +- 101 testes passaram no conjunto de contratos, paridade, idempotência e resolução. + +### Novos testes de compatibilidade legado/real + +- 7 testes passaram cobrindo: + - prefixo 55 e composição da URL de consulta VAS; + - aliases originais e timeout; + - aliases de cancelamento; + - append de MSISDN em profile full; + - Basic auth de divergência via usuário/senha; + - client IDs de histórico VAS e contrato; + - uso de CompleteInvoices no profile bill. + +### Suite `tests/migration` + +Resultado observado após as mudanças: + +- 660 passed +- 4 failed + +As quatro falhas remanescentes são de configuração/contexto de guardrails (`conversation_history` e FRASEOLOGIA) e não estão relacionadas ao `InvoiceResolver` nem aos contratos de integração revisados. + +## Limite desta validação + +A revisão comprova paridade de contrato em nível de código-fonte e testes locais. Ela não é uma certificação de conectividade real porque não foram usados endpoints, credenciais ou rede dos sistemas legados neste ambiente. + +Para homologação real, recomenda-se executar testes de contrato contra um ambiente não produtivo dos serviços TIM, verificando status HTTP, schemas reais, autenticação, timeouts, headers obrigatórios e respostas de erro. + +## Gaps de endurecimento recomendados + +1. Adicionar validação de readiness no startup quando `mock=false`, falhando cedo se endpoint/auth obrigatórios estiverem ausentes. +2. Criar testes de contrato contra ambiente de homologação para cada integração ativa. +3. Comparar periodicamente fixtures mock com schemas/respostas reais para evitar drift. +4. Manter invariantes transacionais: item solicitado, item resolvido e item executado nunca podem divergir silenciosamente. +5. Evoluir mascaramento/observabilidade do cliente migrado para o mesmo nível do `HttpGateway` original, sem registrar secrets. diff --git a/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md b/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md new file mode 100644 index 0000000..c792196 --- /dev/null +++ b/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md @@ -0,0 +1,472 @@ +# Manual do Agent Contas Migrado para agent_framework_oci + +## 1. Objetivo + +Esta versão reconstrói o Agent Contas sobre o `agent_framework_oci` com uma regra arquitetural simples: **o código executável novo não depende do pacote anterior do Contas**. O projeto anterior é apenas referência funcional para preservar regras, contratos de API, fixtures e comportamentos de negócio durante a migração. + +O agente novo reutiliza do framework tudo que é infraestrutura genérica: LangGraph, router, stickiness, supervisor, confirmação transacional, clarificação, memória, summary memory, long-term memory, checkpoints, persistence, RAG, embeddings, MCP Tool Router, guardrails, output supervisor, judges, identity, channels, SSE, usage accounting e telemetria. + +O novo domínio Contas mantém somente o que é realmente específico da TIM: chamadas de faturas, VAS, contestação, protocolos, tracking, SMS, Secure PDF e regras que relacionam essas operações. + +## 2. Regra de independência + +A Definition of Done da migração é: + +```bash +grep -R "agente_contas_tim" app mcp config +``` + +Resultado esperado: nenhuma ocorrência/import do pacote anterior. + +O pacote entregue já inclui `tests/migration/test_no_legacy_dependency.py` para impedir regressão dessa regra. + +## 3. Arquitetura + +```text +Canal / Frontend + | + v +app/main.py + | + v +agent_framework_oci + |-- ChannelGateway / IdentityResolver + |-- LangGraph / AgentWorkflow + |-- EnterpriseRouter / Route Stickiness / Supervisor + |-- Guardrails / Output Supervisor / Judges + |-- Memory / Summary Memory / LTM / Checkpoints + |-- RAG / Embeddings / Cache + |-- MCPToolRouter + |-- Langfuse / Analytics / OTEL / OCI Streaming + | + v +MCP Contas :8400 + | + v +app/domain/contas + |-- TimApiClient + |-- ContasDomainService + `-- fixtures de desenvolvimento + | + v +APIs TIM +``` + +Não existe um segundo LangGraph, LLM gateway, confirmation manager, workflow engine ou memory store dentro do MCP. + +## 4. Agentes de domínio + +A versão migrada possui quatro agentes reais do domínio Contas: + +| Agente | Responsabilidade | +|---|---| +| `faturas_agent` | Consulta de faturas, composição, variação e explicação de cobrança | +| `vas_agent` | Consulta de VAS, histórico, serviços estratégicos/bundles e informação de serviços | +| `contestacao_agent` | Cancelamento transacional de VAS e contestação de cobrança | +| `suporte_contas_agent` | Protocolos, acompanhamento, suporte e encerramento | + +Todos herdam `AgentRuntimeMixin` do framework. Eles não implementam máquina de confirmação/clarificação própria. + +## 5. LangGraph do framework + +O fluxo principal é o `StateGraph` do `agent_framework_oci` usado em `app/workflows/agent_graph.py`: + +```text +START + -> input_guardrails + -> load_long_term_memory + -> routing_decision + -> agente de domínio + -> output_supervisor + -> output_guardrails + -> judge + -> supervisor_review + -> persist_long_term_memory + -> persist + -> END +``` + +O `EnterpriseRouter` decide a intent, agente e tools. O route stickiness decide continuidade da conversa. O runtime do framework controla coleta de parâmetros e confirmação de tools transacionais. + +## 6. Transações + +As tools abaixo são transacionais em `config/tool_policies.yaml`: + +- `cancelar_vas_avulso` +- `tratar_vas_estrategico` +- `contestar_cobranca` + +A confirmação ocorre **antes** da chamada MCP e é responsabilidade do `AgentRuntimeMixin`. O domínio recebe a chamada somente depois de a política do framework permitir execução. + +Isso evita o problema clássico de um "sim" responder à pergunta errada: a confirmação está vinculada ao estado transacional/tool pendente do framework, não a heurísticas no prompt. + +## 7. RAG + +Conhecimento conceitual não é uma API TIM e por isso não é implementado como "workflow de busca" dentro do MCP. + +O projeto usa diretamente: + +- `RagService` +- `create_embedding_provider()` +- `VECTOR_STORE_PROVIDER` +- `GRAPH_STORE_PROVIDER` +- `EMBEDDING_PROVIDER` + +`buscar_informacao` permanece desabilitada no catálogo MCP; perguntas de conhecimento passam pelo RAG nativo do framework. + +## 8. Funcionalidades migradas + +| Funcionalidade do Contas | Nova implementação | Responsabilidade do framework | +|---|---|---| +| Consulta de faturas | `ContasDomainService.consultar_faturas` | seleção da tool, identity, cache, resposta | +| Explicação de fatura | API de fatura + billing analysis como evidência | LLM produz explicação grounded | +| Consulta VAS | `consultar_vas` | routing/tool selection | +| Histórico VAS | `consultar_historico_vas` | routing/tool selection | +| Cancelamento VAS avulso | consulta -> match -> bloqueio -> cancelamento | parâmetros + confirmação + estado | +| VAS estratégico/bundle | domínio retorna serviço e orientação | conversa/continuidade no LangGraph | +| Contestação | faturas/contrato/profile -> protocolo -> contestação -> tracking | parâmetros + confirmação + estado | +| Status de solicitação | `consultar_status_solicitacao` | roteamento e contexto | +| SMS | `enviar_sms` | tool policy/contexto | +| Secure PDF | `recuperar_fatura_pdf` | roteamento/identity | +| Encerramento | efeitos de domínio opcionais | `end_session`, memória e telemetria | +| Guardrails | nenhum código local duplicado | framework | +| Judges | nenhum código local duplicado | framework | +| Memória | nenhum store local de conversa | framework | +| LTM | nenhum mecanismo local | framework | +| Checkpoint | nenhum `MemorySaver` dentro do MCP | framework | +| Telemetria | eventos do runtime/framework | framework | + +## 9. Estrutura do projeto + +```text +app/ + main.py + state.py + agents/ + faturas_agent.py + vas_agent.py + contestacao_agent.py + suporte_contas_agent.py + domain/contas/ + client.py + service.py + fixtures/ + workflows/ + agent_graph.py + observability/ + +config/ + routing.yaml + tools.yaml + tool_policies.yaml + mcp_servers.yaml + mcp_parameter_mapping.yaml + identity.yaml + prompts/ + +contas_mcp/servers/contas_mcp_server/ + main.py + +agent_framework_oci/ + ... framework reutilizado ... +``` + +## 10. Arquivo `.env` + +O `.env` fornecido para esta reconstrução foi preservado byte a byte no pacote. Não foi recomposto nem reduzido. + +O arquivo contém dois grupos: + +1. configurações do `agent_framework_oci`; +2. variáveis TIM de domínio/compatibilidade já compiladas para os ambientes. + +Embora algumas variáveis antigas possam deixar de ser usadas depois da migração, elas foram mantidas para não perder o trabalho de consolidação. A remoção deve ocorrer apenas após testes de DEV/FQA/PRD. + +### Variáveis principais do framework + +- `LLM_PROVIDER` +- `OCI_AUTH_MODE`, `OCI_CONFIG_FILE`, `OCI_PROFILE`, `OCI_COMPARTMENT_ID`, `OCI_REGION` +- `SESSION_REPOSITORY_PROVIDER` +- `MEMORY_REPOSITORY_PROVIDER` +- `CHECKPOINT_REPOSITORY_PROVIDER` +- `VECTOR_STORE_PROVIDER` +- `GRAPH_STORE_PROVIDER` +- `EMBEDDING_PROVIDER` +- `ENABLE_LANGFUSE` +- `ENABLE_INPUT_GUARDRAILS` +- `ENABLE_OUTPUT_GUARDRAILS` +- `ENABLE_JUDGES` +- `ENABLE_SUPERVISOR` +- `ENABLE_ROUTE_STICKINESS` +- `ENABLE_MCP_TOOLS` +- `ENABLE_CONVERSATION_SUMMARY_MEMORY` +- `ENABLE_LONG_TERM_MEMORY` + +### Modo mock x APIs TIM reais + +Mock atual: + +```env +TIM_GATEWAY_MODE=mock +TIM_USE_MOCK_GATEWAY=true +``` + +Integrações reais: + +```env +TIM_GATEWAY_MODE=real +TIM_USE_MOCK_GATEWAY=false +``` + +Os dois valores devem estar coerentes. + +## 11. Integrações TIM + +| Integração | Variável | Método | VPN TIM provável | +|---|---|---|---| +| Complete Invoices | `TIM_COMPLETE_INVOICES_URL` | POST | Sim em FQA interno | +| Billing Analysis | `TIM_DIVERGENCIA_URL` | POST | Sim | +| Consulta VAS | `TIM_URL_CONSULTA_VAS` | GET | Sim | +| Histórico VAS | `TIM_VAS_HISTORY_URL` | GET | Sim | +| Bloqueio VAS | `TIM_URL_BLOQUEIO_VAS` | POST | Sim | +| Cancelamento VAS | `TIM_CANCELAMENTO_URL` | DELETE | Sim | +| Contrato | `TIM_CONTRATO_URL` | GET | Sim | +| Full Profile | `TIM_PROFILE_FULL_URL` | GET | Sim | +| Contestação | `TIM_CUSTOMER_CONTESTATION_URL` | POST | Sim | +| Protocolo | `TIM_PROTOCOL_URL` | POST | Sim | +| Service Request Status | `TIM_SERVICE_REQUEST_STATUS_URL` | POST | Sim | +| Tracking Activities | `TIM_TRACKING_ACTIVITIES_URL` | POST | Sim | +| SMS | `TIM_SMS_URL` | POST | Sim | +| Secure PDF | `TIM_URL_INVOICE_RECOVER` | POST | Sim | + +Os endpoints FQA do `.env` usam `pmidfqa.internal.timbrasil.com.br`; portanto DNS/rota corporativa precisa estar disponível para teste real. + +## 12. Outras integrações + +| Integração | Uso | Ativação | +|---|---|---| +| OCI GenAI | LLM | `LLM_PROVIDER=oci_sdk` + `OCI_AUTH_MODE` | +| Autonomous DB | session/memory/checkpoint/vector/usage | providers `autonomous` + `ADB_*` | +| OCI Embeddings | RAG | `EMBEDDING_PROVIDER=oci` | +| Langfuse | tracing | `ENABLE_LANGFUSE=true` | +| GCP Pub/Sub | analytics corporativo | `ENABLE_ANALYTICS=true`, provider Pub/Sub e credencial GCP | +| MongoDB | sequence Pub/Sub | `PUBSUB_SEQUENCE_PROVIDER=mongodb` | +| Redis | cache/sequence opcional | `ENABLE_REDIS_CACHE=true` ou provider sequence redis | +| OTEL | logs/traces | `ENABLE_OTEL=true` + endpoint | +| OCI Streaming | eventos alternativos | `ENABLE_OCI_STREAMING=true` | + +## 13. Instalação local + +Recomendado: Linux/WSL com Python 3.13. + +```bash +cd contas_migrado_framework_native +uv sync +``` + +Se o `uv` ainda não estiver disponível, instale-o conforme o padrão do seu ambiente e depois execute `uv sync`. + +## 14. Subir o MCP Contas + +Terminal 1: + +```bash +uv run uvicorn contas_mcp.servers.contas_mcp_server.main:app \ + --host 0.0.0.0 --port 8400 +``` + +Validar: + +```bash +curl http://localhost:8400/health +curl http://localhost:8400/mcp/tools/list +``` + +`/health` deve reportar: + +```json +{ + "status": "ok", + "architecture": "framework-native", + "legacy_dependency": false +} +``` + +## 15. Subir o Agent Contas + +Terminal 2: + +```bash +uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 +``` + +Validar: + +```bash +curl http://localhost:8000/health +``` + +## 16. Smoke test do MCP em mock + +```bash +PYTHONPATH=".:agent_framework_oci/libs/agent_framework/src" \ +python scripts/smoke_mcp.py +``` + +Esse teste cobre faturas, invoice explanation, VAS, histórico, cancelamento e contestação usando fixtures migradas para o novo domínio. + +## 17. Testar o agente pelo Gateway + +Exemplo de consulta: + +```bash +curl -X POST http://localhost:8000/gateway/message \ + -H 'Content-Type: application/json' \ + -d '{ + "channel":"web", + "agent_id":"telecom_contas", + "tenant_id":"default", + "payload":{ + "text":"Quero consultar minha fatura", + "session_id":"contas-test-001", + "user_id":"user-001", + "msisdn":"11999999999", + "message_id":"msg-001" + } + }' +``` + +Depois teste continuidade na mesma `session_id`. + +## 18. Teste transacional de VAS + +1. Envie: `Quero cancelar TIM Fashion Mensal`. +2. O framework deve identificar tool transacional e pedir confirmação. +3. Responda `sim` na mesma sessão. +4. Somente então `cancelar_vas_avulso` deve ser chamada. +5. Em modo mock, o resultado deve conter `block.status=200` e `cancellation.status=200`. + +Teste negativo importante: + +1. Entre em estado aguardando confirmação de cancelamento. +2. Envie `você ainda está por aí?`. +3. A frase não pode confirmar a transação. + +## 19. Teste de contestação + +Em mock: + +```text +Quero contestar Tamboro Mensal no valor de 14,99, não reconheço essa cobrança. +``` + +Esperado: + +- route `contestacao_agent`; +- parâmetros `subject` e `valor` coletados; +- confirmação antes da mutação; +- abertura de protocolo; +- contestação; +- tracking; +- resposta final grounded nos retornos da tool. + +## 20. Testes de memória + +### Short-term / summary + +Na mesma sessão: + +```text +Meu serviço é TIM Fashion Mensal. +... +Qual serviço eu mencionei antes? +``` + +### Long-term memory + +Com `ENABLE_LONG_TERM_MEMORY=true`, grave uma informação elegível, encerre a sessão e abra outra sessão com a mesma identidade de negócio. Verifique se o contexto é recuperado conforme as regras de LTM do framework. + +## 21. Testes de RAG + +Valide que a intent de conhecimento não dispara MCP desnecessariamente. Exemplos: + +```text +O que significa cobrança proporcional? +Como funciona o vencimento da fatura? +``` + +O trace deve mostrar `RagService`; a tool `buscar_informacao` não precisa ser executada. + +## 22. Testes de guardrails e judges + +Com as flags habilitadas no `.env`: + +- prompt injection deve passar pelos input guardrails; +- resposta candidata passa pelo Output Supervisor/output guardrails; +- groundedness deve considerar `mcp_results` quando a resposta usa dados TIM; +- judges rodam após a geração e antes da persistência final. + +Use o Langfuse para observar a sequência de nodes do LangGraph. + +## 23. Teste de conectividade/VPN + +O pacote contém: + +```bash +python scripts/check_integrations.py +``` + +O script resolve DNS e testa TCP dos endpoints configurados. Execute antes e depois de conectar a VPN. + +Para APIs FQA, um resultado `DNS_FAIL`, `TCP_FAIL` ou timeout indica que a rede ainda não está pronta. Um `TCP_OK` prova conectividade de rede, mas não autenticação/contrato HTTP. + +## 24. Ativar APIs reais gradualmente + +Não habilite todas as mutações de uma vez. Ordem recomendada: + +1. VPN/DNS; +2. `consultar_faturas`; +3. `consultar_vas`; +4. `consultar_historico_vas`; +5. contrato/profile; +6. billing analysis; +7. Secure PDF; +8. protocol/status/tracking; +9. SMS; +10. cancelamento VAS; +11. contestação. + +Depois faça teste end-to-end completo. + +## 25. Kubernetes + +Use o mesmo `.env` como fonte para construir ConfigMap/Secret, separando segredos no mecanismo corporativo apropriado. O backend necessita alcançar: + +- MCP Contas; +- OCI GenAI; +- Autonomous DB; +- Langfuse, se habilitado; +- endpoints TIM internos em modo real; +- providers de analytics habilitados. + +O MCP pode rodar no mesmo pod como sidecar ou, preferencialmente, como deployment/service separado. Configure `config/mcp_servers.yaml` para o DNS do Service Kubernetes. + +## 26. Critérios de aceite da migração + +A migração é considerada concluída quando: + +- [ ] zero imports/referências executáveis ao pacote anterior; +- [ ] backend e MCP sobem após o diretório anterior ser removido; +- [ ] read-only APIs funcionam em FQA; +- [ ] cancelamento exige confirmação do framework e funciona em FQA; +- [ ] contestação exige confirmação e reproduz efeitos esperados; +- [ ] RAG usa `RagService` do framework; +- [ ] memory/summary/LTM/checkpoint usam providers do framework; +- [ ] guardrails e judges aparecem nos traces; +- [ ] LangGraph é a única máquina de estados conversacional; +- [ ] Pub/Sub/sequence/OTEL/Langfuse são validados conforme ambiente; +- [ ] testes de carga são executados antes de produção. + +## 27. Segurança do `.env` + +O arquivo preservado contém material sensível. Ele foi mantido porque isso foi um requisito explícito da reconstrução. Para distribuição fora do ambiente controlado, rotacione credenciais expostas e substitua valores por Secrets/Vault/Key Vault/Kubernetes Secret conforme política corporativa. diff --git a/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md b/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md new file mode 100644 index 0000000..1311dcc --- /dev/null +++ b/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md @@ -0,0 +1,1881 @@ +# Manual do Desenvolvedor — Workflows do Agente Contas + +> **Projeto de referência:** `agent_contas_fechado_COMPLETO_corrigido_v4` +> **Pasta documentada:** `/workflows` +> **Público:** desenvolvedores que precisam criar, alterar, depurar ou revisar jornadas determinísticas do agente Contas. + +--- + +## 1. Objetivo deste manual + +A pasta `workflows/` contém a **orquestração declarativa de jornadas de negócio** do agente Contas. Ela não é apenas uma coleção de YAMLs: cada arquivo descreve um pequeno grafo de execução que o runtime genérico do `agent_framework_oci` carrega, valida, executa, pausa, retoma e finaliza. + +O princípio central é: + +> **O workflow decide a sequência, as condições e os pontos de pausa. A action executa a operação de domínio. O framework fornece o motor genérico.** + +Isso evita que regras de jornada fiquem escondidas em `if/else` dentro dos agentes ou do framework. + +Este documento explica: + +- como o versionamento dos workflows funciona; +- como ler um arquivo `.vN.yaml`; +- o significado de `name`, `version`, `start`, `nodes`, `edges`, `when`, `priority`, `pause`, `expected_input` e `resume_from`; +- como funcionam `$.input`, `$.vars`, `$.output` e o estado interno; +- como uma `action` declarada no YAML se conecta a Python; +- como um workflow pausa e continua em outro turno; +- como o classificador semântico de `expected_input` funciona; +- como um workflow termina sem trocar o `session_id`; +- como cada arquivo atual da pasta `workflows/` funciona, ponto a ponto; +- como adicionar uma nova versão com segurança. + +--- + +## 2. Estrutura atual da pasta + +```text +workflows/ +├── buscar_fatura.active.yaml +├── buscar_fatura.v1.yaml +├── buscar_informacao.active.yaml +├── buscar_informacao.v2.yaml +├── cancelamento_vas_avulso.active.yaml +├── cancelamento_vas_avulso.v1.yaml +├── contestacao_tool.active.yaml +├── contestacao_tool.v2.yaml +├── finalizar_atendimento.active.yaml +├── finalizar_atendimento.v1.yaml +├── invoice_explanation.active.yaml +├── invoice_explanation.v2.yaml +├── pro_rata.active.yaml +├── pro_rata.v3.yaml +├── termino_desconto.active.yaml +├── termino_desconto.v1.yaml +├── valor_divergente.active.yaml +├── valor_divergente.v1.yaml +├── vas_estrategico.active.yaml +└── vas_estrategico.v3.yaml +``` + +Há sempre dois papéis diferentes: + +1. **`.active.yaml`** — marcador da versão ativa; +2. **`.vN.yaml`** — definição completa e versionada do grafo. + +Exemplo: + +```yaml +# invoice_explanation.active.yaml +version: 2 +``` + +Esse arquivo não contém a lógica. Ele informa ao `FileWorkflowRepository` que, quando alguém pedir o workflow ativo `invoice_explanation`, deve ser carregado: + +```text +invoice_explanation.v2.yaml +``` + +### Regra prática de versionamento + +Não altere silenciosamente a semântica de uma versão já publicada quando a mudança for incompatível ou material. Prefira: + +```text +invoice_explanation.v2.yaml # versão atual +invoice_explanation.v3.yaml # nova implementação +invoice_explanation.active.yaml -> version: 3 +``` + +Assim rollback e auditoria permanecem simples. + +--- + +## 3. Quem faz o quê + +A arquitetura pode ser entendida em quatro camadas. + +```mermaid +flowchart LR + U[Usuário] --> AG[Agente / Router] + AG --> W[Workflow YAML] + W --> RT[WorkflowRuntime do framework] + RT --> A[Actions Python do domínio Contas] + A --> S[Services / MCP / APIs legadas] + A --> RT + RT --> AG +``` + +### 3.1 Workflow YAML + +Responsável por: + +- sequência dos passos; +- branching; +- prioridade das transições; +- definição de pausa; +- contrato da resposta esperada do usuário; +- nó de retomada; +- declaração de valores fixos da jornada; +- escolha de qual action executar. + +### 3.2 `WorkflowRuntime` + +É genérico e pertence ao framework. Ele: + +- carrega o YAML; +- valida o grafo; +- resolve expressões `$.…`; +- executa actions; +- mantém `vars`, `output`, `trace` e estado; +- ordena edges por `priority`; +- avalia `when`; +- implementa pause/resume; +- usa checkpoint do LangGraph em produção; +- retorna `COMPLETED`, `PAUSED` ou `FAILED`. + +O runtime não deve conhecer regras específicas da TIM ou do Contas. + +### 3.3 Actions Python + +No Contas, a maioria das actions declaradas nos YAMLs é registrada em: + +```text +app/domain/contas/workflow_actions.py +``` + +por meio de: + +```python +reg = WorkflowActionRegistry() + +@reg.action("nome_da_action") +def nome_da_action(params, state): + ... + return {...} +``` + +Uma action deve receber: + +```python +params: dict +state: dict +``` + +E deve retornar **sempre um `dict`**. + +### 3.4 Services / MCP / legado + +As actions podem chamar `ContasDomainService`, clientes HTTP, integrações TIM, mocks ou outros componentes. O YAML não deve conter código de transporte. + +--- + +## 4. Anatomia de um workflow + +Um workflow mínimo é: + +```yaml +name: exemplo +version: 1 +start: primeiro + +nodes: + - id: primeiro + action: minha_action + input: + msisdn: $.input.msisdn + +edges: + - from: primeiro + to: END +``` + +### 4.1 `name` + +Nome lógico do workflow. + +Precisa ser coerente com o nome do arquivo: + +```text +exemplo.v1.yaml +name: exemplo +version: 1 +``` + +O repository valida essa correspondência. + +### 4.2 `version` + +Número inteiro da versão do contrato do workflow. + +### 4.3 `start` + +ID do primeiro nó executado. + +### 4.4 `nodes` + +Cada nó representa uma unidade de execução. + +```yaml +- id: preparar + action: preparar_invoice_explanation + input: + msisdn: $.input.msisdn +``` + +O `id` é o nome do nó dentro do grafo. A `action` é o nome registrado no `WorkflowActionRegistry`. + +### 4.5 `edges` + +Definem para onde o grafo segue após um nó. + +```yaml +- from: preparar + to: formatar +``` + +ou condicionalmente: + +```yaml +- from: preparar + to: formatar + priority: 10 + when: + eq: [$.vars.preparar.success, true] +``` + +### 4.6 `END` + +`END` representa término do grafo. + +```yaml +- from: finalizar + to: END +``` + +--- + +## 5. Modelo de estado e expressões `$.…` + +Essa é uma das partes mais importantes para quem altera a pasta `workflows/`. + +### 5.1 `$.input` + +Representa os dados de entrada da execução. + +Exemplo: + +```yaml +input: + msisdn: $.input.msisdn + invoice_id: $.input.invoice_id +``` + +Se o workflow foi iniciado com: + +```json +{ + "msisdn": "11999999999", + "invoice_id": "3000131180" +} +``` + +os dois valores serão passados à action. + +### 5.2 `$.vars.` + +Após cada action retornar um dicionário, o runtime armazena o resultado em: + +```text +$.vars. +``` + +Exemplo: + +```yaml +- id: registrar_protocolo + action: registrar_protocolo +``` + +Se a action retornar: + +```json +{ + "success": true, + "protocolo_id": "1234567890" +} +``` + +então outro nó pode usar: + +```yaml +protocolo_id: $.vars.registrar_protocolo.protocolo_id +``` + +### 5.3 `$.output` + +Aponta para o último resultado de action colocado como output corrente. + +É muito usado em `pause.return_from`: + +```yaml +pause: + return_from: $.output.mensagem +``` + +### 5.4 `$.nodes` + +O runtime também mantém resultados por nó em uma estrutura de nós. Na maior parte dos workflows do Contas, `$.vars` é a forma declarativa utilizada para encadear dados. + +### 5.5 Exemplo encadeado + +```yaml +- id: preparar + action: preparar + +- id: formatar + action: formatar + input: + dados: $.vars.preparar.dados +``` + +Fluxo: + +```text +preparar() -> {dados: X} + | + v +$.vars.preparar.dados + | + v +formatar(dados=X) +``` + +--- + +## 6. Conditions e prioridade de edges + +O runtime agrupa as edges por nó de origem e ordena por `priority` crescente. + +Portanto: + +```yaml +priority: 10 +``` + +é avaliada antes de: + +```yaml +priority: 99 +``` + +### 6.1 Fallback padrão + +Um padrão comum é: + +```yaml +- from: action_x + to: caminho_especial + priority: 10 + when: + eq: [$.vars.action_x.alguma_flag, true] + +- from: action_x + to: caminho_padrao + priority: 99 +``` + +A edge de prioridade 99 funciona como fallback porque não possui `when`. + +### 6.2 Operadores encontrados nos workflows atuais + +Exemplos: + +```yaml +when: + eq: [$.vars.preparar.success, true] +``` + +```yaml +when: + neq: [$.vars.x.barcode, ""] +``` + +```yaml +when: + exists: $.vars.x.barcode +``` + +```yaml +when: + all: + - eq: [$.vars.x.a, true] + - eq: [$.vars.x.b, true] +``` + +```yaml +when: + any: + - eq: [$.vars.x.a, true] + - eq: [$.vars.x.b, true] +``` + +### Regra importante + +As edges devem ser mutuamente compreensíveis. Se nenhuma edge corresponder, o runtime pode falhar com: + +```text +Nenhuma transição do workflow correspondeu ao estado +``` + +Por isso fluxos condicionais normalmente possuem uma edge final sem `when`. + +--- + +## 7. Pause / resume + +Workflows conversacionais podem parar no meio da execução para pedir uma resposta ao usuário. + +Exemplo simplificado: + +```yaml +- id: formatar + action: formatar_invoice_explanation + pause: + enabled: true + return_from: $.output.mensagem + expected_input: + key: resposta_usuario + allowed_values: ["SIM", "NAO"] + normalize: upper_strip + resume_from: decisao +``` + +### 7.1 O que ocorre no primeiro turno + +1. `formatar_invoice_explanation` é executada; +2. a mensagem produzida é obtida de `$.output.mensagem`; +3. o runtime persiste o checkpoint; +4. retorna `status=PAUSED`; +5. a mensagem é enviada ao usuário; +6. o workflow fica aguardando input. + +### 7.2 O que ocorre no turno seguinte + +A nova fala é validada contra `expected_input`. + +Se aceita: + +```text +resposta do usuário + ↓ +normalize + ↓ +$.input.resposta_usuario + ↓ +resume_from: decisao +``` + +### 7.3 Por que pause é separado da action + +No runtime atual, pause/resume é implementado em um nó técnico separado. Isso é deliberado. + +Ao retomar, **a action anterior não é reexecutada**. Isso evita repetir efeitos externos como: + +- abrir protocolo duas vezes; +- cancelar duas vezes; +- enviar SMS novamente; +- criar duas SRs. + +--- + +## 8. `expected_input` + +Exemplo: + +```yaml +expected_input: + key: resposta_usuario + allowed_values: ["SIM", "NAO", "OUTRO"] + normalize: upper_strip +``` + +### `key` + +Nome em que o valor normalizado será gravado em `$.input`. + +### `allowed_values` + +Valores internos permitidos para a decisão do workflow. + +Esses valores são **tokens de controle**, não necessariamente texto exibido ao cliente. + +### `normalize: upper_strip` + +Remove espaços laterais e converte para maiúsculas. + +Exemplo: + +```text +" sim " -> "SIM" +``` + +### `reprompt` + +Mensagem utilizada quando o input não pode ser interpretado pelo contrato. + +--- + +## 9. Semantic classifier de `expected_input` + +O `invoice_explanation` possui um classificador semântico declarativo. + +Ele existe porque respostas reais do usuário raramente são somente `sim` ou `não`. + +Exemplo: + +```text +"entendi, obrigado, era só isso" +``` + +semanticamente é `SIM`. + +Já: + +```text +"então no mês que vem vou pagar menos?" +``` + +não é `SIM`, mesmo contendo sinal de compreensão; é uma continuação da pergunta. + +O YAML define: + +```yaml +semantic_classifier: + enabled: true + include_relevant_context: true + option_actions: + CONTINUAR: + action: contextual_reentry + prompt: | + ... +``` + +### 9.1 Responsabilidade correta + +- **Framework:** executa o classificador e garante que a saída esteja entre os valores permitidos. +- **Workflow/agente:** define o significado de `SIM`, `NAO`, `CONTINUAR`. + +### 9.2 `contextual_reentry` + +Quando a opção classificada possui: + +```yaml +CONTINUAR: + action: contextual_reentry +``` + +o workflow pausado não deve simplesmente tratar a fala como confirmação. A utterance é liberada para nova interpretação pelo roteamento normal, com contexto delimitado. + +Isso é particularmente importante para evitar que hipóteses do usuário virem fatos confirmados. + +--- + +## 10. Estado terminal e nova interação na mesma sessão + +Um workflow pode terminar sem encerrar tecnicamente o `session_id`. + +Isso significa: + +```text +mesma sessão técnica + != +mesmo workflow ativo +``` + +No `invoice_explanation`, por exemplo: + +```yaml +workflow_response_final: true +``` + +indica que aquela action produz a resposta final daquele workflow. + +Depois de uma execução terminal, o framework deve eliminar o latch operacional do workflow para que o próximo turno seja uma nova entrada, ainda na mesma sessão. + +Deve permanecer: + +- `session_id`; +- `session_key`; +- `conversation_key`; +- identidade do cliente; +- auditoria e telemetria; +- long-term memory. + +Não deve continuar controlando a próxima entrada: + +- `pending_domain_workflow`; +- `expected_input`; +- pause antigo; +- active transaction antiga; +- confirmação antiga; +- route stickiness da jornada terminada; +- short-term operational context do workflow fechado. + +Esse detalhe é fundamental ao depurar cenários como: + +```text +Usuário: entendi, obrigado, era só isso +Agente: Seu número de protocolo é ... +Usuário: ah espera +``` + +O terceiro turno é uma nova entrada na mesma sessão, e não um resume do workflow anterior. + +--- + +# 11. Workflows atuais — explicação arquivo por arquivo + +--- + +## 11.1 `buscar_fatura.active.yaml` + +```yaml +version: 1 +``` + +Seleciona `buscar_fatura.v1.yaml` como versão ativa. + +## 11.2 `buscar_fatura.v1.yaml` + +### Objetivo + +Executar uma busca de fatura em um único passo. + +### Cabeçalho + +```yaml +name: buscar_fatura +version: 1 +start: buscar_fatura +``` + +### Nó `buscar_fatura` + +```yaml +- id: buscar_fatura + action: buscar_fatura + input: + invoice_id: $.input.invoice_id + msisdn: $.input.msisdn + customer_id: $.input.customer_id + output: $.input.output +``` + +A action Python está em `app/domain/contas/workflow_actions.py`. + +Com `invoice_id` e `customer_id`, ela tenta buscar a fatura detalhada. Sem os identificadores necessários, usa `consultar_faturas` como fallback. + +### Edge + +```yaml +- from: buscar_fatura + to: END +``` + +Não há branch nem pausa. + +### Modelo mental + +```text +INPUT + | + v +buscar_fatura + | + v +END +``` + +### Quando usar + +Quando a operação é atômica e não precisa de confirmação do usuário. + +--- + +## 11.3 `buscar_informacao.active.yaml` + +```yaml +version: 2 +``` + +Ativa `buscar_informacao.v2.yaml`. + +## 11.4 `buscar_informacao.v2.yaml` + +### Objetivo + +Preparar uma consulta RAG e, em seguida, preparar a resposta para composição pelo framework. + +### Nó 1 — `buscar_informacao` + +```yaml +id: buscar_informacao +action: buscar_informacao_rag +``` + +Recebe: + +- `query`; +- `queries`; +- `top_k`; +- `segment`. + +A action atual devolve uma estrutura declarando `delegate_to_framework_rag=true`, isto é, o domínio sinaliza que a capacidade RAG deve ser executada pelo framework. + +### Nó 2 — `reescrever_resposta` + +Consome os resultados do primeiro nó: + +```yaml +queries: $.vars.buscar_informacao.queries +documents: $.vars.buscar_informacao.documents +answer: $.vars.buscar_informacao.answer +noMatchRag: $.vars.buscar_informacao.noMatchRag +ragRetrievedDocuments: $.vars.buscar_informacao.ragRetrievedDocuments +ragSelectedDocuments: $.vars.buscar_informacao.ragSelectedDocuments +``` + +A action `reescrever_resposta_buscar_informacao` devolve a mensagem e `delegate_to_framework_llm=true`. + +### Fluxo + +```text +buscar_informacao_rag + | + v +reescrever_resposta_buscar_informacao + | + v +END +``` + +### Conceito importante + +A pasta `workflows/` orquestra, mas não deve implementar o mecanismo RAG. O framework continua responsável pelo runtime RAG. + +--- + +## 11.5 `cancelamento_vas_avulso.active.yaml` + +```yaml +version: 1 +``` + +## 11.6 `cancelamento_vas_avulso.v1.yaml` + +### Objetivo + +Executar cancelamento em lote de VAS avulso. + +### Nó único + +```yaml +id: cancelar_vas_avulso +action: cancelamento_vas_avulso_batch +``` + +Entradas: + +- `items`; +- `csp_id`; +- `channel`; +- `social_sec_no`; +- `data_credito_proxima_fatura`; +- `idempotency_key`. + +O YAML também fixa valores do contrato operacional: + +```yaml +request_status: "Fechado" +status: "CLOSED" +``` + +### Fluxo + +```text +cancelamento_vas_avulso_batch -> END +``` + +### Ponto de atenção + +É uma operação transacional. A confirmação do usuário e as políticas de tool podem ocorrer antes da entrada no workflow. Não mova para o YAML uma duplicação de confirmation policy que pertença ao framework. + +O `idempotency_key` é especialmente importante em operações com efeito externo. + +--- + +## 11.7 `contestacao_tool.active.yaml` + +```yaml +version: 2 +``` + +## 11.8 `contestacao_tool.v2.yaml` + +Este é o workflow mais complexo da pasta atual. + +### Objetivo + +Orquestrar a contestação de cobrança, incluindo protocolo, status da fatura, abertura da contestação, SMS quando aplicável, regra de corte, Conta Certa Manual e atualização de status. + +### Visão geral + +```mermaid +flowchart TD + A[registrar_protocolo] --> B[check_invoice_status] + B --> C[abrir_contestacao_cliente] + C -->|success=false| Z[END] + C -->|tem barcode| D[enviar_sms] + C -->|sem SMS| E[consultar_contrato_corte] + D --> E + E -->|Conta Certa Manual elegível| F[abrir_sr_conta_certa_manual] + E -->|caso padrão| G[atualizar_status_sr] + F --> H[atualizar_status_sr_registro] + H --> Z + G --> Z +``` + +### Nó 1 — `registrar_protocolo` + +Primeiro efeito da jornada: + +```yaml +action: registrar_protocolo +``` + +Configura: + +```yaml +scenario: "contestacao" +request_status: "Aberto" +status: "OPENED" +``` + +O protocolo retornado fica acessível em: + +```text +$.vars.registrar_protocolo.protocolo_id +``` + +### Nó 2 — `check_invoice_status` + +Consulta ou reaproveita o `CompleteInvoices` já obtido em prefetch. + +O comentário do YAML deixa clara a intenção arquitetural: evitar uma segunda chamada desnecessária quando o payload já existe na sessão. + +### Nó 3 — `abrir_contestacao_cliente` + +Recebe grande parte do contexto necessário para a operação financeira, inclusive: + +- cliente; +- fatura; +- serviço/item; +- valor; +- descrição; +- protocolo; +- tipo da contestação; +- motivo do ajuste; +- opção de devolução; +- regras de Conta Certa Manual; +- `double_refund`; +- dados de atendimento; +- `skip_invoice_item_validation`. + +O `invoice_status` vem do nó anterior: + +```yaml +invoice_status: $.vars.check_invoice_status.invoice_status +``` + +### Branch de falha financeira + +```yaml +- from: abrir_contestacao_cliente + to: END + priority: 1 + when: + eq: [$.vars.abrir_contestacao_cliente.success, false] +``` + +É avaliado primeiro. Se a validação financeira bloquear a contestação, não deve executar SMS, contrato ou SR. + +### Branch de SMS + +```yaml +when: + all: + - exists: $.vars.abrir_contestacao_cliente.barcode + - neq: [$.vars.abrir_contestacao_cliente.barcode, ""] +``` + +Se a contestação produzir código de boleto, o fluxo passa por `enviar_sms`. + +Caso contrário, a edge `priority: 99` segue diretamente para `consultar_contrato_corte`. + +### Nó `consultar_contrato_corte` + +Determina a regra de data de corte e também trata particularidade de item dependente de plano família. + +### Branch Conta Certa Manual + +O branch é propositalmente composto: + +```yaml +when: + any: + - all: + - apos_data_corte == true + - contestation_success == true + - manual_conta_certa_indicator == true + - all: + - dependent_invoice_item == true + - contestation_registered == true +``` + +Isso expressa duas formas de elegibilidade: + +1. regra normal após data de corte + indicador manual; +2. item de dependente cuja contestação foi registrada. + +### `abrir_sr_conta_certa_manual` + +Cria a SR de Conta Certa Manual. + +### `atualizar_status_sr` + +Caminho padrão, fecha/atualiza o protocolo principal. + +### `atualizar_status_sr_registro` + +Caminho usado após Conta Certa Manual, atualizando a SR correspondente. + +### Pontos de atenção + +- Não trocar prioridades sem revisar todos os branches. +- `success=false` precisa continuar precedendo qualquer efeito posterior. +- Reaproveitamento de prefetch evita chamadas duplicadas. +- É um workflow com múltiplos efeitos externos; qualquer retry deve ser analisado com idempotência. + +--- + +## 11.9 `finalizar_atendimento.active.yaml` + +```yaml +version: 1 +``` + +## 11.10 `finalizar_atendimento.v1.yaml` + +### Objetivo + +Centralizar o fechamento final do atendimento. + +### Nó único `finalizar` + +```yaml +action: finalizar_atendimento_action +``` + +Entradas relevantes: + +- `status`; +- `summary`; +- `msisdn`; +- `social_sec_no`; +- `message_id`; +- tipos informacionais de VAS; +- protocolo; +- flags de supressão/deferimento de eventos. + +### Fluxo + +```text +finalizar_atendimento_action -> END +``` + +### Observação + +Finalizar atendimento é conceitualmente diferente de simplesmente alcançar `END` em qualquer workflow. `END` encerra aquele grafo; `finalizar_atendimento_action` implementa a semântica de negócio de fechamento de atendimento. + +--- + +## 11.11 `invoice_explanation.active.yaml` + +```yaml +version: 2 +``` + +## 11.12 `invoice_explanation.v2.yaml` + +### Objetivo + +Explicar variação de fatura, aguardar a confirmação semântica do cliente e então: + +- registrar aceite e protocolo final; ou +- registrar negativa e transferir para atendimento humano. + +Também possui caminhos de falha de serviço e validação de tentativa. + +### Visão principal + +```mermaid +flowchart TD + A[preparar] -->|success| B[formatar] + A -->|service_failed| F[resposta_falha_servico] + A -->|success=false| C[checar_tentativa] + C -->|limite excedido| D[fim_intencao_invalida] + C -->|caso contrário| E[texto_intencao_invalida] + B --> P{{PAUSE}} + P --> G[decisao] + G -->|SIM| H[registrar_sim] + H --> I[registrar_protocolo_aceite] + I --> Z[END] + G -->|NAO| J[registrar_nao] + J --> K[handoff_pos_explicacao_nao] + K --> Z +``` + +### Nó `preparar` + +Action: + +```text +preparar_invoice_explanation +``` + +Responsável por obter ou reutilizar a explicação base. + +Recebe dados da fatura atual/passada e também: + +```yaml +tentativa_anterior: $.vars.preparar.tentativa +``` + +Isso permite controlar tentativas dentro do estado do workflow. + +### Nó `formatar` + +Action: + +```text +formatar_invoice_explanation +``` + +Ela monta a mensagem, mas **não controla a pausa**. A pausa está declarada no YAML. + +Essa separação é intencional: apresentação e controle de fluxo não devem ficar acoplados. + +### Pause do `formatar` + +Sempre pausa após apresentar a explicação. + +```yaml +allowed_values: ["SIM", "NAO", "CONTINUAR"] +``` + +O semantic classifier diferencia confirmação, negativa e continuação contextual. + +#### Exemplos + +```text +"sim" -> SIM +"entendi, obrigado" -> SIM +"não resolveu" -> NAO +"é a cobrança de 14,99" -> CONTINUAR +"mês que vem fica mais barato?" -> CONTINUAR +``` + +### `decisao` + +É um `no_op`. Sua função é fornecer um ponto explícito no grafo para branching após o resume. + +### Caminho SIM + +```text +decisao + -> registrar_sim + -> registrar_protocolo_aceite + -> END +``` + +`registrar_protocolo_aceite` usa: + +```yaml +workflow_response_final: true +``` + +para indicar que a mensagem retornada é a resposta final do workflow. + +### Caminho NAO + +```text +decisao + -> registrar_nao + -> handoff_pos_explicacao_nao + -> END +``` + +`preparar_handoff_invoice_explanation` materializa: + +```text +session_control = HUMAN_HANDOFF +``` + +A decisão de jornada está no workflow do Contas; a primitive de handoff é do framework. + +### Caminhos de erro + +`preparar` distingue: + +- `success=true`; +- `service_failed=true`; +- `success=false` por validação/tentativa. + +`checar_tentativa` decide se ainda pode pedir novamente ou se o limite foi excedido. + +### Nós `checar_vas_variacao` e `finalizar_nao_resolvido` + +Esses nós estão declarados para a política de VAS variado/não resolvido. Observe que, na versão atual, não existe edge de entrada para `checar_vas_variacao` partindo do fluxo principal SIM/NAO mostrado acima. Antes de reutilizar ou alterar esses nós, valide a intenção de jornada e os testes associados. + +### Ponto crítico de lifecycle + +Quando `registrar_protocolo_aceite` produz `workflow_response_final=true`, o workflow deve ser considerado terminal mesmo que alguma integração legada devolva metadata antiga com `PAUSED`. O próximo turno na mesma sessão não deve reutilizar `expected_input` desse workflow. + +--- + +## 11.13 `pro_rata.active.yaml` + +```yaml +version: 3 +``` + +## 11.14 `pro_rata.v3.yaml` + +### Objetivo + +Explicar cobrança proporcional (`pro rata`) e tratar de forma diferente clientes com Plano Controle. + +### Nó `preparar` + +Action: + +```text +preparar_pro_rata +``` + +Recebe planos e `has_plano_controle`. + +A action decide se a jornada precisa interagir com o usuário: + +```text +await_user_input = true/false +``` + +### Nó `formatar` + +Possui pause condicional: + +```yaml +pause: + enabled: true + when: + eq: [$.vars.preparar.await_user_input, true] +``` + +Portanto, diferente do `invoice_explanation`, este workflow só pausa quando necessário. + +### Expected input + +```yaml +allowed_values: ["SIM", "NAO", "OUTRO"] +``` + +### `decisao_esclarecimento` + +Branch: + +- `SIM` -> `registrar_aceitou`; +- `NAO` -> `devolver_orquestrador`; +- qualquer outra situação -> `reperguntar_esclarecimento`. + +### `reperguntar_esclarecimento` + +Formata novamente e pausa de novo, retomando em `decisao_esclarecimento`. + +Isso forma um pequeno loop conversacional controlado: + +```text +reperguntar + | + pause + | + +----> decisao_esclarecimento +``` + +### Caso sem Plano Controle + +Se `await_user_input=false`, a edge de prioridade 20 sai de `formatar` para: + +```text +registrar_nao_controle -> END +``` + +### Caso SIM + +```text +registrar_aceitou -> END +``` + +Essa action também registra protocolo/evento apropriado. + +--- + +## 11.15 `termino_desconto.active.yaml` + +```yaml +version: 1 +``` + +## 11.16 `termino_desconto.v1.yaml` + +### Objetivo + +Formatar a resposta da capability de término de desconto. + +### Nó único + +```yaml +id: formatar +action: formatar_capability_resposta +``` + +Com: + +```yaml +tipo: termino_desconto +``` + +Além de dados do plano/fatura/evidência de desconto. + +### Conceito + +A mesma action genérica `formatar_capability_resposta` é parametrizada pelo `tipo` da capability. + +### Fluxo + +```text +formatar_capability_resposta(tipo=termino_desconto) -> END +``` + +--- + +## 11.17 `valor_divergente.active.yaml` + +```yaml +version: 1 +``` + +## 11.18 `valor_divergente.v1.yaml` + +### Objetivo + +Formatar resposta para a capability de valor divergente. + +### Nó único + +```yaml +action: formatar_capability_resposta +input: + tipo: valor_divergente + msisdn: $.input.msisdn +``` + +É estruturalmente semelhante ao `termino_desconto`, mas com outro `tipo` e conjunto de inputs. + +### Ponto de atenção + +Se a capability passar a exigir dados adicionais, prefira explicitá-los no YAML, mantendo claro o contrato entre workflow e action. + +--- + +## 11.19 `vas_estrategico.active.yaml` + +```yaml +version: 3 +``` + +## 11.20 `vas_estrategico.v3.yaml` + +### Objetivo + +Tratar VAS estratégico e bundle, apresentar explicação, coletar aceite/negativa e registrar o resultado. + +### Visão geral + +```mermaid +flowchart TD + A[preparar] -->|await_user_input| P{{PAUSE}} + A -->|sem pausa| B[resposta_bundle] + P --> C[decisao] + C -->|SIM| D[resposta_sim] + C -->|NAO e estratégico| E[explicar_cancelamento] + C -->|NAO bundle puro| B + C -->|fallback| F[registrar_outro] + D --> G[registrar_sim] + E --> H[registrar_nao] + B --> I[registrar_bundle] + G --> Z[END] + H --> Z + I --> Z + F --> Z +``` + +### Nó `preparar` + +Action: + +```text +preparar_vas_estrategico +``` + +Recebe `items` e `linhas`. + +Possui pausa condicionada à saída da própria action: + +```yaml +when: + eq: [$.output.await_user_input, true] +``` + +### Pause + +```yaml +allowed_values: ["SIM", "NAO", "OUTRO"] +resume_from: decisao +``` + +### `resposta_bundle` + +Monta texto a partir de: + +```yaml +$.vars.preparar.mensagem_bundle_fechamento +``` + +### `decisao` + +É o ponto de branching depois da pausa. + +#### SIM + +Sempre vai para `resposta_sim`, seja bundle ou estratégico. + +#### NAO + estratégico + +```yaml +all: + - has_estrategico_items == true + - resposta_usuario == NAO +``` + +vai para `explicar_cancelamento`. + +#### NAO + bundle puro + +Quando não há item estratégico, vai para `resposta_bundle`. + +#### fallback + +`priority: 99` -> `registrar_outro`. + +O comentário do arquivo ressalta que, em operação normal, `OUTRO` deveria ser interceptado/reperguntado pelo runtime antes de entrar nessa decisão; o fallback continua existindo como proteção. + +### Registro final + +Há actions diferentes para preservar o caminho de negócio: + +- `registrar_sim`; +- `registrar_nao`; +- `registrar_bundle`; +- `registrar_outro`. + +Todas terminam em `END`. + +--- + +# 12. Mapa workflow -> actions Python + +| Workflow | Action(s) principais | Implementação | +|---|---|---| +| `buscar_fatura` | `buscar_fatura` | `app/domain/contas/workflow_actions.py` | +| `buscar_informacao` | `buscar_informacao_rag`, `reescrever_resposta_buscar_informacao` | mesmo arquivo | +| `cancelamento_vas_avulso` | `cancelamento_vas_avulso_batch` | mesmo arquivo | +| `contestacao_tool` | `registrar_protocolo`, `check_invoice_status`, `abrir_contestacao_cliente`, `enviar_sms`, `consultar_contrato_corte`, `abrir_sr_conta_certa_manual`, `atualizar_status_sr` | mesmo arquivo | +| `finalizar_atendimento` | `finalizar_atendimento_action` | mesmo arquivo | +| `invoice_explanation` | `preparar_invoice_explanation`, `formatar_invoice_explanation`, `checar_tentativa_cvn`, `registrar_atendimento_invoice_explanation`, `registrar_protocolo_inicio`, `preparar_handoff_invoice_explanation`, `checar_vas_variado` | mesmo arquivo | +| `pro_rata` | `preparar_pro_rata`, `formatar_pro_rata`, `registrar_atendimento_pro_rata` | mesmo arquivo | +| `termino_desconto` | `formatar_capability_resposta` | mesmo arquivo | +| `valor_divergente` | `formatar_capability_resposta` | mesmo arquivo | +| `vas_estrategico` | `preparar_vas_estrategico`, `montar_resposta_texto`, `montar_explicacao_cancelamento_vas_estrategico`, `registrar_atendimento_vas_estrategico` | mesmo arquivo | + +`no_op` e `montar_resposta_texto` são actions utilitárias registradas pelo mesmo registry de domínio. + +--- + +# 13. Como criar um novo workflow + +## Passo 1 — definir a responsabilidade + +Pergunte: + +- há mais de uma etapa? +- existe branching? +- existe efeito externo? +- existe pausa conversacional? +- precisa ser retomado em outro turno? + +Se a operação for uma única função sem jornada, talvez uma tool/action simples seja suficiente. + +## Passo 2 — registrar as actions + +Em `workflow_actions.py`: + +```python +@reg.action("consultar_exemplo") +def consultar_exemplo(params, state): + result = service.consultar(...) + return { + "success": True, + "dados": result, + } +``` + +## Passo 3 — criar `nome.v1.yaml` + +```yaml +name: meu_workflow +version: 1 +start: consultar + +nodes: + - id: consultar + action: consultar_exemplo + input: + msisdn: $.input.msisdn + +edges: + - from: consultar + to: END +``` + +## Passo 4 — criar marcador ativo + +```yaml +# meu_workflow.active.yaml +version: 1 +``` + +## Passo 5 — adicionar branches + +Sempre pense em fallback explícito. + +```yaml +- from: consultar + to: sucesso + priority: 10 + when: + eq: [$.vars.consultar.success, true] + +- from: consultar + to: falha + priority: 99 +``` + +## Passo 6 — adicionar pause somente quando a jornada exige input + +Não coloque pausa dentro da lógica Python da action se ela faz parte do contrato do fluxo. + +## Passo 7 — testar + +No mínimo: + +- happy path; +- cada branch; +- falha da integração; +- input ausente; +- pause; +- resume; +- resposta inválida; +- idempotência de efeitos externos; +- terminalidade; +- novo turno após finalização. + +--- + +# 14. Como criar uma nova versão + +Suponha que `vas_estrategico.v3.yaml` precise mudar materialmente. + +1. copie para `vas_estrategico.v4.yaml`; +2. altere internamente `version: 4`; +3. implemente/teste a nova lógica; +4. mantenha v3 disponível; +5. altere somente depois: + +```yaml +# vas_estrategico.active.yaml +version: 4 +``` + +### Rollback + +Basta voltar o marker: + +```yaml +version: 3 +``` + +sem apagar a v4. + +--- + +# 15. Como depurar um workflow + +## 15.1 Comece pelo status + +Procure: + +```text +COMPLETED +PAUSED +FAILED +``` + +## 15.2 Confira `workflow_name` e `workflow_version` + +Isso confirma qual YAML realmente foi carregado. + +## 15.3 Confira `trace` + +Exemplo: + +```text +preparar -> COMPLETED +formatar -> COMPLETED +formatar -> pause_resume RESUMED +decisao -> COMPLETED +registrar_sim -> COMPLETED +registrar_protocolo_aceite -> COMPLETED +``` + +O trace responde rapidamente: + +- qual action executou; +- qual nó foi o último; +- se houve resume; +- se alguma action foi repetida. + +## 15.4 Confira `vars` + +Ao investigar uma edge: + +```yaml +when: + eq: [$.vars.consultar_contrato_corte.apos_data_corte, true] +``` + +primeiro valide o conteúdo real de: + +```text +vars.consultar_contrato_corte.apos_data_corte +``` + +Não conclua que a edge está errada sem verificar a saída da action. + +## 15.5 Confira `pause.expected_input` + +Se o sistema está tratando uma frase como resposta de um fluxo anterior, procure: + +```text +pending_domain_workflow +expected_input +transaction_status +workflow_resume +``` + +Após workflow terminal, esses latches não devem sequestrar o próximo turno. + +## 15.6 Confira o marker `.active.yaml` + +Um erro comum é editar `v3.yaml`, mas o marker continuar apontando para v2. + +--- + +# 16. Regras de desenho recomendadas + +## 16.1 Workflow orquestra; action executa + +Bom: + +```yaml +when: + eq: [$.vars.validar.success, false] +``` + +Action retorna a evidência; YAML escolhe o próximo passo. + +Evite colocar toda a jornada dentro de uma única action gigante. + +## 16.2 Não colocar regra TIM no runtime genérico + +Se a regra pertence a contestação, VAS ou fatura, ela deve ficar no domínio/configuração do agente, não hardcoded no framework. + +## 16.3 Side effects precisam de idempotência + +Especialmente: + +- cancelamento; +- contestação; +- protocolo; +- SMS; +- criação de SR. + +## 16.4 Pausa não deve reexecutar action anterior + +Mantenha o desenho em que o pause é um contrato do nó e o resume segue para `resume_from`. + +## 16.5 Prioridade deve ser intencional + +Use números que deixem clara a hierarquia: + +```text +1 bloqueio terminal crítico +10 caminho específico +20 segundo caminho específico +99 fallback +``` + +## 16.6 Não use output textual para decidir operação financeira + +Branching deve usar campos estruturados como: + +```text +success +barcode +apos_data_corte +dependent_invoice_item +``` + +não palavras encontradas em uma frase produzida por LLM. + +--- + +# 17. Anti-patterns + +### 17.1 Alterar `.active.yaml` sem criar a versão + +Errado: + +```yaml +version: 4 +``` + +sem existir `nome.v4.yaml`. + +### 17.2 Action não registrada + +Se o YAML contém: + +```yaml +action: minha_action +``` + +mas o registry não possui esse nome, o runtime falhará com action não registrada. + +### 17.3 Referenciar `$.vars` de nó que ainda não executou + +Exemplo incorreto: + +```yaml +start: B + +B: + input: + protocolo: $.vars.A.protocolo +``` + +se `A` nunca foi executado. + +### 17.4 Branch sem fallback + +Pode provocar falha de transição. + +### 17.5 Usar `pause` para esconder estado de domínio + +`pause` deve indicar interação com usuário, não substituir persistência correta de transação. + +### 17.6 Reutilizar workflow terminal como contexto ativo + +Um workflow terminado pode permanecer no histórico para auditoria, mas não deve continuar fornecendo `expected_input` ao próximo turno. + +--- + +# 18. Checklist de code review + +Antes de aprovar alteração em `workflows/`: + +- [ ] `name` corresponde ao arquivo; +- [ ] `version` corresponde ao sufixo `.vN`; +- [ ] `active.yaml` aponta para uma versão existente; +- [ ] `start` existe; +- [ ] IDs de nós são únicos; +- [ ] todas as actions estão registradas; +- [ ] todos os `$.input` necessários são fornecidos pelo caller; +- [ ] referências `$.vars.` apontam para nós que executaram antes; +- [ ] branches específicos têm prioridade anterior ao fallback; +- [ ] existe fallback quando necessário; +- [ ] `END` está alcançável; +- [ ] effects externos são idempotentes ou protegidos; +- [ ] pause não reexecuta action de efeito externo; +- [ ] `resume_from` existe; +- [ ] `allowed_values` são tokens internos coerentes; +- [ ] semantic classifier não transforma pergunta/hipótese em confirmação; +- [ ] workflow terminal limpa latch operacional; +- [ ] próximo turno na mesma sessão é testado; +- [ ] testes de happy path e todos os branches existem. + +--- + +# 19. Resumo conceitual para novos desenvolvedores + +Se você lembrar somente destas dez regras, já consegue navegar pela pasta com segurança: + +1. **`.active.yaml` escolhe a versão; `.vN.yaml` contém a lógica.** +2. **`nodes` executam actions; `edges` decidem o próximo nó.** +3. **`$.input` é entrada; `$.vars.` é resultado de nó anterior.** +4. **Menor `priority` é avaliada primeiro.** +5. **Uma edge sem `when` normalmente é o fallback.** +6. **`pause` suspende a jornada; `resume_from` determina onde continuar.** +7. **Tokens `SIM/NAO/CONTINUAR/OUTRO` são controle interno, não fraseologia.** +8. **Actions fazem domínio/integração; o YAML faz orquestração.** +9. **`END` termina o grafo; finalização de atendimento pode envolver action própria.** +10. **Workflow terminado não deve controlar o próximo turno, mesmo quando o `session_id` permanece igual.** + +--- + +# 20. Referências de código dentro do projeto + +Para aprofundar a implementação: + +```text +/workflows/ + definições declarativas do Contas + +/app/domain/contas/workflow_actions.py + implementação das actions usadas pelos workflows + +/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/models.py + schema Pydantic do DSL + +/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/repository.py + resolução de active version + +/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py + executor, branching, pause/resume e LangGraph + +/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/registry.py + registro e resolução das actions + +/app/workflows/agent_graph.py + grafo principal do agente Contas e integração com router/guardrails/judges +``` + +--- + +## Apêndice A — Exemplo completo comentado + +```yaml +name: exemplo_confirmacao +version: 1 +start: preparar + +nodes: + # Executa domínio e produz mensagem + dados estruturados. + - id: preparar + action: preparar_exemplo + input: + msisdn: $.input.msisdn + + # Apenas apresenta a mensagem e pausa. + - id: apresentar + action: montar_resposta_texto + input: + dados: + texto_usuario: $.vars.preparar.mensagem + pause: + enabled: true + return_from: $.output.mensagem + expected_input: + key: resposta_usuario + allowed_values: ["SIM", "NAO"] + normalize: upper_strip + resume_from: decidir + + # Nó estrutural para branch pós-resume. + - id: decidir + action: no_op + input: {} + + - id: confirmar + action: executar_exemplo + input: + msisdn: $.input.msisdn + + - id: cancelar + action: montar_resposta_texto + input: + dados: + texto_usuario: "Operação não realizada." + +edges: + - from: preparar + to: apresentar + + - from: apresentar + to: decidir + + - from: decidir + to: confirmar + priority: 10 + when: + eq: [$.input.resposta_usuario, SIM] + + - from: decidir + to: cancelar + priority: 20 + when: + eq: [$.input.resposta_usuario, NAO] + + - from: confirmar + to: END + + - from: cancelar + to: END +``` + +Leitura em português simples: + +> Prepare os dados, mostre uma mensagem, pare e aguarde SIM/NAO. Quando o usuário responder, continue em `decidir`. Se SIM, execute a operação; se NAO, responda que nada foi feito. Depois encerre o workflow. + +--- + +**Fim do manual.** diff --git a/tests/docs/MATRIZ_MIGRACAO.md b/tests/docs/MATRIZ_MIGRACAO.md new file mode 100644 index 0000000..23aa131 --- /dev/null +++ b/tests/docs/MATRIZ_MIGRACAO.md @@ -0,0 +1,223 @@ +# Matriz de Migração — Contas -> agent_framework_oci + +| Capacidade | Destino novo | Reuso framework | Código de domínio novo | Dependência anterior | +|---|---|---:|---:|---:| +| LangGraph | `app/workflows/agent_graph.py` | Sim | composição mínima | Não | +| Router | EnterpriseRouter | Sim | routing.yaml | Não | +| Stickiness | framework | Sim | configuração | Não | +| Supervisor | framework | Sim | configuração | Não | +| Confirmação | AgentRuntimeMixin | Sim | tool policy | Não | +| Clarificação | AgentRuntimeMixin/MCP mapping | Sim | schemas/mapping | Não | +| Sessions | framework repository | Sim | Não | Não | +| Message memory | framework | Sim | Não | Não | +| Summary memory | framework | Sim | Não | Não | +| LTM | framework | Sim | Não | Não | +| Checkpoint | framework | Sim | Não | Não | +| RAG | RagService | Sim | conteúdo/config | Não | +| Guardrails | GuardrailPipeline | Sim | config | Não | +| Output Supervisor | framework | Sim | Não | Não | +| Judges | JudgePipeline | Sim | config | Não | +| MCP router | framework | Sim | tool catalog | Não | +| Faturas | MCP/domain | Não aplicável | Sim | Não | +| Billing Analysis | MCP/domain | Não aplicável | Sim | Não | +| Consulta/Histórico VAS | MCP/domain | Não aplicável | Sim | Não | +| Bloqueio/Cancelamento VAS | MCP/domain | confirmação no framework | Sim | Não | +| Contestação | MCP/domain | confirmação/estado no framework | Sim | Não | +| Protocol/Status/Tracking | MCP/domain | contexto no framework | Sim | Não | +| SMS | MCP/domain | contexto no framework | Sim | Não | +| Secure PDF | MCP/domain | contexto no framework | Sim | Não | +| Langfuse | framework | Sim | Não | Não | +| Pub/Sub/sequence | framework | Sim | configuração | Não | +| OCI Streaming | framework | Sim | configuração | Não | +| OTEL | framework | Sim | configuração | Não | + +## Regra + +O pacote anterior não é uma biblioteca do novo projeto. Se uma regra específica for necessária, ela deve ser portada e testada dentro de `app/domain/contas`; infraestrutura genérica deve ser eliminada em favor do framework. + + +## Contratos TIM validados por regressão + +| Integração | Paridade coberta | Estado | +|---|---|---| +| CompleteInvoices | método/payload/header `ClientID` | ✅ | +| Query VAS | URL por MSISDN, `clientId=AIAAGENTCR`, auth | ✅ | +| VAS History | query `msisdn`, `clientId`, `messageId`, auth | ✅ | +| Block VAS | payload PMid + fallbacks e headers | ✅ | +| Cancel VAS | DELETE, channel, protocol, headers, messageId | ✅ | +| Contract Information | GET por MSISDN, `clientId`, auth | ✅ | +| Profile/Line Info | GET e header `ClientID` | ✅ | +| Billing Analysis | GET por MSISDN + channel | ✅ | +| Bill PDF | POST detalhado + criptografia | ✅ | +| Secure PDF | GET com parâmetros criptografados | ✅ | +| Customer Contestation | payload/headers principais | ✅ | +| Service Request Status | envelope `serviceRequest` | ✅ | +| Tracking Activities | customer/invoice/activity/user | ✅ | +| Protocol V2 | envelope Siebel + headers corporativos | ✅ | +| SMS Barcode | payload completo + retry/RCT | ✅ | + +## Jornadas compostas e comportamento conversacional + +| Capacidade original | Implementação migrada | Reuso do framework | Regressão | +|---|---|---:|---:| +| Cancelamento VAS -> contestação | dois workflows encadeados no MCP | `WorkflowRuntime` | ✅ | +| Composição final cancelamento | `app/domain/contas/vas_cancellation_message.py` | domínio determinístico | ✅ 19 casos originais | +| Fallback VAS History | `ContasDomainService.cancelar_vas_avulso` | transporte via adapter | ✅ | +| Cancelamento parcial em lote | action expõe cancelados/falhas/candidatos | WorkflowRuntime + IdempotencyStore | ✅ | +| Idempotência transacional | `create_idempotency_store()` | framework | ✅ | +| Replay pós-finalização | Channel short-circuit | framework | ✅ | +| Idle nudge replay | Channel short-circuit | framework | ✅ | +| Processing interruption | replay + classificador LLM fail-safe | `LLMProvider` framework | ✅ | +| Correção Fim/Mim -> Sim | channel transcription | framework | ✅ | +| Finalização status/summary | regra pura de domínio | workflow framework | ✅ | +| Protocolo informacional final | action + ProtocolV2 | WorkflowRuntime | ✅ | + +### Bootstrap MCP + +`WorkflowRuntime`, checkpointer e `IdempotencyStore` são inicializados de forma lazy. Isto evita dependência de Oracle/Redis para endpoints de diagnóstico e garante que o backend durável só seja aberto quando um workflow realmente precisar ser executado. + +## Incrementos de paridade - baseline 420 + +| Capacidade original | Implementação migrada | Responsabilidade | Estado | +|---|---|---|---:| +| InvoiceContextProvider / prefetch | `InvoiceContextService` + `agent_framework.cache.Cache` | domínio escolhe evidências; framework fornece cache | ✅ | +| Isolamento de invoice context por sessão | chave `session_id:msisdn:invoice_id` | framework cache | ✅ | +| Plano família titular/dependente | normalização antes do workflow + contestação única no titular | domínio + WorkflowRuntime | ✅ | +| Correção de linha por invoice detail | normalização determinística | domínio | ✅ | +| CVAL fail-stop | edge `success=false -> END` | WorkflowRuntime | ✅ | +| Snapshot parcial em falha | `WorkflowRuntime` recupera último state do LangGraph | framework | ✅ | +| Retry Billing Analysis / RCT 079-084 | metadata `_transport` + `RCTPolicy` | domínio define códigos; observer publica | ✅ | +| Finalização invoice explanation | protocolo informacional somente após workflow executado | domínio + WorkflowRuntime | ✅ | +| VEB fechado / force RT15 | reuso ou novo protocolo conforme flags | domínio | ✅ | +| Inicialização AgentWorkflow | router/agentes/grafo dentro de `__init__` | aplicação/framework | ✅ | + +### Incrementos de paridade — baseline 435 + +| Capacidade original | Implementação migrada | Responsabilidade | Status | +|---|---|---|---| +| Invoice prefetch single-flight | `InvoiceContextService` + `agent_framework.cache.Cache` | domínio escolhe evidências; framework fornece cache | ✅ | +| CVN de prefetch sem duplicação | `business_events` + cache markers | domínio define código; observer framework publica | ✅ | +| Latch invoice/workflow já executado | `AgentRuntimeMixin.business_workflows_executed` | framework | ✅ | +| Batch cancellation max 5 | action async + semaphore | domínio action sobre WorkflowRuntime | ✅ | +| Protocolo por linha antes de cancelar | action `cancelamento_vas_avulso_batch` | domínio + adapter TIM | ✅ | +| Erro estruturado de workflow | `WorkflowRunResult.error_details` | framework | ✅ | +| Provider error de contestação | MCP mapping sobre `error_details` | domínio/MCP fino | ✅ | + + +## 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. + +## Finalização - paridade adicional (baseline 533) + +| Capability original | Implementação migrada | Framework reutilizado | Evidência | +|---|---|---|---| +| CVN aceite/recusa no encerramento | `finalizar_atendimento_action` | `business_events` + `AgentObserver` | testes de finalização estendida | +| Protocolo RT-15 informacional | action de domínio + TIM client | WorkflowRuntime/observer/idempotência | RCT.085/086 + CVN.010/011 | +| Nota de invoice explanation | valor canônico `Explicação dos valores da fatura` | workflow latch do framework | regressão | +| Handoff/retention suppression | flags no state/domain action | estado persistido do framework | regressão | +| SAD decision tree | `SAD.001/002/003/004/005/006/007` como business events | AgentObserver | regressão | +| Classificação VAS sem invoice detail | aliases determinísticos de domínio | nenhuma engine paralela | regressão | +| Regressão offline de workflow | `WorkflowRuntime(... allow_deterministic_fallback=True)` somente em teste | DSL/actions do framework | 18 casos históricos executados | + +## Atualização de paridade - baseline 550 + +| Capability | Original | Migrado | Evidência | +|---|---|---|---| +| Finalização com prefetch de fatura | CVN lookup + RT-15 quando aplicável | Implementado | regressão de finalização | +| Supressão após transição para negócio | não reemite CVN/MPI | Implementado | regressão dedicada | +| VEB terminal | não duplica RT-15/CVN | Implementado | regressão dedicada | +| Precedência de tipo pela fatura | total/parcial/sem match | Implementado | regressão dedicada | +| Protocolo já existente | não duplica RT-15 | Implementado | regressão dedicada | +| Matcher fonético/transcrição | catálogo real | Implementado sem xfails | suíte de transcrição | + +## VAA — rastreabilidade de cancelamento/contestação + +| Capability histórica | Implementação migrada | Framework reutilizado | Estado | +|---|---|---|---| +| VAA.001–004 cancelamento | `cancelamento_vas_avulso_batch` retorna `business_events` | AgentObserver / analytics | OK | +| VAA.005–009 contestação/boleto | `abrir_contestacao_cliente` retorna `business_events` | AgentObserver / analytics | OK | +| VAA.012–015 SMS | `enviar_sms` retorna `business_events` e mantém fluxo em erro | AgentObserver / analytics | OK | +| VAA.016–017 status SR | `atualizar_status_sr` retorna `business_events` | AgentObserver / analytics | OK | + + +## 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. + +### Paridade de eventos conversacionais — baseline 565 + +| Família | Paridade adicionada | +|---|---| +| VEB | ordem dos branches de VAS estratégico e metadata do turno/URA | +| MPI | contexto de invoice explanation, pró-rata e cancelamento | +| CVN | contexto conversacional/protocolo no encerramento | +| SAD | `llmResponse`, `messageId`, sessão/canal e `sessionEndAt` normalizado | + +## Incremento de paridade — baseline 570 + +| Funcionalidade original | Implementação migrada | Responsabilidade | +|---|---|---| +| Cancelamento solicitado para item estratégico/bundle | `InvoiceResolver` redireciona para workflow `vas_estrategico` | Domínio + WorkflowRuntime | +| Invoice explanation SIM | recomenda `resolvido` pelo último node do workflow | MCP adapter fino sobre WorkflowRuntime | +| Invoice explanation NÃO sem VAS variado | recomenda `nao_resolvido` | MCP adapter fino sobre WorkflowRuntime | +| Pró-rata aceito / sem Plano Controle | recomenda `resolvido` | MCP adapter fino sobre WorkflowRuntime | +| Orientação de cancelamento de VAS estratégico por parceiro | action declara `requires_rag/rag_queries`; `AgentRuntimeMixin` chama `RagService` | Framework | +| Gate padrão de regressão | `pytest -q` -> `tests/` | Projeto migrado | + +## 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. + + +### Paridade de wrappers históricos — baseline 593 + +| Comportamento original | Implementação migrada | Estado | +|---|---|---| +| `tipo_atendimento=contestacao` | `_workflow_payload(contestacao_tool)` | ✅ | +| Contexto do turno no workflow | payload MCP preserva IDs/canal/mensagem | ✅ | +| CPF como alias de socialSecNo | normalização no wrapper composto | ✅ | +| `cancelados` sem `results.success` | fallback agregado do wrapper | ✅ | +| `itens_para_contestacao` | alias de `contestation_candidates` | ✅ | +| falha block/cancel ainda elegível a RT-02 | composição de dois WorkflowRuntime | ✅ | +| SMS falha sem derrubar jornada | `sms_not_send_error` | ✅ | +| Bundle + Estratégico + NÃO | protocolo deferido + RAG obrigatório | ✅ | + +## Complemento de paridade — baseline 599 + +| Comportamento histórico | Implementação migrada | Prova | +|---|---|---| +| Itens já contestados | normalização em `abrir_contestacao_cliente` + compositor determinístico | teste de wrapper 1:1 | +| Itens contestados/não contestados | classificação na action de domínio | regressão de contestação | +| Total contestado somente dos itens aceitos | cálculo determinístico no domínio | regressão de contestação | +| Contestação retorna valor zero | fallback para total efetivamente cancelado | teste de wrapper 1:1 | +| Valor na fala em pt-BR | normalização na borda MCP | teste `R$ 14,99` | +| `next_subject` | ignorado pelo compositor determinístico | teste de wrapper 1:1 | +| Billing Analysis indisponível | fraseologia canônica e `auto_finalize_on_failure=false` | regressão invoice explanation | + +### Cobertura 1:1 de wrappers — baseline 615 + +Além dos testes de domínio/actions/workflows, a suíte passa a reproduzir diretamente +outcomes históricos dos wrappers `cancelar_vas_single` e `finalize_support`, cobrindo +no-match, candidatos explícitos, falhas parciais RT-01→RT-02, SMS, protocolos, +`protocol_closed`, plano família/titular-dependente e protocolo informacional deferido. + +Esses testes são classificados como **paridade explícita de contrato externo**, e não +apenas cobertura indireta por actions internas. diff --git a/tests/docs/OBSERVABILITY_CODE_MAPPING.md b/tests/docs/OBSERVABILITY_CODE_MAPPING.md new file mode 100644 index 0000000..35045a3 --- /dev/null +++ b/tests/docs/OBSERVABILITY_CODE_MAPPING.md @@ -0,0 +1,67 @@ +# Mapeamento contratual da observabilidade do Contas + +O Contas usa o mecanismo genérico `ObservabilityCodeMapper` do framework para adaptar **identificadores internos de observabilidade** aos códigos exigidos pelo contrato externo. + +Arquivo: + +```text +config/observability_mapping.yaml +``` + +Configuração de exemplo deste agente: + +```yaml +version: "1" +mappings: + guardrail.dlex_in: GRL.004 + guardrail.tox: GRL.005 +``` + +O mapping é feito pelo nome canônico emitido internamente. Assim, uma generation/observation criada como `guardrail.dlex_in` aparece externamente como `GRL.004`, e `guardrail.tox` como `GRL.005`. + +A substituição acontece antes dos providers de observabilidade. O mesmo nome contratual é usado por Langfuse, OTEL e EventBus nos caminhos que passam por `Telemetry`. Eventos publicados pelo `AgentObserver` também continuam usando o mesmo mapper. + +Quando um nome de span/generation é substituído, o nome interno é preservado em metadata: + +- `observability_name_internal` +- `observability_name_mapped` +- `observability_code_mapped: true` + +Para eventos estruturados, permanecem disponíveis os campos equivalentes `event_code_internal` e `event_code_mapped`. + +Códigos/names ausentes na tabela passam sem alteração. Para acrescentar outro contrato, adicione somente uma nova entrada ao YAML; não altere Python nem o guardrail/judge. + +Este arquivo pertence ao agente/deployment. O framework contém apenas a engine genérica de mapping e não conhece os códigos contratuais deste agente ou de qualquer cliente. + +## Diagnóstico de carregamento + +No startup o agente registra uma linha `Observability mapping:` com `enabled`, `path`, quantidade de entradas, amostras resolvidas e o arquivo real de onde `agent_framework` foi importado. Isso permite detectar `.venv` antigo/cópia errada do framework e path relativo incorreto. + +A normalização é aplicada em duas barreiras: + +1. `Telemetry._start_observation()` — última barreira para spans/generations criados pelo Telemetry; +2. `LangfuseAnalyticsPublisher` — necessário porque esse publisher usa o SDK Langfuse diretamente e não passa pelo Telemetry. + +Assim, uma configuração como: + +```yaml +mappings: + guardrail.dlex_in: GRL.004 + guardrail.tox: GRL.005 +``` + +é aplicada independentemente de qual dos dois caminhos produziu a observation. + +## Normalização na fronteira do LLM provider + +A normalização não depende apenas do `Telemetry`. O `generation_name` é resolvido pelo `ObservabilityCodeMapper` antes de o provider LLM iniciar qualquer instrumentação. Isso garante que nomes como `guardrail.dlex_in` já cheguem ao tracer como `GRL.004`. + +Quando o provider já recebe o `Telemetry` do framework, a auto-instrumentação `langfuse.openai` é desabilitada para evitar uma segunda observation fora do contrato central. + +O caminho relativo configurado em `OBSERVABILITY_CODE_MAPPING_PATH` é procurado no diretório corrente e nos roots de importação Python, permitindo iniciar o Uvicorn fora do diretório raiz do agente sem perder o mapping. + +## Compatibilidade automática do framework + +A partir desta versão, o framework possui um registry default interno (`agent_framework/config/observability_mapping.yaml`) carregado mesmo quando o agente não possui `OBSERVABILITY_CODE_MAPPING_*`. O arquivo do agente, quando habilitado, funciona como overlay. Isso permite substituir somente a versão do framework em agentes legados sem mudar a taxonomia GRL nem as decisões históricas dos rails. + +Veja também `agent_framework_oci/libs/agent_framework/docs/OBSERVABILITY_DEFAULT_OVERLAY_COMPATIBILITY.md`. diff --git a/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md b/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md new file mode 100644 index 0000000..0e9b071 --- /dev/null +++ b/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md @@ -0,0 +1,43 @@ +# Observability Contract Registry + +`config/observability_mapping.yaml` é a única tabela usada pelo framework para duas responsabilidades relacionadas ao contrato externo: + +1. traduzir nomes/códigos semânticos para labels exigidos pela observabilidade do cliente; +2. preservar ações legadas de guardrails (`retry`, `handover`, etc.) sem hardcode de nomes no Python. + +A sintaxe v1 continua válida: + +```yaml +mappings: + guardrail.dlex_in: GRL.004 +``` + +A forma rica adiciona `action` e `aliases`: + +```yaml +mappings: + guardrail.revprec: + action: retry + aliases: [REVPREC, TIM_REVPREC] +``` + +`label` é opcional. Quando ausente, o nome de observabilidade não é renomeado. `action` também é opcional. + +## Precedência de ação + +Para uma decisão negada, o framework usa: + +1. `metadata.terminal_action` retornado pelo rail; +2. `on_deny` do `guardrails.yaml`; +3. `action` resolvida pelo `observability_mapping.yaml`; +4. `BLOCK` como fallback fail-safe. + +Isso mantém compatibilidade com rails internos e externos sem que `OutputSupervisor` ou `ParallelRailExecutor` conheçam nomes como `REVPREC`, `CMP`, `SCO`, `GND`, `ATH` ou `HUMAN`. + +## Aliases + +Uma entrada `guardrail.revprec` é automaticamente resolvida também por `REVPREC`. Aliases explícitos permitem associar nomes externos ou históricos, por exemplo `TIM_REVPREC`. + +## Compatibilidade + +Mappings escalares continuam funcionando sem alteração. Agentes que não habilitam o mapper continuam em passthrough e usam `BLOCK` para negações sem ação explícita. diff --git a/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md b/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md new file mode 100644 index 0000000..d7abf75 --- /dev/null +++ b/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md @@ -0,0 +1,26 @@ +# Correção do merge Default + Overlay de Observabilidade + +## Problema +O default do framework estava ativo, porém em alguns caminhos o overlay do agente não era carregado. O efeito observado no Langfuse era `GRL.DLEX_IN`/`GRL.TOX` (default) em vez de `GRL.004`/`GRL.005` (Contas). + +## Correção +O framework agora monta um único registry efetivo antes de qualquer resolução: + +1. carrega `agent_framework/config/observability_mapping.yaml`; +2. localiza o overlay do agente; +3. faz merge por chave canônica, com o agente sobrescrevendo o default; +4. reconstrói os aliases somente depois do merge; +5. usa esse único registry em LLM provider, Telemetry, Analytics, OutputSupervisor e ParallelRailExecutor. + +## Descoberta do overlay +Além de `OBSERVABILITY_CODE_MAPPING_PATH`, o framework autodetecta `config/observability_mapping.yaml` no cwd e nos roots de importação Python. O arquivo default empacotado do framework é excluído dessa descoberta. + +Assim um agente com arquivo convencional de overlay não depende de alterar seu launcher ou `.env` para que a customização seja aplicada. + +## Resultado esperado no Contas +- `guardrail.dlex_in` -> `GRL.004` +- `guardrail.tox` -> `GRL.005` +- componentes não sobrescritos continuam herdando o default do framework. + +## Compatibilidade +Agentes antigos sem overlay continuam usando apenas o default do framework e preservam a taxonomia/ações históricas. diff --git a/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md b/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md new file mode 100644 index 0000000..ca3d11f --- /dev/null +++ b/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md @@ -0,0 +1,83 @@ +# OutputSupervisor sem taxonomia contratual hardcoded + +## Objetivo + +O `OutputSupervisor` do framework trabalha somente com eventos semânticos e ações de runtime. Códigos contratuais externos/numerados pertencem exclusivamente ao `ObservabilityCodeMapper` configurado pelo agente/deployment. + +## Eventos internos + +Exemplos de eventos internos: + +```text +guardrail.output_supervisor.started +guardrail.result.allow +guardrail.result.block +guardrail.result.retry +guardrail.output..completed +guardrail.output_supervisor.completed +``` + +Se um cliente exigir códigos próprios, configure `config/observability_mapping.yaml`. O supervisor não conhece a taxonomia externa. + +## Ação quando um rail nega + +O framework não decide mais a ação procurando nomes específicos de rails. A ação pode vir do próprio resultado: + +```python +metadata={"terminal_action": "retry"} +``` + +ou do YAML: + +```yaml +output: + - code: MY_VALIDATION + enabled: true + on_deny: retry +``` + +Valores suportados são os valores de `RailAction`, como `block`, `retry` e `handover`. + +## Remediação por rewrite + +Rewrite também é uma capacidade genérica. O rail/policy declara a remediação: + +```yaml +output: + - code: MY_WORDING_POLICY + enabled: true + on_block: + type: rewrite + max_attempts: 1 + prompt_id: FALLBACK + profile_name: grl + component_name: guardrail.wording.rewrite +``` + +O supervisor não verifica se o código é `FRASEOLOGIA` ou qualquer outro nome. Um guardrail externo do agente pode usar exatamente o mesmo contrato. + +## Mensagens de UX + +Mensagens de fallback/handover pertencem ao agente: + +```yaml +output_supervisor: + max_retries: 3 + fallback_message: "..." + handover_message: "..." +``` + +Assim o framework não precisa conhecer idioma, marca ou fraseologia do atendimento. + +## Contas + +O Contas preserva seu comportamento atual: + +- `TIM_REVPREC` declara `terminal_action=retry` no próprio rail externo; +- `CMP` está configurado com `on_deny: retry`; +- `TIM_FRASEOLOGIA`, quando habilitado, declara remediação `rewrite` no agente; +- textos de fallback/handover ficam no `config/guardrails.yaml` do Contas. + +## Compatibilidade + +Rails que retornam apenas `allowed=false` e não declaram policy continuam em `block`, que é o fail-closed genérico. Não há mais inferência de ação pelo nome do rail. diff --git a/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md b/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md new file mode 100644 index 0000000..fef7b89 --- /dev/null +++ b/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md @@ -0,0 +1,112 @@ +# Pente-fino de paridade — Contas original x Contas migrado + +## Escopo + +Comparação funcional e arquitetural das 18 capabilities solicitadas, tomando como fonte de verdade o projeto Contas original e preservando, no migrado, as responsabilidades genéricas do `agent_framework_oci` (roteamento, confirmação, pause/resume, RAG, memória, observabilidade e política de tools). + +Tools/capabilities avaliadas: + +`consultar_faturas`, `consultar_plano`, `invoice_explanation`, `buscar_informacao`, `consultar_vas`, `consultar_historico_vas`, `cancelar_vas_avulso`, `tratar_vas_estrategico`, `validar_contestacao`, `contestar_cobranca`, `finalizar_atendimento`, `consultar_status_solicitacao`, `enviar_sms`, `recuperar_fatura_pdf`, `pro_rata`, `termino_desconto`, `valor_divergente`, `retomar_workflow`. + +## Resultado executivo + +Depois das correções deste pente-fino, as 18 capabilities estão expostas no MCP e habilitadas no registry do agente. Os workflows conversacionais principais foram preservados do original. Os YAMLs `buscar_fatura`, `buscar_informacao`, `cancelamento_vas_avulso`, `finalizar_atendimento`, `invoice_explanation`, `pro_rata`, `termino_desconto`, `valor_divergente` e `vas_estrategico` permanecem equivalentes ao original. `contestacao_tool` contém uma diferença intencional de segurança: se a validação financeira falhar, o fluxo termina antes de SMS/contrato/SR. + +A suíte completa do projeto após as mudanças executa **715 testes com sucesso**. + +## Paridade por capability + +| Capability | Fonte/semântica no original | Situação após pente-fino | Ação tomada | +|---|---|---|---| +| `consultar_faturas` | `complete_invoices` + prefetch de `bill_pdf` + resumo semântico | Corrigida | Mantida a API de Complete Invoices e restaurados `invoice_amount`, `invoice_amount_open`, período e emissão a partir do PDF, sem inventar campo no backend. | +| `consultar_plano` | Evidência da própria fatura/billing analysis | OK | Extração determinística de seções `Plano/Planos`; não usa RAG nem inferência livre da LLM. | +| `invoice_explanation` | Workflow v2 + evidência da fatura + capability LLM de reescrita + pause | Corrigida | `invoice_detail` e resumo semântico agora chegam ao workflow; composição volta a ser da LLM do framework; `await_user_input=True` restaurado. | +| `buscar_informacao` | Tool RAG ativa (`queries` e `query` legado) | Corrigida arquiteturalmente | Reexposta como façade MCP compatível. Não duplica RAG no domínio: retorna `requires_rag` e delega ao `RagService` do framework. Suporta `queries[]` e `query`. | +| `consultar_vas` | Consulta de VAS ativos | OK | Mantida integração direta e resolução de domínio para os fluxos que precisam classificar item. | +| `consultar_historico_vas` | Histórico de VAS/serviços | OK | Mantido contrato da integração e normalização usada pelo cancelamento. | +| `cancelar_vas_avulso` | Tool ativa, somente avulso, confirmação, cancelamento + contestação automática | OK | Confirmação permanece no framework; preflight resolve nome/classe contra a fatura; workflow composto preserva cancelamento + contestação e protocolos. | +| `tratar_vas_estrategico` | `vas_estrategico`, bundle/estratégico | OK | Alias semântico migrado para `tratar_vas_estrategico`; workflow v3 preservado; redirecionamento automático evita cancelar estratégico como avulso. | +| `validar_contestacao` | Não existia como tool pública; regras CVAL existiam na execução | Corrigida | A pré-validação agora usa a mesma `validate_contestation_items` da execução financeira; deixa de aprovar algo que seria bloqueado depois. Usa o valor pedido pelo cliente, não o `resolved_value` da fatura. | +| `contestar_cobranca` | Workflow/ações de contestação e Conta Certa | OK + hardening | Workflow preservado e CVAL fail-closed. Adicionado edge de segurança para não continuar com SMS/contrato/SR após falha financeira. | +| `finalizar_atendimento` | Tool ativa; `status` obrigatório e regras estritas de finalização | Corrigida | `status` voltou a ser requisito explícito e é extraído genericamente pelo framework com enum semântico; `erro_falha_sistema` continua reservado ao sistema. | +| `consultar_status_solicitacao` | Integração de status SR usada internamente | Corrigida | Removido mapeamento incorreto `interaction_key -> protocol` (interaction_key é identidade da interação/mensagem, não protocolo). Protocolo é extraído da fala ou recuperado de aliases do contexto de workflow. | +| `enviar_sms` | Ação de integração usada pelos workflows | OK | Mantida integração TIM; continua sem regra de negócio dentro do framework. | +| `recuperar_fatura_pdf` | SecurePDF/invoice recover no original | Corrigida | Agora participa do `InvoiceContextService` para obter `customer_id` antes do SecurePDF quando não vier explicitamente. | +| `pro_rata` | Workflow v3; regra explícita de exatamente 2 planos e `has_plano_controle` | Corrigida | O MCP deriva os planos da evidência do Bill PDF/billing analysis, deduplica repetição DANFE/linha e falha fechado se não houver exatamente dois planos. `has_plano_controle` é determinístico. | +| `termino_desconto` | Capability/backend existente; versão migrada havia cristalizado causa sem evidência | Corrigida + hardening | A causa só é afirmada quando backend/mock fornece evidência causal explícita de desconto/promoção. Sem essa prova, responde que o motivo não está disponível; parcelas e texto do cliente não viram fato. | +| `valor_divergente` | Capability/backend existente | Corrigida | Restaurada a semântica original de alteração no valor do plano por linha, em vez de texto genérico sobre billing analysis. | +| `retomar_workflow` | Resume era interno ao runtime/executor original | OK arquiteturalmente | Exposto como façade genérica do `WorkflowRuntime.aresume`; não replica estado conversacional dentro do domínio Contas. | + +## Correções relevantes encontradas + +### 1. Contexto de fatura + +O original não obtinha o valor total diretamente de `complete_invoices`. Ele construía um contexto enriquecido a partir do PDF parseado. A migração já possuía parser e `InvoiceContextService`, mas faltava publicar integralmente o resumo semântico. Foi restaurada a cadeia: + +`Complete Invoices -> invoice/customer id -> Bill PDF -> parser -> total_geral -> invoice_amount/invoice_amount_open`. + +### 2. `invoice_explanation` + +O YAML migrado preservava o `pause`, mas a action migrada não retornava `await_user_input=True`, ao contrário do original. Isso tornava a condição de pause falsa. Também havia sido eliminada a etapa de composição LLM específica. Agora a action retorna o gate de pause e uma diretiva `requires_llm_composition`, mantendo a LLM no framework, não no domínio. + +### 3. `buscar_informacao` + +A route ainda referenciava `buscar_informacao`, mas a tool estava `enabled: false` e nem era exposta pelo MCP. Isso criava uma discrepância entre o contrato original e a configuração migrada. A façade foi restaurada sem reintroduzir RAG customizado no Contas. + +### 4. `pro_rata` + +O schema/descrição original exigia **exatamente dois planos**. A migração aceitava `planos=[]` e podia afirmar pró-rata mesmo sem evidência. Agora os planos são derivados da fatura e a operação retorna `NOT_APPLICABLE` quando a evidência não comprova exatamente dois planos. + +### 5. Pré-validação de contestação + +`validar_contestacao` apenas resolvia o item e retornava `eligible=true`; a validação CVAL real só acontecia depois, já no workflow transacional. Agora a pré-validação e a execução usam a mesma regra financeira, evitando confirmação para uma operação que será inevitavelmente bloqueada. + +### 6. Status de solicitação + +O mapper usava `interaction_key` como `protocol`. Isso é semanticamente incorreto: `interaction_key` identifica a interação do framework. O protocolo agora vem da mensagem ou de campos de protocolo existentes no contexto transacional (`protocol_number`, `protocolo_id`, `contestacao_protocol`, `cancelamento_vas_protocol`). + +## Arquivos principais alterados + +- `contas_mcp/servers/contas_mcp_server/main.py` +- `app/domain/contas/service.py` +- `app/domain/contas/workflow_actions.py` +- `app/domain/contas/invoice_context.py` (correção anterior da Opção A, mantida) +- `config/tools.yaml` +- `config/mcp_parameter_mapping.yaml` +- `workflows/contestacao_tool.v2.yaml` (hardening já presente) +- `tests/migration/test_requested_tools_parity_pente_fino.py` +- testes de invoice context previamente adicionados/mantidos + +## Testes de regressão adicionados + +O novo arquivo `tests/migration/test_requested_tools_parity_pente_fino.py` verifica, entre outros pontos: + +- exposição e habilitação das 18 tools; +- façade framework-native de RAG; +- consulta determinística de plano; +- propagação de `invoice_detail` e resumo semântico; +- composição LLM + pause de `invoice_explanation`; +- regra de exatamente dois planos em pró-rata; +- CVAL na pré-validação e rejeição de valor acima da cobrança; +- semântica de término de desconto e valor divergente; +- contratos diretos de VAS, histórico, status, SMS e PDF; +- ausência do mapeamento incorreto `interaction_key -> protocol`; +- obrigatoriedade/classificação de status na finalização. + +## Resultado dos testes + +```text +715 passed +``` + +A suíte completa disponível no pacote foi executada, não apenas os testes novos. + +## Histórico autoritativo de descontos + +A capability `termino_desconto` não deve deduzir a causa da retirada de desconto a partir de parcelas, ausência do item ou texto do cliente. Foi introduzida a integração MCP `consultar_historico_descontos`, com mock em `app/domain/contas/fixtures/discount_history.json`. + +O serviço retorna fatos estruturados (`discount_name`, `plan_name`, `previous_value`, `current_value`, `start_date`, `end_date`, `discount_status`, `termination_reason` e `termination_reason_description`). O workflow `termino_desconto` chama essa fonte obrigatoriamente antes de compor a resposta. Se a causa não estiver explícita, mantém `epistemic_status=insufficient_evidence`; quando a causa está presente, usa `epistemic_status=grounded_fact`. + +A referência temporal do mock é explícita: `current_value` representa a situação contratual em `as_of_date`, enquanto `last_billed_discount_value` representa o desconto aplicado no último período faturado (`last_billed_period`). Isso evita tratar como contradição o caso em que uma fatura referente a período anterior ainda contém o desconto, embora o benefício já esteja encerrado na data contratual corrente. + +O mock serve somente para ilustrar o contrato que deverá ser substituído pela integração real. A resposta ao cliente é composta no agente; o serviço não retorna frase pronta. diff --git a/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md b/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md new file mode 100644 index 0000000..394cfd7 --- /dev/null +++ b/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md @@ -0,0 +1,302 @@ +# Relatório de Correções — Agent Framework OCI + Contas + +Data: 2026-08-28 +Base analisada: `agent_contas_oci_template (6).zip` + +## 1. Objetivo + +Este trabalho tratou as frentes técnicas identificadas a partir do comparativo de 30 replays e, principalmente, dos contratos de regressão já existentes no próprio projeto. A separação arquitetural foi preservada: + +- **Framework**: lifecycle transacional, coleta genérica de parâmetros, confirmação, snapshot, roteamento/continuidade, guardrails e infraestrutura horizontal. +- **Agent Contas / domínio / MCP**: semântica TIM, prompts voltados ao cliente, contrato das capabilities, evidência de fatura, regras de contestação, pró-rata e mapeamentos de integração. + +Nenhuma regra TIM foi movida para o core do framework. + +## 2. Correções realizadas no framework + +### 2.1 Coleta de parâmetros sem expor nomes internos + +**Problema** +O runtime possuía um pequeno dicionário hardcoded para `order_id`, `reason` e `customer_id` e, para qualquer outro parâmetro, podia produzir o nome técnico convertido para texto. Isso explica respostas da família `informe subject` apontadas no relatório. + +**Correção** +O framework agora usa metadados declarados pelo agente em `args_schema`: + +- `user_prompt`: pergunta exata voltada ao cliente, com maior prioridade; +- `label`: rótulo amigável opcional; +- `description`: fallback semântico; +- sem metadados: pergunta neutra que **não expõe o nome técnico**. + +Além disso, o framework pergunta **um parâmetro por vez**, embora o extrator LLM continue capaz de consumir vários valores espontaneamente informados no mesmo turno. + +**Resultado arquitetural** +O framework continua sem saber o significado de `subject`, `valor`, `order_id` etc. A semântica pertence ao agente. + +### 2.2 Snapshot imutável da confirmação + +**Problema** +Havia `pending_tool_call` e `active_transaction`, mas não existia um snapshot separado e explícito que representasse exatamente a operação apresentada ao usuário no momento da confirmação. + +**Correção** +Foi introduzido `confirmation_snapshot`, contendo: + +- `transaction_id`; +- `tool_name`; +- cópia dos `arguments`; +- `started_from_intent`. + +Ao entrar em `AWAITING_CONFIRMATION`, o snapshot é congelado. Um `sim` executa **esse snapshot**, mesmo que `active_transaction`, `pending_tool_call` ou outro contexto seja alterado depois. Ao concluir/cancelar a transação, o snapshot operacional é limpo. + +**Benefício** +Garante o contrato: + +> confirmar = executar exatamente tool + parâmetros que estavam congelados quando a confirmação foi solicitada. + +### 2.3 Itens do framework já presentes nesta versão e apenas revalidados + +Não foram duplicadas correções que já estavam na base recebida: + +- extração LLM de parâmetros transacionais; +- precedência de confirmação explícita; +- `transaction_interruption=intent_shift`; +- encerramento/limpeza de transações `COMPLETED`, `FAILED`, `CANCELLED`, `BLOCKED`, `OUT_OF_SCOPE`; +- route stickiness sem reaproveitar transação terminal; +- replay pós-finalização sem reabrir atendimento; +- validação direta de `expected_protocols` no CMP; +- isolamento do contexto operacional dos guardrails após intent shift no Contas. + +## 3. Correções realizadas no Agent Contas / MCP + +### 3.1 Prompts declarativos dos parâmetros + +Foram adicionados `user_prompt` às capabilities transacionais: + +- `cancelar_vas_avulso.subject` → `Qual serviço você deseja cancelar?` +- `tratar_vas_estrategico.subject` → `Qual serviço ou benefício você deseja tratar?` +- `validar_contestacao.subject` → `Qual cobrança ou item você não reconhece?` +- `validar_contestacao.valor` → `Qual é o valor da cobrança?` +- `contestar_cobranca.subject` → `Qual cobrança ou item você deseja contestar?` +- `contestar_cobranca.valor` → `Qual é o valor da cobrança que você deseja contestar?` + +Assim, a linguagem de atendimento fica no domínio e o framework apenas executa o contrato. + +### 3.2 Capability `buscar_informacao` restaurada sem duplicar RAG + +A capability voltou a existir no registry/MCP para manter paridade de contrato, mas não reimplementa recuperação no domínio. + +Ela devolve um contrato explícito: + +- `requires_rag=true`; +- `source=agent_framework.rag`; +- `rag_queries=[...]`. + +Portanto, a API antiga é preservada e o RAG continua sendo responsabilidade do framework. + +### 3.3 `invoice_explanation` preserva evidência suficiente para composição + +O retorno passa a preservar também: + +- `invoice_detail`; +- `invoice_amount`; +- `invoice_period`; +- `invoice_emissao`. + +A action `formatar_invoice_explanation` agora sinaliza: + +- `await_user_input=true`; +- `requires_llm_composition=true`; +- `response_instruction` de composição grounded; +- preservação da pergunta `Com essa explicação, sanei sua dúvida?`. + +### 3.4 Pró-rata determinístico e fail-closed + +Foram restaurados helpers de preparação do pró-rata: + +- derivação determinística dos planos a partir do PDF parseado; +- uso da visão contratual por linha, evitando confundir DANFE com plano consolidado; +- identificação de plano controle; +- exigência de **exatamente dois planos**; +- falha fechada com `requires_exactly_two_plans` quando o contrato não é atendido. + +Nenhum LLM é usado nessa decisão. + +### 3.5 CVAL aplicado também na pré-validação + +`validar_contestacao` deixou de apenas aceitar o item após o preflight e passou a executar a mesma validação CVAL usada antes do efeito financeiro. + +A validação usa: + +- item resolvido; +- **valor originalmente solicitado pelo cliente**; +- evidência de `billing_analysis`; +- `validation_log` estruturado. + +Valor solicitado acima do valor comprovado é bloqueado com `reason=CVAL` e erro `valor_ajuste_maior_que_item`. + +Foi corrigido também um teste de regressão inconsistente: ele exigia aprovar R$ 50 para um item comprovado em R$ 10, ao mesmo tempo em que dizia proteger a regra “valor não pode exceder o item”. O caso positivo foi ajustado para R$ 10; a implementação não foi enfraquecida para satisfazer uma expectativa insegura. + +### 3.6 Grounding de término de desconto e valor divergente + +`termino_desconto` foi endurecido para não transformar uma hipótese de negócio em fato. O workflow só informa causa de retirada/término quando a evidência de backend/mock contém um campo causal explicitamente associado a desconto/promoção (por exemplo `discount_reason`, `terminationReason`, status de desconto/promoção encerrado ou data de término registrada). Contadores como `1/12`, `8/12` ou `12/12`, ausência de desconto na fatura e o próprio texto do cliente não são tratados como prova de expiração. + +Quando a causa não está disponível, a resposta informa que os dados existentes não registram o motivo, sem afirmar fim de fidelidade ou expiração promocional. + +`valor_divergente` preserva a semântica de alteração do valor do plano e referência segura ao final da linha, conforme contrato de regressão. + +### 3.7 Status de solicitação não usa `interaction_key` como protocolo + +Foi removido: + +`interaction_key -> protocol` + +O protocolo agora é extraído explicitamente da mensagem, impedindo que `message_id`/`interaction_key` seja tratado como protocolo de atendimento. + +### 3.8 Finalização exige status explícito + +`finalizar_atendimento` agora declara `status` em `requires`, e o mapping possui extração explícita do campo. Isso preserva o contrato de domínio e evita finalização sem estado definido. + +### 3.9 Prompt de billing mais grounded + +O `FaturasAgent` recebeu regra explícita para não transformar ausência de evidência em hipótese factual. Sem evidência, ele não pode afirmar como causa: + +- fim de promoção; +- perda de elegibilidade; +- alteração de consumo; +- reajuste tarifário; +- mudança de plano. + +Isso endereça diretamente o comportamento observado no comparativo, em que hipóteses eram apresentadas como explicação. + +### 3.10 Prompt de suporte não simula efeitos de lifecycle + +O `SuporteContasAgent` foi reforçado para não anunciar em texto livre: + +- transferência; +- encerramento; +- protocolo; +- sucesso operacional. + +Resultados terminais devem refletir apenas o estado/tool atual. Handoff e finalização continuam controlados pela orquestração. + +## 4. Fontes alterados + +### Framework + +| Arquivo | Alteração | +|---|---| +| `agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` | Prompt declarativo de parâmetros; remoção de labels hardcoded; pergunta neutra sem leak; `confirmation_snapshot`; execução a partir do snapshot; limpeza do snapshot no lifecycle. | +| `agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py` | Sincronizado com o source para manter o artefato de build consistente. | +| `agent_framework_oci/tests/test_transactional_tool_flow.py` | Regressões para `user_prompt`, ausência de leak de nome técnico e confirmação por snapshot imutável. | + +### Agent Contas / MCP + +| Arquivo | Alteração | +|---|---| +| `config/tools.yaml` | `user_prompt` dos parâmetros; capability `buscar_informacao`; `finalizar_atendimento.status` obrigatório. | +| `config/mcp_parameter_mapping.yaml` | Protocolo deixa de vir de `interaction_key`; extração explícita de `protocol`; extração explícita de `status` na finalização. | +| `config/prompts/billing.yaml` | Proibição explícita de hipóteses causais sem evidência. | +| `config/prompts/support.yaml` | Não simular handoff/finalização/protocolo; tratamento terminal grounded. | +| `app/domain/contas/service.py` | `buscar_informacao`; preservação de `invoice_detail`, amount, period e emissão em `invoice_explanation`. | +| `app/domain/contas/workflow_actions.py` | Metadados de composição LLM/await no invoice explanation; semântica de `termino_desconto` e `valor_divergente`. | +| `contas_mcp/servers/contas_mcp_server/main.py` | Registro `buscar_informacao`; helpers de pró-rata; preparação fail-closed; CVAL na pré-validação; dispatch das novas/restauradas capabilities. | +| `tests/migration/test_framework_agent_gap_fixes.py` | Novos contratos de regressão framework × agente. | +| `tests/migration/test_requested_tools_parity_pente_fino.py` | Correção do caso positivo CVAL inconsistente (R$50 → R$10 comprovados). | + +## 5. Validação executada + +### Framework — testes focados das frentes alteradas + +Resultado: + +`40 passed` + +Incluiu: + +- transaction tool flow; +- confirmação voltada ao cliente; +- extração LLM/prevalência de parâmetros; +- route stickiness / intent shift; +- novos testes de snapshot e user-facing parameter contract. + +### Agent Contas — regressão completa de migração + +Resultado final: + +`729 passed` + +Antes das correções, o `test_requested_tools_parity_pente_fino.py` expunha 11 falhas. Após as correções: + +`14 passed` nesse arquivo e `729 passed` em toda `tests/migration`. + +### Suíte completa do framework + +Resultado observado na árvore corrigida: + +- `225 passed` +- `10 failed` + +Os mesmos 10 casos foram executados contra o ZIP original recebido e falham da mesma forma. Portanto são **falhas preexistentes e não introduzidas por este patch**. Estão concentradas em: + +- compatibilidade de double de LLM em um teste unitário; +- checkpoint repository/recovery; +- compact telemetry Langfuse legado; +- transactional workflow unit tests; +- dois testes estáticos que procuram um layout de `agent_template_backend` inexistente nesse caminho. + +Esses itens não pertencem às frentes do comparativo tratadas neste patch e não foram mascarados. + +## 6. Relação com o relatório comparativo + +### Problemas do relatório atacados diretamente + +- nomes internos de parâmetros na fala; +- coleta transacional sem contrato amigável; +- confirmação sem snapshot explícito; +- risco de reinterpretar argumentos depois do pedido de confirmação; +- explicação de cobrança baseada em hipótese sem evidência; +- gaps de capability/paridade MCP já formalizados pelos testes do projeto; +- pró-rata sem preparação determinística completa; +- pre-validation/CVAL incompleta; +- confusão entre identificador de interação e protocolo; +- finalização sem `status` obrigatório. + +### Problemas que já estavam corrigidos nesta versão recebida + +- intent shift durante transação; +- limpeza de transação terminal; +- replay pós-finalização; +- barge-in pós-finalização no framework de interrupção; +- `expected_protocols`/CMP; +- contexto histórico de transação anterior nos guardrails do Contas. + +## 7. Pontos que continuam sendo política de negócio do Contas + +Não foram movidos para o framework, de propósito: + +- escada comercial de retenção TIM; +- quando exatamente transferir para humano após retenção; +- primeira/segunda ocorrência de fora de escopo; +- escalonamento jurídico/Anatel específico TIM; +- política de ressarcimento em dobro; +- interpretação de conjuntos de cobranças como “nenhuma delas/todas”; +- regras específicas de VAS avulso/estratégico e ações comerciais. + +Esses comportamentos devem ser implementados/testados no domínio Contas quando os cenários executáveis correspondentes estiverem disponíveis. O pacote recebido não contém os 30 YAMLs de replay citados no PDF, portanto este relatório **não afirma** que os 30 replays agora passam; afirma apenas os resultados das suítes efetivamente presentes e executadas no pacote. + +## 8. Conclusão + +A principal correção estrutural foi tornar a fronteira mais clara: + +- o **framework** controla coleta, lifecycle e confirmação sem expor nomes internos e sem reinterpretar o que foi confirmado; +- o **agente Contas** fornece a linguagem de negócio e os contratos/evidências específicos; +- o **MCP Contas** mantém capabilities e validações determinísticas de domínio sem absorver responsabilidades de conversa/RAG do framework. + +A regressão do Contas presente no projeto ficou integralmente verde (`729 passed`). + +### Serviço MCP de histórico de descontos + +Foi adicionada a tool interna `consultar_historico_descontos` para representar a fonte autoritativa de status e término de descontos. O mock está em `app/domain/contas/fixtures/discount_history.json` e a implementação em `contas_mcp/servers/contas_mcp_server/discount_history_service.py`. + +`termino_desconto` consulta esse serviço obrigatoriamente e só verbaliza uma causa quando `termination_reason`, `termination_reason_description` ou outro campo causal explicitamente permitido estiver presente. Códigos técnicos permanecem em metadados; a resposta usa a descrição legível do sistema. Sem causa explícita, o fluxo continua fail-closed. + +O mock também distingue explicitamente a **situação contratual na data de referência** da **última fatura emitida**. No cenário atual, `current_value=0` significa valor contratual do desconto em `as_of_date=2025-11-20`; a última fatura cobre `14/10 a 13/11` e ainda registra R$ 80,00 de desconto. Isso é temporalmente consistente: o desconto estava vigente no período faturado e aparece como encerrado na situação contratual de 20/11/2025. Os campos `last_billed_discount_value`, `last_billed_period`, `last_invoice_issue_date` e `current_value_reference` documentam essa diferença. diff --git a/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md b/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md new file mode 100644 index 0000000..66c2042 --- /dev/null +++ b/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md @@ -0,0 +1,423 @@ +# Relatório técnico — políticas alternativas de operação por linha + +## 1. Objetivo + +O Agent Contas possui duas políticas alternativas para controlar operações em uma linha (MSISDN) diferente da linha identificada/autenticada no início do atendimento. + +A implementação permanece no **Agent Contas/MCP Contas**, sem regra TIM hardcoded no core do `agent_framework_oci`. + +A política ativa entregue no projeto continua sendo a **ALT1 — somente a linha autenticada**. + +A ALT2 foi evoluída para não inferir autorização a partir de fatura, billing ou texto do cliente. Ela depende de uma fonte explícita de autorização: a nova tool MCP mock `consultar_linhas_autorizadas`. + +## 2. Políticas disponíveis + +### 2.1 ALT1 — `authenticated_line_only` — PADRÃO + +Fonte: + +```text +contas_mcp/servers/contas_mcp_server/line_policy_alt1.py +``` + +Comportamento: + +- a linha operacional continua sendo a linha identificada pelo `business_context` da chamada; +- uma linha citada em texto livre **não substitui** a identidade da sessão; +- se o cliente mencionar explicitamente outra linha — número completo ou referência como `final 4321` — a execução é bloqueada antes de qualquer operação de domínio; +- o bloqueio é terminal para o turno e interrompe as tools seguintes; +- a mensagem devolvida é: + +```text +Por segurança, este atendimento só permite consultar ou realizar operações na linha identificada na chamada. Não posso usar outra linha informada na conversa. +``` + +Exemplo: + +```text +linha autenticada: 11999999999 +cliente: "quero cancelar o streaming do número da minha esposa, final quatro três dois um" + +resultado: +LINE_POLICY_BLOCKED / other_line_not_allowed +nenhuma consulta/cancelamento é executado para a outra linha +``` + +### 2.2 ALT2 — `authorized_related_lines` + +Fonte: + +```text +contas_mcp/servers/contas_mcp_server/line_policy_alt2.py +``` + +Comportamento: + +- a linha autenticada continua sendo a origem de confiança; +- uma outra linha só pode ser usada se for retornada pelo serviço explícito `consultar_linhas_autorizadas`; +- referências como `final 4321` são resolvidas somente contra as linhas autorizadas retornadas por esse serviço; +- se houver exatamente uma correspondência, ela vira o `effective_msisdn` da operação; +- se não houver correspondência, a operação é bloqueada; +- se houver mais de uma correspondência, o fluxo exige esclarecimento; +- se o serviço de linhas autorizadas falhar, o ALT2 opera em **fail-closed**: somente a linha autenticada permanece autorizada; +- a presença de um MSISDN em `invoice_detail`, `billing_analysis` ou outra evidência de cobrança **não concede autorização operacional**; +- um número pronunciado pelo cliente também **não concede autorização**. + +Exemplo: + +```text +linha autenticada: 11999999999 +consultar_linhas_autorizadas retorna: + - 11999999999 (titular) + - 11988884321 (dependente autorizado) + +cliente: "quero cancelar o TIM Fashion da linha final 4321" + +resultado ALT2: +requested reference = 4321 +effective_msisdn = 11988884321 +operação pode prosseguir nessa linha +``` + +## 3. Novo serviço MCP mock — `consultar_linhas_autorizadas` + +### 3.1 Objetivo + +Foi criada uma tool MCP side-effect-free para representar a integração que, em produção, deve consultar um serviço de identidade/conta e responder **quais linhas o atendimento autenticado está autorizado a operar**. + +Tool: + +```text +consultar_linhas_autorizadas +``` + +Registro MCP: + +```text +contas_mcp/servers/contas_mcp_server/main.py +``` + +Implementação mock: + +```text +contas_mcp/servers/contas_mcp_server/authorized_lines_service.py +``` + +Fixture mock: + +```text +app/domain/contas/fixtures/authorized_lines.json +``` + +### 3.2 Contrato de entrada + +A consulta parte da identidade já autenticada no atendimento. O cliente não informa qual linha deve ser autorizada. + +Exemplo: + +```json +{ + "msisdn": "11999999999", + "customer_key": "11999999999", + "contract_key": "3000131180" +} +``` + +O `msisdn` acima é a linha autenticada/original da chamada. + +### 3.3 Contrato de saída mock + +```json +{ + "success": true, + "status": "SUCCESS", + "source": "mock", + "authenticated_msisdn": "11999999999", + "authorized_lines": [ + { + "msisdn": "11999999999", + "relationship": "titular", + "status": "ACTIVE", + "authorized": true + }, + { + "msisdn": "11988884321", + "relationship": "dependente", + "status": "ACTIVE", + "authorized": true + } + ], + "authorized_msisdns": [ + "11999999999", + "11988884321" + ] +} +``` + +### 3.4 Por que existe uma tool MCP separada + +O objetivo é deixar explícita a arquitetura de produção: + +```text +identidade autenticada da chamada + ↓ +consultar_linhas_autorizadas + ↓ +serviço legado/CRM/IAM/conta + ↓ +lista de linhas realmente autorizadas + ↓ +line_policy_alt2 + ↓ +resolve referência conversacional + ↓ +0 matches → bloqueia/clarifica +1 match → effective_msisdn +>1 matches → clarifica +``` + +A autorização não pertence ao LLM. O LLM/text extractor pode interpretar `final 4321`, mas não decide se `4321` é uma linha autorizada. + +### 3.5 Comportamento em produção + +O arquivo `authorized_lines_service.py` é propositalmente um mock de referência. Em produção, ele deve ser substituído por um adapter que consulte o serviço corporativo responsável pela relação titular/dependentes/linhas autorizadas. + +O contrato recomendado deve preservar pelo menos: + +```text +success +authenticated_msisdn +authorized_lines[].msisdn +authorized_lines[].status +authorized_lines[].authorized +authorized_lines[].relationship +authorized_msisdns +``` + +Se a integração real falhar ou não puder provar a autorização da outra linha, o comportamento esperado do ALT2 é fail-closed. + +## 4. Fluxo ALT2 atualizado + +O fluxo completo ficou: + +```text +mensagem do cliente + ↓ +extrai requested_line_reference + ex.: suffix=4321 + ↓ +business_context mantém 11999999999 + ↓ +MCP detecta ALT2 ativo + ↓ +consultar_linhas_autorizadas(11999999999) + ↓ +authorized_lines_evidence + ↓ +line_policy_alt2 + ↓ +resolve 4321 somente contra authorized_lines_evidence + ↓ +effective_msisdn = 11988884321 + ↓ +só então a tool/workflow de negócio é executada +``` + +A ALT2 não usa mais `invoice_detail`, `billing_analysis` ou `complete_invoices_payload` como fonte de **autorização** de linha. + +## 5. Política ativa + +O MCP importa sempre: + +```text +contas_mcp/servers/contas_mcp_server/line_policy.py +``` + +No pacote entregue, `line_policy.py` é uma cópia exata de `line_policy_alt1.py`. + +Portanto, **o comportamento corrente permanece bloqueando operações em outra linha**. + +O `/health` informa a política carregada: + +```json +{ + "line_policy": "authenticated_line_only", + "line_policy_description": "Somente a linha identificada/autenticada na chamada pode ser consultada ou alterada." +} +``` + +## 6. Como ativar ALT1 + +Forma recomendada: + +```bash +python scripts/select_line_policy.py alt1 +``` + +Depois reinicie o backend/MCP Server. + +Linux/macOS: + +```bash +cp contas_mcp/servers/contas_mcp_server/line_policy_alt1.py \ + contas_mcp/servers/contas_mcp_server/line_policy.py +``` + +PowerShell: + +```powershell +Copy-Item ` + contas_mcp/servers/contas_mcp_server/line_policy_alt1.py ` + contas_mcp/servers/contas_mcp_server/line_policy.py -Force +``` + +## 7. Como ativar ALT2 + +```bash +python scripts/select_line_policy.py alt2 +``` + +Depois reinicie o backend/MCP Server. + +Ao iniciar com ALT2, o MCP passa a consultar automaticamente `consultar_linhas_autorizadas` quando houver uma referência explícita a linha no turno. + +Não é necessário inserir manualmente: + +```python +context["authorized_msisdns"] = [...] +``` + +nem: + +```python +args["authorized_msisdns"] = [...] +``` + +A lista vem do serviço MCP de autorização. + +## 8. Como alterar o mock para testes + +Para ilustrar outra linha autorizada, edite apenas: + +```text +app/domain/contas/fixtures/authorized_lines.json +``` + +Exemplo: + +```json +{ + "msisdn": "11977771234", + "relationship": "dependente", + "status": "ACTIVE", + "authorized": true +} +``` + +Não altere `line_policy_alt2.py` para cadastrar linhas. + +Esse desenho deixa claro que a política apenas **consome autorização**; ela não é o cadastro das linhas autorizadas. + +## 9. Abrangência + +A política é aplicada no ponto único `_invoke()` do MCP Contas antes da execução de domínio. Dessa forma cobre as tools/serviços baseados em MSISDN, inclusive quando passam por workflows. + +Cobertura funcional inclui: + +- `consultar_faturas` +- `consultar_plano` +- `invoice_explanation` +- `consultar_vas` +- `consultar_historico_vas` +- `cancelar_vas_avulso` +- `tratar_vas_estrategico` +- `validar_vas_subject` +- `validar_contestacao` +- `contestar_cobranca` +- `pro_rata` +- `termino_desconto` +- `valor_divergente` +- `consultar_status_solicitacao` +- `enviar_sms` +- `recuperar_fatura_pdf` +- `finalizar_atendimento` + +`consultar_linhas_autorizadas` é a fonte de autorização da ALT2 e não passa pela própria política para evitar dependência circular. + +`buscar_informacao` não depende de linha e `retomar_workflow` apenas retoma execução já iniciada. + +## 10. Arquivos alterados/criados + +### Política de linha + +```text +contas_mcp/servers/contas_mcp_server/line_policy.py +contas_mcp/servers/contas_mcp_server/line_policy_alt1.py +contas_mcp/servers/contas_mcp_server/line_policy_alt2.py +``` + +### Novo serviço MCP de autorização + +```text +contas_mcp/servers/contas_mcp_server/authorized_lines_service.py +app/domain/contas/fixtures/authorized_lines.json +contas_mcp/servers/contas_mcp_server/main.py +``` + +### Referência conversacional e seleção da política + +```text +app/domain/contas/line_reference.py +scripts/select_line_policy.py +``` + +### Testes e documentação + +```text +tests/migration/test_line_policy_alternatives.py +docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md +``` + +## 11. Validação + +Testes específicos da política e do novo mock: + +```text +12 passed +``` + +Smoke ALT2: + +```text +policy = authorized_related_lines +authorized = [11999999999, 11988884321] +requested = final 4321 +allowed = true +effective = 11988884321 +``` + +Após o smoke, ALT1 foi restaurado e validado como política ativa entregue. + +Suíte completa de migração com ALT1 ativa: + +```text +765 passed +``` + +## 12. Decisão arquitetural + +Responsabilidades finais: + +| Camada | Responsabilidade | +|---|---| +| Framework | identidade/contexto, execução genérica, terminalidade e short-circuit de tools | +| Agent/MCP Contas | política ALT1/ALT2 e integração de autorização | +| `consultar_linhas_autorizadas` | informar quais linhas a identidade autenticada está autorizada a operar | +| Backend real futuro | fonte de verdade de titular/dependentes/autorização | +| LLM | interpretar a referência conversacional; nunca conceder autorização | + +A principal regra arquitetural é: + +> **linha mencionada ≠ linha autorizada** + +A autorização precisa vir de uma fonte explícita e confiável. Na versão demonstrativa essa fonte é o mock MCP `consultar_linhas_autorizadas`; em produção, deve ser substituída pela integração corporativa correspondente. diff --git a/tests/docs/VALIDACAO_MIGRACAO.md b/tests/docs/VALIDACAO_MIGRACAO.md new file mode 100644 index 0000000..f960a68 --- /dev/null +++ b/tests/docs/VALIDACAO_MIGRACAO.md @@ -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 +``` diff --git a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 1f8ed39..8357fb8 100644 Binary files a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc index f1ebe29..64ddc57 100644 Binary files a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc index cabcf0d..fa16a45 100644 Binary files a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc index dcbeadd..ebc73db 100644 Binary files a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc index 926bf2a..dd946b0 100644 Binary files a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc index 04df92e..c2915b0 100644 Binary files a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc index 0e26019..e15ac38 100644 Binary files a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..9eff3ee Binary files /dev/null and b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc index 1136a28..61bb8fd 100644 Binary files a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc index b0520f1..48a4e77 100644 Binary files a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc index edbf7e1..d51697f 100644 Binary files a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc index b931732..cfb1ae9 100644 Binary files a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc index 16e04ef..da2211f 100644 Binary files a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc index b246278..7cf7ad8 100644 Binary files a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc index 048e47e..f0bfbaf 100644 Binary files a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc index ede5d1f..89dc352 100644 Binary files a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc index f9b7993..383a5f0 100644 Binary files a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc index 0ba4609..a8bcabe 100644 Binary files a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc index 0a86270..3cdc6d8 100644 Binary files a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc index 61c210c..07ecd4c 100644 Binary files a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc index 8c05746..c26f17c 100644 Binary files a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc index 0e84c2d..a8c1765 100644 Binary files a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc index d37dc7e..4dcb845 100644 Binary files a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc index 14c5743..8f00c16 100644 Binary files a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc index 7f89119..3be12a5 100644 Binary files a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc index 46bba07..36f5cd9 100644 Binary files a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc index 4be4182..c4d9299 100644 Binary files a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc index 63edf42..46d0518 100644 Binary files a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc index 210ed65..abf7764 100644 Binary files a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc index 2bad33a..3ce9bae 100644 Binary files a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc index ecb5e81..033b9f2 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc index 7adc904..e9532de 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc index ef4c528..b8246ac 100644 Binary files a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc index 00d4cbc..9d23286 100644 Binary files a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc index cc24aed..89698e8 100644 Binary files a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc index 3c46450..e27cb21 100644 Binary files a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc index f54e8dd..edb6eed 100644 Binary files a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc index a00a868..9dc2b6c 100644 Binary files a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc index 98170a2..f55a535 100644 Binary files a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc index 46609e0..0d1e4a6 100644 Binary files a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc index b07780c..33f8843 100644 Binary files a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc index 74c60d5..9e8dc9d 100644 Binary files a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc index 004efbe..ab8c9b2 100644 Binary files a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc index d3aed9b..8fa25a6 100644 Binary files a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc index feef625..ad44cdc 100644 Binary files a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc index ae1b709..0f02333 100644 Binary files a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc index 52fcc86..10d1a9a 100644 Binary files a/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc index 235e056..1e3f40e 100644 Binary files a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc index 327619a..b191a85 100644 Binary files a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc index 632d130..d194b2b 100644 Binary files a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc index b1cd14d..9a190a3 100644 Binary files a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc index bde8924..e3f5367 100644 Binary files a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc index a63a299..0e53775 100644 Binary files a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc index aa89d56..a0f2d60 100644 Binary files a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc index 1d7688b..2444470 100644 Binary files a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc index 1896825..745a525 100644 Binary files a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc index 1ae2238..2c6e069 100644 Binary files a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc index d60b9b4..6b5347b 100644 Binary files a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc index fccbe76..cf9e1a3 100644 Binary files a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc index 105b9b8..5872fb1 100644 Binary files a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc index 95a1c25..c28ae8c 100644 Binary files a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc index ad63ecb..90fead3 100644 Binary files a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc index cf44e09..8fdda3b 100644 Binary files a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc index 44d653e..7ba20e3 100644 Binary files a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc index a3d0a27..3b6d41c 100644 Binary files a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc index f028ce7..d54802f 100644 Binary files a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/migration/test_coer_semantic_prompt_contract.py b/tests/migration/test_coer_semantic_prompt_contract.py new file mode 100644 index 0000000..8fd4788 --- /dev/null +++ b/tests/migration/test_coer_semantic_prompt_contract.py @@ -0,0 +1,27 @@ +from agent_framework.guardrails.calibrated.prompts.coerencia import build_coer_prompt + + +def test_coer_prompt_delega_intencao_parametros_e_execucao_para_camadas_seguintes(): + prompt = build_coer_prompt("qualquer fala", "") + lowered = prompt.lower() + assert "não tente decidir aqui" in lowered + assert "qual é a intenção" in lowered + assert "faltam parâmetros" in lowered + assert "mudança de intenção" in lowered + assert "compreensível mas sem todos os parâmetros -> 1" in lowered + + +def test_coer_prompt_trata_negacao_sem_heuristica_textual_chumbada(): + prompt = build_coer_prompt("qualquer fala", "") + lowered = prompt.lower() + assert "contendo negação/discordância -> 1" in lowered + assert "tire esse \"não\"" not in lowered + assert "não quero parcelar" not in lowered + assert "cancelar, tirar cobrança" not in lowered + + +def test_coer_prompt_bloqueia_apenas_incompreensibilidade_semantica_real(): + prompt = build_coer_prompt("qualquer fala", "") + lowered = prompt.lower() + assert "bloquear apenas incompreensibilidade" in lowered + assert "não incerteza de negócio" in lowered