New features: Route Stickness, Handoff, Clarification, Read-Only/Transactional, Long Term Memory

This commit is contained in:
2026-08-03 08:57:02 -03:00
parent e684b0ecc3
commit 8e414e4e26
604 changed files with 38978 additions and 402 deletions

View File

@@ -0,0 +1,520 @@
### Long-Term Memory Implementation Guide
### Concept
Long-Term Memory (LTM) is the `agent_framework` capability that stores and retrieves durable facts beyond the lifetime of a conversation session.
Unlike message history, which is normally associated with a `session_id`, Long-Term Memory is associated with the business identity of the user or customer. In the current implementation, this identity consists of:
```text
tenant_id
agent_id
customer_key
```
This allows an agent to retrieve preferences, identity information, projects and constraints even when a new session is created.
### Purpose
Long-Term Memory is used to:
- maintain continuity across sessions;
- personalize responses;
- prevent users from repeating previously supplied information;
- reduce the need to send the full conversation history to the model;
- store preferences, current projects, preferred names and constraints;
- isolate memory across tenants, agents and customers.
Example:
```text
Session A:
"Call me Cris. My preferred language is Python."
Session B, with another session_id and the same customer_key:
"What do you remember about me?"
Expected response:
"Your preferred name is Cris and your preferred language is Python."
```
### Memory type differences
#### Conversation Memory
Stores messages from the current conversation and is normally associated with the `session_id`.
#### Summary Memory
Stores a summary of the conversation to reduce the context size sent to the model.
#### Long-Term Memory
Stores durable facts across sessions and is associated with the business identity, primarily the `customer_key`.
### Components
#### LongTermMemoryManager
Coordinates:
- memory loading;
- identity-based retrieval;
- context rendering;
- durable fact extraction;
- fact persistence;
- deduplication and updates.
#### LongTermMemoryStore
Persistence interface used by the manager.
#### SQLiteLongTermMemoryStore
Reference implementation based on SQLite.
It is suitable for:
- local development;
- testing;
- demonstrations;
- low-scale environments.
#### InMemoryLongTermMemoryStore
In-memory implementation used for quick tests.
Its content is lost when the backend process stops.
#### LongTermMemoryExtractor
Identifies durable facts in messages.
Examples:
```text
preferred_name = Cris
preferred_language = Python
current_project = Atlas
```
#### LongTermMemoryItem
Data model representing a persisted item, including identity, key, value, category, confidence and metadata.
#### AgentRuntime
Loads memory before agent execution and injects the rendered context into the prompt.
#### persist_long_term_memory node
LangGraph node responsible for persisting facts after the final response has been generated and validated.
### File structure
```text
libs/
└── agent_framework/
└── src/
└── agent_framework/
└── memory/
├── __init__.py
├── long_term_extractor.py
├── long_term_memory.py
├── long_term_models.py
└── long_term_store.py
```
### Execution flow
```text
User message
AgentRuntime.prepare_memory_context()
├── Conversation Memory
├── Summary Memory
└── Long-Term Memory
long_term_memory_context
Agent prompt
Agent
Guardrails / Judges / Supervisor
persist_long_term_memory
LongTermMemoryExtractor
LongTermMemoryStore
```
### Framework configuration
### New modules
Copy:
```text
libs/agent_framework/src/agent_framework/memory/long_term_extractor.py
libs/agent_framework/src/agent_framework/memory/long_term_memory.py
libs/agent_framework/src/agent_framework/memory/long_term_models.py
libs/agent_framework/src/agent_framework/memory/long_term_store.py
```
### Update memory/__init__.py
Export the Long-Term Memory components:
```python
from agent_framework.memory.long_term_memory import (
LongTermMemoryManager,
create_long_term_memory_manager,
)
from agent_framework.memory.long_term_models import LongTermMemoryItem
from agent_framework.memory.long_term_store import (
InMemoryLongTermMemoryStore,
LongTermMemoryStore,
SQLiteLongTermMemoryStore,
create_long_term_memory_store,
)
```
### Update settings.py
Add:
```python
ENABLE_LONG_TERM_MEMORY: bool = False
LONG_TERM_MEMORY_PROVIDER: str = "sqlite"
LONG_TERM_MEMORY_SQLITE_PATH: str = "./data/agent_framework.db"
LONG_TERM_MEMORY_TABLE: str = "agentfw_long_term_memory"
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS: int = 20
LONG_TERM_MEMORY_MIN_CONFIDENCE: float = 0.70
LONG_TERM_MEMORY_AUTO_EXTRACT: bool = True
LONG_TERM_MEMORY_INJECT_CONTEXT: bool = True
```
### AgentRuntime integration
The runtime must:
1. verify that the feature is enabled;
2. create the manager when needed;
3. retrieve facts using the identity;
4. populate the workflow state;
5. inject the rendered context into the prompt.
State fields:
```python
long_term_memories: list[dict]
long_term_memory_context: str
long_term_memory_write_result: dict
```
### AgentWorkflow initialization
Create the manager in `AgentWorkflow`:
```python
self.long_term_memory_manager = create_long_term_memory_manager(
settings,
telemetry=telemetry,
)
```
### Correct agent initialization
Do not pass `long_term_memory_manager` through `agent_kwargs` when the constructors of `BillingAgent`, `ProductAgent`, `OrdersAgent` and `SupportAgent` do not declare that parameter.
This initialization causes an error:
```python
agent_kwargs = {
"telemetry": telemetry,
"settings": settings,
"memory": memory,
"summary_memory": summary_memory,
"long_term_memory_manager": self.long_term_memory_manager,
}
self.billing = BillingAgent(llm, **agent_kwargs)
```
Resulting error:
```text
TypeError: BillingAgent.__init__() got an unexpected keyword argument
'long_term_memory_manager'
```
The recommended approach is to create agents using their existing signatures and inject the manager as an attribute after initialization:
```python
agent_kwargs = {
"telemetry": telemetry,
"tool_router": getattr(self, "tool_router", None),
"rag_service": self.rag_service,
"cache": self.cache,
"settings": settings,
"observer": self.observer,
"memory": memory,
"summary_memory": summary_memory,
}
self.billing = BillingAgent(llm, **agent_kwargs)
self.product = ProductAgent(llm, **agent_kwargs)
self.orders = OrdersAgent(llm, **agent_kwargs)
self.support = SupportAgent(llm, **agent_kwargs)
for agent in (
self.billing,
self.product,
self.orders,
self.support,
):
agent.long_term_memory_manager = self.long_term_memory_manager
```
This approach avoids changing every agent constructor and keeps the feature encapsulated in the framework.
### LangGraph configuration
Register the node:
```python
builder.add_node(
"persist_long_term_memory",
self._node(
"persist_long_term_memory",
self.persist_long_term_memory,
),
)
```
Update the edges:
```python
builder.add_edge(
"supervisor_review",
"persist_long_term_memory",
)
builder.add_edge(
"persist_long_term_memory",
"persist",
)
```
Implement:
```python
async def persist_long_term_memory(
self,
state: AgentState,
) -> dict[str, object]:
result = await self.long_term_memory_manager.persist_turn(state)
return {
"long_term_memory_write_result": result,
}
```
Final flow:
```text
supervisor_review
persist_long_term_memory
persist
```
### Environment variables
```env
ENABLE_LONG_TERM_MEMORY=true
LONG_TERM_MEMORY_PROVIDER=sqlite
LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db
LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
LONG_TERM_MEMORY_AUTO_EXTRACT=true
LONG_TERM_MEMORY_INJECT_CONTEXT=true
```
### SQLite database path
A relative path is resolved from the directory in which the backend is started.
To prevent different databases from being created accidentally, prefer an absolute path in development environments:
```env
LONG_TERM_MEMORY_SQLITE_PATH=/mnt/c/Asus_Projects/agent_platform_oci_long_term_memory/data/agent_framework.db
```
Create the directory before starting:
```bash
mkdir -p data
```
### Testing
### Test 1 — Persistence
Send:
```json
{
"session_id": "default:telecom_contas:memory-session-a",
"customer_key": "11999999999",
"message": "Call me Cris. My preferred language is Python and my current project is Atlas."
}
```
### Test 2 — Retrieval in another session
Use another `session_id` while keeping the same `customer_key`:
```json
{
"session_id": "default:telecom_contas:memory-session-b",
"customer_key": "11999999999",
"message": "What do you remember about me, my preferences and my project?"
}
```
Expected result:
```text
Your preferred name is Cris.
Your preferred language is Python.
Your current project is Atlas.
```
### Test 3 — Isolation
Use another customer:
```json
{
"session_id": "default:telecom_contas:memory-session-c",
"customer_key": "another-customer",
"message": "What is my preferred name and current project?"
}
```
The data associated with `11999999999` must not be returned.
### Test 4 — Frontend reset
Restart or reset the frontend and verify that it still sends the same `customer_key`.
Memory must survive a `session_id` change. Resetting the frontend does not delete the SQLite database.
### Test 5 — Backend restart
Restart Uvicorn and repeat the query.
With:
```env
LONG_TERM_MEMORY_PROVIDER=sqlite
```
memory must remain available.
With:
```env
LONG_TERM_MEMORY_PROVIDER=memory
```
memory is lost when the process stops.
### Direct SQLite verification
Find the database:
```bash
find . -name "agent_framework.db" -type f
```
Open it:
```bash
sqlite3 ./data/agent_framework.db
```
Query:
```sql
SELECT
tenant_id,
agent_id,
customer_key,
memory_type,
memory_key,
memory_value,
confidence,
created_at,
updated_at
FROM agentfw_long_term_memory
ORDER BY updated_at DESC;
```
### Success criteria
The implementation is working when:
- memory is retrieved with another `session_id`;
- the same `customer_key` retrieves previous facts;
- another `customer_key` cannot access those facts;
- restarting the frontend does not erase memory;
- restarting the backend does not erase memory when using SQLite;
- the `persist_long_term_memory` node runs;
- the prompt receives `long_term_memory_context`.
### Best practices
- Persist only durable facts.
- Do not store the complete conversation as Long-Term Memory.
- Isolate data by `tenant_id`, `agent_id` and `customer_key`.
- Do not use `session_id` as the permanent user identity.
- Persist only after final validations.
- Avoid persisting temporary tool results.
- Record telemetry for reads, writes, updates and failures.
- Define retention and deletion policies.
- Use an absolute SQLite path in environments with multiple working directories.
- Move to an enterprise database for production and high-availability environments.
### Reference implementation limitations
The current implementation uses rule-based extraction and SQLite as the reference provider.
Recommended future enhancements:
- LLM-based fact extraction;
- vector-based semantic memory;
- episodic memory;
- expiration and versioning;
- semantic deduplication;
- consent policies;
- query and deletion APIs;
- Oracle Autonomous Database provider;
- encryption and sensitive-data classification.

