mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 10:13:46 +00:00
New features: Route Stickness, Handoff, Clarification, Read-Only/Transactional, Long Term Memory
This commit is contained in:
520
Documentacao/Long_Term_Memory_Implementation_Guide_EN.md
Normal file
520
Documentacao/Long_Term_Memory_Implementation_Guide_EN.md
Normal 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.
|
||||
272
Documentacao/MANUAL_AGENT_PLATFORM_GATEWAYS.md
Normal file
272
Documentacao/MANUAL_AGENT_PLATFORM_GATEWAYS.md
Normal 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
|
||||
Binary file not shown.
Binary file not shown.
520
Documentacao/Manual_Long_Term_Memory_PT.md
Normal file
520
Documentacao/Manual_Long_Term_Memory_PT.md
Normal 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.
|
||||
@@ -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).
|
||||
|
||||
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.
|
||||
86
Documentacao/README_SEMANTIC_ROUTE_STICKINESS.md
Normal file
86
Documentacao/README_SEMANTIC_ROUTE_STICKINESS.md
Normal 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.
|
||||
90
Documentacao/README_TOOL_POLICIES.md
Normal file
90
Documentacao/README_TOOL_POLICIES.md
Normal 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.
|
||||
25
Documentacao/RELEASE_NOTES_MCP_PARAMETER_EXTRACTION_FIX.md
Normal file
25
Documentacao/RELEASE_NOTES_MCP_PARAMETER_EXTRACTION_FIX.md
Normal 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`.
|
||||
@@ -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.
|
||||
36
Documentacao/RELEASE_NOTES_TOOL_POLICIES.md
Normal file
36
Documentacao/RELEASE_NOTES_TOOL_POLICIES.md
Normal 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.
|
||||
BIN
Documentacao/Route_Stickiness_Semantica_Agent_Framework_OCI.docx
Normal file
BIN
Documentacao/Route_Stickiness_Semantica_Agent_Framework_OCI.docx
Normal file
Binary file not shown.
35
Documentacao/TEST_RESULTS_ROUTE_STICKINESS.md
Normal file
35
Documentacao/TEST_RESULTS_ROUTE_STICKINESS.md
Normal 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.
|
||||
27
Documentacao/VALIDACAO_TRANSACIONAL_BACKEND_MCP.md
Normal file
27
Documentacao/VALIDACAO_TRANSACIONAL_BACKEND_MCP.md
Normal 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
|
||||
```
|
||||
Reference in New Issue
Block a user