# Manual Detalhado de Regras do Agente de Contas O objetivo deste manual é permitir entender **por que** o agente escolheu uma regra, como os componentes estão amarrados e onde diagnosticar decisões inesperadas. # Prefácio técnico — como funciona o motor de decisão do Agente de Contas Antes de estudar cada regra individualmente, é importante entender que o Agente de Contas **não decide o que fazer por um único classificador de intenção**. O comportamento final é resultado de uma cadeia de mecanismos coordenados pelo Agent Framework OCI e especializados pelo domínio Contas. Uma mensagem pode: - ser bloqueada ou sanitizada por guardrails; - continuar uma transação que já estava em andamento; - preencher um parâmetro que estava faltando; - corrigir um parâmetro informado anteriormente; - abandonar explicitamente uma transação anterior; - retomar um workflow pausado; - produzir uma ou várias intents; - ser reclassificada com base no contexto; - selecionar uma tool; - ter essa tool alterada por uma validação de domínio; - exigir confirmação antes de executar; - iniciar ou retomar um workflow transacional; - produzir resultados parciais diferentes para vários itens; - passar por guardrails de saída; - ser avaliada por judges; - ser persistida em checkpoint/memória para o próximo turno. Por isso, para diagnosticar corretamente uma decisão, a pergunta não deve ser apenas: > “Qual intent foi encontrada?” A pergunta correta é: > **“Em qual estado a conversa estava, quem tinha precedência naquele turno, qual intent foi encontrada, quais parâmetros foram resolvidos, qual validação de domínio ocorreu, qual tool efetiva foi escolhida, qual workflow foi executado e quais controles de saída aprovaram a resposta?”** --- ## P.1. Visão geral do motor Em alto nível, um turno passa pelas seguintes camadas: ```mermaid flowchart TD U["Mensagem do cliente"] --> IG["Input Guardrails"] IG --> MEM["Memória / Checkpoint"] MEM --> TX["Continuidade transacional / Workflow pausado"] TX --> RT["Routing / Intent"] RT --> MI["Multi-intent / Pending Topics"] MI --> AG["Agente proprietário"] AG --> CP["Coleta de parâmetros"] CP --> TR["Reconciliação temporal quando necessária"] TR --> PV["Pre-validation / validação de domínio"] PV --> CF["Confirmação transacional quando necessária"] CF --> TL["Tool efetiva / MCP"] TL --> WF["Workflow declarativo ou execução direta"] WF --> OR["Resultado operacional / evidências"] OR --> OS["Output Supervisor + Output Guardrails"] OS --> J["Judges"] J --> PS["Persistência / memória / checkpoint"] PS --> F["Resposta ao cliente"] ``` Nem todos os turnos percorrem todos os blocos. Uma simples consulta de plano, por exemplo, pode passar rapidamente por routing → tool → resposta. Já uma contestação ou cancelamento pode atravessar praticamente todo o motor. --- ## P.2. Princípio de precedência: o estado atual pode ser mais importante que a nova intent O router não trata cada mensagem como uma conversa nova. Antes de classificar uma nova intent, o framework verifica se existe uma operação ativa. Isso evita erros clássicos como interpretar: ```text Agente: Deseja confirmar o cancelamento? Cliente: Sim. ``` como uma nova intent genérica. A precedência conceitual é: ```text 1. segurança de entrada 2. workflow/transação já ativa 3. parâmetro pendente 4. confirmação pendente 5. mudança explícita de intenção 6. routing determinístico 7. routing semântico/LLM 8. fallback ``` Na prática, há nuances importantes: em `AWAITING_CONFIRMATION`, uma confirmação inequívoca é consumida antes de procurar mudança de assunto; em `COLLECTING_PARAMETERS`, se a fala fornece um dos campos pendentes, a transação continua proprietária do turno. --- ## P.3. Existem três máquinas de estado diferentes Um ponto essencial para manutenção é não confundir três famílias de estado. ### P.3.1. Estados de roteamento do domínio Declarados em `config/routing.yaml` através de `state_policies`: | Estado | Agente proprietário | |---|---| | `WAITING_FATURAS_CONFIRMATION` | `faturas_agent` | | `WAITING_VAS_CONFIRMATION` | `vas_agent` | | `WAITING_CONTESTACAO_CONFIRMATION` | `contestacao_agent` | | `WAITING_SUPORTE_CONTAS_CONFIRMATION` | `suporte_contas_agent` | | `COLLECTING_FATURAS_PARAMETERS` | `faturas_agent` | | `COLLECTING_VAS_PARAMETERS` | `vas_agent` | | `COLLECTING_CONTESTACAO_PARAMETERS` | `contestacao_agent` | | `COLLECTING_SUPORTE_CONTAS_PARAMETERS` | `suporte_contas_agent` | Esses estados servem principalmente para **fixar a propriedade do próximo turno**. Eles não representam, sozinhos, o status completo da transação. ### P.3.2. Estados transacionais do runtime O runtime do framework trabalha com estados canônicos: ```text COLLECTING_PARAMETERS AWAITING_CONFIRMATION WORKFLOW_PAUSED TOOL_RESULT_CLARIFICATION EXECUTING ``` Estados terminais: ```text COMPLETED FAILED CANCELLED BLOCKED OUT_OF_SCOPE ``` Também podem aparecer estados intermediários como `CONFIRMED` durante o ciclo de execução e avaliação. Esses estados controlam: - se ainda faltam parâmetros; - se o usuário precisa confirmar; - se a tool já pode executar; - se há workflow pausado; - se uma resposta curta pertence à operação atual; - se a transação anterior ainda deve influenciar o turno. ### P.3.3. Estados do engine de workflows O motor YAML de workflows possui sua própria semântica operacional: ```text executando ├─ COMPLETED ├─ PAUSED / WAITING_INPUT └─ FAILED ``` Um workflow pausado preserva: - `workflow_name`; - `execution_id`; - nó onde ocorreu a pausa; - payload da execução; - contrato de `expected_input`; - nó de retorno definido por `resume_from`. Portanto: > `WAITING_CONTESTACAO_CONFIRMATION`, `AWAITING_CONFIRMATION` e `WAITING_INPUT` podem todos significar “algo está esperando o usuário”, mas pertencem a camadas diferentes e têm funções diferentes. --- ## P.4. Máquina de estados transacional A máquina conceitual de uma tool transacional é: ```mermaid stateDiagram-v2 [*] --> COLLECTING_PARAMETERS: faltam dados COLLECTING_PARAMETERS --> COLLECTING_PARAMETERS: dado parcial ou correção COLLECTING_PARAMETERS --> AWAITING_CONFIRMATION: contrato completo + pre-validation OK COLLECTING_PARAMETERS --> CANCELLED: abandono explícito / intent shift autorizado AWAITING_CONFIRMATION --> EXECUTING: confirmação SIM AWAITING_CONFIRMATION --> CANCELLED: confirmação NÃO AWAITING_CONFIRMATION --> AWAITING_CONFIRMATION: pergunta / resposta inconclusiva AWAITING_CONFIRMATION --> CANCELLED: abandono explícito EXECUTING --> WORKFLOW_PAUSED: workflow pede nova entrada EXECUTING --> COMPLETED: execução concluída EXECUTING --> FAILED: falha operacional EXECUTING --> BLOCKED: validação/política bloqueia EXECUTING --> OUT_OF_SCOPE: política de domínio impede continuidade WORKFLOW_PAUSED --> EXECUTING: resume válido WORKFLOW_PAUSED --> FAILED: erro no resume WORKFLOW_PAUSED --> COMPLETED: workflow termina COMPLETED --> [*] FAILED --> [*] CANCELLED --> [*] BLOCKED --> [*] OUT_OF_SCOPE --> [*] ``` Essa máquina é genérica. Cada regra pode utilizar apenas uma parte dela. --- ## P.5. Routing: como a intenção é determinada O `EnterpriseRouter` combina decisões determinísticas e semânticas. A sequência principal é: ```text state/transaction precedence ↓ keywords determinísticas ↓ LLM Router, se ENABLE_LLM_ROUTER=true ↓ fallback_agent ``` O `routing.yaml` define, por intent: - `name`; - `domain`; - `agent`; - `priority`; - `description`; - `mcp_tools`; - `keywords`; - `examples`. ### Matching determinístico As keywords podem casar por mecanismos como: - correspondência exata; - sequência ordenada de tokens; - sequência de tokens informativos tolerando conectores. Se várias intents casarem, a decisão considera: ```text menor valor de priority → match mais específico/estrito → keyword mais longa/específica ``` ### Routing semântico Se o determinístico não for suficiente e o LLM router estiver habilitado, o framework envia ao classificador o catálogo permitido de intents, incluindo descrição e exemplos. O LLM **classifica**. Ele não deve inventar: - novo nome de intent; - agente; - tool; - regra de negócio. Esses elementos continuam vindo da configuração declarativa. --- ## P.6. Route stickiness Route stickiness existe para impedir que uma conversa pule de agente sem necessidade quando o cliente continua falando sobre o mesmo assunto. Ela é útil em diálogos como: ```text Cliente: Minha conta veio alta. Agente: Identifiquei estes itens... Cliente: E esse valor de 19,99? ``` A segunda fala é curta e, isoladamente, poderia ser ambígua. O contexto e a stickiness ajudam a manter o agente proprietário. Mas stickiness **não pode aprisionar a conversa**. Uma nova solicitação explícita pode preemptar a rota anterior quando as regras de intent shift permitirem. --- ## P.7. Intent shift Intent shift é o mecanismo usado quando o cliente abandona ou interrompe explicitamente o assunto corrente e inicia outro. Exemplo: ```text Agente: Qual cobrança deseja contestar? Cliente: Esquece isso. Quero ver minha fatura. ``` O comportamento esperado é: ```text transação anterior COLLECTING_PARAMETERS ↓ detecção de abandono / mudança explícita ↓ CANCELLED ↓ limpeza de next_state / parâmetros / latches ↓ novo routing ↓ contas_invoice_query ``` O router registra metadados como: ```text transaction_interruption = intent_shift ``` e o runtime encerra corretamente o contrato anterior. ### Intent shift não é qualquer frase diferente Uma regra importante da implementação atual é: > **fornecer um parâmetro pendente tem precedência sobre tentar classificar uma mudança semântica de intenção.** Se a transação espera `valor` e o cliente diz: ```text É 19,99. ``` isso deve ser consumido como parâmetro, e não interpretado como nova intent. Da mesma forma, uma frase como: ```text Isso mesmo, pode cancelar. ``` durante confirmação/coleta não deve abandonar a transação apenas porque possui palavras que poderiam casar com outra regra. --- ## P.8. Coleta de parâmetros Tools transacionais possuem um contrato de parâmetros. Esse contrato nasce da combinação de: - `config/tools.yaml`; - `config/tool_policies.yaml`; - `config/mcp_parameter_mapping.yaml`; - schema declarado pela tool; - dados já existentes no state/contexto. O runtime diferencia: ```text known_arguments missing_parameters resolved_arguments requested_arguments ``` ### Primeira tentativa: coleta tradicional do turno atual O comportamento normal é tentar extrair os campos pendentes **somente da fala atual**. Isso evita que o histórico seja reutilizado indiscriminadamente. Exemplo: ```text missing_parameters = ["subject", "valor"] Cliente: É o Tamboro. ``` Resultado possível: ```text subject = Tamboro valor = pendente ``` O runtime permanece em: ```text COLLECTING_PARAMETERS ``` e solicita somente o campo restante. --- ## P.9. Reconciliação temporal A reconciliação temporal é uma **segunda linha de resolução**, e não a primeira. Ela existe para resolver casos em que a informação está espalhada por turnos relacionados. Princípio implementado: ```text extração do turno atual ↓ faltam required fields? ├─ não → segue fluxo └─ sim ↓ reconciliação temporal ↓ pre-validation autoritativa ``` ### Exemplo ```text Cliente: Não reconheço o Tamboro. ... Agente: Qual valor você quer contestar? Cliente: É a de 19,99. ``` A fala atual fornece explicitamente o valor. O histórico recente pode ajudar a associar: ```text subject = Tamboro valor = 19,99 ``` Mas essa associação ainda é apenas **candidata**. Ela precisa passar por validação de domínio. ### Regras da reconciliação A implementação foi deliberadamente restritiva: 1. a fala atual é a fonte principal; 2. contexto histórico serve para resolver referência, não para inventar uma nova afirmação; 3. campos já conhecidos podem ser preservados; 4. correção atual prevalece temporalmente sobre valor antigo; 5. um campo pode ser: - `resolved`; - `preserve`; - `clear`; - `unresolved`; 6. ambiguidade deve resultar em `unresolved`, não em chute; 7. reconciliação não valida se o dado existe no backend; 8. a autoridade final é a **pre-validation**. ### Por que isso é importante Sem essa divisão, o LLM poderia concluir erroneamente: ```text “19,99 apareceu perto de Tamboro no histórico, então é Tamboro.” ``` O motor permite criar o candidato, mas somente a validação de domínio pode confirmar que essa relação é verdadeira. --- ## P.10. Contextual reentry Contextual reentry é usado quando uma intent informativa produz contexto suficiente para que uma resposta curta do cliente abra uma ação transacional. Exemplo típico: ```text Cliente: Não reconheço essas cobranças. → contas_invoice_explanation Agente explica os itens. Cliente: É a de 14,99. ``` A segunda fala, isoladamente, não contém: ```text “quero contestar” ``` Mas o contexto imediatamente anterior pode mostrar que o cliente estava identificando qual cobrança não reconhecia. Nesse cenário, o runtime mantém: - fala atual separada; - contexto conversacional delimitado; - intent/reentrada contextual; - parâmetros candidatos; - validação autoritativa antes da ação. Contextual reentry não transforma todo o histórico em evidência operacional. Ele usa apenas contexto limitado para interpretação. --- ## P.11. Pre-validation: a autoridade de domínio antes da execução Pre-validation é um dos mecanismos mais importantes do Agente de Contas. Ela acontece antes de uma ação sensível e deve responder perguntas como: ```text O serviço existe? Esse é realmente o nome canônico? Esse valor pertence a esse item? O VAS é avulso, estratégico ou bundle? A cobrança pode ser contestada? A tool inicialmente escolhida continua correta? ``` No `tool_policies.yaml`, uma tool pode declarar um pre-validator. Exemplos: ```text cancelar_vas_avulso ↓ validar_vas_subject contestar_cobranca ↓ validar_contestacao ``` ### Pre-validation pode alterar a operação Esse é o motivo pelo qual: ```text intent encontrada != tool final necessariamente ``` Exemplo: ```text Cliente: Quero cancelar Paramount+. ``` Routing inicial: ```text contas_vas_cancel → cancelar_vas_avulso ``` Validação de domínio: ```text Paramount+ → classe estratégica/bundle ``` Resultado: ```text effective_tool = tratar_vas_estrategico ``` A intenção do cliente continua sendo cancelar, mas a **operação válida para aquela entidade** mudou. --- ## P.12. Confirmação de transação Tools marcadas com: ```yaml require_confirmation: true ``` não podem executar imediatamente. A sequência é: ```text tool selecionada ↓ parâmetros completos ↓ pre-validation aprovada ↓ AWAITING_CONFIRMATION ↓ pergunta ao cliente ↓ SIM / NÃO / CONTINUAR ``` ### Classificação determinística primeiro Respostas inequívocas são tratadas sem LLM quando possível: ```text sim pode confirmar não cancela essa operação ``` ### Fallback semântico Em `routing.yaml`, `transaction_confirmation.semantic_fallback` permite classificar respostas inconclusivas em: ```text SIM NAO CONTINUAR ``` `CONTINUAR` significa: - pergunta adicional; - correção; - nova informação; - ambiguidade; - possível mudança de assunto. Ou seja, `CONTINUAR` **não executa** nem rejeita a transação automaticamente. ### Multi-intent na confirmação Uma resposta pode confirmar a operação atual e ainda introduzir outro assunto: ```text Sim, e me manda a segunda via. ``` O motor pode: 1. confirmar a transação pendente; 2. executar a ação; 3. colocar a segunda via em `pending_topics`; 4. continuar depois sem perder a intenção secundária. --- ## P.13. Workflow transacional Nem toda tool precisa de workflow. Workflows são usados quando a execução precisa de: - múltiplos passos; - condições; - efeitos externos ordenados; - pausa; - resume; - tratamento de caminhos alternativos; - auditoria clara do fluxo. Eles ficam em: ```text workflows/*.yaml ``` e apontam para actions Python, principalmente em: ```text app/domain/contas/workflow_actions.py ``` Um workflow declara: - `name`; - `version`; - `start`; - `nodes`; - `action`; - `edges`; - condições `when`; - prioridades; - configuração `pause`; - `expected_input`; - `resume_from`; - término em `END`. ### Exemplo conceitual ```text START ↓ validar / preparar ↓ executar integração ↓ condição? ├─ precisa perguntar algo → PAUSED │ ↓ │ resposta │ ↓ │ resume │ └─ não → próximo nó ↓ END ``` A lógica de orquestração fica em YAML; a regra de domínio executável fica em Python. --- ## P.14. Pause e resume Um workflow pode pedir uma nova entrada sem perder a execução corrente. Quando isso ocorre: ```text status = WAITING_INPUT / PAUSED workflow_name = ... execution_id = ... paused_at = ... ``` O nó declara: ```text expected_input resume_from return_from ``` Na próxima mensagem, o runtime tenta primeiro verificar se ela satisfaz o contrato de entrada esperado. Se satisfizer: ```text mensagem ↓ match_expected_input ↓ resume(execution_id) ↓ resume_from ``` Se não satisfizer, o motor pode: - repedir esclarecimento; - permitir reentrada contextual; - detectar abandono/intenção diferente conforme política. --- ## P.15. Multi-intent O agente suporta frases com vários objetivos: ```text “Cancela o Tamboro, manda a segunda via e consulta o status do protocolo.” ``` O objetivo do multi-intent planner é decompor **ações independentes**, e não desmontar os parâmetros internos de uma mesma ação. Correto: ```text 1. cancelar Tamboro 2. consultar/obter segunda via 3. consultar status ``` Incorreto: ```text 1. Tamboro 2. valor 19,99 3. motivo ``` se esses elementos forem apenas parâmetros de uma única contestação. --- ## P.16. Pending topics Quando uma frase contém mais de uma ação e uma delas precisa ser resolvida primeiro, as demais podem ser persistidas em: ```text pending_topics ``` Um pending topic representa uma intenção já descoberta, mas ainda não executada. Exemplo: ```text Cliente: Cancela o Tamboro e me manda a segunda via. ``` Possível ciclo: ```text cancelamento ↓ coleta / confirmação ↓ execução ↓ topic concluído ↓ drain pending_topics ↓ segunda via ``` Isso evita exigir que o usuário repita a segunda solicitação. --- ## P.17. Vários itens dentro da mesma regra Multi-intent e multi-item são conceitos diferentes. Exemplo multi-item: ```text “Cancela Tamboro e Paramount+.” ``` Há uma única intenção de alto nível: ```text cancelar serviços ``` mas existem duas entidades. Por isso o motor pode produzir: ```text items: - Tamboro - Paramount+ ``` e validar cada uma separadamente. O resultado também deve permanecer separado: ```text Tamboro → avulso → cancelamento permitido → success Paramount+ → estratégico → tratar_vas_estrategico → não executar cancelamento avulso ``` Nunca se deve usar o sucesso de um item para afirmar sucesso dos outros. --- ## P.18. Guardrails de entrada Antes do routing, o grafo principal executa `input_guardrails`. No Agente de Contas, `config/guardrails.yaml` habilita: | Rail | Papel no motor | |---|---| | `INPUT_SIZE` | limita/valida tamanho de entrada | | `MSK` | sanitização/masking de conteúdo sensível conforme framework | | `DLEX_IN` | validação lexical/entrada | | `TOX` | toxicidade de entrada | | `PINJ` | proteção contra prompt injection | | `COER` | coerência/inteligibilidade da entrada | | `VLOOP` | detecção de repetição/loop conversacional | O `COER` **não deve decidir intent**, parâmetros, validade de negócio ou intent shift. Essas responsabilidades pertencem ao router e ao runtime transacional. O `VLOOP` possui tratamento especial para não bloquear respostas legítimas repetidas como `sim` durante `AWAITING_CONFIRMATION`. Se um input é bloqueado, a resposta de segurança ainda passa pela cadeia de guardrails de saída antes de chegar ao cliente. --- ## P.19. Guardrails de retrieval e tool Além da entrada/saída existem rails específicos para outros pontos: ```yaml retrieval: - RAGSEC - RET_REL tool: - TOOL_VAL ``` Conceitualmente: - `RAGSEC` protege o uso de conteúdo recuperado; - `RET_REL` verifica relevância da recuperação; - `TOOL_VAL` valida uso/resultado de tools de acordo com o contrato do framework. Esses controles não substituem a pre-validation de domínio. São camadas complementares. --- ## P.20. Output Supervisor e guardrails de saída Depois que o agente gera uma resposta, ela não é necessariamente entregue imediatamente. O grafo passa por: ```text agente ↓ Output Supervisor ↓ Output Guardrails ``` No agente Contas estão configurados rails como: | Rail | Função geral | |---|---| | `TIM_OOS` | política de escopo do atendimento Contas/TIM | | `TIM_AOFERTA` | evita oferta proativa indevida | | `TIM_REVPREC` | evita verbalizar ação transacional antes da evidência real | | `PINJ` | proteção contra conteúdo de prompt injection refletido | | `DLEX_OUT` | validação lexical de saída | | `TOXOUT` | toxicidade de saída | | `CMP` | coerência/completude operacional, incluindo protocolos esperados | | `GND` | groundedness em evidência | | `ALUC_RISK` | risco de alucinação | O Output Supervisor pode: ```text ALLOW SANITIZE RETRY BLOCK HANDOVER ``` conforme os resultados dos rails e políticas declaradas. `guardrails.yaml` define: ```text max_retries = 3 fallback_message handover_message ``` Isso é especialmente importante em ações transacionais: o agente não deve dizer “cancelei” apenas porque tentou chamar uma tool. A resposta precisa estar apoiada no resultado operacional. --- ## P.21. Judges Depois dos guardrails de saída, o grafo executa judges. No `config/judges.yaml` atual existem dois judges externos: ```text tim_response_quality threshold = 0.70 tim_groundedness threshold = 0.60 ``` Configurações relevantes: ```text enabled = true fail_closed = true sample_rate = 1.0 always_run_for_transactional = true ``` Os judges recebem um contexto reduzido contendo, entre outros: - pergunta; - resposta final; - intent; - route; - resultados MCP; - resultados RAG; - evidências transacionais; - status da transação; - policy da tool; - tools transacionais envolvidas. ### Diferença entre guardrail e judge Uma forma útil de pensar: ```text Guardrail: “Esta entrada/saída pode prosseguir?” Judge: “Quão boa e fundamentada está a resposta produzida?” ``` Os dois podem usar LLM, mas têm responsabilidades diferentes. --- ## P.22. Supervisor review Após os judges, o grafo ainda possui `supervisor_review`. Fluxo real do grafo principal: ```text START ↓ input_guardrails ↓ load_long_term_memory ↓ routing_decision ↓ conversation_policy ↓ agente / handoff / controle de sessão ↓ output_supervisor ↓ output_guardrails ↓ judge ↓ supervisor_review ↓ persist_long_term_memory ↓ persist ↓ END ``` Esse fluxo ajuda a entender por que uma resposta produzida pelo agente pode não ser exatamente a mesma resposta persistida/entregue ao usuário. --- ## P.23. Memória, checkpoint e persistência O estado do turno não vive apenas na memória do LLM. O framework persiste estruturas operacionais para permitir: - continuidade; - pause/resume; - confirmação em outro turno; - coleta progressiva de parâmetros; - pending topics; - idempotência; - auditoria; - observabilidade. O contexto canônico possui precedência de dados: ```text tool_arguments ↓ business_context ↓ context ↓ session ↓ session.metadata ↓ state ``` Ou seja, um parâmetro explicitamente resolvido para uma tool deve prevalecer sobre um valor genérico mais antigo no state. --- ## P.24. Evidência operacional versus contexto conversacional O framework diferencia duas coisas que não devem ser confundidas. ### Contexto conversacional Serve para entender: ```text “esse” “o primeiro” “a de 19,99” “isso mesmo” ``` ### Evidência operacional Serve para afirmar fatos: ```text serviço existe cancelamento foi concluído protocolo foi gerado valor pertence à fatura status foi atualizado ``` Histórico conversacional **não é evidência de backend**. Por isso: > O LLM pode usar a conversa para descobrir qual item o cliente quis dizer, mas somente tool/pre-validation/backend pode confirmar que esse item existe ou que uma transação ocorreu. --- ## P.25. Idempotência e proteção contra reexecução Transações conversacionais precisam tolerar: - retry HTTP; - retry do canal; - usuário repetindo confirmação; - checkpoint reprocessado; - reload da aplicação. Por isso o runtime mantém identificadores e estado transacional suficientes para distinguir: ```text pedido novo continuação resume retry reexecução indevida ``` Workflows utilizam `execution_id`, e transações mantêm identificação/estado próprio. Isso reduz o risco de executar duas vezes uma ação apenas porque a mesma mensagem reapareceu. --- ## P.26. Conversation policy Após `routing_decision`, o grafo passa por `conversation_policy`. Essa camada pode decidir que a rota final do turno é: ```text faturas_agent vas_agent contestacao_agent suporte_contas_agent handoff human_handoff end_session supervisor_agent conversation_policy_response ``` Ou seja, routing de intent não é necessariamente o último controle antes do agente. Políticas conversacionais podem interferir em continuidade, encerramento e transferência. --- ## P.27. Por que a ordem dos mecanismos importa Considere: ```text Cliente: “Sim, e manda a segunda via.” ``` durante `AWAITING_CONFIRMATION`. Uma implementação ingênua poderia encontrar: ```text “segunda via” → contas_invoice_query ``` e perder a confirmação. O motor correto avalia: ```text 1. existe AWAITING_CONFIRMATION? 2. “sim” confirma a transação? 3. existe texto remanescente? 4. o restante contém uma segunda intent? 5. executa transação principal 6. preserva segunda via como pending topic 7. continua a conversa ``` Outro exemplo: ```text Cliente: “É a de 19,99.” ``` durante `COLLECTING_PARAMETERS`. Ordem correta: ```text 1. verificar parâmetro pendente 2. extrair 19,99 3. se necessário reconciliar com contexto 4. validar subject + valor 5. só depois considerar nova intent ``` É essa precedência que impede o motor de “pular” de regra em cada mensagem curta. --- ## P.28. Resumo dos mecanismos e suas fontes | Mecanismo | Fonte principal | Responsabilidade | |---|---|---| | Routing | `config/routing.yaml` + `EnterpriseRouter` | descobrir intent/agente | | State policies | `config/routing.yaml` | manter propriedade do turno | | Route stickiness | router/runtime | manter continuidade contextual | | Intent shift | `EnterpriseRouter` + runtime | abandonar corretamente operação anterior | | Multi-intent | `MultiIntentPlanner` | separar objetivos independentes | | Pending topics | runtime/grafo | preservar intents ainda não executadas | | Coleta de parâmetros | runtime + tool contract | preencher required fields | | Reconciliação temporal | `transaction_parameters.py` | recuperar relação coerente entre turnos | | Contextual reentry | router/runtime | usar contexto imediato para reentrada | | Pre-validation | `tool_policies.yaml` + validators | autoridade de domínio antes da ação | | Confirmação | policy + runtime + semantic fallback | impedir execução sem aceite | | Workflow | `workflows/*.yaml` | orquestrar passos/condições | | Pause/resume | workflow runtime | continuar execução em outro turno | | Guardrails de entrada | `guardrails.yaml` | proteger/sanitizar entrada | | Retrieval/tool guardrails | `guardrails.yaml` | controlar RAG/tools | | Output Supervisor | framework + `guardrails.yaml` | retry/block/sanitize/handover | | Guardrails de saída | `guardrails.yaml` | validar resposta final | | Judges | `judges.yaml` | avaliar qualidade/groundedness | | Conversation policy | `conversation_policy.py` | continuidade/transferência/controle | | Checkpoint | framework persistence | durabilidade de estado | | Long-term memory | framework memory | contexto durável apropriado | | Idempotência | runtime/workflow | evitar execução duplicada | | Telemetria | observability config/framework | explicar o caminho executado | --- ## P.29. Regra de ouro para interpretar o agente Quando houver um comportamento inesperado, nunca analise apenas uma linha do `routing.yaml`. Siga esta cadeia: ```text Mensagem ↓ Input guardrails ↓ Estado anterior ↓ Transação/workflow ativo? ↓ Parâmetro pendente? ↓ Confirmação pendente? ↓ Intent shift? ↓ Routing ↓ Multi-intent / pending topics ↓ Tool candidata ↓ Coleta / reconciliação temporal ↓ Pre-validation ↓ Tool efetiva ↓ Confirmação ↓ Workflow / MCP ↓ Resultado operacional ↓ Output Supervisor / guardrails ↓ Judges ↓ Persistência ↓ Resposta ``` Esse é o **motor de decisão real** do Agente de Contas. Os capítulos seguintes documentam como cada regra específica utiliza esse motor. --- ## 1. Como ler este manual Cada regra é documentada em seis níveis: 1. **Intenção de negócio** — o que a regra significa e o que ela não significa. 2. **Roteamento** — explicação atributo por atributo do bloco correspondente em `config/routing.yaml`. 3. **Descoberta da intent** — como o framework chega à intent: estado, keywords, LLM e fallback. 4. **Tools e parâmetros** — o que cada tool consulta/processa/executa, dados necessários, pre-validation, confirmação e retorno. 5. **Workflow** — nós, actions Python, condições, pause/resume e término. 6. **Diagnóstico** — exemplos que mostram por que duas frases parecidas podem produzir tratamentos diferentes. A distinção mais importante é: **intent != tool != workflow** A intent descreve o objetivo conversacional. A tool representa uma capability. O workflow orquestra uma execução. E, em regras como VAS, o domínio ainda pode reclassificar a entidade e trocar a tool efetiva com base em evidência autoritativa. ## 2. Como o framework encontra uma intent A ordem implementada no `EnterpriseRouter` é: ```text 1. state_policies ↓ 2. keywords determinísticas ↓ 3. LLM router, quando ENABLE_LLM_ROUTER=true ↓ 4. fallback_agent ``` Antes de tratar uma fala isoladamente, o router verifica estados como `WAITING_*_CONFIRMATION` e `COLLECTING_*_PARAMETERS`. Isso impede que uma resposta curta como “sim” seja interpretada como uma nova solicitação. No matching por keywords existem três estratégias: - **exact**: termo único em token ou frase multi-token contida no texto; - **ordered_tokens**: permite pequenos tokens entre as palavras da keyword, preservando ordem; - **ordered_content_tokens**: tolera conectores curtos omitidos, como variações de “qual é o meu plano” / “qual o meu plano”. Se mais de uma intent casar, a ordenação é: ```text menor priority → estratégia mais estrita → keyword mais longa ``` Isto é importante: em `routing.yaml`, **priority 110 ganha de priority 145**, e não o contrário. Se não houver keyword suficiente e o LLM router estiver habilitado, o classificador recebe `name`, `description` e alguns `examples` das intents permitidas. A resposta do LLM não inventa agente/tool: esses dados são recuperados do catálogo YAML. Pedidos compostos podem ainda passar pelo `MultiIntentPlanner`, que separa objetivos independentes sem transformar parâmetros de uma única ação em intents artificiais. ## 3. Amarração entre os componentes ```mermaid flowchart LR U["Mensagem do cliente"] --> R["routing.yaml"] R --> ST["state_policies / intent"] ST --> AG["Agente proprietário"] AG --> TL["tools.yaml"] TL --> TP["tool_policies.yaml"] TL --> PM["mcp_parameter_mapping.yaml"] TP --> PV["pre-validation"] PM --> MCP["contas_mcp_server"] PV --> MCP MCP --> IR["classificação de domínio / invoice_resolver"] IR --> WF["workflow YAML ou execução direta"] WF --> WA["workflow_actions.py"] WA --> DS["service.py"] DS --> CL["client.py"] CL --> ENV[".env + tim_integration_defaults.yaml"] ``` A seta `pre-validation → classificação de domínio` é especialmente importante. É nela que um pedido como “cancelar Paramount+” pode deixar de usar a operação de VAS avulso mesmo que a intent inicial tenha sido de cancelamento. ## 4. Inventário das regras | Intent | Regra | Agente | Prioridade | Tools | |---|---|---|---:|---| | `contas_no_match` | No Match / Clarificação | `suporte_contas_agent` | 200 | — | | `contas_plan_information` | Consulta de Plano | `faturas_agent` | 142 | `consultar_plano` | | `contas_invoice_query` | Consulta / Segunda Via de Fatura | `faturas_agent` | 140 | `consultar_faturas` | | `contas_invoice_explanation` | Explicação de Fatura | `faturas_agent` | 150 | `invoice_explanation` | | `contas_contestation` | Contestação de Cobrança | `contestacao_agent` | 120 | `consultar_faturas`, `invoice_explanation`, `contestar_cobranca` | | `contas_vas_cancel` | Cancelamento de VAS | `contestacao_agent` | 145 | `consultar_vas`, `cancelar_vas_avulso` | | `contas_vas_strategic` | VAS Estratégico / Bundle | `contestacao_agent` | 125 | `consultar_vas`, `tratar_vas_estrategico` | | `contas_vas_information` | Consulta de VAS | `vas_agent` | 110 | `consultar_vas` | | `contas_vas_history` | Histórico de VAS | `vas_agent` | 105 | `consultar_historico_vas` | | `contas_knowledge` | Conhecimento Geral de Contas | `suporte_contas_agent` | 120 | — | | `contas_service_status` | Status de Solicitação | `suporte_contas_agent` | 90 | `consultar_status_solicitacao` | | `contas_pro_rata` | Pró-rata | `faturas_agent` | 128 | `pro_rata` | | `contas_termino_desconto` | Término de Desconto | `faturas_agent` | 127 | `termino_desconto` | | `contas_valor_divergente` | Valor Divergente | `faturas_agent` | 126 | `valor_divergente` | # 5. Regra — No Match / Clarificação **Intent:** `contas_no_match` **Agente:** `suporte_contas_agent` **Domínio:** `telecom_contas` **Prioridade:** `200` ## O que esta regra significa Fala incompreensível, transcrição corrompida ou fragmento sem informação suficiente para determinar uma solicitação/objeto com segurança. Inclui enunciados que contêm uma palavra de domínio, mas deixam o objeto sem referente recuperável (por exemplo, verbo de cobrança seguido apenas de pronome/indefinido). Não use para assunto fora de escopo, reclamação, pedido de humano, jurídico/Anatel/Procon ou frase compreensível que apenas não corresponda a outra intent. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_no_match | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | suporte_contas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 200 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Fala incompreensível, transcrição corrompida ou fragmento sem informação suficiente para determinar uma solicitação/objeto com segurança. Inclui enunciados que contêm uma palavra de domínio, mas deixam o objeto sem referente recuperável (por exemplo, verbo de cobrança seguido apenas de pronome/indefinido). Não use para assunto fora de escopo, reclamação, pedido de humano, jurídico/Anatel/Procon ou frase compreensível que apenas não corresponda a outra intent. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | — | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | — | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | asdkj qweoi zxcm
hã trrr blá qx
[fala sem conteúdo inteligível]
está cobrando um
veio uma aqui | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. Esta regra não possui keywords determinísticas; portanto depende de estado, classificador LLM/fallback ou lógica conversacional específica. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `asdkj qweoi zxcm`, `hã trrr blá qx`, `[fala sem conteúdo inteligível]` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=suporte_contas_agent` e `mcp_tools=[]`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra Esta intent não declara MCP tools. O tratamento é conversacional, de clarificação ou de conhecimento por mecanismos do agente/framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_no_match` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 6. Regra — Consulta de Plano **Intent:** `contas_plan_information` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `142` ## O que esta regra significa Consulta do plano principal ou dos planos efetivamente contratados na linha, incluindo nome, identificação, valor e dados contratuais do plano atual. Use somente quando a pergunta exigir dados do cliente. Não use para perguntas gerais sobre funcionamento, tarifação, regras, condições ou características de um plano/produto, ainda que o usuário cite o nome de um plano específico. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_plan_information | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 142 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Consulta do plano principal ou dos planos efetivamente contratados na linha, incluindo nome, identificação, valor e dados contratuais do plano atual. Use somente quando a pergunta exigir dados do cliente. Não use para perguntas gerais sobre funcionamento, tarifação, regras, condições ou características de um plano/produto, ainda que o usuário cite o nome de um plano específico. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_plano | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | qual é meu plano
qual e meu plano
qual é o meu plano
qual e o meu plano
meu plano contratado
plano contratado
nome do meu plano
que plano eu tenho
quais são meus planos
quais sao meus planos | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Qual é meu plano?
Qual é o meu plano contratado?
Que plano eu tenho?
Qual o nome do meu plano?
Quais são os planos da minha linha?
Quanto custa o meu plano atual? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `qual é meu plano`, `qual e meu plano`, `qual é o meu plano`, `qual e o meu plano`, `meu plano contratado`, `plano contratado`, `nome do meu plano`, `que plano eu tenho`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `142` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Qual é meu plano?`, `Qual é o meu plano contratado?`, `Que plano eu tenho?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['consultar_plano']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_plano` **O que faz:** Consulta exclusivamente o nome e valor do plano ou planos contratados a partir da evidência de billing analysis; não retorna VAS como plano. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna `{plans, count, source}`. Cada plano contém pelo menos `name`, `value`, `type` e `contestable`, extraído exclusivamente de `billing_analysis.currentInvoice`. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_plan_information` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 7. Regra — Consulta / Segunda Via de Fatura **Intent:** `contas_invoice_query` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `140` ## O que esta regra significa Consulta, visualização, obtenção ou segunda via de fatura/conta sem pedido de explicação de cobrança. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_invoice_query | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 140 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Consulta, visualização, obtenção ou segunda via de fatura/conta sem pedido de explicação de cobrança. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_faturas | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | quero minha fatura
ver minha fatura
consultar fatura
consultar minha fatura
mostrar minha fatura
me mostra minha fatura
segunda via
segunda via da fatura
obter fatura
baixar fatura
minhas faturas
fatura atual
última fatura
ultima fatura
fatura em aberto
conta em aberto | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Quero minha fatura
Quero ver a minha conta
Pode consultar minha fatura atual?
Preciso da segunda via da fatura
Tenho alguma fatura em aberto? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `quero minha fatura`, `ver minha fatura`, `consultar fatura`, `consultar minha fatura`, `mostrar minha fatura`, `me mostra minha fatura`, `segunda via`, `segunda via da fatura`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `140` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Quero minha fatura`, `Quero ver a minha conta`, `Pode consultar minha fatura atual?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['consultar_faturas']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_faturas` **O que faz:** Consulta faturas atuais/anteriores, status, vencimento, valor e existência de fatura aberta sem iniciar o workflow de explicação. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o payload de faturas e, quando disponível no contexto enriquecido, `invoice_amount`, `invoice_amount_open`, `invoice_period`, `invoice_emissao`, `invoice_id` e `customer_id`. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_invoice_query` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 8. Regra — Explicação de Fatura **Intent:** `contas_invoice_explanation` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `150` ## O que esta regra significa Explicação, composição, diferença, aumento, variação, dúvida ou não reconhecimento informativo de valores/itens de uma fatura já conhecida. Use esta intent quando o cliente estiver tentando entender uma cobrança ou disser que não reconhece itens/valores, mas ainda não pedir explicitamente contestação, cancelamento, estorno, ajuste ou retirada. Não use para fragmentos que apenas mencionem cobrança sem um objeto/referente recuperável; nesses casos solicite clarificação via contas_no_match. ### Limite da regra É informativa. Não deve executar contestação/cancelamento apenas porque o usuário disse que não reconhece algo. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_invoice_explanation | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 150 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Explicação, composição, diferença, aumento, variação, dúvida ou não reconhecimento informativo de valores/itens de uma fatura já conhecida. Use esta intent quando o cliente estiver tentando entender uma cobrança ou disser que não reconhece itens/valores, mas ainda não pedir explicitamente contestação, cancelamento, estorno, ajuste ou retirada. Não use para fragmentos que apenas mencionem cobrança sem um objeto/referente recuperável; nesses casos solicite clarificação via contas_no_match. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | invoice_explanation | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | explicar fatura
explique minha fatura
entender minha fatura
entender minha conta
fatura veio alta
conta veio alta
veio mais cara
conta subiu
minha conta subiu
fatura subiu
minha fatura subiu
conta aumentou
minha conta aumentou
fatura aumentou
minha fatura aumentou
valor aumentou
diferença de valor
divergência na fatura
divergencia na fatura
não entendi a cobrança
nao entendi a cobranca
por que cobraram
por que veio esse valor
composição da fatura
composicao da fatura
detalhar cobrança
detalhar cobranca
não estou reconhecendo
nao estou reconhecendo
não reconheço esses itens
nao reconheco esses itens
itens que não reconheço
itens que nao reconheco
cobranças que não reconheço
cobrancas que nao reconheco | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Contratamos um plano, mas eu não estou reconhecendo esses itens eventuais.
Tem cobranças que eu não reconheço e quero entender de onde vieram.
Minha fatura veio mais alta e quero entender o que mudou.
Quero entender minha conta
Por que esse valor mudou?
Explique essas cobranças da minha fatura
Por que minha conta aumentou este mês? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `explicar fatura`, `explique minha fatura`, `entender minha fatura`, `entender minha conta`, `fatura veio alta`, `conta veio alta`, `veio mais cara`, `conta subiu`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `150` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Contratamos um plano, mas eu não estou reconhecendo esses itens eventuais.`, `Tem cobranças que eu não reconheço e quero entender de onde vieram.`, `Minha fatura veio mais alta e quero entender o que mudou.` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['invoice_explanation']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `invoice_explanation` **O que faz:** Explica composição, diferença ou variação de valores de uma fatura; não deve ser usada apenas para consultar/obter a fatura. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna evidências (`complete_invoices`, `billing_analysis`, `invoice_detail`, período/valor/emissão) e uma instrução para o LLM explicar sem inventar cobranças. **Workflow(s):** `workflows/invoice_explanation.v2.yaml`. ## Workflows envolvidos ### Workflow `invoice_explanation.v2.yaml` **Nome lógico:** `invoice_explanation` · **versão:** `2` · **start:** `preparar` **Por que existe dessa forma:** Foi construído como workflow conversacional porque a explicação não termina na primeira resposta: o agente apresenta a composição, pausa para saber se a explicação resolveu, pode registrar aceite, detectar nova intenção/variação VAS e preparar handoff para outra regra. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `preparar` | `preparar_invoice_explanation` | Executa `preparar_invoice_explanation`. É o ponto inicial. | não | | `formatar` | `formatar_invoice_explanation` | Executa `formatar_invoice_explanation`. | sim; `return_from=$.output.mensagem`; resposta em `resposta_usuario`; permitidos=['SIM', 'NAO', 'CONTINUAR']; `resume_from=decisao` | | `checar_tentativa` | `checar_tentativa_cvn` | Executa `checar_tentativa_cvn`. | não | | `texto_intencao_invalida` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `fim_intencao_invalida` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `resposta_falha_servico` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `decisao` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | | `registrar_sim` | `registrar_atendimento_invoice_explanation` | Executa `registrar_atendimento_invoice_explanation`. | não | | `registrar_protocolo_aceite` | `registrar_protocolo_inicio` | Executa `registrar_protocolo_inicio`. | não | | `registrar_nao` | `registrar_atendimento_invoice_explanation` | Executa `registrar_atendimento_invoice_explanation`. | não | | `handoff_pos_explicacao_nao` | `preparar_handoff_invoice_explanation` | Executa `preparar_handoff_invoice_explanation`. | não | | `checar_vas_variacao` | `checar_vas_variado` | Executa `checar_vas_variado`. | não | | `finalizar_nao_resolvido` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `preparar` | `formatar` | 10 | `{"eq": ["$.vars.preparar.success", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `preparar` | `resposta_falha_servico` | 20 | `{"eq": ["$.vars.preparar.service_failed", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `preparar` | `checar_tentativa` | 30 | `{"eq": ["$.vars.preparar.success", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_tentativa` | `fim_intencao_invalida` | 10 | `{"eq": ["$.vars.checar_tentativa.limite_excedido", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_tentativa` | `texto_intencao_invalida` | 20 | sempre | Transição incondicional. | | `formatar` | `decisao` | — | sempre | Transição incondicional. | | `decisao` | `registrar_sim` | 10 | `{"eq": ["$.input.resposta_usuario", "SIM"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao` | `registrar_nao` | 20 | `{"eq": ["$.input.resposta_usuario", "NAO"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `registrar_sim` | `registrar_protocolo_aceite` | — | sempre | Transição incondicional. | | `registrar_protocolo_aceite` | `END` | — | sempre | Transição incondicional. | | `registrar_nao` | `handoff_pos_explicacao_nao` | — | sempre | Transição incondicional. | | `handoff_pos_explicacao_nao` | `END` | — | sempre | Transição incondicional. | | `checar_vas_variacao` | `finalizar_nao_resolvido` | 10 | `{"eq": ["$.vars.checar_vas_variacao.tem_vas_variado", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_vas_variacao` | `END` | 20 | sempre | Transição incondicional. | | `finalizar_nao_resolvido` | `END` | — | sempre | Transição incondicional. | | `texto_intencao_invalida` | `END` | — | sempre | Transição incondicional. | | `fim_intencao_invalida` | `END` | — | sempre | Transição incondicional. | | `resposta_falha_servico` | `END` | — | sempre | Transição incondicional. | #### Como funciona a pausa e o resume - Em `formatar`, a execução pausa quando `null` é verdadeira. A mensagem devolvida ao cliente vem de `$.output.mensagem`. O runtime persiste `workflow_name` e `execution_id`; a próxima fala é normalizada no campo `resposta_usuario` e a execução retorna em `decisao`. Valores esperados: `['SIM', 'NAO', 'CONTINUAR']`. #### Término - O fluxo termina após `registrar_protocolo_aceite` quando a transição final é alcançada. - O fluxo termina após `handoff_pos_explicacao_nao` quando a transição final é alcançada. - O fluxo termina após `checar_vas_variacao` quando a transição final é alcançada. - O fluxo termina após `finalizar_nao_resolvido` quando a transição final é alcançada. - O fluxo termina após `texto_intencao_invalida` quando a transição final é alcançada. - O fluxo termina após `fim_intencao_invalida` quando a transição final é alcançada. - O fluxo termina após `resposta_falha_servico` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_invoice_explanation` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. - `app/domain/contas/invoice_resolver.py` e parsers: resolução canônica/classificação de itens de fatura. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. ### Diagnóstico especial — por que a explicação pode levar a outra regra `invoice_explanation.v2` foi construído para não “engolir” a conversa depois de explicar a fatura. Após apresentar a evidência, ele pode pausar, receber a avaliação do cliente e preparar um handoff. Por isso uma frase posterior como “é a de R$ 14,99” pode deixar de ser uma simples explicação e fornecer o parâmetro que faltava para uma contestação; da mesma forma, a identificação de um VAS pode alimentar um fluxo de cancelamento/tratamento. # 9. Regra — Contestação de Cobrança **Intent:** `contas_contestation` **Agente:** `contestacao_agent` **Domínio:** `telecom_contas` **Prioridade:** `120` ## O que esta regra significa Pedido transacional de contestação, estorno, ajuste, retirada ou correção de uma cobrança. Na primeira fala de não reconhecimento sem alvo concreto, use contas_invoice_explanation. Porém, numa reentrada contextual após a explicação, se o pedido original era não reconhecimento e o cliente identificar inequivocamente a cobrança/item/valor que continua questionando, trate a solicitação como contestação daquele alvo; a execução continua sujeita a validação e confirmação transacional. ### Limite da regra É transacional. Precisa haver intenção de contestar/estornar/ajustar/retirar e um alvo suficientemente resolvido. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_contestation | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | contestacao_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 120 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Pedido transacional de contestação, estorno, ajuste, retirada ou correção de uma cobrança. Na primeira fala de não reconhecimento sem alvo concreto, use contas_invoice_explanation. Porém, numa reentrada contextual após a explicação, se o pedido original era não reconhecimento e o cliente identificar inequivocamente a cobrança/item/valor que continua questionando, trate a solicitação como contestação daquele alvo; a execução continua sujeita a validação e confirmação transacional. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_faturas
invoice_explanation
contestar_cobranca | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | quero contestar
quero contestação
quero contestacao
contestar essa cobrança
contestar essa cobranca
abrir contestação
abrir contestacao
quero estorno
pedir estorno
quero ajuste na fatura
ajustar essa cobrança
ajustar essa cobranca
retirar essa cobrança
retirar essa cobranca
tirar essa cobrança
tirar essa cobranca | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Contexto anterior: cliente não reconhece uma cobrança; após a explicação identifica 'é a de quatorze e noventa e nove'.
Após a explicação da fatura, o cliente identifica pelo nome ou valor qual cobrança continua não reconhecendo.
Quero contestar essa cobrança
Quero abrir uma contestação para esse valor
Quero um estorno dessa cobrança
Não reconheço essa cobrança e quero contestar
Esse valor está errado e quero um ajuste na fatura | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `quero contestar`, `quero contestação`, `quero contestacao`, `contestar essa cobrança`, `contestar essa cobranca`, `abrir contestação`, `abrir contestacao`, `quero estorno`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `120` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Contexto anterior: cliente não reconhece uma cobrança; após a explicação identifica 'é a de quatorze e noventa e nove'.`, `Após a explicação da fatura, o cliente identifica pelo nome ou valor qual cobrança continua não reconhecendo.`, `Quero contestar essa cobrança` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=contestacao_agent` e `mcp_tools=['consultar_faturas', 'invoice_explanation', 'contestar_cobranca']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_faturas` **O que faz:** Consulta faturas atuais/anteriores, status, vencimento, valor e existência de fatura aberta sem iniciar o workflow de explicação. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o payload de faturas e, quando disponível no contexto enriquecido, `invoice_amount`, `invoice_amount_open`, `invoice_period`, `invoice_emissao`, `invoice_id` e `customer_id`. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ### Tool `invoice_explanation` **O que faz:** Explica composição, diferença ou variação de valores de uma fatura; não deve ser usada apenas para consultar/obter a fatura. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna evidências (`complete_invoices`, `billing_analysis`, `invoice_detail`, período/valor/emissão) e uma instrução para o LLM explicar sem inventar cobranças. **Workflow(s):** `workflows/invoice_explanation.v2.yaml`. ### Tool `contestar_cobranca` **O que faz:** Executa contestação no novo domínio Contas após confirmação transacional do framework. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `action` | | operation_type | `transactional` | | require_confirmation | `True` | | parâmetros obrigatórios pela policy | `subject`, `valor` | | pre-validation | `validar_contestacao` (`fail_open=False`) | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `subject` | `string` | extraído de `message` via `llm` | Referência a um item concreto e identificável da fatura (serviço, produto, plano, cobrança específica ou benefício). Uma referência genérica ao documento ou à cobrança como um todo não identifica o subject; quando não houver entidade concreta suficiente para resolução contra a evidência da fatura, mantenha subject ausente/null. | | `valor` | `number` | extraído de `message` via `llm` | Valor monetário explicitamente associado pelo cliente ao item, cobrança ou operação. Se não houver valor explícito ou houver dúvida razoável, manter ausente/null. | | `motivo` | `string` | extraído de `message` via `llm` | Motivo informado pelo cliente para a solicitação ou contestação. Não inventar motivo quando não estiver explícito. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Extração conversacional:** - `subject`: Extraia o nome do serviço/item cuja cobrança será contestada. Estratégia: `llm`. - `valor`: Extraia o valor monetário explicitamente associado ao item. Retorne null se não houver valor. Estratégia: `llm`. - `motivo`: Extraia o motivo da contestação em frase curta; null se não informado. Estratégia: `llm`. **Retorno esperado:** Retorna `success`, protocolo, resultado da contestação, tracking e evidências auxiliares de fatura/contrato/perfil. **Workflow(s):** `workflows/contestacao_tool.v2.yaml`. ## Workflows envolvidos ### Workflow `invoice_explanation.v2.yaml` **Nome lógico:** `invoice_explanation` · **versão:** `2` · **start:** `preparar` **Por que existe dessa forma:** Foi construído como workflow conversacional porque a explicação não termina na primeira resposta: o agente apresenta a composição, pausa para saber se a explicação resolveu, pode registrar aceite, detectar nova intenção/variação VAS e preparar handoff para outra regra. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `preparar` | `preparar_invoice_explanation` | Executa `preparar_invoice_explanation`. É o ponto inicial. | não | | `formatar` | `formatar_invoice_explanation` | Executa `formatar_invoice_explanation`. | sim; `return_from=$.output.mensagem`; resposta em `resposta_usuario`; permitidos=['SIM', 'NAO', 'CONTINUAR']; `resume_from=decisao` | | `checar_tentativa` | `checar_tentativa_cvn` | Executa `checar_tentativa_cvn`. | não | | `texto_intencao_invalida` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `fim_intencao_invalida` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `resposta_falha_servico` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `decisao` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | | `registrar_sim` | `registrar_atendimento_invoice_explanation` | Executa `registrar_atendimento_invoice_explanation`. | não | | `registrar_protocolo_aceite` | `registrar_protocolo_inicio` | Executa `registrar_protocolo_inicio`. | não | | `registrar_nao` | `registrar_atendimento_invoice_explanation` | Executa `registrar_atendimento_invoice_explanation`. | não | | `handoff_pos_explicacao_nao` | `preparar_handoff_invoice_explanation` | Executa `preparar_handoff_invoice_explanation`. | não | | `checar_vas_variacao` | `checar_vas_variado` | Executa `checar_vas_variado`. | não | | `finalizar_nao_resolvido` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `preparar` | `formatar` | 10 | `{"eq": ["$.vars.preparar.success", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `preparar` | `resposta_falha_servico` | 20 | `{"eq": ["$.vars.preparar.service_failed", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `preparar` | `checar_tentativa` | 30 | `{"eq": ["$.vars.preparar.success", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_tentativa` | `fim_intencao_invalida` | 10 | `{"eq": ["$.vars.checar_tentativa.limite_excedido", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_tentativa` | `texto_intencao_invalida` | 20 | sempre | Transição incondicional. | | `formatar` | `decisao` | — | sempre | Transição incondicional. | | `decisao` | `registrar_sim` | 10 | `{"eq": ["$.input.resposta_usuario", "SIM"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao` | `registrar_nao` | 20 | `{"eq": ["$.input.resposta_usuario", "NAO"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `registrar_sim` | `registrar_protocolo_aceite` | — | sempre | Transição incondicional. | | `registrar_protocolo_aceite` | `END` | — | sempre | Transição incondicional. | | `registrar_nao` | `handoff_pos_explicacao_nao` | — | sempre | Transição incondicional. | | `handoff_pos_explicacao_nao` | `END` | — | sempre | Transição incondicional. | | `checar_vas_variacao` | `finalizar_nao_resolvido` | 10 | `{"eq": ["$.vars.checar_vas_variacao.tem_vas_variado", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `checar_vas_variacao` | `END` | 20 | sempre | Transição incondicional. | | `finalizar_nao_resolvido` | `END` | — | sempre | Transição incondicional. | | `texto_intencao_invalida` | `END` | — | sempre | Transição incondicional. | | `fim_intencao_invalida` | `END` | — | sempre | Transição incondicional. | | `resposta_falha_servico` | `END` | — | sempre | Transição incondicional. | #### Como funciona a pausa e o resume - Em `formatar`, a execução pausa quando `null` é verdadeira. A mensagem devolvida ao cliente vem de `$.output.mensagem`. O runtime persiste `workflow_name` e `execution_id`; a próxima fala é normalizada no campo `resposta_usuario` e a execução retorna em `decisao`. Valores esperados: `['SIM', 'NAO', 'CONTINUAR']`. #### Término - O fluxo termina após `registrar_protocolo_aceite` quando a transição final é alcançada. - O fluxo termina após `handoff_pos_explicacao_nao` quando a transição final é alcançada. - O fluxo termina após `checar_vas_variacao` quando a transição final é alcançada. - O fluxo termina após `finalizar_nao_resolvido` quando a transição final é alcançada. - O fluxo termina após `texto_intencao_invalida` quando a transição final é alcançada. - O fluxo termina após `fim_intencao_invalida` quando a transição final é alcançada. - O fluxo termina após `resposta_falha_servico` quando a transição final é alcançada. ### Workflow `contestacao_tool.v2.yaml` **Nome lógico:** `contestacao_tool` · **versão:** `2` · **start:** `registrar_protocolo` **Por que existe dessa forma:** Foi separado da conversa porque contestação produz efeitos externos e exige uma sequência auditável: protocolo, validação de status, abertura da contestação, SMS/contrato/SR e atualização de status. A confirmação é feita antes pelo runtime; o workflow recebe uma operação já confirmada. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `registrar_protocolo` | `registrar_protocolo` | Executa `registrar_protocolo`. É o ponto inicial. | não | | `enviar_sms` | `enviar_sms` | Executa `enviar_sms`. | não | | `check_invoice_status` | `check_invoice_status` | Executa `check_invoice_status`. | não | | `abrir_contestacao_cliente` | `abrir_contestacao_cliente` | Executa `abrir_contestacao_cliente`. | não | | `consultar_contrato_corte` | `consultar_contrato_corte` | Executa `consultar_contrato_corte`. | não | | `abrir_sr_conta_certa_manual` | `abrir_sr_conta_certa_manual` | Executa `abrir_sr_conta_certa_manual`. | não | | `atualizar_status_sr` | `atualizar_status_sr` | Executa `atualizar_status_sr`. | não | | `atualizar_status_sr_registro` | `atualizar_status_sr` | Executa `atualizar_status_sr`. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `registrar_protocolo` | `check_invoice_status` | — | sempre | Transição incondicional. | | `check_invoice_status` | `abrir_contestacao_cliente` | 10 | sempre | Transição incondicional. | | `abrir_contestacao_cliente` | `END` | 1 | `{"eq": ["$.vars.abrir_contestacao_cliente.success", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `abrir_contestacao_cliente` | `enviar_sms` | 10 | `{"all": [{"exists": "$.vars.abrir_contestacao_cliente.barcode"}, {"neq": ["$.vars.abrir_contestacao_cliente.barcode", ""]}]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `abrir_contestacao_cliente` | `consultar_contrato_corte` | 99 | sempre | Transição incondicional. | | `enviar_sms` | `consultar_contrato_corte` | — | sempre | Transição incondicional. | | `consultar_contrato_corte` | `abrir_sr_conta_certa_manual` | 10 | `{"any": [{"all": [{"eq": ["$.vars.consultar_contrato_corte.apos_data_corte", true]}, {"eq": ["$.vars.consultar_contrato_corte.contestation_success", true]}, {"any": [{"eq": ["$.input.manual_conta_certa_indicator", true]}, {"eq": ["$.vars.abrir_contestacao_cliente.manual_conta_certa_indicator", true]}]}]}, {"all": [{"eq": ["$.vars.consultar_contrato_corte.dependent_invoice_item", true]}, {"eq": ["$.vars.consultar_contrato_corte.contestation_registered", true]}]}]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `consultar_contrato_corte` | `atualizar_status_sr` | 99 | sempre | Transição incondicional. | | `abrir_sr_conta_certa_manual` | `atualizar_status_sr_registro` | 10 | sempre | Transição incondicional. | | `atualizar_status_sr_registro` | `END` | — | sempre | Transição incondicional. | | `atualizar_status_sr` | `END` | — | sempre | Transição incondicional. | #### Término - O fluxo termina após `abrir_contestacao_cliente` quando a condição `{"eq": ["$.vars.abrir_contestacao_cliente.success", false]}`. - O fluxo termina após `atualizar_status_sr_registro` quando a transição final é alcançada. - O fluxo termina após `atualizar_status_sr` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_contestation` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. - `app/domain/contas/invoice_resolver.py` e parsers: resolução canônica/classificação de itens de fatura. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Contestação: `TIM_PROTOCOL_*`, `TIM_CUSTOMER_CONTESTATION_*`, `TIM_TRACKING_ACTIVITIES_*`, `TIM_SMS_*`, `TIM_CONTRATO_*`, `TIM_PROFILE_FULL_AUTH` e defaults de canal/client id. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. ### Diagnóstico especial — “não reconheço” não é automaticamente contestação O agente diferencia **entender** de **executar uma contestação**. Uma primeira fala como “não reconheço essa cobrança” pode cair em `contas_invoice_explanation`, pois ainda não existe pedido inequívoco de estorno/ajuste/contestação. Depois que a fatura é explicada, se o cliente identifica o alvo — por nome ou valor — e manifesta que continua questionando/quer contestar, o contexto pode reentrar em `contas_contestation`. Essa separação existe para não disparar uma transação financeira a partir de uma dúvida informativa. `contestar_cobranca` exige `subject` e `valor`, passa por `validar_contestacao`, pede confirmação e só depois executa o workflow transacional. # 10. Regra — Cancelamento de VAS **Intent:** `contas_vas_cancel` **Agente:** `contestacao_agent` **Domínio:** `telecom_contas` **Prioridade:** `145` ## O que esta regra significa Cancelamento ou retirada explicitamente solicitada de serviço/VAS. Não use para simples alegação de contratação ou cobrança não reconhecida sem pedido de ação; nesses casos use contas_invoice_explanation. ### Limite da regra A intent significa que o cliente quer cancelar; **não garante** que o item seja tecnicamente cancelável como VAS avulso. A classe autoritativa do item é verificada depois. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_vas_cancel | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | contestacao_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 145 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Cancelamento ou retirada explicitamente solicitada de serviço/VAS. Não use para simples alegação de contratação ou cobrança não reconhecida sem pedido de ação; nesses casos use contas_invoice_explanation. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_vas
cancelar_vas_avulso | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | quero cancelar
cancelar meu
cancela o
cancele o
cancelar serviço
cancelar servico
cancelar assinatura
retirar serviço
retirar servico
desativar serviço
desativar servico
tirar esse vas | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Quero cancelar esse serviço
Quero retirar essa assinatura | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `quero cancelar`, `cancelar meu`, `cancela o`, `cancele o`, `cancelar serviço`, `cancelar servico`, `cancelar assinatura`, `retirar serviço`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `145` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Quero cancelar esse serviço`, `Quero retirar essa assinatura` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=contestacao_agent` e `mcp_tools=['consultar_vas', 'cancelar_vas_avulso']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_vas` **O que faz:** Consulta serviços VAS ativos. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o catálogo de VAS ativos fornecido pela integração TIM. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ### Tool `cancelar_vas_avulso` **O que faz:** Executa cancelamento de VAS avulso no novo domínio Contas; confirmação e estado são do framework. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `action` | | operation_type | `transactional` | | require_confirmation | `True` | | parâmetros obrigatórios pela policy | `subject` | | pre-validation | `validar_vas_subject` (`fail_open=False`) | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `subject` | `string` | extraído de `message` via `llm` | Referência a um ou mais serviços, produtos ou benefícios concretos e identificáveis nas evidências VAS/fatura do cliente. Preserve referências múltiplas e anafóricas quando o contexto imediato as resolver (por exemplo, "os dois", "ambos", "Netflix e HBO", "todos os VAS avulsos"). Categorias genéricas sem foco conversacional não identificam entidade; se o texto e o contexto não permitirem resolução segura para um item real nas evidências autorizadas, mantenha subject ausente/null. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Extração conversacional:** - `subject`: Extraia somente o nome do serviço que o cliente quer cancelar. Retorne null se não estiver claro. Estratégia: `llm`. **Retorno esperado:** Retorna `success`; serviço resolvido; evidência usada (`active`/`history`); resultado de bloqueio e cancelamento. Em falha retorna `reason` como `service_not_found`, `already_inactive_or_blocked`, `block_vas_failed` ou `cancel_vas_failed`. **Workflow(s):** `workflows/cancelamento_vas_avulso.v1.yaml`. ## Workflows envolvidos ### Workflow `cancelamento_vas_avulso.v1.yaml` **Nome lógico:** `cancelamento_vas_avulso` · **versão:** `1` · **start:** `cancelar_vas_avulso` **Por que existe dessa forma:** É propositalmente curto: a complexidade de elegibilidade e confirmação fica antes da execução. Quando o workflow começa, os itens já devem estar canônicos e confirmados; a action batch executa bloqueio/cancelamento e tratamento composto por item. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `cancelar_vas_avulso` | `cancelamento_vas_avulso_batch` | Executa `cancelamento_vas_avulso_batch`. É o ponto inicial. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `cancelar_vas_avulso` | `END` | — | sempre | Transição incondicional. | #### Término - O fluxo termina após `cancelar_vas_avulso` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_vas_cancel` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. - `app/domain/contas/invoice_resolver.py` e parsers: resolução canônica/classificação de itens de fatura. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - VAS: endpoints/credenciais de consulta, histórico, bloqueio/cancelamento e defaults TIM; entre as variáveis presentes no cliente estão `TIM_VAS_HISTORY_URL`, `TIM_VAS_HISTORY_AUTH`, `TIM_VAS_HISTORY_TIMEOUT` e configuração de gateway. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. ### Diagnóstico especial — por que `Tamboro` e `Paramount+` podem seguir caminhos diferentes Esta é uma regra crítica do agente. A frase **“quero cancelar Tamboro e Paramount+”** pode inicialmente ser reconhecida como `contas_vas_cancel`, porque o objetivo do usuário é cancelar serviços. Isso **não significa** que todos os itens serão executados pela mesma tool. A decisão ocorre em duas camadas: 1. **Roteamento conversacional**: `routing.yaml` decide que existe um pedido de cancelamento e entrega `consultar_vas` + `cancelar_vas_avulso`. 2. **Classificação autoritativa de cada entidade**: antes de executar, `validar_vas_subject`/`invoice_resolver` confronta o nome canônico com a fatura. O método `_classify()` dá precedência a `entry.estrategico`, `entry.classe` e depois à seção da fatura. - `avulso` → `cancelar_vas_avulso`; - `estrategico` ou `bundle` → `vas_estrategico` / `tratar_vas_estrategico`. No código, `_build_item()` define `tool_category="vas_estrategico"` quando `item_type` é `bundle` ou `estrategico`. No pre-validator MCP, se a tool solicitada era `cancelar_vas_avulso` mas a entidade resolvida é estratégica, `effective_tool` muda para `tratar_vas_estrategico` e a decisão informa `action_changed`. **Tamboro** normalmente permanece no caminho avulso quando a evidência da fatura o classifica assim. O workflow `cancelamento_vas_avulso.v1` então executa a action batch de cancelamento. **Paramount+** pode aparecer em seção/registro classificado como estratégico. Nesse caso o domínio impede que ele seja cancelado como se fosse um VAS avulso e o direciona para `vas_estrategico.v3`, que é um fluxo de explicação/orientação e não um cancelamento técnico automático. Há ainda um caso de **pedido misto** já coberto pelos testes: quando múltiplas entidades canônicas foram pré-validadas em `items[]`, o preflight não deve tentar resolver novamente a string agregada `"Tamboro Mensal, Paramount+"`. O snapshot preserva os itens. Na execução batch, cada resultado pode ser diferente — por exemplo, Tamboro com `success=true` e Paramount+ com `success=false/reason=service_not_found`; a resposta final deve preservar os dois resultados, sem afirmar que ambos foram cancelados. **Onde olhar ao depurar este comportamento** - `config/routing.yaml` → `contas_vas_cancel` e `contas_vas_strategic`; - `config/tool_policies.yaml` → `cancelar_vas_avulso.pre_validation.tool=validar_vas_subject`; - `config/mcp_parameter_mapping.yaml` → extração de `subject`; - `app/domain/contas/parsers/bill_processor.py` → carimbo `estrategico`; - `app/domain/contas/invoice_resolver.py` → `_classify()` e `_build_item()`; - `contas_mcp/servers/contas_mcp_server/main.py` → `_validate_vas_subject()` e troca de `effective_tool`; - `workflows/cancelamento_vas_avulso.v1.yaml`; - `workflows/vas_estrategico.v3.yaml`; - `tests/migration/test_subject_entity_resolution_all_domains.py`; - `tests/migration/test_tim_aoferta_transaction_continuation.py`. Esse mecanismo deve ser lido como **proteção de semântica de negócio**: a intent descreve o que o cliente quer; a classificação de domínio decide qual operação é legal/correta para cada item. # 11. Regra — VAS Estratégico / Bundle **Intent:** `contas_vas_strategic` **Agente:** `contestacao_agent` **Domínio:** `telecom_contas` **Prioridade:** `125` ## O que esta regra significa Tratamento de serviço estratégico, parceiro ou benefício incluso com fluxo específico. ### Limite da regra Representa tratamento específico de estratégico/bundle. Não é sinônimo de cancelamento técnico automático. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_vas_strategic | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | contestacao_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 125 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Tratamento de serviço estratégico, parceiro ou benefício incluso com fluxo específico. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_vas
tratar_vas_estrategico | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | apple music
deezer
disney
disney+
netflix
youtube premium
globoplay
amazon prime
prime video
serviço estratégico
servico estrategico
benefício incluso
beneficio incluso | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Não reconheço o Netflix
Quero tratar esse serviço estratégico
Meu plano inclui Deezer? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `apple music`, `deezer`, `disney`, `disney+`, `netflix`, `youtube premium`, `globoplay`, `amazon prime`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `125` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Não reconheço o Netflix`, `Quero tratar esse serviço estratégico`, `Meu plano inclui Deezer?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=contestacao_agent` e `mcp_tools=['consultar_vas', 'tratar_vas_estrategico']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_vas` **O que faz:** Consulta serviços VAS ativos. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o catálogo de VAS ativos fornecido pela integração TIM. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ### Tool `tratar_vas_estrategico` **O que faz:** Trata VAS estratégico/bundle no novo domínio Contas; diálogo e estado são do framework. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `conversational` | | operation_type | `conversational` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | `subject` | | pre-validation | `validar_vas_subject` (`fail_open=False`) | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `subject` | `string` | extraído de `message` via `llm` | Referência a um serviço, produto ou benefício concreto e identificável nas evidências VAS/fatura do cliente. Categorias ou referências genéricas não identificam uma entidade por si só; se o texto não permitir resolver univocamente para um item real, mantenha subject ausente/null. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Extração conversacional:** - `subject`: Extraia somente o nome do serviço estratégico ou incluso citado pelo cliente. Estratégia: `llm`. **Retorno esperado:** Retorna serviço estratégico encontrado, `accepted_explanation` e orientação de tratamento. Não executa cancelamento automático de VAS estratégico/bundle. **Workflow(s):** `workflows/vas_estrategico.v3.yaml`. ## Workflows envolvidos ### Workflow `vas_estrategico.v3.yaml` **Nome lógico:** `vas_estrategico` · **versão:** `3` · **start:** `preparar` **Por que existe dessa forma:** Existe porque estratégico/bundle não é equivalente a VAS avulso. O fluxo primeiro explica/trata o serviço, pode pausar aguardando aceite e escolhe caminhos distintos para estratégico e bundle. Ele evita que uma solicitação genérica de 'cancelar' seja transformada automaticamente em cancelamento técnico. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `preparar` | `preparar_vas_estrategico` | Executa `preparar_vas_estrategico`. É o ponto inicial. | sim; `return_from=$.output.mensagem`; resposta em `resposta_usuario`; permitidos=['SIM', 'NAO', 'OUTRO']; `resume_from=decisao` | | `resposta_bundle` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `decisao` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | | `resposta_sim` | `montar_resposta_texto` | Executa `montar_resposta_texto`. | não | | `explicar_cancelamento` | `montar_explicacao_cancelamento_vas_estrategico` | Executa `montar_explicacao_cancelamento_vas_estrategico`. | não | | `registrar_sim` | `registrar_atendimento_vas_estrategico` | Executa `registrar_atendimento_vas_estrategico`. | não | | `registrar_nao` | `registrar_atendimento_vas_estrategico` | Executa `registrar_atendimento_vas_estrategico`. | não | | `registrar_bundle` | `registrar_atendimento_vas_estrategico` | Executa `registrar_atendimento_vas_estrategico`. | não | | `registrar_outro` | `registrar_atendimento_vas_estrategico` | Executa `registrar_atendimento_vas_estrategico`. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `preparar` | `decisao` | 10 | `{"eq": ["$.vars.preparar.await_user_input", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `preparar` | `resposta_bundle` | 20 | sempre | Transição incondicional. | | `decisao` | `resposta_sim` | 10 | `{"eq": ["$.input.resposta_usuario", "SIM"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao` | `explicar_cancelamento` | 20 | `{"all": [{"eq": ["$.vars.preparar.has_estrategico_items", true]}, {"eq": ["$.input.resposta_usuario", "NAO"]}]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao` | `resposta_bundle` | 30 | `{"eq": ["$.vars.preparar.has_estrategico_items", false]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao` | `registrar_outro` | 99 | sempre | Transição incondicional. | | `resposta_sim` | `registrar_sim` | — | sempre | Transição incondicional. | | `explicar_cancelamento` | `registrar_nao` | — | sempre | Transição incondicional. | | `registrar_sim` | `END` | — | sempre | Transição incondicional. | | `registrar_nao` | `END` | — | sempre | Transição incondicional. | | `resposta_bundle` | `registrar_bundle` | — | sempre | Transição incondicional. | | `registrar_bundle` | `END` | — | sempre | Transição incondicional. | | `registrar_outro` | `END` | — | sempre | Transição incondicional. | #### Como funciona a pausa e o resume - Em `preparar`, a execução pausa quando `{"eq": ["$.output.await_user_input", true]}` é verdadeira. A mensagem devolvida ao cliente vem de `$.output.mensagem`. O runtime persiste `workflow_name` e `execution_id`; a próxima fala é normalizada no campo `resposta_usuario` e a execução retorna em `decisao`. Valores esperados: `['SIM', 'NAO', 'OUTRO']`. #### Término - O fluxo termina após `registrar_sim` quando a transição final é alcançada. - O fluxo termina após `registrar_nao` quando a transição final é alcançada. - O fluxo termina após `registrar_bundle` quando a transição final é alcançada. - O fluxo termina após `registrar_outro` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_vas_strategic` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. - `app/domain/contas/invoice_resolver.py` e parsers: resolução canônica/classificação de itens de fatura. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - VAS: endpoints/credenciais de consulta, histórico, bloqueio/cancelamento e defaults TIM; entre as variáveis presentes no cliente estão `TIM_VAS_HISTORY_URL`, `TIM_VAS_HISTORY_AUTH`, `TIM_VAS_HISTORY_TIMEOUT` e configuração de gateway. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 12. Regra — Consulta de VAS **Intent:** `contas_vas_information` **Agente:** `vas_agent` **Domínio:** `telecom_contas` **Prioridade:** `110` ## O que esta regra significa Consulta ou explicação de VAS, serviço adicional, assinatura, aplicativo, parceiro, bundle ou benefício adicional. Não use esta intent para perguntas sobre nome, identificação ou valor do plano principal/plano contratado da linha. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_vas_information | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | vas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 110 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Consulta ou explicação de VAS, serviço adicional, assinatura, aplicativo, parceiro, bundle ou benefício adicional. Não use esta intent para perguntas sobre nome, identificação ou valor do plano principal/plano contratado da linha. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_vas | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | vas
serviço adicional
servico adicional
aplicativo
assinatura
serviços ativos
servicos ativos
quais são meus serviços
quais sao meus servicos
meus serviços
meus servicos
benefício
beneficio
incluso no plano
pacote digital
parceiro | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | O que é esse serviço?
Quais VAS estão ativos?
Esse aplicativo está incluso?
Quais serviços adicionais eu tenho? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `vas`, `serviço adicional`, `servico adicional`, `aplicativo`, `assinatura`, `serviços ativos`, `servicos ativos`, `quais são meus serviços`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `110` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `O que é esse serviço?`, `Quais VAS estão ativos?`, `Esse aplicativo está incluso?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=vas_agent` e `mcp_tools=['consultar_vas']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_vas` **O que faz:** Consulta serviços VAS ativos. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o catálogo de VAS ativos fornecido pela integração TIM. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_vas_information` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - VAS: endpoints/credenciais de consulta, histórico, bloqueio/cancelamento e defaults TIM; entre as variáveis presentes no cliente estão `TIM_VAS_HISTORY_URL`, `TIM_VAS_HISTORY_AUTH`, `TIM_VAS_HISTORY_TIMEOUT` e configuração de gateway. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 13. Regra — Histórico de VAS **Intent:** `contas_vas_history` **Agente:** `vas_agent` **Domínio:** `telecom_contas` **Prioridade:** `105` ## O que esta regra significa Consulta de histórico de VAS/serviços que já estiveram ativos ou sofreram alterações. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_vas_history | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | vas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 105 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Consulta de histórico de VAS/serviços que já estiveram ativos ou sofreram alterações. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_historico_vas | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | histórico de vas
historico de vas
histórico de serviços
historico de servicos
serviços anteriores
servicos anteriores
já tive esse serviço
ja tive esse servico | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Quero ver meu histórico de serviços
Eu já tive esse VAS antes? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `histórico de vas`, `historico de vas`, `histórico de serviços`, `historico de servicos`, `serviços anteriores`, `servicos anteriores`, `já tive esse serviço`, `ja tive esse servico`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `105` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Quero ver meu histórico de serviços`, `Eu já tive esse VAS antes?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=vas_agent` e `mcp_tools=['consultar_historico_vas']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_historico_vas` **O que faz:** Consulta histórico de serviços VAS. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna histórico de VAS/serviços, usado também como fallback quando um serviço não aparece mais entre os ativos. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_vas_history` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - VAS: endpoints/credenciais de consulta, histórico, bloqueio/cancelamento e defaults TIM; entre as variáveis presentes no cliente estão `TIM_VAS_HISTORY_URL`, `TIM_VAS_HISTORY_AUTH`, `TIM_VAS_HISTORY_TIMEOUT` e configuração de gateway. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 14. Regra — Conhecimento Geral de Contas **Intent:** `contas_knowledge` **Agente:** `suporte_contas_agent` **Domínio:** `telecom_contas` **Prioridade:** `120` ## O que esta regra significa Perguntas gerais, conceituais, explicativas ou de procedimento sobre funcionamento, tarifação, regras, condições e características de planos, produtos, faturas e serviços que não exigem consultar dados específicos do cliente. Use esta intent quando o usuário perguntar como um plano/produto funciona, mesmo citando seu nome. Não use para descobrir qual plano está contratado na linha nem para consultar valor/dados contratuais do plano atual. ### Limite da regra Serve para conhecimento geral que não requer consultar dados particulares da linha; perguntas sobre 'meu plano/minha fatura' pertencem a intents de dados do cliente. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_knowledge | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | suporte_contas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 120 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Perguntas gerais, conceituais, explicativas ou de procedimento sobre funcionamento, tarifação, regras, condições e características de planos, produtos, faturas e serviços que não exigem consultar dados específicos do cliente. Use esta intent quando o usuário perguntar como um plano/produto funciona, mesmo citando seu nome. Não use para descobrir qual plano está contratado na linha nem para consultar valor/dados contratuais do plano atual. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | — | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | o que é
o que significa
como funciona
como é a tarifação
como funciona a tarifação
tarifação do plano
tarifacao do plano
qual a regra
quais são as regras
quais sao as regras
como faço
onde encontro
informação sobre
informacao sobre | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Como funciona o Infinity Pós?
Como funciona a tarifação do Infinity Pós?
Quais são as regras do Infinity Pós?
O que significa cobrança proporcional?
Como funciona o vencimento da fatura? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `o que é`, `o que significa`, `como funciona`, `como é a tarifação`, `como funciona a tarifação`, `tarifação do plano`, `tarifacao do plano`, `qual a regra`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `120` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Como funciona o Infinity Pós?`, `Como funciona a tarifação do Infinity Pós?`, `Quais são as regras do Infinity Pós?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=suporte_contas_agent` e `mcp_tools=[]`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra Esta intent não declara MCP tools. O tratamento é conversacional, de clarificação ou de conhecimento por mecanismos do agente/framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_knowledge` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 15. Regra — Status de Solicitação **Intent:** `contas_service_status` **Agente:** `suporte_contas_agent` **Domínio:** `telecom_contas` **Prioridade:** `90` ## O que esta regra significa Consulta de status, andamento, protocolo ou acompanhamento de solicitação já aberta. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_service_status | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | suporte_contas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 90 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Consulta de status, andamento, protocolo ou acompanhamento de solicitação já aberta. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | consultar_status_solicitacao | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | protocolo
status da solicitação
status da solicitacao
andamento
acompanhamento
acompanhar solicitação
acompanhar solicitacao
pedido em andamento | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Qual o status da minha solicitação?
Quero acompanhar o protocolo | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `protocolo`, `status da solicitação`, `status da solicitacao`, `andamento`, `acompanhamento`, `acompanhar solicitação`, `acompanhar solicitacao`, `pedido em andamento`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `90` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Qual o status da minha solicitação?`, `Quero acompanhar o protocolo` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=suporte_contas_agent` e `mcp_tools=['consultar_status_solicitacao']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `consultar_status_solicitacao` **O que faz:** Consulta status de solicitação/protocolo TIM. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | | `protocol` | `string` | extraído de `message` via `llm` | Número ou identificador de protocolo/solicitação informado pelo cliente ou retornado por sistema. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Extração conversacional:** - `protocol`: Extraia somente o número de protocolo explicitamente informado pelo cliente; não use message_id ou interaction_key como protocolo. Estratégia: `llm`. **Retorno esperado:** Retorna o status da solicitação/SR consultada pelo protocolo. **Workflow:** não há workflow dedicado; a execução é direta no MCP/serviço ou é tratada pelo runtime do framework. ## Workflow Não existe workflow dedicado para esta regra. A capability é executada diretamente ou a resposta é produzida pelo runtime/agente. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_service_status` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. - `app/domain/contas/service.py`: regra de domínio e contrato de retorno. - `app/domain/contas/client.py`: chamada concreta das integrações TIM. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Solicitação/finalização: `TIM_SERVICE_REQUEST_STATUS_*`, `TIM_DEFAULT_CHANNEL` e configuração de protocolo. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 16. Regra — Pró-rata **Intent:** `contas_pro_rata` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `128` ## O que esta regra significa Cobrança proporcional/pró-rata decorrente de troca de plano. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_pro_rata | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 128 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Cobrança proporcional/pró-rata decorrente de troca de plano. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | pro_rata | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | pró-rata
pro rata
pro-rata
cobrança proporcional
cobranca proporcional
troca de plano | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Por que veio cobrança proporcional?
Troquei de plano e vieram duas cobranças | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `pró-rata`, `pro rata`, `pro-rata`, `cobrança proporcional`, `cobranca proporcional`, `troca de plano`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `128` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Por que veio cobrança proporcional?`, `Troquei de plano e vieram duas cobranças` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['pro_rata']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `pro_rata` **O que faz:** Explica cobrança proporcional e conduz pause/resume pelo WorkflowRuntime. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `conversational` | | operation_type | `conversational` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | | `planos` | `array` | fornecido pelo runtime/contexto | Lista de planos identificados no contexto ou retornados por sistemas para análise de pró-rata. | | `has_plano_controle` | `boolean` | fornecido pelo runtime/contexto | Indicador booleano que informa se há plano Controle entre os planos considerados no fluxo. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna o resultado do workflow conversacional de pró-rata, podendo pausar para esclarecimento. **Workflow(s):** `workflows/pro_rata.v3.yaml`. ## Workflows envolvidos ### Workflow `pro_rata.v3.yaml` **Nome lógico:** `pro_rata` · **versão:** `3` · **start:** `preparar` **Por que existe dessa forma:** Pró-rata exige explicação e possível esclarecimento do cliente. Por isso o workflow contém pause/resume e caminhos diferentes para plano Controle, aceite e necessidade de repergunta. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `preparar` | `preparar_pro_rata` | Executa `preparar_pro_rata`. É o ponto inicial. | não | | `formatar` | `formatar_pro_rata` | Executa `formatar_pro_rata`. | sim; `return_from=$.output.mensagem`; resposta em `resposta_usuario`; permitidos=['SIM', 'NAO', 'OUTRO']; `resume_from=decisao_esclarecimento` | | `decisao_esclarecimento` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | | `devolver_orquestrador` | `no_op` | Executa `no_op`. É nó de decisão/orquestração, sem efeito de domínio. | não | | `reperguntar_esclarecimento` | `formatar_pro_rata` | Executa `formatar_pro_rata`. | sim; `return_from=$.output.mensagem`; resposta em `resposta_usuario`; permitidos=['SIM', 'NAO', 'OUTRO']; `resume_from=decisao_esclarecimento` | | `registrar_nao_controle` | `registrar_atendimento_pro_rata` | Executa `registrar_atendimento_pro_rata`. | não | | `registrar_aceitou` | `registrar_atendimento_pro_rata` | Executa `registrar_atendimento_pro_rata`. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `preparar` | `formatar` | — | sempre | Transição incondicional. | | `formatar` | `decisao_esclarecimento` | 10 | `{"eq": ["$.vars.preparar.await_user_input", true]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `formatar` | `registrar_nao_controle` | 20 | sempre | Transição incondicional. | | `decisao_esclarecimento` | `registrar_aceitou` | 10 | `{"eq": ["$.input.resposta_usuario", "SIM"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao_esclarecimento` | `devolver_orquestrador` | 20 | `{"eq": ["$.input.resposta_usuario", "NAO"]}` | A transição só ocorre quando a expressão declarativa é verdadeira; quando há várias saídas, o runtime avalia por prioridade crescente. | | `decisao_esclarecimento` | `reperguntar_esclarecimento` | 30 | sempre | Transição incondicional. | | `devolver_orquestrador` | `END` | — | sempre | Transição incondicional. | | `reperguntar_esclarecimento` | `decisao_esclarecimento` | — | sempre | Transição incondicional. | | `registrar_nao_controle` | `END` | — | sempre | Transição incondicional. | | `registrar_aceitou` | `END` | — | sempre | Transição incondicional. | #### Como funciona a pausa e o resume - Em `formatar`, a execução pausa quando `{"eq": ["$.vars.preparar.await_user_input", true]}` é verdadeira. A mensagem devolvida ao cliente vem de `$.output.mensagem`. O runtime persiste `workflow_name` e `execution_id`; a próxima fala é normalizada no campo `resposta_usuario` e a execução retorna em `decisao_esclarecimento`. Valores esperados: `['SIM', 'NAO', 'OUTRO']`. - Em `reperguntar_esclarecimento`, a execução pausa quando `null` é verdadeira. A mensagem devolvida ao cliente vem de `$.output.mensagem`. O runtime persiste `workflow_name` e `execution_id`; a próxima fala é normalizada no campo `resposta_usuario` e a execução retorna em `decisao_esclarecimento`. Valores esperados: `['SIM', 'NAO', 'OUTRO']`. #### Término - O fluxo termina após `devolver_orquestrador` quando a transição final é alcançada. - O fluxo termina após `registrar_nao_controle` quando a transição final é alcançada. - O fluxo termina após `registrar_aceitou` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_pro_rata` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 17. Regra — Término de Desconto **Intent:** `contas_termino_desconto` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `127` ## O que esta regra significa Término de desconto ou promoção refletido na fatura. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_termino_desconto | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 127 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Término de desconto ou promoção refletido na fatura. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | termino_desconto | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | término de desconto
termino de desconto
desconto acabou
fim do desconto | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Meu desconto acabou? | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `término de desconto`, `termino de desconto`, `desconto acabou`, `fim do desconto`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `127` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Meu desconto acabou?` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['termino_desconto']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `termino_desconto` **O que faz:** Analisa término/retirada de desconto somente com causa suportada por evidência autoritativa. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | | `nome_plano` | `string` | extraído de `message` via `llm` | Nome do plano ao qual a informação ou tratamento se refere. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Extração conversacional:** - `nome_plano`: Extraia o nome do plano se explicitamente citado; null se ausente. Estratégia: `llm`. **Retorno esperado:** Retorna resposta formatada a partir de evidência autoritativa sobre o término do desconto. **Workflow(s):** `workflows/termino_desconto.v1.yaml`. ## Workflows envolvidos ### Workflow `termino_desconto.v1.yaml` **Nome lógico:** `termino_desconto` · **versão:** `1` · **start:** `formatar` **Por que existe dessa forma:** A regra foi reduzida a formatação porque a decisão deve estar apoiada em evidência autoritativa de desconto; o workflow não inventa causa nem cria transação. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `formatar` | `formatar_capability_resposta` | Executa `formatar_capability_resposta`. É o ponto inicial. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `formatar` | `END` | — | sempre | Transição incondicional. | #### Término - O fluxo termina após `formatar` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_termino_desconto` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # 18. Regra — Valor Divergente **Intent:** `contas_valor_divergente` **Agente:** `faturas_agent` **Domínio:** `telecom_contas` **Prioridade:** `126` ## O que esta regra significa Capability de valor divergente. ### Limite da regra A regra deve permanecer dentro da descrição declarada; quando o objetivo muda, o roteamento/handoff deve mudar também. ## `routing.yaml` atributo por atributo | Atributo | Valor nesta regra | Como participa da decisão | |---|---|---| | `name` | contas_valor_divergente | Identificador estável da intent. É o valor gravado na decisão de roteamento e usado para recuperar tools/agente. | | `domain` | telecom_contas | Domínio lógico. Impede que a intent seja tratada como capability genérica sem contexto de Contas. | | `agent` | faturas_agent | Agente proprietário da conversa depois que a intent é escolhida. | | `priority` | 126 | Desempate determinístico entre keywords concorrentes. No `EnterpriseRouter`, **menor número vence**. | | `description` | Capability de valor divergente. | Definição semântica usada pelo fallback LLM e pela manutenção humana. Deve dizer também quando **não** usar a intent. | | `mcp_tools` | valor_divergente | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. | | `keywords` | valor divergente
divergência de valor
divergencia de valor | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. | | `examples` | Tem um valor divergente na minha conta | Exemplos fornecidos ao classificador LLM. Não são regex nem são executados diretamente. | ## Como esta intent é encontrada 1. O router procura primeiro keywords desta regra. Exemplos: `valor divergente`, `divergência de valor`, `divergencia de valor`. 2. Um match pode ser exato ou tolerante a tokens intermediários. Se outra intent também casar, a prioridade `126` participa do desempate. 3. Se o caminho LLM for usado, a descrição acima e exemplos como `Tem um valor divergente na minha conta` entram no catálogo de classificação. 4. Ao escolher a intent, o router entrega `agent=faturas_agent` e `mcp_tools=['valor_divergente']`. O agente não precisa hardcodar esse mapeamento. ## Tools desta regra ### Tool `valor_divergente` **O que faz:** Trata variação/divergência de valor conforme workflow do Contas. | Propriedade | Valor | |---|---| | MCP server | `contas` | | enabled | `True` | | tool_type | `read_only/implícito` | | operation_type | `read_only` | | require_confirmation | `False` | | parâmetros obrigatórios pela policy | — | | pre-validation | — | **Dados necessários para chamada** | Campo | Tipo | Origem / obtenção | Significado | |---|---|---|---| | `msisdn` | `string` | mapeado automaticamente de `customer_key` | Número da linha do cliente (MSISDN) usado para consultar dados associados ao atendimento. | **Mapeamento técnico:** `customer_key → msisdn`, `session_key → session_id`. **Retorno esperado:** Retorna resposta formatada pelo workflow de divergência de valor. **Workflow(s):** `workflows/valor_divergente.v1.yaml`. ## Workflows envolvidos ### Workflow `valor_divergente.v1.yaml` **Nome lógico:** `valor_divergente` · **versão:** `1` · **start:** `formatar` **Por que existe dessa forma:** É um capability workflow simples de apresentação: recebe a evidência preparada pelo domínio e produz a resposta sem efeitos transacionais. #### Nós | Nó | Action Python | Papel | Pause/Resume | |---|---|---|---| | `formatar` | `formatar_capability_resposta` | Executa `formatar_capability_resposta`. É o ponto inicial. | não | #### Transições e condições | De | Para | Prioridade | Condição | Interpretação | |---|---|---:|---|---| | `formatar` | `END` | — | sempre | Transição incondicional. | #### Término - O fluxo termina após `formatar` quando a transição final é alcançada. ## Amarração Python - `agent_framework_oci/.../routing/enterprise_router.py`: resolve `contas_valor_divergente` e entrega agente/tools. - `app/agents/runtime.py` + runtime do framework: seleção/execução de tools, parâmetros, confirmação e pause/resume. - `contas_mcp/servers/contas_mcp_server/main.py`: enriquecimento, preflight, validação e dispatch MCP. ### `.env` e defaults relevantes - Configuração estrutural: `ROUTING_CONFIG_PATH`, `TOOLS_CONFIG_PATH`, `TOOL_POLICIES_PATH`, `MCP_PARAMETER_MAPPING_PATH`, `MCP_SERVERS_CONFIG_PATH`. - Roteamento: `ENABLE_LLM_ROUTER` e parâmetros `ROUTE_STICKINESS_*` quando habilitados. - Billing/fatura: URLs e timeouts de fatura/PDF definidos no `.env` e complementados por `config/tim_integration_defaults.yaml`. - Resiliência de integração: `TIM_GATEWAY_MODE`, `TIM_GATEWAY_DEFAULT_TIMEOUT`, `TIM_GATEWAY_RETRY_MAX_RETRIES`, `TIM_GATEWAY_RETRY_BACKOFF_FACTOR`, `TIM_USE_MOCK_GATEWAY`. # Regras transversais ## Confirmação transacional `tool_policies.yaml` é a fonte declarativa que diferencia leitura, conversa e transação. `cancelar_vas_avulso` e `contestar_cobranca` usam `require_confirmation: true`. O runtime coleta parâmetros, chama a pre-validation e só então pergunta ao usuário. Uma resposta inequívoca é processada deterministicamente; respostas inconclusivas podem usar o `transaction_confirmation.semantic_fallback` definido em `routing.yaml`. ## Coleta de parâmetros Os campos obrigatórios vêm da policy (`requires`) e do schema da tool. `mcp_parameter_mapping.yaml` informa o que pode vir automaticamente do contexto e o que deve ser extraído da mensagem. Se um campo obrigatório continuar faltando, a sessão entra em `COLLECTING_*_PARAMETERS`, e `state_policies` mantém a fala seguinte no agente proprietário. ## Pre-validation Pre-validation não deve produzir efeito externo. Ela resolve entidade, elegibilidade, valor e classe de negócio. Em VAS, `validar_vas_subject` pode mudar a tool efetiva. Em contestação, `validar_contestacao` protege contra item/valor não suportado pela fatura. ## Pause/Resume de workflow Um nó com `pause.enabled=true` devolve uma mensagem ao cliente e persiste a execução. A próxima fala é entregue ao mesmo workflow por `execution_id`, normalizada conforme `expected_input` e retomada em `resume_from`. `retomar_workflow` é infraestrutura; não representa uma intent de negócio. ## Multi-intent O MultiIntentPlanner deve separar objetivos independentes, por exemplo “cancelar X e mandar segunda via”. Ele não deve decompor `subject`, `valor` e `motivo` de uma única contestação em intents diferentes. Em pedidos com vários VAS, os itens canônicos permanecem em `items[]` para evitar reinterpretação da string agregada. ## State stickiness Estados `WAITING_*_CONFIRMATION` e `COLLECTING_*_PARAMETERS` têm precedência sobre uma nova classificação de uma fala curta. Mudança explícita de assunto pode liberar handoff conforme as políticas de continuidade/intent shift. # Guia de diagnóstico de uma decisão inesperada Use esta sequência: 1. **Qual era o estado antes da fala?** Veja `transaction_status`, workflow pausado e `state_policies`. 2. **Qual intent foi escolhida e por qual método?** `state`, `keyword`, LLM ou fallback. 3. **Qual keyword venceu?** Lembre que menor `priority` vence e keywords mais específicas desempatarão. 4. **Quais tools a intent autorizou?** 5. **Qual tool o runtime realmente selecionou?** Compare `selection_keywords`, transação ativa e pending topics. 6. **Quais parâmetros foram coletados?** Diferencie `requested_arguments` de `resolved_arguments`. 7. **O pre-validator alterou algo?** Verifique `effective_tool_name`, `action_changed`, `domain_policy.class` e entidade canônica. 8. **Havia múltiplos itens?** Se sim, `items[]` deve ser a fonte de verdade; não re-resolver a string unida. 9. **Qual workflow começou?** Confira versão ativa e `execution_id`. 10. **Em qual nó/aresta terminou ou pausou?** Avalie condições por prioridade. 11. **Qual integração falhou?** Diferencie erro de classificação, elegibilidade e erro técnico (`service_not_found`, timeout, backend etc.). 12. **A resposta final preservou resultados parciais?** Um item bem-sucedido não autoriza afirmar sucesso para os demais. # Exemplo completo — “quero cancelar Tamboro e Paramount+” ```text Mensagem ↓ routing.yaml → contas_vas_cancel ↓ tools candidatas: consultar_vas + cancelar_vas_avulso ↓ extração de subject / resolução de múltiplas entidades ↓ validar_vas_subject / invoice_resolver ├─ Tamboro Mensal → avulso → cancelar_vas_avulso └─ Paramount+ → estratégico/bundle conforme evidência → tratar_vas_estrategico ↓ cada item preserva sua classe e resultado ├─ avulso → confirmação → cancelamento_vas_avulso.v1 └─ estratégico → vas_estrategico.v3 → explicação/orientação/pause-resume ``` A lição arquitetural é que **o verbo do usuário escolhe a intenção, mas a evidência do domínio escolhe a operação válida para a entidade**. É exatamente por isso que dois serviços citados na mesma frase podem terminar em regras diferentes. # Arquivos-fonte mais importantes - `config/routing.yaml` - `config/tools.yaml` - `config/tool_policies.yaml` - `config/mcp_parameter_mapping.yaml` - `config/agents.yaml` - `config/tim_integration_defaults.yaml` - `.env.example` - `contas_mcp/servers/contas_mcp_server/main.py` - `app/domain/contas/invoice_resolver.py` - `app/domain/contas/parsers/bill_processor.py` - `app/domain/contas/workflow_actions.py` - `app/domain/contas/service.py` - `app/domain/contas/client.py` - `agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py` - `agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` - `agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py`