View File

@@ -0,0 +1,272 @@
# Agent Platform OCI — Manual Oficial de Agent Gateway e MCP Gateway
## Objetivo
Este documento consolida:
- Arquitetura oficial
- Inventário dos componentes
- Procedimento completo de execução local
- MCP Gateway
- Agent Gateway
- Backend Runtime
- Frontend
- Testes E2E
- Troubleshooting
- Decisões arquiteturais
---
# Arquitetura Oficial
Frontend (5173)
Agent Gateway (9000)
Agent Template Backend / Runtime (8000)
MCP Gateway (8300)
Telecom MCP Server (8100)
Retail MCP Server (8200)
---
# Portas Oficiais
| Componente | Porta |
|------------|--------|
| Frontend | 5173 |
| Agent Gateway | 9000 |
| Backend Runtime | 8000 |
| MCP Gateway | 8300 |
| Telecom MCP Server | 8100 |
| Retail MCP Server | 8200 |
---
# Variáveis Oficiais
## Agent Template Backend
ENABLE_MCP_TOOLS=true
MCP_GATEWAY_ENABLED=true
MCP_GATEWAY_URL=http://localhost:8300
MCP_GATEWAY_TIMEOUT_SECONDS=60
MCP_GATEWAY_AGENT_ID=telecom_contas
MCP_GATEWAY_TENANT_ID=default
## Agent Gateway
DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
## MCP Gateway
MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
---
# Ordem de Inicialização
1. Telecom MCP Server
2. Retail MCP Server
3. MCP Gateway
4. Agent Template Backend
5. Agent Gateway
6. Frontend
---
# Terminal 1 — Telecom MCP Server
cd mcp/servers/telecom_mcp_server
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m uvicorn main:app --host 0.0.0.0 --port 8100 --reload
Validação:
curl http://localhost:8100/health
---
# Terminal 2 — Retail MCP Server
cd mcp/servers/retail_mcp_server
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m uvicorn main:app --host 0.0.0.0 --port 8200 --reload
Validação:
curl http://localhost:8200/health
---
# Terminal 3 — MCP Gateway
cd apps/mcp_gateway
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
python -m uvicorn app.main:app --host 0.0.0.0 --port 8300 --reload
Validações:
curl http://localhost:8300/health
curl http://localhost:8300/ready
curl http://localhost:8300/v1/tools
Teste:
curl -X POST http://localhost:8300/v1/tools/consultar_fatura/invoke
---
# Terminal 4 — Agent Template Backend
cd templates/agent_template_backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
Validações:
curl http://localhost:8000/health
curl http://localhost:8000/agents
---
# Terminal 5 — Agent Gateway
cd apps/agent_gateway
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
export AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
python -m uvicorn app.main:app --host 0.0.0.0 --port 9000 --reload
Validações:
curl http://localhost:9000/health
Teste:
curl -X POST http://localhost:9000/gateway/message
---
# Terminal 6 — Frontend
cd agent_frontend
npm install
npm run dev -- --host 0.0.0.0 --port 5173
Abrir:
http://localhost:5173
Backend URL:
http://localhost:9000
---
# Fluxo de Tools
Agent
MCPToolRouter
MCPGatewayClient
MCP Gateway
MCP Server
---
# Teste Integrado E2E
Frontend
Agent Gateway
Backend Runtime
MCP Gateway
Telecom MCP Server
Resultado esperado:
- Agent Gateway recebe requisição
- Runtime executa LangGraph
- MCP Gateway resolve tool
- MCP Server responde
- Usuário recebe resposta
---
# Troubleshooting
## Backend chamando MCP Server direto
Confirmar:
MCP_GATEWAY_ENABLED=true
MCP_GATEWAY_URL=http://localhost:8300
## Porta incorreta
A porta oficial do MCP Gateway é:
8300
## Agent Gateway não encontra Backend
Validar:
curl http://localhost:8000/health
## MCP Gateway não encontra MCP Server
Validar:
curl http://localhost:8100/health
curl http://localhost:8200/health
---
# Decisões Arquiteturais Oficiais
- Agent Gateway centraliza governança
- Runtime executa LangGraph
- Runtime executa LLM
- MCP Gateway centraliza tools
- MCP Servers executam tools
- Backend usa MCP Gateway
- gateway_runtime.env.example foi removido
- MCP_GATEWAY_* fica no .env do backend
- Porta oficial MCP Gateway = 8300

