3061 lines
146 KiB
Markdown
3061 lines
146 KiB
Markdown
# Manual Detalhado de Regras do Agente de Contas
|
|
|
|
> Versão aprofundada gerada a partir do pacote `agent_contas_oci_template (9)(1).zip`. O objetivo é 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<br>hã trrr blá qx<br>[fala sem conteúdo inteligível]<br>está cobrando um<br>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<br>qual e meu plano<br>qual é o meu plano<br>qual e o meu plano<br>meu plano contratado<br>plano contratado<br>nome do meu plano<br>que plano eu tenho<br>quais são meus planos<br>quais sao meus planos | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. |
|
|
| `examples` | Qual é meu plano?<br>Qual é o meu plano contratado?<br>Que plano eu tenho?<br>Qual o nome do meu plano?<br>Quais são os planos da minha linha?<br>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<br>ver minha fatura<br>consultar fatura<br>consultar minha fatura<br>mostrar minha fatura<br>me mostra minha fatura<br>segunda via<br>segunda via da fatura<br>obter fatura<br>baixar fatura<br>minhas faturas<br>fatura atual<br>última fatura<br>ultima fatura<br>fatura em aberto<br>conta em aberto | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. |
|
|
| `examples` | Quero minha fatura<br>Quero ver a minha conta<br>Pode consultar minha fatura atual?<br>Preciso da segunda via da fatura<br>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<br>explique minha fatura<br>entender minha fatura<br>entender minha conta<br>fatura veio alta<br>conta veio alta<br>veio mais cara<br>conta subiu<br>minha conta subiu<br>fatura subiu<br>minha fatura subiu<br>conta aumentou<br>minha conta aumentou<br>fatura aumentou<br>minha fatura aumentou<br>valor aumentou<br>diferença de valor<br>divergência na fatura<br>divergencia na fatura<br>não entendi a cobrança<br>nao entendi a cobranca<br>por que cobraram<br>por que veio esse valor<br>composição da fatura<br>composicao da fatura<br>detalhar cobrança<br>detalhar cobranca<br>não estou reconhecendo<br>nao estou reconhecendo<br>não reconheço esses itens<br>nao reconheco esses itens<br>itens que não reconheço<br>itens que nao reconheco<br>cobranças que não reconheço<br>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.<br>Tem cobranças que eu não reconheço e quero entender de onde vieram.<br>Minha fatura veio mais alta e quero entender o que mudou.<br>Quero entender minha conta<br>Por que esse valor mudou?<br>Explique essas cobranças da minha fatura<br>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<br>invoice_explanation<br>contestar_cobranca | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. |
|
|
| `keywords` | quero contestar<br>quero contestação<br>quero contestacao<br>contestar essa cobrança<br>contestar essa cobranca<br>abrir contestação<br>abrir contestacao<br>quero estorno<br>pedir estorno<br>quero ajuste na fatura<br>ajustar essa cobrança<br>ajustar essa cobranca<br>retirar essa cobrança<br>retirar essa cobranca<br>tirar essa cobrança<br>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'.<br>Após a explicação da fatura, o cliente identifica pelo nome ou valor qual cobrança continua não reconhecendo.<br>Quero contestar essa cobrança<br>Quero abrir uma contestação para esse valor<br>Quero um estorno dessa cobrança<br>Não reconheço essa cobrança e quero contestar<br>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<br>cancelar_vas_avulso | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. |
|
|
| `keywords` | quero cancelar<br>cancelar meu<br>cancela o<br>cancele o<br>cancelar serviço<br>cancelar servico<br>cancelar assinatura<br>retirar serviço<br>retirar servico<br>desativar serviço<br>desativar servico<br>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<br>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<br>tratar_vas_estrategico | Conjunto de tools que o roteamento entrega como capabilities esperadas para esta intent. |
|
|
| `keywords` | apple music<br>deezer<br>disney<br>disney+<br>netflix<br>youtube premium<br>globoplay<br>amazon prime<br>prime video<br>serviço estratégico<br>servico estrategico<br>benefício incluso<br>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<br>Quero tratar esse serviço estratégico<br>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<br>serviço adicional<br>servico adicional<br>aplicativo<br>assinatura<br>serviços ativos<br>servicos ativos<br>quais são meus serviços<br>quais sao meus servicos<br>meus serviços<br>meus servicos<br>benefício<br>beneficio<br>incluso no plano<br>pacote digital<br>parceiro | Gatilhos determinísticos. O router tenta match exato, sequência ordenada de tokens e tokens informativos. |
|
|
| `examples` | O que é esse serviço?<br>Quais VAS estão ativos?<br>Esse aplicativo está incluso?<br>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<br>historico de vas<br>histórico de serviços<br>historico de servicos<br>serviços anteriores<br>servicos anteriores<br>já tive esse serviço<br>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<br>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 é<br>o que significa<br>como funciona<br>como é a tarifação<br>como funciona a tarifação<br>tarifação do plano<br>tarifacao do plano<br>qual a regra<br>quais são as regras<br>quais sao as regras<br>como faço<br>onde encontro<br>informação sobre<br>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?<br>Como funciona a tarifação do Infinity Pós?<br>Quais são as regras do Infinity Pós?<br>O que significa cobrança proporcional?<br>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<br>status da solicitação<br>status da solicitacao<br>andamento<br>acompanhamento<br>acompanhar solicitação<br>acompanhar solicitacao<br>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?<br>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<br>pro rata<br>pro-rata<br>cobrança proporcional<br>cobranca proporcional<br>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?<br>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<br>termino de desconto<br>desconto acabou<br>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<br>divergência de valor<br>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`
|