From 9ed4782f9ddd2d906e9b765bce555b302079f569 Mon Sep 17 00:00:00 2001 From: T3782834 Date: Mon, 31 Aug 2026 21:11:57 -0300 Subject: [PATCH] ajuste no guardraild COE --- .../__pycache__/__init__.cpython-313.pyc | Bin 1501 -> 1502 bytes .../__pycache__/base.cpython-313.pyc | Bin 1375 -> 1376 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 9160 -> 9161 bytes .../__pycache__/custom_rails.cpython-313.pyc | Bin 4404 -> 4405 bytes .../framework_llm_client.cpython-313.pyc | Bin 17783 -> 17784 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 5923 -> 5924 bytes .../output_supervisor.cpython-313.pyc | Bin 24306 -> 24307 bytes .../parallel_executor.cpython-313.pyc | Bin 21862 -> 21863 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 10889 -> 10890 bytes .../__pycache__/rail_action.cpython-313.pyc | Bin 654 -> 655 bytes .../__pycache__/rail_decision.cpython-313.pyc | Bin 1605 -> 1606 bytes .../__pycache__/rail_result.cpython-313.pyc | Bin 1004 -> 1005 bytes .../__pycache__/rails.cpython-313.pyc | Bin 41639 -> 41640 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 2994 -> 2995 bytes .../__pycache__/_compat.cpython-313.pyc | Bin 2445 -> 2446 bytes .../contestation_validation.cpython-313.pyc | Bin 2177 -> 2178 bytes .../__pycache__/contracts.cpython-313.pyc | Bin 6901 -> 6902 bytes .../__pycache__/input_size.cpython-313.pyc | Bin 3649 -> 3650 bytes .../__pycache__/llm_adapter.cpython-313.pyc | Bin 3799 -> 3800 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 7454 -> 7455 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 8894 -> 8895 bytes .../output_sanitization.cpython-313.pyc | Bin 13910 -> 13911 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 12661 -> 12662 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 473 -> 474 bytes .../__pycache__/_context.cpython-313.pyc | Bin 5871 -> 5872 bytes .../ausencia_oferta_proativa.cpython-313.pyc | Bin 11265 -> 11266 bytes .../__pycache__/coerencia.cpython-313.pyc | Bin 9485 -> 4158 bytes .../__pycache__/dlex_in.cpython-313.pyc | Bin 1304 -> 1305 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 1815 -> 1816 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 17652 -> 17653 bytes .../__pycache__/fraseologia.cpython-313.pyc | Bin 7633 -> 7634 bytes .../__pycache__/out_of_scope.cpython-313.pyc | Bin 17493 -> 17494 bytes .../prompts/__pycache__/pinj.cpython-313.pyc | Bin 8424 -> 8425 bytes .../__pycache__/ragsec.cpython-313.pyc | Bin 1120 -> 1121 bytes .../__pycache__/revprec.cpython-313.pyc | Bin 5411 -> 5412 bytes .../prompts/__pycache__/tox.cpython-313.pyc | Bin 644 -> 645 bytes .../toxicidade_output.cpython-313.pyc | Bin 928 -> 929 bytes .../calibrated/prompts/coerencia.py | 170 +- .../__pycache__/__init__.cpython-313.pyc | Bin 1663 -> 1664 bytes .../rails/__pycache__/alcada.cpython-313.pyc | Bin 4999 -> 5000 bytes .../rails/__pycache__/anatel.cpython-313.pyc | Bin 9148 -> 9149 bytes .../__pycache__/confirmation.cpython-313.pyc | Bin 11042 -> 11043 bytes .../rails/__pycache__/dlex_in.cpython-313.pyc | Bin 3279 -> 3280 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 3331 -> 3332 bytes .../rails/__pycache__/ragsec.cpython-313.pyc | Bin 5462 -> 5463 bytes .../rails/__pycache__/revprec.cpython-313.pyc | Bin 5470 -> 5471 bytes .../rails/__pycache__/tox.cpython-313.pyc | Bin 8293 -> 8294 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 533 -> 534 bytes .../rules/__pycache__/alcada.cpython-313.pyc | Bin 1881 -> 1882 bytes .../__pycache__/pinj_patterns.cpython-313.pyc | Bin 3730 -> 3731 bytes .../__pycache__/tox_blocklist.cpython-313.pyc | Bin 1506 -> 1507 bytes docs/FIX_COER_SEMANTIC_LLM_20260831.md | 27 + tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md | 71 + tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md | 27 + .../FIX_CONTAS_REPORT_20260831_SECOND_PASS.md | 27 + ...S_RESIDUAL_CONVERSATION_PARITY_20260831.md | 63 + ..._AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md | 24 + ...NARIO_23_LIVE_INTERNET_BALANCE_20260831.md | 29 + ...PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md | 46 + ...ED_FIELD_CORRECTION_PRECEDENCE_20260829.md | 51 + .../LEGACY_INTEGRATION_PARITY_20260822.md | 104 + tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md | 472 +++++ .../MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md | 1881 +++++++++++++++++ tests/docs/MATRIZ_MIGRACAO.md | 223 ++ tests/docs/OBSERVABILITY_CODE_MAPPING.md | 67 + tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md | 43 + tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md | 26 + .../OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md | 83 + .../docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md | 112 + ...CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md | 302 +++ .../RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md | 423 ++++ tests/docs/VALIDACAO_MIGRACAO.md | 295 +++ .../conftest.cpython-313-pytest-9.0.2.pyc | Bin 836 -> 825 bytes ...sarial_parity.cpython-313-pytest-9.0.2.pyc | Bin 53235 -> 53224 bytes ...ntract_parity.cpython-313-pytest-9.0.2.pyc | Bin 37551 -> 37540 bytes ...nd_guardrails.cpython-313-pytest-9.0.2.pyc | Bin 4266 -> 4255 bytes ...rapper_parity.cpython-313-pytest-9.0.2.pyc | Bin 29078 -> 29067 bytes ...site_workflow.cpython-313-pytest-9.0.2.pyc | Bin 60209 -> 60198 bytes ...aining_parity.cpython-313-pytest-9.0.2.pyc | Bin 40370 -> 40359 bytes ...ompt_contract.cpython-313-pytest-9.0.2.pyc | Bin 0 -> 6832 bytes ...s_domain_mock.cpython-313-pytest-9.0.2.pyc | Bin 64314 -> 64303 bytes ...ation_routing.cpython-313-pytest-9.0.2.pyc | Bin 17220 -> 17209 bytes ...ss_rules_full.cpython-313-pytest-9.0.2.pyc | Bin 23194 -> 23183 bytes ...ilure_mapping.cpython-313-pytest-9.0.2.pyc | Bin 7671 -> 7660 bytes ..._out_of_scope.cpython-313-pytest-9.0.2.pyc | Bin 3514 -> 3503 bytes ...ck_regression.cpython-313-pytest-9.0.2.pyc | Bin 8384 -> 8373 bytes ...ty_resolution.cpython-313-pytest-9.0.2.pyc | Bin 10095 -> 10084 bytes ...y_mcp_service.cpython-313-pytest-9.0.2.pyc | Bin 20547 -> 20536 bytes ...aining_parity.cpython-313-pytest-9.0.2.pyc | Bin 17949 -> 17938 bytes ...ls_judges_spi.cpython-313-pytest-9.0.2.pyc | Bin 10132 -> 10121 bytes ...rity_extended.cpython-313-pytest-9.0.2.pyc | Bin 39475 -> 39464 bytes ...ent_gap_fixes.cpython-313-pytest-9.0.2.pyc | Bin 6823 -> 6812 bytes ...ion_directive.cpython-313-pytest-9.0.2.pyc | Bin 5881 -> 5870 bytes ...ive_structure.cpython-313-pytest-9.0.2.pyc | Bin 18969 -> 18958 bytes ...rag_directive.cpython-313-pytest-9.0.2.pyc | Bin 9457 -> 9446 bytes ...k_zero_legacy.cpython-313-pytest-9.0.2.pyc | Bin 1792 -> 1781 bytes ...binary_parity.cpython-313-pytest-9.0.2.pyc | Bin 12226 -> 12215 bytes ..._dict_history.cpython-313-pytest-9.0.2.pyc | Bin 1984 -> 1973 bytes ...faults_parity.cpython-313-pytest-9.0.2.pyc | Bin 6448 -> 6437 bytes ...drail_context.cpython-313-pytest-9.0.2.pyc | Bin 8352 -> 8341 bytes ...user_feedback.cpython-313-pytest-9.0.2.pyc | Bin 7690 -> 7679 bytes ...mework_native.cpython-313-pytest-9.0.2.pyc | Bin 43119 -> 43108 bytes ...inal_response.cpython-313-pytest-9.0.2.pyc | Bin 4555 -> 4544 bytes ...andoff_policy.cpython-313-pytest-9.0.2.pyc | Bin 10774 -> 10763 bytes ...ed_meaningful.cpython-313-pytest-9.0.2.pyc | Bin 4628 -> 4617 bytes ..._alternatives.cpython-313-pytest-9.0.2.pyc | Bin 23151 -> 23140 bytes ...subject_guard.cpython-313-pytest-9.0.2.pyc | Bin 21833 -> 21822 bytes ...cy_dependency.cpython-313-pytest-9.0.2.pyc | Bin 1902 -> 1891 bytes ..._code_mapping.cpython-313-pytest-9.0.2.pyc | Bin 23705 -> 23694 bytes ...compatibility.cpython-313-pytest-9.0.2.pyc | Bin 20456 -> 20445 bytes ...ider_boundary.cpython-313-pytest-9.0.2.pyc | Bin 5721 -> 5710 bytes ...istry_actions.cpython-313-pytest-9.0.2.pyc | Bin 16951 -> 16940 bytes ...liance_anatel.cpython-313-pytest-9.0.2.pyc | Bin 17447 -> 17436 bytes ...on_validation.cpython-313-pytest-9.0.2.pyc | Bin 51115 -> 51104 bytes ...transcription.cpython-313-pytest-9.0.2.pyc | Bin 11611 -> 11600 bytes ...ation_message.cpython-313-pytest-9.0.2.pyc | Bin 23892 -> 23881 bytes ...vas_variation.cpython-313-pytest-9.0.2.pyc | Bin 142656 -> 142645 bytes ...n_adversarial.cpython-313-pytest-9.0.2.pyc | Bin 43308 -> 43297 bytes ...orkflow_cases.cpython-313-pytest-9.0.2.pyc | Bin 18734 -> 18723 bytes ...act_hardcodes.cpython-313-pytest-9.0.2.pyc | Bin 9978 -> 9967 bytes ...a_full_parity.cpython-313-pytest-9.0.2.pyc | Bin 18867 -> 18856 bytes ...ional_context.cpython-313-pytest-9.0.2.pyc | Bin 5907 -> 5896 bytes ...ration_compat.cpython-313-pytest-9.0.2.pyc | Bin 14149 -> 14138 bytes ...and_contracts.cpython-313-pytest-9.0.2.pyc | Bin 14550 -> 14539 bytes ...ty_pente_fino.cpython-313-pytest-9.0.2.pyc | Bin 30796 -> 30785 bytes ...c_uncertainty.cpython-313-pytest-9.0.2.pyc | Bin 8254 -> 8243 bytes ...ance_guidance.cpython-313-pytest-9.0.2.pyc | Bin 5887 -> 5885 bytes ...nto_grounding.cpython-313-pytest-9.0.2.pyc | Bin 4962 -> 4951 bytes ..._continuation.cpython-313-pytest-9.0.2.pyc | Bin 18765 -> 18754 bytes ...ntract_parity.cpython-313-pytest-9.0.2.pyc | Bin 12427 -> 12416 bytes ...d_idempotency.cpython-313-pytest-9.0.2.pyc | Bin 14792 -> 14781 bytes ...tadata_parity.cpython-313-pytest-9.0.2.pyc | Bin 34894 -> 34883 bytes ...andoff_bypass.cpython-313-pytest-9.0.2.pyc | Bin 14387 -> 14376 bytes ...eration_types.cpython-313-pytest-9.0.2.pyc | Bin 6147 -> 6136 bytes ...ispute_parity.cpython-313-pytest-9.0.2.pyc | Bin 13790 -> 13779 bytes ...onse_renderer.cpython-313-pytest-9.0.2.pyc | Bin 4414 -> 4403 bytes ...ecution_latch.cpython-313-pytest-9.0.2.pyc | Bin 7692 -> 7681 bytes ...ast_contracts.cpython-313-pytest-9.0.2.pyc | Bin 17453 -> 17442 bytes .../test_coer_semantic_prompt_contract.py | 27 + 139 files changed, 4473 insertions(+), 120 deletions(-) create mode 100644 docs/FIX_COER_SEMANTIC_LLM_20260831.md create mode 100644 tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md create mode 100644 tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md create mode 100644 tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md create mode 100644 tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md create mode 100644 tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md create mode 100644 tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md create mode 100644 tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md create mode 100644 tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md create mode 100644 tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md create mode 100644 tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md create mode 100644 tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md create mode 100644 tests/docs/MATRIZ_MIGRACAO.md create mode 100644 tests/docs/OBSERVABILITY_CODE_MAPPING.md create mode 100644 tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md create mode 100644 tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md create mode 100644 tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md create mode 100644 tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md create mode 100644 tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md create mode 100644 tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md create mode 100644 tests/docs/VALIDACAO_MIGRACAO.md create mode 100644 tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc create mode 100644 tests/migration/test_coer_semantic_prompt_contract.py 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 f71d1b6d8533595b15f5349527141c7aa017db8d..d3fa4e7a71cc96f834932eefe8903efe8cd52035 100644 GIT binary patch delta 37 rcmcc1eUF>_GcPX}0}!~0PTR=+k%gy9KR2&LKUv?gxU_gOJL@$7&3OxK delta 36 qcmcb|eV3d2GcPX}0}ya5o4S$vBMWz>er{fgeu}s)&Tw`4CVj; 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 247d90093b4899ad5bbc0df092a1d2ee2f50a1fe..ddedee0ccb4721dec7a0e8dcf7a5d3118fe2617d 100644 GIT binary patch delta 38 scmZ3iw?vQoGcPX}0}!~0PTR;mnVF|rKR2&LKUv?gxU_ilO6DqI0L>f=R{#J2 delta 37 rcmZ3Yw^)z+GcPX}0}ya5o4S#EGBbCTer{fgeu}M()qNyj%=G;3hh4BexqXPqTh*UWtCPzGHD|@#Yv-t^fc5J`5HB delta 39 tcmaF9it*VhM()qNyj%=Gz_D!VMs7D&?k4@*yb}Esecxcu&9SUp0RZ{Y3$*|M 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 2a37530b96c9725cbadb8a7ace73fbdce11e3b34..892395c349d4c23396f9f15bcefb9ee2a2916d6d 100644 GIT binary patch delta 38 scmeAS?F!}o%*)Hg00eHL(>8KHX6C8V&&?~*Pu6!VE-l{tlX-#~0N6eZIRF3v delta 37 rcmeAQ?F{Aq%*)Hg00bP%rf%eZ%*pPN^rpQ7&@?78_D^8_^j(X$KK 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 0796c121afd7681592e1f44b5f4e2c85b10d1135..13d5a9c03b42ee290c0c651ef1306417b2e3b4dd 100644 GIT binary patch delta 38 scmeBU?Pulw%*)Hg00eHL(>8MFGxF5x=jN5@C+j;FmlkhsW#nW80JkIx4gdfE delta 37 rcmeBY?PKNs%*)Hg00bP%rf%fUXXLKY&&?~*Pto@c_T1dY$jJx*v7!mB 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 6ffb379f273d9f56fde3c0f64df59c1aa94e5656..41e348e4cf9be56a7634f18f3b6210c0b4f796be 100644 GIT binary patch delta 38 scmX@gbBu@kGcPX}0}!~0PTR;`&dgJUWtCPzGHD|@#btcn>hgY&GcPX}0}ya5o4S!ZfRnpLKR2&LKSkd+*mH9_rw8L?VdH7h&&?~*Pu6!VE-l`?iS0HM0Ktq4W&i*H delta 37 rcmeAZ?iJ?#%*)Hg00bP%rf%e(!^YjLpPN^rpQ7&@?74X}+ifNQySEDk 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 fe11483a0e92f442046741acf8d1ee079c20aef8..bce60815c6de5c69ba929ea8b4592268dbdd8c04 100644 GIT binary patch delta 38 scmZn^Y!c-D%*)Hg00eHL(>8LevGPpO&&?~*Pu6!VE-l_{!|KWm0J1{~*8l(j delta 37 rcmZn?Y!u}F%*)Hg00bP%rf%d`W96Q#pPN^rpQ7&@?77*N)s-0ltnmqU 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 7e2e6f2c0c91b77e8deb3e674cdc88228715f274..9316b89e5f749fab5365b6386c819854b7a2c03c 100644 GIT binary patch delta 38 scmexr`puO4GcPX}0}!~0PTRob4Z5!GcPX}0}!~0PTR=+mxZTYKR2&LKUv?gxU_h)FsmLX0M_dY`~Uy| delta 37 rcmX>kb5Mr+GcPX}0}ya5o4S$vFAH~@er{fgeu}D!|jKpPN^rpRDg#Tw1)@TEI#X0P1!Nc>n+a delta 37 rcmeyC^fih5GcPX}0}ya5o4S!(Re-xiKR2&LKSkd+*mJXufR!Qu8K1)Z&?|pPN^rpRDg#Tw1(&yOtLt0M)AtvH$=8 delta 37 rcmZpQXpG?g%*)Hg00bP%rf%e3sKq@;KR2&LKSkd+*mLs^EiXm@&j|}q 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 3e3772fe27a53c09c51fdba96b36dbde33d09a47..131939a251faa6dd8d41f03c74d841e2c54a7d9d 100644 GIT binary patch literal 4158 zcma)9&u`pB6t0ku+36_;GiU$G|yCvHI%;>!1)y}RDDl&;dP?H$j&`QG<^@6Cf}o(VjB z-hcl4y-GKq!iW9`3wA+x{iXhLfAJ-6ueImf#Zjxh zQfw`F-W6+-EEkaxR_a)6Uf#MYUJs43<=r&YQaF{AKc=}3C5_Q(R`B33)+)_ayBh?T ztCy^DnMorhibRS`*-??^QYe>OrE|G1)PZ)n5>Ag&y^HBETWHdKWt|MQNo6do3Jbhg z-riSnkV>&DV_vjg>m9KbE0LBz84;;aV0quTi&b9WG5#`X%O4Y!TjM%d z858GnsAC<;NOc0`LX*)fB2}aEK9(1zz)m@IXJQ>9VxAXc_7StPLWZ=BuGn%;Iu5{< zzbHpLMG&D%gfxLQMXqcImyR}nd5L2v1GLkgi#fz7^Vw<44UfvclkmsPqu9Z%? zPGHpts5siTItUderM(b#CMoZw00an1yf{==+TJiWHwz`Lz(N*FAZVAVu)If!blu=j zqF&n{KP}S~0a4g41i7Z4C0v1@{)02P05|<>{wc4&aLvQ_;u}k^cx%EKcldFS#{Ww>IY(QqaJdiNsvDX1ddEe^Lh&`aQYiuHO@Ua6z zg>j)%tks}!rod_j7>`GE#naK+Hw#IFBTdvV?}4`n?J8Jk(>l6ZUSY8(!EX#hCKx<| zQIvN;IZ+;_6iW!iL(oU9i=CadogGTf&d#}=oo-OP0NH1HV7 z>k|13w#zRsBh?kpA$~+9Zx(tzI7>;0DO%a^1ENc98*gF=Z(!yYA}Jzbeu9+5wa_6N zN*hv)HLNzaevc5V1l%x$iTogS_J$$RAf=4bv~UAU>8)o#8glHWVM!GdwJljrN)^R) zO)?0B*;A9K4suDNDDVeEYfMDD-Y;*%O3ZYZ^Y%4yC-Q`e>tTxkGScK4llN$sH4vB+ zZzVd1St1h_7>!^B4YQoC#RSWcaP4@&oNrOUkwPAa8g#9|BlkO72OgIVmCHG+~s;iU$ ziJ)|g_o({Dgr`-z)51(bs<5x32a+b<=cK9F1RT{tZh-VP&>w|h=6VjSA|>%wCr+EzS%q&~aCzmegr(&|xnB zaL8#E(VT!N@VL&r;xp_Ikb#)^9o+yV#&X5@%3b)5C;=;z=z&Oo0eS>9U8b)5Xt)3? z!K@q@Fx?*)Jx7oq_vSfALX6-ZC9jF{IWZwmRr@-XOlWoosL|;l>_=n@R>s|kDFAKo zgiRo^3CbK5n3lHq`>k);kq{?X1`V?rgjO@+0h*f;2OI&;49(*{x*hT$1g8!pXw7Vb zFySO19Ei_7539~{9RXTocy&>L_$SS8hp@&xObixIOgzN^05aDwYO6=2LO5>2XMy3S z)u^2)EeHvYsrL<4uj8hJh^J;_dT`J3-l(YCF{;wXyjYxlTda{GKVlw2k4kB6XEUNS zW%!u)X>+-WAP@XC%h!LrQ*&(23VU--IE_{(v%n$+14?uX|DjUODVULUf_Z&LpqM5K zrrFK`*lm!%JY?$wU9A$U)butu29g3+s-7??ZZ~S@q*knwm$IwyxYdfJrPuW)t`az`fwA;w?#k~Hsyy$x5m0UG8#73QvYV;Bv4+;0zPyma*qRGz3$ zjlD52q3+>?^3V@GXT#%zd()QL^-xUp5Jxi=^{R6Q9^t?XXVtQseKvk}Sm-#ys>%+k z0rn|GdJ6ws8(+7)qZhiTUp@N7_g*{u!-XSnq3i$VFWp)`@|IToBk7+%**fyhFSQpnvy9ItxgMnS7(X_s$ zLzJcSzdeDk@rkz*6BV&~+n&ZP4o{wY9$!a`zVH9F@RZ;B`;6zm{Lk{czTe{4{sr+E B;jaJy literal 9485 zcma)C-)|e)btYxm-m*!P6h%?=>4NDtq)qXMq_r$5X+T?CRok_wlCn|sMZ6r2sF8;= z-1)&({j@=W1T7G>XcGkJ(=G}m*aiF$Yy$*-%={O63bs#us)I$)*M8r*cSfSU3#1zFU`mf(8m5!otHc!+fRFU?4^|0R?sG1YHQT9pT zc$!~$!90~K9N%+;#H}|gl|eYsDhSoV;nCychH__Wt|RTceyE&qrj8B{-%+)h8_z;D z4@1A+P(eCV>+NwToq*h3A6Q7tJ>YpR$I+to^Y_WgM}O6fYzmYw^AF|Rp3swxl_-^Fgw+Ah43a1>lMrJux$Dc&CTAN3>)5*SQ~1>ua|!j91bp$t{_PdJOLnV+XNVg{Xr zPfyzI^^H-zsXqIU5C8Yq|Dw9EY|2PypQ_Uo2^I#DM!mzoecas}bRYE&kNXX!9S{CP zUNAUQ#E$!&d2y0`8hK8rY78Ypf=>aq9o24a!>kw~it!ttT0?f3qDeRpchq)kyWWuX z09nR6Gz$Z4j*B}w2s=KWUAc~zI6868*0k^E-vV36TYqXy^WCYSyQz*Ny&(RuzzC_8 z#4X@6nvhfC7C$(lM_f`$bN&w_Fi4*}A@fWwBT^qDk#xdE^u%L=fA z-yb%V7$4)xNf_bek$aj`Rb_#U1$0b!PQz%1YZ~eTzR)xH3$H;NKmxNJF~C7iHDTT` zXO008Ck2xtr83B*LIwEly=uJ6)kygl?6Tn<2#B}gJgx-#MNbi={-LN8Y1 zbSiLI^{MNe!D4U?@|(c~k7@xqQZkOcK#a94Hw!1^+Q>!x&RoZ>ST02}fDY#X zJBTR-hz#B&Xny#9PJD>r-1oqn3e2(tSge7YO|rJP8Ya7|Ksc}l1Wkz9RIMuzapWS& z#NZv?t0rDdnpzl4b642yX1~ypfQ^T+3`IOyvmf>!9lhH-?mp?_gs$(+k;LY>mf;LQ zyZcCoo(d6S95FR{`!EcUc?lrR?cED+YJy-LaMa6wgjIo1a5i=%_K-d>FC8O}#?|n8 z_I&K-7S4S)fh&v!+zm(|3=I)1CG*y3M|JLXTI=g=_1O>qO>JzpTlfd!yY9nI+xk#V zL%@opDrf_H=I6}$xQdD} zqK$hX1Zd(_M1-mqkO4w)#0Bh^tYAQ^ESLo|Gw`~)YeDpk+sbMrUodxs?QJ2G;}T~m zrWzkhNO!UF)DQ6iZ0h{x1(1LeE%IX=A@H)+wX-)>*GJ)J%(hzFsDf$s9Kh&o-D{Xg zcJ8+uTdmIhZKSAW>!T=4LKIeDO{$~Gst4WUhkvbV8%R?G7bT6c9j&cJ?%dUh+JGb> z+GKeThw;cTV-y!%!G#gNfW^Q04KsuS=;40oWI#}dk%4HiTc z33B4571-R)??z!}DHxMB4g4)1H}J-6j6*-Mt3t|9K;T@i8uZ@j z9UOP}xU;K4 z8bgCHC+5HkvQ?m_R?(ubyFT0}qsZztB2_ZFNVX0ca-*+nWo^8DM4UWI1US6+cioNZ}LEABp9xta+}S` z3D?f$Ud*A191r#mdT;j!4k-JS@k`I^n_vS?GAKLgw(?U@)8H( zDJ-m1f{cr9R zuZsS~rJxxA6|DDm|6zBpFZm;&#wyGcXcY7qRp1(LBsqZ|-}vAf)H>*93}Haw?%e5YzSXhEyK@JvoZo4;pMq0!e|BPo-k{grd(>;rCRB9; zH}9gZ@X%RTnj#`T)t~@@B>I~~)iyiZTied&1Q_XTZ-`Ae5^QR}iH7msePdC@^mSwz zo0xfutgj_Um3O}?zH}UsIsZ{>_Pw)b5e))0vk4nRp$0hu}v2pV+ zO7+!rTrQO!0hFwk)@C@SXz&rIZZt)!%IeJ?dB-p$uE`s5vNJ{JDMg|w^F+B^JN8i& zY4a5<)H�B)157Ku6Z*`lFGtAa0@skQJKd!u;(C*LylbD8-?o*Thtao)>_1dT;d ziLw^~TP+-4I|&3RkdM*Bh~4D~a|eJujlNYXJqexc6O0&q#?e+9SorYW1JSMkjkMmL zbRcw^={cxc;FpDqHU`qf_2Z#IbX8%0ofxo={Rz>SA9=7^TD~v^?|E6wR zYQV*);Kn8i<1z0Ik{lkR@ke2K9tY5mm!=MI*TL7c|m4Qwi0Zse9Z6clGdo~TtZ zcmq1K=g6a~7envs_foHFhEwKC%ucdT(X;XjfL0FR#?_?^5`5+8kKjINd5<$XPz1bj z>ZcdLGfnN$w4#{U02R!@c%~$LLpC5tEYiw{dq{ORIAc~DG0*vsEQ(!TcLC3bFL+Y; z0F!1Z?FQ|fq|7jWrK}P$S|mIoFH6RuYt*-@0oXFv$v#;_((^>9PYLHbl0zt?h7NgN zn#M5}s~iRVVQ=5geY=kyWIx`2Y)WH(0yCWv$l1Y659r+MsqVY`ePCmbwsRMKXwii; z6>u;yQO17)eOSWjshGj4B{O{(YTI+dhWf_7MTUBj75$99-5GK60`@TM`GmpAGX^zr zh#Y30B952AD{Ee!k;?-kF^+~7VJ(J6ECXV(SujsuiK-oN6K3WTgCIfxmAAO|8|m^r&GKeWkgY?rkAqm8b!?{5+rz+RM?w2M^{dLB{pR24+OSOw5O&(tce%caRX^(GN1kplylOIqS_! zQ&T7sWkYqnep7`zM6g>A`bFysY<$f-$pqZI764XZ6#7lB-< zRZC!?S$On-G1&5eKgeFp_^gVx9K(_uhhY9DjH2FgeZ=eeD4hSBwTX13qKN?*E99q`N*B2>T!QoJ`@RfZ|f{5F8 zMQ9JR0lPKggdr=)VVN?hvCyBGxf^e3!`z-3%*Lwb0Tu`&OO`yAu#@ZQ~U_MCI8Yf?L{yajzXh^K%0DpP1Fz7 zJ=aCwfoBf6ypm~@v{_$KV!aZqA~2>3;A~P~;ejzUWr7(!rhbw-7?S3Sid&g9viLVn z7bidiQ8OXw(X3*^>cLrfAb@z_a8?v6jLpreK)(czbA6$kTxl8?Zun5q|vU>`PvF4-1v5bs>ufn87-Az`ewTur9v5z6$$Ai*enqg7xlCvuYg{GTLw z>xDykm|rd%i~_*b_yjNeq+Y44eTAQAe_VN+EeSTVC)}x|9)@9G>28iFZRzSB^$zi1 zL!LSmLmp<83})>AK%ux^ zox2Vmg+Con#x2p0GV{{0eS`LE=Dr_BPftR}Yx&;zwJ%z6te};{Rrf-oUTRH@=I<5UKC}RrF1~ l#r(1q|0#aGyiqQff4B18^6KxOl}qJs|KF`)xx6a({Xer9+R^|3 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 7d104145f6c4cbbb9ca3e0bfd5cb0e6c77c21e64..a91ffe5f8b8357e8235e37eb6b4c91fcda0b166e 100644 GIT binary patch delta 38 scmbQiHIs|`GcPX}0}!~2PTR=+mzk$WKR2&LKUv?gxU_h)FbgXq0LA|bSpWb4 delta 37 rcmbQqHG_-$GcPX}0}ya5o4S$vFEe+yer{fgeu}zW)ZYsa 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 c69bef6ad910fac4cb8573c026c55fb6d460ed90..2ca4865343d197b7efaa4f320492fb950c00825d 100644 GIT binary patch delta 40 ucmccG!Fa8Mk^3_*FBbz4xQkBP$bHX=XQF;?UWtCPzGHD|@#Zg1iHraT4-Ihu delta 39 tcmccC!FaWUk^3_*FBbz4a4eg;k^7z#_XPdiyb}Esecxcu&0n1o83F$c4G#bS 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 a0659fafcc6a70234d92f40aa919ea6e79506cd1..5d759dfdd23e6b995325ed072129b58acb4585f4 100644 GIT binary patch delta 38 scmaFi_|lR4GcPX}0}!~2PTR;`CC}5TpPN^rpRDg#Tw1(&f_wxc0OuzQ=l}o! delta 37 rcmaFq_`;F8J!GV*lj=jN5@C+j;FmljWMXZ#BQtF8*& delta 36 qcmZo=ZDHm9%*)Hg00bP%rf%deWaMtw&&?~*Pto@c_MF_o_!j`CdEKR2&LKUv?gxU_h4Ig=(M0KKpZRsaA1 delta 37 rcmZ3;zJQ(kGcPX}0}ya5o4S!Zk%@b%er{fgeu} 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 fc0d11b97e2802e08c8c8ac67e3ced9dcefdaa6a..b332bab4d00cab4250fdd2a81a0bcb8cf3fea6db 100644 GIT binary patch delta 37 rcmey*)48LyX5;D9&&?~*Pu6!VE-l{7$lk^S0LZBd+yDRo delta 37 rcmeBBZ&&C3%*)Hg00bP%rf%eZ&BooKpPN^rpQ7&@?75kVy^RL|!b=Kz 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 e7f40e17241febe1280a253c50c938cd03529d5c..31ea4b6ed35ad7daec8311567381ad0e33af15fd 100644 GIT binary patch delta 38 scmdnvzSo`mGcPX}0}!~2PTR=+m7AwiKR2&LKUv?gxU_gPH_t|K0OGm}+5i9m delta 37 rcmdn%zQ>*WGcPX}0}ya5o4S$vD>rwCer{fgeu}vc|nr!PKR2&LKUv?gxU_ilb=Dtj0NQU1W&i*H delta 37 rcmca0d0vwHGcPX}0}ya5o4S$vAS-vLer{fgeu}8J+Vdd%8&&?~*Pu6!VE-l`Ci&c{y0K<{9 delta 37 rcmZpXYL?>u%*)Hg00bP%rf%du!phyHpPN^rpQ7&@?78_it0p@Dy=4lg 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 9d4c5d2b6076f2a78582c231727cf365ea6e2f78..e62ed4203d02d6511df08bda5482a1847331580f 100644 GIT binary patch delta 38 scmcbnbzO`5GcPX}0}!~2PTR{+}3+f57F 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 8c92df81b62f8182e11909dc1c92de3d5e4bf191..0de4a468c3e4783adb72d0d20318804a98821f20 100644 GIT binary patch delta 38 scmcbobzh77GcPX}0}!~2PTR=+lAWhZKR2&LKUv?gxU_ilfA)A@0On2%XaE2J delta 36 qcmcbwbx(`?GcPX}0}ya5o4S$vB|CSger{fgeu}g-L delta 36 qcmbQnGL?nqABW%E=2S 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 83b9ffb178da5931f084adf5217b253d91efcc13..414c3b868d4160b2590b7ab9556742cef4f6c609 100644 GIT binary patch delta 38 scmbOvJ6V?dGcPX}0}!~2PTR=6f{SN@er{fgezLw}acS}9eOy 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 1f8ed39d917b78e1c8939fc03fb016dc38aeddb5..8357fb839af95b9979a0130c2e82e97268583e25 100644 GIT binary patch delta 44 ycmX@Ywv&zfGcPX}0}w=C>)Xhk#3ZbzpPN^rpORRTsGpplS`?p_SuwerX(s?AIu2?8 delta 55 zcmdnVc7%=lGcPX}0}${po4S!ZiAl~Ch7^Gftn5=#>Glk-!H;?puKHgmANJ`Dhx CKM|(@ delta 58 zcmaDcpZW8AX710tyj%=Gz`tzjM(+E}avu7*c_sQOi6x2p$@zIDiN*10nHBK`iA9OI Nxy9+5|1!Tm4FFOM7E}NL 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 cabcf0dbd7d54ae42e9e594b0e39eb967b273189..fa16a451bfb845032612acb03443257ab8e27f87 100644 GIT binary patch delta 47 zcmZ3#lxfLQChpI?yj%=G5Ou9@BljUDVK4pMyb}GC#F9k))Xh^n^D+PKR2&LKP9mwQ9n69wJ1Ib8h`!dhk=vMA*h4=zuS7p3u_RGHIX|^1J}t9ia}2Xp9sq8) B50?M{ delta 58 zcmeBv%sA~aBll-sUM>b8;9oX%BeyZLT(Ev_UWtB6Vo9QYa(-S(VsU(0W<`8KVo_pl MZgKkNKxV5v04XaI!vFvP 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 04df92ed2a66f77428452446890c5ab687bf47b6..c2915b09392c9183af6f2218c14f3a131cf56dee 100644 GIT binary patch delta 47 zcmdmZjd|HMX710tyj%=G5Phw0BllHiVHf?}yb}GC#F9k)jd|lWX710tyj%=Gz`tzjM((T3a{l_cc_sQOi6x2p$@zIDiN*10nHBK`iA9OI Nxy9+5KQkLY2LMXI6_o%0 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 0e260192e82d1af5eb601bc251c53788cb490f68..e15ac3828d40d02b276ecc3b11e1125366423d9a 100644 GIT binary patch delta 47 zcmdnAn`!xOChpI?yj%=G5Phw0Bew^Ou$O*rUWtB6Vo9QYa(-%2d|GD3=0X<7nE-Ap B58(g+ delta 58 zcmZ3!n`zT-ChpI?yj%=Gz`tzjMs5!lxlsMwyb}GC#F9k)Ge#G=I9 M+~V}jX)KO20VuT;4FCWD 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 0000000000000000000000000000000000000000..9eff3eedb90ca247ff5287e1788ed310678b8c55 GIT binary patch literal 6832 zcmeHM&2QX96d!LUyGfj+v<)pS0-W@7qja}Py6u6a0;xb~MUlFSij;#ju_s%D*WQk8 z(&R!9To6K>ia16^h~$72iGQF{{sDV;Q6j4n2TpK84X2)XGyaH|Mx`8D39;66{C>~R zGjEcm%m|btUT0D!m6`=2d zf4f&ex`!5|-LMe9+R%_vEU$uW8pO*ycT{xJ`lUd=RM7MVb zFM1O%k~@VT!K~CcQ61>UnMdlm$64;i`Gv%Jup8$W66c|AoL@woYp6JU{qfLkkC?YT z%9JHyk`Q@EVQy>dJ6mz7FS0V89EP)RhKd zTleeKWwztNsfOyFPQZ$QKiN0V!y^rd0;AM&wtoDSno6BGibLy)YwNb7K+6oJoV%<{ zZVWKa55ivIa5(T*W5uP8s}dF-R5fCnDx(d?MDPMI&xDc6k}6Eg}N zaV!WEv0Ouo$rdOrml4e^HCDn8L`*VNOx3ALYeW^_OCz=l;el21FQigf64ks!3sI~S zoj9sPYaX|E*eLkOIg|4{@S(ft;n3^fezuuw4Nc#DcV~FyYx4{9OY81?JMu{%m)8q{ zT;9fUYvQsmpNwETBc}$STrZ?3=s>Buh08ngsE;SsZU*wiHjY~pPx$g^1lt)oH2~$> z%@hS4C{?%c#E$%ikH^<81@ibdj$0Ft`|=wRY-i-u0F-N&QWSKcRNcbkJMsx1m)4d8 zxwMVr*2E=WJ`uroMotYtxwf35paZ4q7B20`vp$}Ca5RwTwsG8=c+Qt+BiPQ!sR1Y- z98FQsfl@UX?ZPKV8t;-TCGih?yCnWUL*n6&_FpDTvPhN&yU9}W!$;_;$x=jmx^aHq zN$Id4m)$r&=QsnoJaC=C7LORGC*CROIK7~jZYvh>2*LQuy5VRx)3@$hnn46RWBh<% z{k-FZh{?R;^$&IcI`1XuT!T@C0=H7eI8hxdN{{oF?E)_>N~=c_Y8q)0dg>j97X_{5 zT-WBLP@^Vc-Z9W?#1ddafuRXgG<%T=w#oQ(>^O88lEoCj;pHdz5l|4a#?$FrNYfS<;3YvNg7o{C^QBc}$S z+^nT2=s>Bug=Yn4*^gELXW=YE(27`28N;*F3T1yPr(~#HET=5(*{R%VA6M2PER}5> zwLoMjLahi>7qTX=*Yh`8WH{~s54VUL$H zd;IFjMbn1e6*f@PpcbUzhLz^##-d>wI??Gr?C*T9727nt3xxx^Dl&f9RJVT84Erhj z@s%g`sF632xIXC(-=;+hOdx$HCOZSu^8$#|g6=74>ZFfnHX)Oo*~W2e;u&9_ zj9@z>rv{+hgiJCv4?0k)ZsD07`HYXJ)*+LGJw9$tJmt%0BG}HzsR1b0A(M>FgASCc z!AR_FMLFzC${6frSZH`mjcOpJsPX`Hi4vpB%y6Opk*dN%tYf@R6J8=UC=jVjya=#t zvrkGjk1(AP!*ohe7%|`ob*Wy8iW{JU|J0%Ulo!PpQ4bJjpa}T}5{^YDYu`-W);*J6 rW+j*hPDkXt2HhV$k|h0t(C6r9H2)}HklvRbjpU_^yN9qeEnfc%OwMG@ literal 0 HcmV?d00001 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 1136a28551abec4309b50ca3d4529fd54d62314b..61bb8fd4a9c6c88401a55fb8f710470461296cae 100644 GIT binary patch delta 47 zcmdn>jd}eyX710tyj%=G5Phw0Bey1xu&sV>UWtB6Vo9QYa(-%2d|GD3<{+LI?*M~u B5h?%x delta 58 zcmZ4gjd|BMX710tyj%=Gz`tzjMs7_WIS>8Zyb}GC#F9k)Ge#G=I9 N+~V}j?mREv0RTcp6;=QM 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 b0520f1d67447fcbff31f99856c10c57b135d26f..48a4e77dc82ea2c3dc0917bd3f8344b90ce4f68d 100644 GIT binary patch delta 47 zcmX@o#<;VMk^3_*FBbz4h+dhrk^2Lqu&;h@UWtB6Vo9QYa(-%2d|GD3W=SS(M*wLL B4>bS) delta 58 zcmdnl#(1QSk^3_*FBbz4@GqOXk^2LqT!emZUWtB6Vo9QYa(-S(VsU(0W<`8KVo_pl MZgKi%J|=BP05Crjr2qf` 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 edbf7e16335e7c4b1f3f1782e0305d92c8ba1553..d51697f3bf2fb10b718dbd901892a80fa2946532 100644 GIT binary patch delta 47 zcmbQWm9c*-Bll-sUM>b8h`!dhk$Vr5u(y70UWtB6Vo9QYa(-%2d|GD3=BG?s!vS%| B5V`;W delta 58 zcmeC*$~bE)Bll-sUM>b8;9oX%BljLAxiJ0Qyb}GC#F9k)Ge#G=I9 N+~V}jx0tqu0{||U6y^W` 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 b9317326269a0445cc1e0c41a63f40a5c36d3149..cfb1ae99f753d4d6b02bfab5656ba58ccb5d7a98 100644 GIT binary patch delta 45 zcmexv{l=R6GcPX}0}w=C>)Xh^gi+XCKR2&LKP9mwQ9n69wJ1Ig B55E8a delta 58 zcmdn7fbsAGM()qNyj%=Gz`tzjMs90Pxgh=Ayb}GC#F9k)Ge#G=I9 M+~V}jk(~G403$jSF8}}l 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 f9b7993efdc75c70474a7f9f2aa001c71acab792..383a5f0c1f8756c0f4bc0f80bc1cd3821c5f89b0 100644 GIT binary patch delta 47 zcmbQ+!#Jsjk^3_*FBbz4L|^OM$j#3r?5&@hSE8SiSdyrpoS#}0pO#s%*@Ee*GXPKT B4=?}# delta 58 zcmbQ#!#KBxk^3_*FBbz4@GqOXk(-}ME=)f+uS7p3u_RGHIX|x?u{b_0vm(AAu_!S& Mw>W*XF4I$I00IpYTmS$7 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 0ba4609c8c01feb3c265933dc7d644cc214df079..a8bcabeee026ab9d9f4efa293325a63fc8359d23 100644 GIT binary patch delta 45 zcmbQ@-|5f&nU|M~0SKb6^=;&4;S=`M&&?~*Pf09E)KAV&Es9Uetk|r}mo5naGeHgd delta 56 zcmeD5pW@H`nU|M~0SNe)P2I@N!Y3D^pPN^rpORRTsGpplSCUv9pO#q>UyxXon44Ri KzFCPcT@nD_HWDTP 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 0a86270dd13400b7db173a9b1179ba697df6d6a5..3cdc6d8c29028b174160cf076177a182ba4c3595 100644 GIT binary patch delta 47 zcmdnIg=xhWChpI?yj%=G5Phw0BX>EIu)BV4UWtB6Vo9QYa(-%2d|GD3<^@cXrvm_S C=n#AW delta 58 zcmZ3ng=zB^ChpI?yj%=Gz`tzjM(%PZxgh=Ayb}GC#F9k)Ge#G=I9 N+~V}jQ<)}D2LLU+6sQ0I 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 61c210c2e8b517e53fd0bbe4d7ee35dd8ff1a464..07ecd4c7c234ce8b31ec2d98d5a782fc521aa215 100644 GIT binary patch delta 45 zcmZ2(I>(gzGcPX}0}zN_nY59+l3mzEKR2&LKP9mwQ9n69wJ1I)Xh^idEQ8KR2&LKP9mwQ9n69wJ1Ib8h`!dhkvp7K*i}C_uS7p3u_RGHIX|^1J}t9ia|7#47XVo^ B4}Aat delta 58 zcmeC1!Z>pZBll-sUM>b8;9oX%BX>BfT!4OVUWtB6Vo9QYa(-S(VsU(0W<`8KVo_pl MZgKkNQr4L+01luNsQ>@~ 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 d37dc7e76dab1917625da9e79afd76559af600a6..4dcb84555e1a09cf3c5ffa005012fd610f2b58ad 100644 GIT binary patch delta 45 zcmez9`OK61GcPX}0}w=C>)Xi9#3bybpPN^rpORRTsGpplS`?p_S+QB0>6j7#Pr456 delta 56 zcmaFn`O%a6GcPX}0}${po4S#kiAl~^KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeX|17F(m*3Q4<*e 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 14c574325144a88cb8f3e20e711ed531fdda151c..8f00c16abb212e435372d87f5127b3c3dc44bc12 100644 GIT binary patch delta 45 zcmZqR`^wAxnU|M~0SKb6^=;&S$Rg~ZpPN^rpORRTsGpplS`?p_S+SXm^&2AqL9-6z delta 56 zcmey$+rY>DnU|M~0SNe)P2I@-kVVd0KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee-{oZ;Sx>coYu+ 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 7f891192c4ce89aa97cb8d1a8157653562fbd0db..3be12a5618d86c02d9936588dae16ed5f76c8a34 100644 GIT binary patch delta 45 zcmX>UzdfG&GcPX}0}w=A>)Xhk%OvcipPN^rpORRTsGpplS`?p_S+RK)Xhk$|~%spPN^rpORRTsGpplS`?p_S+Ti~)rbiIHu?@V delta 56 zcmdnWe}JF+GcPX}0}${po4S!Zl~pc8KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeRDIb5fcFA9};2! 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 4be418297d77ea50040cad71e7dfa09b7edf217a..c4d92993d0c99a4084070459aa0626e99291fc77 100644 GIT binary patch delta 45 zcmdmBwA6_EGcPX}0}w=A>)XivfJfL@KR2&LKP9mwQ9n69wJ1IO24f*b|Qc 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 63edf425f9dba36d24c28b87a4ef061feb010239..46d05183362f3ea7ed6c4014ab8cbdf03d03a192 100644 GIT binary patch delta 45 zcmZ4BIMtE+GcPX}0}zN_nY5An8KUyxXon44Ri KzFC3sz6=2EBNE2| 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 2bad33ac1da0294ddf7829b410d84c121d709af0..3ce9baed6bf36ab047ae4635ae13c5b596097403 100644 GIT binary patch delta 47 zcmaEVf$7NwChpI?yj%=G5Phw0BXGe#G=I9 N+~V}j%UJd;1^`%O6}bQa 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 ecb5e8142ad40d0a2d0bec1533985728ee0e8efe..033b9f2fa683d0fefda7bfd108a5d33da074a465 100644 GIT binary patch delta 45 zcmX@Dd_bA|GcPX}0}zN_nY5AHj#b!KKR2&LKP9mwQ9n69wJ1IUyxXon44Ri KzIi@Z7CQjqMiRXM 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 00d4cbc2acf15f35767114c023d90c29e6e9fef6..9d23286a76219939c9aad08dcd110a4582828f3c 100644 GIT binary patch delta 47 zcmaFAh4IN2M()qNyj%=GAbMreMs7!TVQ2l^yb}GC#F9k)Ge#G=I9 M+~V}j@$9U@06ohS<^TWy 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 cc24aede8c71784e1e56eb478bad68f8c13ad986..89698e83b4ceb933599d4b1f202e1f532a771af6 100644 GIT binary patch delta 47 zcmX@PigDj6M()qNyj%=G@O$Hgjobmu!k+rMc_sQOi6x2p$@!^8@oAYAn=6?w1Ofno CauB=# delta 58 zcmdnDit*$sM()qNyj%=Gz`tzjM(zM+xe)!_yb}GC#F9k)Ge#G=I9 M+~V}jdCV6A0W9kjYXATM 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 3c46450fe29bf0453bfb224c742c034468d72725..e27cb212532da91e7b1548e5b06ea36d32877bc5 100644 GIT binary patch delta 45 zcmaFI_n43SGcPX}0}w=C>)XgJ$0}^EpPN^rpORRTsGpplS`?p_S+UujRhb8h`!dhk$VEOu&aJe) B5Rd=> delta 58 zcmeC%$vAT-Bll-sUM>b8;9oX%BliSmxd8p#yb}GC#F9k)Ge#G=I9 N+~V}j>zE%z001ag6ubZc 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 a00a86857f64bf8aa074811bca65d8d0d4fd3818..9dc2b6c679155debb0fd61f4284a6d9536775e23 100644 GIT binary patch delta 47 zcmaDcpYiT|M()qNyj%=G5Phw0Blk8Y;V}K&yb}GC#F9k))XgZn@KoGKR2&LKP9mwQ9n69wJ1I&LKQvhTI B58?m- delta 58 zcmZ3}!nnPKk^3_*FBbz4@GqOXk-MBnE=E5$uS7p3u_RGHIX|x?u{b_0vm(AAu_!S& Mw>W+CRG#Uk036p84gdfE 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 b07780c8e98f57a159b4991939065abcd97b581f..33f884353b4aca14ce0394ec6a2fd3596d7ed2be 100644 GIT binary patch delta 47 zcmZ49!8oUbk^3_*FBbz4L|^OM$Zf(S?5dxeSE8SiSdyrpoS#}0pO#s%IhN;?IRIAV B4{ZPd delta 58 zcmbQ!!MMDGk^3_*FBbz4@GqOXk=ukvEW)s5YH)d01HVInE(I) 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 74c60d54e87b455ad83f1480183fea3528f6c9dd..9e8dc9d7e0369cf9173c83a1d0a07cd220a5cb73 100644 GIT binary patch delta 47 zcmZ48&%B_Ynfo&@FBbz4h+dhrkz0dF*jqn0uS7p3u_RGHIX|^1J}t9ib0AaHQ2W*X8&lL#01fC9s{jB1 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 004efbe4c42e88c9ef0a4d670cbdf4136a461027..ab8c9b273784a39e6e30e24524ac129e3fd9532a 100644 GIT binary patch delta 45 zcmcZ|bs>uTGcPX}0}w=C>)XgZLsZyLKR2&LKP9mwQ9n69wJ1I(OKdEV(t(M delta 56 zcmcZ*bvugtGcPX}0}${po4S#EhNxVmer{fgeoA6VqJDCIUP)qcd|GBjd_iJSVs376 K`sQt-v%~=$-V`9c_sQOi6x2p$@zIDiN*10nHBK`iA9OI Nxy9+5@3Ccv0RT%f6)FG# 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 feef6258ee09c1e799d49a2e20fff6de46c5c2fa..ad44cdc0b0a62099c61d936bba58c0c8e3701cd6 100644 GIT binary patch delta 55 zcmX?bi(~684(`vqyj%=G5Phw0Bll}gVMqPkyb}GC#F9k)~az6UGc_sQOi6x2p$@zIDiN*10nHBK`iA9OI Uxy9+toLudkT#VZ}xtMg60sFfZX8-^I 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 ae1b7099b639d1a61a52de7a20d8091eb5790c37..0f0233309cecffd95a0ba5df56f5e8aaddecd4bd 100644 GIT binary patch delta 47 zcmZ2;iD}^_ChpI?yj%=G5Ou9@BljFJVPE~+yb}GC#F9k)0suiy6&(No 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 52fcc86af14c2a30d6b6696f606536bfc38cfa48..10d1a9abf195a263f656db449a089ea3491e437b 100644 GIT binary patch delta 47 zcmZ2CiE;5HM()qNyj%=G5Phw0BX=vau#st^j1$ B58wa* delta 58 zcmZ2HiE-T|M()qNyj%=Gz`tzjM($Q-IbZ$Uyb}GC#F9k)Ge#G=I9 M+~V}ji)XiP%p@G7pPN^rpORRTsGpplS`?p_S+RKylb;FzWLpoT delta 56 zcmaFw`^%U6GcPX}0}${po4S#^nMp2IKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee*&lKNSETOB2rk 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 327619a828fa780e688cf8c8a67e775718ddee79..b191a853b1f6219c95f8fd302cef3e538e6d0d5e 100644 GIT binary patch delta 47 zcmdlynQ_HrM()qNyj%=G5Phw0BX=f~u)Th6UWtB6Vo9QYa(-%2d|GD3=1EM7?f_zP B4^jXC delta 58 zcmZ26nQ`-EM()qNyj%=Gz`tzjM(#`|IWPU(yb}GC#F9k)Ge#G=I9 M+~V}j?M#a902*i$egFUf 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 632d1308b008cc931951cc3cab110b917634a893..d194b2b6b2cea918bb877d7071e11dc756ff6b70 100644 GIT binary patch delta 45 zcmbQN*P+M#nU|M~0SKb6^=;&4WfKn8&&?~*Pf09E)KAV&Es9Uetk|r__EZ1>DD4g> delta 56 zcmeCso2UyxXon44Ri KzFC>=sQ>`d7ZOtd 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 b1cd14d3681ac06e2c194a0a17576a84c4c5bce0..9a190a32eba03ada3377a7339c53377e84fd6c4f 100644 GIT binary patch delta 45 zcmX?_w=0kPGcPX}0}w=C>)XiP%P8!rpPN^rpORRTsGpplS`?p_S+RK=W4|c?UJVb{ delta 56 zcmdm$cQlXtGcPX}0}${po4S#^mr*W6KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 Kee-I@ep3JzMHB)6 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 bde8924ec003d189cd914111b67698d1087ae1bb..e3f5367f2e134ded6d3d6627ccec533d5ce9c918 100644 GIT binary patch delta 45 zcmcasc)F1LGcPX}0}w=C>)Xh!#whHjpPN^rpORRTsGpplS`?p_S+UulvE3X1SQrl{ delta 56 zcmX?Ic&(88GcPX}0}${po4S!(jZrR8KR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeX|Q=yEy<2dJ|Is 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 a63a2999c64cfd38202f58259f39faf2fb779027..0e53775b264c55f80fa738448f22ede8663668a2 100644 GIT binary patch delta 47 zcmX@}f$`u6M()qNyj%=G5VE*uBX B5W@ff delta 58 zcmX^3f$_`-M()qNyj%=Gz`tzjM(zL(xp4j5yb}GC#F9k)Ge#G=I9 M+~V}jc^sGX0Zr`{D*ylh 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 aa89d56b1b894004cbce3e0cdda7b53a529dc5fe..a0f2d6054c863f0c4a570626f3d9e792f6151543 100644 GIT binary patch delta 45 zcmdnzu-Sq8GcPX}0}zN_nY5AnHKVYHer{fgeoA6VqJDCIYEgVzX2oU^CM`JtN*4}j delta 56 zcmdn&u+M?}GcPX}0}${po4S$vHKSaxer{fgeoA6VqJDCIUP)qcd|GBjd_iJSVs376 K`esfhEja)RMiSTn 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 1d7688b607b1a0b14db8d6cb4eab542a3e5770e2..2444470c64973cefa6dfa001b0d6210e39304e36 100644 GIT binary patch delta 44 ycmeyb`&XCiGcPX}0}#}{-pI9*LpW4FH?KrLC9xz?KRG|OC_XK-V)F?Ob3Oo4{0~6@ delta 46 zcmeyX`(KyqGcPX}0}#A;vXN^ghiJHdZeEFgN@7W(esX?ZNn&w)v61oSV;tsu0Dfu^ A7XSbN 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 1896825e6acfa353ce547c765d2e31f6178942c6..745a525ae167bd2badf8ef8d8ca6143398a636fe 100644 GIT binary patch delta 45 zcmaE)c3qA8GcPX}0}zN_nY5AHol)3TKR2&LKP9mwQ9n69wJ1I*iSf`RM()qNyj%=GAbMreM((xD!v6ZXc_sQOi6x2p$@!^8@oAYAo3AmKy8-}h CR1huz delta 58 zcmX>!iSg_tM()qNyj%=Gz`tzjM((xDa#8xZc_sQOi6x2p$@zIDiN*10nHBK`iA9OI Nxy9+5PcxUh0su3e6rKP8 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 d60b9b4128946ec543e2ae741d729983359ccd2f..6b5347bfc9a0a70e8054c6ff87f4eb043b39d90b 100644 GIT binary patch delta 45 zcmeB9Y)It(%*)Hg00dFj`ZjX6@e14N=jN5@rzDmn>L=%?7R9G!R%~9+dq)WXMWGLX delta 56 zcmZoj>`vtV%*)Hg00jKYrf%eJ)Xif&m`=jpPN^rpORRTsGpplS`?p_S+Ti-DZm^6SjrDB delta 56 zcmdm6e4?29GcPX}0}${po4S$PpGhuQKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeRD2TfH?pU2oqWW 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 105b9b88611c3cd8fe436c378f6f25788410d390..5872fb192781067188173628017731c50ea1daa7 100644 GIT binary patch delta 47 zcmX>%f$8uBChpI?yj%=G5Phw0BR3C|u#0|fUWtB6Vo9QYa(-%2d|GD3W>Y4~HUMC| B4+a1L delta 58 zcmX>+f$7`?ChpI?yj%=Gz`tzjMs6M^Ie-1!yb}GC#F9k)Ge#G=I9 M+~V}jT1=8{02dDvG5`Po 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 95a1c252412862c44aa2bca5f7ebb0d21db56335..c28ae8ca89faf3ff9362b43b806fba6d213ec8a9 100644 GIT binary patch delta 44 ycmdm7u%dwbGcPX}0}zN_nY56bS=doOH?KrLC9xz?KRG|OC_XK-VzVYQmni@?3k|;j delta 55 zcmZ2cu(^QyGcPX}0}${po4SyjSUyxXon44Ri KzPVkfkQD&;W)lhk 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 cf44e0951bcb10f2278cd3b2ced13a4cfa24480f..8fdda3b2d16c1cdb21dbc69026cb4bea590e85dd 100644 GIT binary patch delta 45 zcmcbYeL0)^GcPX}0}w=C>)XgZfl=62KR2&LKP9mwQ9n69wJ1I)XgJ#wqNepPN^rpORRTsGpplS`?p_S+Uuk^FKQPGlqGcPX}0}${po4S!(j8o2AKR2&LKP9mwQ9n69uOzWJJ}t8%z96wEF*mn3 KeX|MYe|7-iRT8%V 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 a3d0a270752d191620c009722a2d7c50c89f2c15..3b6d41c52b2a41f640ba6b1756eec7c78a547acc 100644 GIT binary patch delta 45 zcmeCNX|&<~%*)Hg00g2}CT-*nU=()N&&?~*Pf09E)KAV&Es9Uetk_)1I9Ub&DIX3e delta 56 zcmZp)>9OJd%*)Hg00jKYrf%d8V3hOI&&?~*Pf09E)KAXOD@iPlPs^-`FGws(%*`!M K-<-!dSq1>zyAqcG 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 f028ce71d687cca9f71299fc40d84dccf413e984..d54802fda671073a4fb39d6bfbe7db38a54ea7eb 100644 GIT binary patch delta 47 zcmZ46!MLb{k^3_*FBbz4L|^OM$j#3r?5LlcSE8SiSdyrpoS#}0pO#s%*@Ee-BLGur B4@>|6 delta 58 zcmZ3~!ML`Ak^3_*FBbz4@GqOXk(-}M&PP8tuS7p3u_RGHIX|x?u{b_0vm(AAu_!S& Mw>W*XF4I>>00towcmMzZ 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