View File

@@ -0,0 +1,520 @@
### Manual de Implementação — Long-Term Memory
### Conceito
A Long-Term Memory (LTM) é a capacidade do `agent_framework` de armazenar e recuperar fatos duradouros além da duração de uma sessão de conversa.
Diferentemente do histórico de mensagens, que normalmente está associado a um `session_id`, a memória de longo prazo é associada à identidade de negócio do usuário ou cliente. Na implementação atual, essa identidade é composta por:
```text
tenant_id
agent_id
customer_key
```
Isso permite que um agente recupere preferências, informações de identidade, projetos e restrições mesmo quando uma nova sessão é criada.
### Para que serve
A Long-Term Memory serve para:
- manter continuidade entre sessões;
- personalizar respostas;
- evitar que o usuário repita informações já fornecidas;
- reduzir a necessidade de enviar todo o histórico ao modelo;
- armazenar preferências, projetos atuais, nomes preferidos e restrições;
- isolar a memória entre tenants, agentes e clientes.
Exemplo:
```text
Sessão A:
"Me chame de Cris. Minha linguagem preferida é Python."
Sessão B, com outro session_id e o mesmo customer_key:
"O que você lembra sobre mim?"
Resposta esperada:
"Seu nome preferido é Cris e sua linguagem preferida é Python."
```
### Diferença entre os tipos de memória
#### Conversation Memory
Mantém as mensagens da conversa atual e normalmente está associada ao `session_id`.
#### Summary Memory
Mantém um resumo da conversa para reduzir o tamanho do contexto enviado ao modelo.
#### Long-Term Memory
Mantém fatos duradouros entre sessões e é associada à identidade de negócio, principalmente ao `customer_key`.
### Componentes da funcionalidade
#### LongTermMemoryManager
Responsável por coordenar:
- carregamento das memórias;
- recuperação por identidade;
- renderização do contexto;
- extração de novos fatos;
- persistência dos fatos;
- deduplicação e atualização.
#### LongTermMemoryStore
Interface de persistência utilizada pelo manager.
#### SQLiteLongTermMemoryStore
Implementação de referência baseada em SQLite.
É apropriada para:
- desenvolvimento local;
- testes;
- demonstrações;
- ambientes de baixa escala.
#### InMemoryLongTermMemoryStore
Implementação em memória utilizada para testes rápidos.
O conteúdo é perdido quando o processo do backend é encerrado.
#### LongTermMemoryExtractor
Responsável por identificar fatos duradouros nas mensagens.
Exemplos de fatos:
```text
preferred_name = Cris
preferred_language = Python
current_project = Atlas
```
#### LongTermMemoryItem
Modelo que representa um item persistido, incluindo identidade, chave, valor, categoria, confiança e metadados.
#### AgentRuntime
Carrega a memória antes da execução do agente e injeta o contexto no prompt.
#### Nó persist_long_term_memory
Nó do LangGraph responsável por persistir os fatos após a geração e validação da resposta final.
### Estrutura dos arquivos
```text
libs/
└── agent_framework/
└── src/
└── agent_framework/
└── memory/
├── __init__.py
├── long_term_extractor.py
├── long_term_memory.py
├── long_term_models.py
└── long_term_store.py
```
### Fluxo de execução
```text
Mensagem do usuário
AgentRuntime.prepare_memory_context()
├── Conversation Memory
├── Summary Memory
└── Long-Term Memory
long_term_memory_context
Prompt do agente
Agente
Guardrails / Judges / Supervisor
persist_long_term_memory
LongTermMemoryExtractor
LongTermMemoryStore
```
### Configuração do framework
### Novos módulos
Copie os arquivos:
```text
libs/agent_framework/src/agent_framework/memory/long_term_extractor.py
libs/agent_framework/src/agent_framework/memory/long_term_memory.py
libs/agent_framework/src/agent_framework/memory/long_term_models.py
libs/agent_framework/src/agent_framework/memory/long_term_store.py
```
### Atualização de memory/__init__.py
Exporte os componentes da Long-Term Memory:
```python
from agent_framework.memory.long_term_memory import (
LongTermMemoryManager,
create_long_term_memory_manager,
)
from agent_framework.memory.long_term_models import LongTermMemoryItem
from agent_framework.memory.long_term_store import (
InMemoryLongTermMemoryStore,
LongTermMemoryStore,
SQLiteLongTermMemoryStore,
create_long_term_memory_store,
)
```
### Atualização de settings.py
Adicione as configurações:
```python
ENABLE_LONG_TERM_MEMORY: bool = False
LONG_TERM_MEMORY_PROVIDER: str = "sqlite"
LONG_TERM_MEMORY_SQLITE_PATH: str = "./data/agent_framework.db"
LONG_TERM_MEMORY_TABLE: str = "agentfw_long_term_memory"
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS: int = 20
LONG_TERM_MEMORY_MIN_CONFIDENCE: float = 0.70
LONG_TERM_MEMORY_AUTO_EXTRACT: bool = True
LONG_TERM_MEMORY_INJECT_CONTEXT: bool = True
```
### Integração com AgentRuntime
O runtime deve:
1. verificar se a funcionalidade está habilitada;
2. criar o manager quando necessário;
3. recuperar os fatos pela identidade;
4. preencher o estado;
5. injetar o contexto no prompt.
Campos adicionados ao estado:
```python
long_term_memories: list[dict]
long_term_memory_context: str
long_term_memory_write_result: dict
```
### Inicialização no AgentWorkflow
O manager deve ser criado no `AgentWorkflow`:
```python
self.long_term_memory_manager = create_long_term_memory_manager(
settings,
telemetry=telemetry,
)
```
### Inicialização correta dos agentes
O `long_term_memory_manager` não deve ser passado pelo `agent_kwargs` caso os construtores de `BillingAgent`, `ProductAgent`, `OrdersAgent` e `SupportAgent` não declarem esse parâmetro.
Esta inicialização causa erro:
```python
agent_kwargs = {
"telemetry": telemetry,
"settings": settings,
"memory": memory,
"summary_memory": summary_memory,
"long_term_memory_manager": self.long_term_memory_manager,
}
self.billing = BillingAgent(llm, **agent_kwargs)
```
Erro resultante:
```text
TypeError: BillingAgent.__init__() got an unexpected keyword argument
'long_term_memory_manager'
```
A forma recomendada é criar os agentes com a assinatura já existente e injetar o manager como atributo após a inicialização:
```python
agent_kwargs = {
"telemetry": telemetry,
"tool_router": getattr(self, "tool_router", None),
"rag_service": self.rag_service,
"cache": self.cache,
"settings": settings,
"observer": self.observer,
"memory": memory,
"summary_memory": summary_memory,
}
self.billing = BillingAgent(llm, **agent_kwargs)
self.product = ProductAgent(llm, **agent_kwargs)
self.orders = OrdersAgent(llm, **agent_kwargs)
self.support = SupportAgent(llm, **agent_kwargs)
for agent in (
self.billing,
self.product,
self.orders,
self.support,
):
agent.long_term_memory_manager = self.long_term_memory_manager
```
Essa abordagem evita alterar os construtores de todos os agentes e mantém a funcionalidade encapsulada no framework.
### Configuração do LangGraph
Registre o nó:
```python
builder.add_node(
"persist_long_term_memory",
self._node(
"persist_long_term_memory",
self.persist_long_term_memory,
),
)
```
Altere o fluxo:
```python
builder.add_edge(
"supervisor_review",
"persist_long_term_memory",
)
builder.add_edge(
"persist_long_term_memory",
"persist",
)
```
Implemente o método:
```python
async def persist_long_term_memory(
self,
state: AgentState,
) -> dict[str, object]:
result = await self.long_term_memory_manager.persist_turn(state)
return {
"long_term_memory_write_result": result,
}
```
Fluxo final:
```text
supervisor_review
persist_long_term_memory
persist
```
### Variáveis de ambiente
```env
ENABLE_LONG_TERM_MEMORY=true
LONG_TERM_MEMORY_PROVIDER=sqlite
LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db
LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
LONG_TERM_MEMORY_AUTO_EXTRACT=true
LONG_TERM_MEMORY_INJECT_CONTEXT=true
```
### Caminho do banco SQLite
O caminho relativo é resolvido a partir do diretório em que o backend é iniciado.
Para evitar que bancos diferentes sejam criados acidentalmente, prefira um caminho absoluto em ambientes de desenvolvimento:
```env
LONG_TERM_MEMORY_SQLITE_PATH=/mnt/c/Asus_Projects/agent_platform_oci_long_term_memory/data/agent_framework.db
```
Crie a pasta antes de iniciar:
```bash
mkdir -p data
```
### Como testar
### Teste 1 — Gravação
Envie:
```json
{
"session_id": "default:telecom_contas:memory-session-a",
"customer_key": "11999999999",
"message": "Me chame de Cris. Minha linguagem preferida é Python e meu projeto atual se chama Atlas."
}
```
### Teste 2 — Recuperação em outra sessão
Utilize outro `session_id`, mantendo o mesmo `customer_key`:
```json
{
"session_id": "default:telecom_contas:memory-session-b",
"customer_key": "11999999999",
"message": "O que você lembra sobre mim, minhas preferências e meu projeto?"
}
```
Resultado esperado:
```text
Seu nome preferido é Cris.
Sua linguagem preferida é Python.
Seu projeto atual se chama Atlas.
```
### Teste 3 — Isolamento
Utilize outro cliente:
```json
{
"session_id": "default:telecom_contas:memory-session-c",
"customer_key": "outro-cliente",
"message": "Qual é meu nome preferido e qual é meu projeto atual?"
}
```
Os dados de `11999999999` não devem aparecer.
### Teste 4 — Reinicialização do frontend
Reinicie ou resete o frontend e confirme que ele continua enviando o mesmo `customer_key`.
A memória deve sobreviver à troca do `session_id`. O reset do frontend não apaga o SQLite.
### Teste 5 — Reinicialização do backend
Reinicie o Uvicorn e repita a consulta.
Com:
```env
LONG_TERM_MEMORY_PROVIDER=sqlite
```
a memória deve continuar disponível.
Com:
```env
LONG_TERM_MEMORY_PROVIDER=memory
```
a memória será perdida quando o processo for encerrado.
### Verificação direta no SQLite
Localize o banco:
```bash
find . -name "agent_framework.db" -type f
```
Abra:
```bash
sqlite3 ./data/agent_framework.db
```
Consulte:
```sql
SELECT
tenant_id,
agent_id,
customer_key,
memory_type,
memory_key,
memory_value,
confidence,
created_at,
updated_at
FROM agentfw_long_term_memory
ORDER BY updated_at DESC;
```
### Critérios de sucesso
A implementação está funcionando quando:
- a memória é recuperada com outro `session_id`;
- o mesmo `customer_key` recupera os fatos anteriores;
- outro `customer_key` não acessa esses fatos;
- reiniciar o frontend não apaga a memória;
- reiniciar o backend não apaga a memória quando o provider é SQLite;
- o nó `persist_long_term_memory` é executado;
- o prompt recebe `long_term_memory_context`.
### Boas práticas
- Persistir somente fatos duradouros.
- Não armazenar a conversa completa como Long-Term Memory.
- Isolar dados por `tenant_id`, `agent_id` e `customer_key`.
- Não utilizar `session_id` como identidade permanente do usuário.
- Persistir somente depois das validações finais.
- Evitar armazenar resultados temporários de ferramentas.
- Registrar telemetria de leitura, escrita, atualização e falha.
- Definir políticas de retenção e exclusão.
- Usar caminho absoluto para SQLite em ambientes com múltiplos diretórios de execução.
- Migrar para um banco corporativo em ambientes de produção e alta disponibilidade.
### Limitações da implementação de referência
A implementação atual utiliza extração baseada em regras e SQLite como provider de referência.
Evoluções recomendadas:
- extração de fatos com LLM;
- memória semântica com vetores;
- memória episódica;
- expiração e versionamento;
- deduplicação semântica;
- política de consentimento;
- API de consulta e exclusão;
- provider Oracle Autonomous Database;
- criptografia e classificação de dados sensíveis.

