mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 18:23:46 +00:00
New features: Route Stickness, Handoff, Clarification, Read-Only/Transactional, Long Term Memory
This commit is contained in:
244
Documentacao/README_ROUTE_STICKINESS_SEMANTICA.md
Normal file
244
Documentacao/README_ROUTE_STICKINESS_SEMANTICA.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# Route Stickiness Semântica e Controle Global de Sessão no Agent Framework OCI
|
||||
|
||||
## Objetivo
|
||||
|
||||
A route stickiness semântica evita executar novamente o Enterprise Router quando uma nova mensagem continua claramente sob responsabilidade do agente ativo. A implementação usa um perfil LLM leve e não contém regexes, listas de frases, palavras específicas de idioma ou regras conversacionais por domínio.
|
||||
|
||||
A funcionalidade é opcional e preserva integralmente o comportamento anterior quando desabilitada, quando não existe agente ativo, quando a confiança é baixa ou quando ocorre erro na inferência.
|
||||
|
||||
## Decisão arquitetural
|
||||
|
||||
O classificador possui uma responsabilidade transversal e restrita:
|
||||
|
||||
- `CONTINUE`: a mensagem continua com o agente ativo;
|
||||
- `ROUTE`: a mensagem deve seguir para o Enterprise Router normal;
|
||||
- `HUMAN_HANDOFF`: o usuário solicitou atendimento humano;
|
||||
- `END_SESSION`: o usuário solicitou ou confirmou o encerramento do atendimento.
|
||||
|
||||
Ele não responde ao usuário, não escolhe outro agente, não executa ferramentas e não interpreta regras de negócio. As duas ações globais são encaminhadas para nós próprios do grafo, evitando que cada agente implemente prompts ou regras de sessão.
|
||||
|
||||
Fluxo:
|
||||
|
||||
```text
|
||||
Todos os turnos com a funcionalidade habilitada
|
||||
-> classificador semântico leve
|
||||
CONTINUE + agente ativo -> agente ativo
|
||||
ROUTE/baixa confiança/erro -> Enterprise Router
|
||||
HUMAN_HANDOFF -> nó global human_handoff
|
||||
END_SESSION -> nó global end_session
|
||||
|
||||
No primeiro turno, CONTINUE é normalizado para ROUTE porque ainda não existe agente ativo. Handoff e encerramento podem ser reconhecidos mesmo no primeiro turno.
|
||||
```
|
||||
|
||||
## Por que não há regras determinísticas
|
||||
|
||||
A interpretação de linguagem natural por regex exige manutenção contínua para novas construções, idiomas e domínios. Além disso, transfere aos times dos agentes a responsabilidade de manter flags e padrões de continuidade.
|
||||
|
||||
Esta implementação mantém no código apenas decisões técnicas inevitáveis:
|
||||
|
||||
- funcionalidade habilitada ou desabilitada;
|
||||
- validação de que `CONTINUE` exige agente ativo;
|
||||
- threshold de confiança;
|
||||
- fallback em timeout, erro ou JSON inválido.
|
||||
|
||||
Não existem `DEFAULT_FOLLOWUP_PATTERNS`, regras de repetição, listas de pronomes ou keywords de continuidade.
|
||||
|
||||
## Configuração
|
||||
|
||||
### `.env`
|
||||
|
||||
```dotenv
|
||||
ENABLE_ROUTE_STICKINESS=true
|
||||
ROUTE_STICKINESS_LLM_PROFILE=route_continuity
|
||||
ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
|
||||
ROUTE_STICKINESS_HISTORY_TURNS=2
|
||||
ROUTE_STICKINESS_MAX_TOKENS=80
|
||||
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
||||
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
||||
```
|
||||
|
||||
- `ENABLE_ROUTE_STICKINESS`: ativa a capacidade.
|
||||
- `ROUTE_STICKINESS_LLM_PROFILE`: perfil existente em `llm_profiles.yaml`.
|
||||
- `ROUTE_STICKINESS_CONFIDENCE_THRESHOLD`: confiança mínima para bypass.
|
||||
- `ROUTE_STICKINESS_HISTORY_TURNS`: quantidade de turnos recentes enviados ao classificador.
|
||||
- `ROUTE_STICKINESS_MAX_TOKENS`: limite de saída do classificador.
|
||||
- `HUMAN_HANDOFF_MESSAGE`: mensagem devolvida pelo nó global de transferência humana.
|
||||
- `END_SESSION_MESSAGE`: mensagem devolvida pelo nó global de encerramento.
|
||||
|
||||
### Perfil leve
|
||||
|
||||
```yaml
|
||||
profiles:
|
||||
route_continuity:
|
||||
provider: oci_openai
|
||||
model: openai.gpt-4.1-mini
|
||||
temperature: 0
|
||||
max_tokens: 80
|
||||
timeout_seconds: 5
|
||||
```
|
||||
|
||||
O modelo acima é apenas um exemplo. Deve ser substituído pelo menor modelo aprovado e disponível no ambiente OCI. O framework reutiliza o mecanismo já existente de `LLM_PROFILES_PATH`; não há uma segunda configuração de provider/model específica para a funcionalidade.
|
||||
|
||||
## Contexto enviado ao modelo
|
||||
|
||||
O classificador recebe somente:
|
||||
|
||||
- agente ativo;
|
||||
- descrições das capacidades dos agentes derivadas das intents já existentes;
|
||||
- intent e domínio anteriores;
|
||||
- histórico recente limitado;
|
||||
- mensagem atual.
|
||||
|
||||
Não são enviados RAG completo, resultados MCP integrais, prompt do agente ou regras de negócio.
|
||||
|
||||
## Exemplos
|
||||
|
||||
### Continuidade
|
||||
|
||||
```text
|
||||
Usuário: Qual é o meu plano?
|
||||
Agente: Seu plano é Controle 50GB.
|
||||
Usuário: O que está incluso?
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "continuity",
|
||||
"route": "product_agent",
|
||||
"route_bypassed": true
|
||||
}
|
||||
```
|
||||
|
||||
### Mudança de domínio
|
||||
|
||||
```text
|
||||
Usuário: Qual é o meu plano?
|
||||
Agente: Seu plano é Controle 50GB.
|
||||
Usuário: Agora quero contestar uma cobrança.
|
||||
```
|
||||
|
||||
O classificador retorna `ROUTE` e o Enterprise Router seleciona o agente apropriado.
|
||||
|
||||
### Baixa confiança ou falha
|
||||
|
||||
Qualquer resultado abaixo do threshold, timeout ou JSON inválido executa o Enterprise Router. A funcionalidade é fail-safe e nunca força continuidade em caso de dúvida.
|
||||
|
||||
## Telemetria
|
||||
|
||||
Evento `router.continuity`:
|
||||
|
||||
```json
|
||||
{
|
||||
"decision": "CONTINUE",
|
||||
"confidence": 0.97,
|
||||
"active_agent": "product_agent",
|
||||
"route_bypassed": true,
|
||||
"profile_name": "route_continuity"
|
||||
}
|
||||
```
|
||||
|
||||
Quando ocorre bypass, `route_decision.method` é `continuity` e o estado final contém:
|
||||
|
||||
- `active_agent`;
|
||||
- `route_bypassed`;
|
||||
- `continuity_signal`.
|
||||
|
||||
## Testes
|
||||
|
||||
```bash
|
||||
pytest -q tests/unit/test_semantic_route_stickiness.py
|
||||
```
|
||||
|
||||
Os testes validam:
|
||||
|
||||
- continuidade com bypass;
|
||||
- mudança de assunto com fallback para o router;
|
||||
- baixa confiança;
|
||||
- saída inválida;
|
||||
- primeiro turno sem chamada ao classificador.
|
||||
|
||||
## Benchmark recomendado
|
||||
|
||||
Executar a mesma conversação com a funcionalidade desabilitada e habilitada, registrando por turno:
|
||||
|
||||
- `route_bypassed`;
|
||||
- `route_decision.method`;
|
||||
- latência do `llm.route_continuity`;
|
||||
- chamadas ao `llm.router`;
|
||||
- tokens por perfil;
|
||||
- latência total p50, p95 e p99.
|
||||
|
||||
A redução de tempo total somente deve ser atribuída à stickiness quando houver `route_bypassed=true` e ausência da geração `llm.router` no mesmo turno.
|
||||
|
||||
|
||||
## Contratos globais
|
||||
|
||||
### Human handoff
|
||||
|
||||
Quando a decisão for `HUMAN_HANDOFF`, o router retorna:
|
||||
|
||||
```json
|
||||
{
|
||||
"route": "human_handoff",
|
||||
"intent": "human_handoff",
|
||||
"method": "continuity",
|
||||
"handoff": true,
|
||||
"metadata": {
|
||||
"session_control": "HUMAN_HANDOFF",
|
||||
"route_bypassed": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
O nó `human_handoff` produz os campos:
|
||||
|
||||
- `session_control=HUMAN_HANDOFF`;
|
||||
- `human_handoff_requested=true`;
|
||||
- `session_ended=false`;
|
||||
- `next_state=HUMAN_HANDOFF_REQUESTED`.
|
||||
|
||||
O evento `session.human_handoff.requested` é emitido para que o Channel Gateway ou a integração do cliente encaminhe a conversa à plataforma humana. O framework não presume uma fila, fornecedor ou protocolo específico.
|
||||
|
||||
### Encerramento
|
||||
|
||||
Quando a decisão for `END_SESSION`, o router retorna:
|
||||
|
||||
```json
|
||||
{
|
||||
"route": "end_session",
|
||||
"intent": "end_session",
|
||||
"method": "continuity",
|
||||
"metadata": {
|
||||
"session_control": "END_SESSION",
|
||||
"route_bypassed": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
O nó `end_session` produz:
|
||||
|
||||
- `session_control=END_SESSION`;
|
||||
- `session_ended=true`;
|
||||
- `human_handoff_requested=false`;
|
||||
- `next_state=SESSION_ENDED`.
|
||||
|
||||
O evento `session.end.requested` é emitido antes da persistência. O backend continua responsável por aplicar a política concreta de expiração, fechamento ou limpeza da sessão em cada canal.
|
||||
|
||||
## Exemplos
|
||||
|
||||
| Mensagem | Contexto | Decisão esperada | Destino |
|
||||
|---|---|---|---|
|
||||
| `o que está incluso?` | `product_agent` ativo | `CONTINUE` | `product_agent` |
|
||||
| `agora quero contestar uma cobrança` | `product_agent` ativo | `ROUTE` | Enterprise Router |
|
||||
| `quero falar com uma pessoa` | com ou sem agente ativo | `HUMAN_HANDOFF` | nó `human_handoff` |
|
||||
| `obrigado, pode encerrar` | com ou sem agente ativo | `END_SESSION` | nó `end_session` |
|
||||
|
||||
## Segurança e fallback
|
||||
|
||||
- Somente decisões acima do threshold são aceitas.
|
||||
- `CONTINUE` sem agente ativo vira `ROUTE`.
|
||||
- JSON inválido, timeout ou erro usa o Enterprise Router.
|
||||
- Handoff e encerramento não executam agentes de domínio nem ferramentas MCP.
|
||||
- O classificador não encerra fisicamente conexões nem seleciona filas humanas; ele emite um contrato global para integração.
|
||||
Reference in New Issue
Block a user