View File

@@ -73,3 +73,7 @@ docker compose up --build
```
No compose, o backend usa `config/mcp_servers.docker.yaml` para apontar para `telecom-mcp` e `retail-mcp`.
## Operações read-only e transacionais
Use `config/tool_policies.yaml` no backend para classificar somente as operações que precisam de tratamento adicional. A validação é aplicada no roteador central antes do MCP Gateway/Server. O arquivo é opcional e templates antigos continuam usando as políticas já presentes em `tools.yaml`. A configuração completa e o roteiro de migração estão em [README_TOOL_POLICIES.md](README_TOOL_POLICIES.md).

View 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.

View File

@@ -0,0 +1,86 @@
# Semantic Route Stickiness and Global Session Control in Agent Framework OCI
## Purpose
This optional capability uses a lightweight LLM profile and no regex, phrase lists, or domain-specific language rules. It classifies each turn as:
- `CONTINUE`: keep the active agent;
- `ROUTE`: run the regular Enterprise Router;
- `HUMAN_HANDOFF`: request human assistance;
- `END_SESSION`: finish the automated session.
The classifier does not answer the user, execute tools, or implement domain rules. Human handoff and session ending are handled by global graph nodes.
## Flow
```text
Incoming turn
-> lightweight semantic classifier
CONTINUE + active agent -> active agent
ROUTE / low confidence / error -> Enterprise Router
HUMAN_HANDOFF -> human_handoff node
END_SESSION -> end_session node
```
`CONTINUE` is converted to `ROUTE` when there is no active agent. Global session actions can be detected on the first turn.
## Configuration
```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=I will transfer your request to a person.
END_SESSION_MESSAGE=The session has ended. Thank you for contacting us.
```
```yaml
profiles:
route_continuity:
provider: oci_openai
model: openai.gpt-4.1-mini
temperature: 0
max_tokens: 80
timeout_seconds: 5
```
Use the smallest approved model available in the target OCI environment.
## Human handoff contract
The router returns route `human_handoff`, intent `human_handoff`, `handoff=true`, and metadata `session_control=HUMAN_HANDOFF`. The graph node sets:
- `human_handoff_requested=true`;
- `session_ended=false`;
- `next_state=HUMAN_HANDOFF_REQUESTED`.
It emits `session.human_handoff.requested`. The customer integration remains responsible for choosing the human queue and protocol.
## End-session contract
The router returns route `end_session`, intent `end_session`, and metadata `session_control=END_SESSION`. The graph node sets:
- `session_ended=true`;
- `human_handoff_requested=false`;
- `next_state=SESSION_ENDED`.
It emits `session.end.requested`. Channel-specific session expiration or connection closing remains an integration responsibility.
## Safety behavior
- Only decisions above the configured confidence threshold are accepted.
- Invalid JSON, timeout, low confidence, or errors fall back to the Enterprise Router.
- Human handoff and session ending do not execute domain agents or MCP tools.
- The classifier never selects a human queue and never physically closes a channel connection.
## Tests
Run:
```bash
PYTHONPATH=libs/agent_framework/src pytest -q tests/unit/test_semantic_route_stickiness.py
```
The suite covers CONTINUE, ROUTE, low confidence, invalid output, HUMAN_HANDOFF, END_SESSION, first-turn global actions, and CONTINUE without an active agent.

View File

@@ -0,0 +1,90 @@
# Políticas mínimas para tools MCP read-only e transacionais
## Objetivo
O framework diferencia operações de consulta (`read_only`) e operações que alteram estado (`transactional`) imediatamente antes da chamada MCP. Essa classificação não substitui autorização, idempotência ou regras de negócio do servidor MCP; ela acrescenta somente a proteção conversacional mínima, especialmente confirmação explícita.
## Onde configurar
A parametrização pertence ao backend da aplicação:
```text
templates/agent_template_backend/config/tool_policies.yaml
```
A biblioteca compartilhada contém apenas o loader e a validação. O caminho é opcional:
```dotenv
TOOL_POLICIES_PATH=./config/tool_policies.yaml
```
## Exemplo
```yaml
version: 1
defaults:
operation_type: read_only
require_confirmation: false
tool_policies:
consultar_plano:
operation_type: read_only
alterar_plano:
operation_type: transactional
require_confirmation: true
requires: [new_plan_id]
```
Para executar `alterar_plano`, os argumentos precisam conter `new_plan_id` e um booleano literal de confirmação:
```json
{"new_plan_id": "CONTROLE_100", "confirmed": true}
```
Também é aceito `"confirmation": true`. Strings como `"true"` não são aceitas como confirmação.
## Compatibilidade
- Se `tool_policies.yaml` não existir, o framework continua usando `tool_type`, `requires`, `confirmation_required` e `execution_policy` de `tools.yaml`.
- Tools antigas sem política continuam executando como antes.
- Uma política explícita no arquivo novo prevalece para `operation_type` e confirmação daquela tool.
- O catálogo `tools.yaml` continua sendo a fonte de endpoint, schema, habilitação e cache.
- O novo arquivo não deve ser colocado em `libs/agent_framework`, pois as decisões variam por aplicação e domínio.
## Fluxo de execução
```text
agente -> MCPToolRouter -> validação da política -> mapeamento de parâmetros -> MCP Gateway/Server
```
Uma chamada bloqueada retorna `ok=false`, `metadata.blocked_by_policy=true`, o tipo da operação e a origem da política. O servidor MCP permanece a autoridade final para autenticação, autorização, validação, idempotência e transação de negócio.
## Migração recomendada
1. Atualize a biblioteca sem criar o arquivo: o comportamento permanece legado.
2. Crie `config/tool_policies.yaml` no backend.
3. Cadastre primeiro apenas operações transacionais que exigem confirmação.
4. Teste chamadas sem confirmação, com confirmação booleana e com campos obrigatórios ausentes.
5. Remova gradualmente duplicações de confirmação de `tools.yaml` quando todos os templates consumidores já usarem a nova configuração.
## Runtime transacional mínimo (correção de amarração)
A lista `mcp_tools` do roteamento é uma **allowlist**, não uma ordem para executar todas as ferramentas. O runtime agora:
1. executa automaticamente somente ferramentas `read_only`;
2. seleciona no máximo uma ação transacional compatível com o pedido do usuário;
3. quando `require_confirmation: true`, persiste `pending_tool_call` e `transaction_status: AWAITING_CONFIRMATION`;
4. no turno de confirmação, reutiliza a mesma chamada e executa com `confirmed: true`;
5. publica no estado `available_mcp_tools`, `selected_tool_call`, `tool_policy_result`, `confirmation_required` e `confirmation_received`.
Para o cenário de exemplo, o pedido `123` (ou `PED-ENTREGUE`) retorna `ENTREGUE` no MCP Retail. Use:
```text
Quero devolver o pedido 123 porque me arrependi da compra.
Sim, confirmo a devolução.
```
O contrato MCP foi padronizado para usar `reason` tanto no catálogo quanto no servidor FastMCP. `tool_policies.yaml` prevalece sobre os campos legados de `tools.yaml`; estes permanecem alinhados nos templates para compatibilidade.

View File

@@ -0,0 +1,25 @@
# Correção da extração de parâmetros MCP
## Problema corrigido
O bloco `extract` do `mcp_parameter_mapping.yaml` existia na configuração e na
documentação, mas não era executado pelo runtime. Além disso, valores do
Business Context podiam sobrescrever argumentos explícitos, fazendo
`contract_key` substituir o `order_id` informado pelo usuário.
## Correções
- implementação da extração genérica `strategy: llm` após a escolha da tool;
- suporte preservado para `strategy: month_name_pt`;
- profile dedicado `mcp_parameter_extraction`;
- telemetria `llm.mcp_parameter_extraction`;
- `extract` deixou de ser interpretado como mapeamento simples;
- argumentos explícitos/extraídos têm precedência sobre Business Context;
- remoção de `contract_key: order_id` dos templates;
- `order_id` configurado como `string`;
- atualização das variantes em `Tuning-Performance`.
## Resultado esperado
Para a mensagem `consultar pedido 123`, a chamada MCP deve receber
`order_id=123`, mesmo quando o Business Context contém outro `contract_key`.

View File

@@ -0,0 +1,21 @@
# Correção — mudança de consulta para ação transacional
## Problema
Após `consultar pedido 123`, a mensagem `Quero devolver o pedido 123` podia permanecer no `orders_agent` por route stickiness. Como a intent anterior só expunha tools de consulta, o runtime executava novamente `consultar_pedido` e a resposta direta repetia o status do pedido.
## Correções
- Keywords explícitas configuradas no `routing.yaml` podem preemptar a route stickiness quando apontam para outra intent/agente.
- `retail_support_exchange_return` passa a ter prioridade maior que `retail_order_tracking` para mensagens de troca/devolução.
- Tools transacionais declaram `selection_keywords` no `tools.yaml`.
- A resposta direta read-only é bloqueada quando a mensagem contém uma ação transacional registrada, mesmo que a intent anterior ainda esteja ativa.
- A seleção da action tool usa configuração, não aliases de domínio fixos no runtime.
## Fluxo esperado
1. `consultar pedido 123``orders_agent``consultar_pedido` → resposta direta.
2. `Quero devolver o pedido 123` → preempção da stickiness → `support_agent` / `retail_support_exchange_return`.
3. `consultar_pedido` valida o pedido.
4. `solicitar_devolucao` é selecionada e, com confirmação obrigatória, gera `AWAITING_CONFIRMATION`.
5. `Sim, confirmo` executa a action tool uma única vez.

View File

@@ -0,0 +1,36 @@
# Release notes - políticas read-only/transacionais
## Alterações
- Novo `ToolPolicyRegistry` opcional na biblioteca compartilhada.
- Validação central no `MCPToolRouter`, inclusive para chamadas diretas.
- Tipos mínimos `read_only` e `transactional`.
- Confirmação estrita por `confirmed: true` ou `confirmation: true`.
- Suporte opcional a campos obrigatórios por política.
- Fallback automático para `tool_type`, `requires`, `confirmation_required` e `execution_policy` de `tools.yaml`.
- `config/tool_policies.yaml` e variável `TOOL_POLICIES_PATH` nos templates principais, Day Zero e variantes de `Tuning-Performance/Normal` e `Tuning-Performance/Route_Stickness`.
- Testes unitários de política e compatibilidade adicionados em `tests/unit/test_tool_policies.py`.
## Verificações executadas
- Compilação de `libs`, `templates`, `Tuning-Performance` e `tests`: aprovada.
- Validação estrutural dos seis arquivos YAML: aprovada.
- Casos isolados do loader (política transacional, confirmação, ausência de arquivo e ausência de cadastro): aprovados.
- Renderização dos dois manuais Word atualizados: aprovada, sem cortes ou sobreposição nas páginas adicionadas.
## Limitação do ambiente de validação
A suíte `pytest` foi preparada, mas não pôde ser executada integralmente neste ambiente porque `pytest` e as dependências de runtime do projeto não estavam instalados e o acesso ao índice de pacotes expirou. Para reproduzir em um ambiente do projeto:
```bash
PYTHONPATH=libs/agent_framework/src:templates/agent_template_backend python -m pytest -q
```
## Correção de integração backend/MCP
- `mcp_tools` passou a ser tratado como allowlist.
- Ações não são mais executadas automaticamente junto com consultas.
- Confirmação transacional é persistida e retomada no turno seguinte.
- Corrigida incompatibilidade `reason`/`motivo` no MCP Retail.
- Adicionado pedido entregue determinístico para testes (`123`).
- Removida keyword genérica `produto` da intenção Telecom para evitar colisão com devoluções Retail.
- Templates Normal e Route_Stickness em `Tuning-Performance` foram sincronizados.

View File

@@ -0,0 +1,35 @@
# Test Results - Semantic Route Stickiness and Global Session Control
Date: 2026-07-31
## Command
```bash
PYTHONPATH=libs/agent_framework/src pytest -q tests/unit/test_semantic_route_stickiness.py
```
## Result
```text
9 passed
```
## Covered scenarios
1. `CONTINUE` bypasses the Enterprise Router.
2. `ROUTE` falls back to the Enterprise Router.
3. Low-confidence `CONTINUE` falls back safely.
4. Invalid model output falls back safely.
5. With no active agent, the lightweight classifier can still detect global session actions.
6. `HUMAN_HANDOFF` returns the global `human_handoff` route and session-control metadata.
7. `END_SESSION` returns the global `end_session` route and session-control metadata.
8. Global actions work on the first turn.
9. `CONTINUE` without an active agent is normalized to `ROUTE`.
## Additional validation
```bash
python -m compileall -q libs/agent_framework/src templates/agent_template_backend/app
```
Compilation completed successfully.

View File

@@ -0,0 +1,27 @@
# Validação — integração transacional Agent Template Backend / MCP
## Correções implementadas
- `mcp_tools` é tratado como allowlist, não como lista de execução automática.
- Tools `read_only` continuam disponíveis para enriquecimento de contexto.
- Somente uma tool transacional compatível com a solicitação é selecionada.
- `require_confirmation: true` cria `pending_tool_call` e `AWAITING_CONFIRMATION`.
- O turno de confirmação executa a chamada pendente com `confirmed: true`.
- O estado expõe `selected_tool_call`, `tool_policy_result`, `confirmation_required`, `confirmation_received` e `transaction_status`.
- `reason` foi padronizado entre catálogo, mapping e FastMCP Retail.
- Pedido `123` e `PED-ENTREGUE` retornam status `ENTREGUE` para testes positivos.
- A keyword genérica `produto` foi removida da intenção Telecom para não capturar devoluções Retail.
- Templates `Normal` e `Route_Stickness` em `Tuning-Performance` foram atualizados.
## Teste recomendado
1. `Quero devolver o pedido 123 porque me arrependi da compra.`
2. Esperado: `transaction_status=AWAITING_CONFIRMATION`, sem execução de `solicitar_devolucao`.
3. `Sim, confirmo a devolução.`
4. Esperado: `transaction_status=COMPLETED` e execução única de `solicitar_devolucao`.
## Resultado automatizado
```text
7 passed
```