Projeto do Agent Contas ORACLE
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# Sample guideline, please follow similar structure for guideline with code samples
|
||||
# 1. Suggest using streams instead of simple loops for better readability.
|
||||
# <example>
|
||||
# *Comment:
|
||||
# Category: Minor
|
||||
# Issue: Use streams instead of a loop for better readability.
|
||||
# Code Block:
|
||||
#
|
||||
# ```java
|
||||
# // Calculate squares of numbers
|
||||
# List<Integer> squares = new ArrayList<>();
|
||||
# for (int number : numbers) {
|
||||
# squares.add(number * number);
|
||||
# }
|
||||
# ```
|
||||
# Recommendation:
|
||||
#
|
||||
# ```java
|
||||
# // Calculate squares of numbers
|
||||
# List<Integer> squares = Arrays.stream(numbers)
|
||||
# .map(n -> n * n) // Map each number to its square
|
||||
# .toList();
|
||||
# ```
|
||||
# </example>
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
# Correção: deadlock/espera cross-loop na geração de sequence
|
||||
|
||||
## Problema
|
||||
|
||||
A API síncrona `agent_framework.observer.event()` podia ser chamada em uma worker thread sem event loop ativo. Nesse caso, a implementação anterior executava `asyncio.run(aevent(...))`, criando um novo event loop temporário. Ao mesmo tempo, `analytics/tim_sequence.py` compartilhava instâncias globais de `asyncio.Lock` (`_mongo_index_lock` e `_memory_lock`) entre chamadas que podiam vir de event loops diferentes.
|
||||
|
||||
Na primeira operação Mongo, `_ensure_mongo_ttl_index_once()` mantinha `_mongo_index_lock` durante a criação do índice TTL. A contenção por outro loop podia deixar a segunda chamada aguardando indefinidamente.
|
||||
|
||||
## Alterações aplicadas
|
||||
|
||||
1. `observer.py`
|
||||
- removido `asyncio.run()` do caminho síncrono de `event()`;
|
||||
- adicionado um event loop dedicado e reutilizável para chamadas síncronas;
|
||||
- submissão cross-thread feita com `asyncio.run_coroutine_threadsafe()`;
|
||||
- encerramento best-effort do loop no shutdown do processo.
|
||||
|
||||
2. `analytics/tim_sequence.py`
|
||||
- `_mongo_index_lock`: `asyncio.Lock` -> `threading.Lock`;
|
||||
- `_memory_lock`: `asyncio.Lock` -> `threading.Lock`;
|
||||
- inicialização do índice TTL movida para uma função síncrona protegida por lock de thread e chamada via `asyncio.to_thread()`;
|
||||
- o contador de fallback em memória usa uma seção crítica curta e thread-safe.
|
||||
|
||||
3. Testes
|
||||
- `tests/test_observer_cross_loop_deadlock_fix.py` valida:
|
||||
- múltiplas worker threads usando `event()` compartilham o mesmo loop síncrono do observer;
|
||||
- sequence em memória permanece monotônica entre event loops independentes;
|
||||
- criação do índice TTL ocorre apenas uma vez sob contenção cross-loop.
|
||||
|
||||
## Validação executada
|
||||
|
||||
```bash
|
||||
PYTHONPATH=libs/agent_framework/src pytest -q tests/test_observer_cross_loop_deadlock_fix.py
|
||||
```
|
||||
|
||||
Resultado: `3 passed`.
|
||||
|
||||
A suíte completa do repositório possui falhas preexistentes/independentes desta alteração, incluindo conflitos de coleta de arquivos `test_long_term_memory.py`, caminhos estáticos de template e testes de checkpoint/workflow. Esses itens não foram alterados por esta correção.
|
||||
@@ -0,0 +1,133 @@
|
||||
# Inventário — Agent Gateway + MCP Gateway Overlay
|
||||
|
||||
Este inventário lista os arquivos incluídos no overlay `agent_platform_agent_gateway_mcp_gateway_overlay.zip`, indicando a área, o tipo de alteração e a finalidade de cada arquivo.
|
||||
|
||||
## Resumo
|
||||
|
||||
| Área | Quantidade |
|
||||
|---|---:|
|
||||
| Documentação | 1 |
|
||||
| Agent Gateway | 10 |
|
||||
| MCP Gateway | 5 |
|
||||
| Agent Framework | 4 |
|
||||
| Template Backend | 2 |
|
||||
| MCP Server Mock | 2 |
|
||||
| Deploy | 2 |
|
||||
|
||||
## Arquivos por área
|
||||
|
||||
### Documentação
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `README_AGENT_GATEWAY_AND_MCP_GATEWAY_EVOLUTION.md` | Novo / overlay | Documento principal do overlay. Explica a nova arquitetura sem AI Gateway separado, com Agent Gateway governando políticas/modelos e MCP Gateway separado para tools. |
|
||||
|
||||
### Agent Gateway
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `apps/agent_gateway/app/config/governance_loader.py` | Novo / overlay | Carrega o arquivo YAML de governança do Agent Gateway a partir de AGENT_GATEWAY_GOVERNANCE_CONFIG. |
|
||||
| `apps/agent_gateway/app/governance/__init__.py` | Novo / overlay | Inicializa o pacote Python de governança do Agent Gateway. |
|
||||
| `apps/agent_gateway/app/governance/audit.py` | Novo / overlay | Centraliza logging/auditoria das decisões de governança do Agent Gateway, com proteção simples para não logar mensagem completa. |
|
||||
| `apps/agent_gateway/app/governance/evaluation_hooks.py` | Novo / overlay | Hooks antes e depois da chamada ao backend/runtime. Serve para amostragem, evaluator, scoring ou integração futura com Langfuse. |
|
||||
| `apps/agent_gateway/app/governance/model_policies.py` | Novo / overlay | Resolve políticas de modelo/profile no Agent Gateway. Define qual provider/model/profile deve ser usado por operação, tenant e agente. |
|
||||
| `apps/agent_gateway/app/governance/rate_limit.py` | Novo / overlay | Implementa rate limit em memória por tenant, agente e canal antes de encaminhar a requisição ao backend/runtime. |
|
||||
| `apps/agent_gateway/app/governance/usage.py` | Novo / overlay | Hook para registrar uso de gateway, políticas aplicadas e respostas do backend. Pronto para plugar métricas, banco, Langfuse ou OTEL. |
|
||||
| `apps/agent_gateway/app/governance_middleware.py` | Novo / overlay | Componente principal de governança do Agent Gateway. Aplica rate limit, resolve model_policy, gera headers/metadados e executa hooks antes/depois do backend. |
|
||||
| `apps/agent_gateway/app/routes/governed_proxy_example.py` | Novo / overlay | Exemplo de rota governada para demonstrar como aplicar governança antes de encaminhar para o Agent Backend/Runtime. |
|
||||
| `apps/agent_gateway/config/gateway_governance.yaml` | Novo / overlay | Configuração de governança do Agent Gateway: profiles, operation_profiles, providers permitidos, rate limits, headers propagados e evaluation hooks. |
|
||||
|
||||
### MCP Gateway
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `apps/mcp_gateway/Dockerfile` | Novo / overlay | Imagem Docker do MCP Gateway. |
|
||||
| `apps/mcp_gateway/app/__init__.py` | Novo / overlay | Inicializa o pacote Python da aplicação MCP Gateway. |
|
||||
| `apps/mcp_gateway/app/main.py` | Novo / overlay | Aplicação FastAPI do MCP Gateway. Expõe health, ready, catálogo de tools e endpoint de invoke com auth, autorização, mapping, cache, timeout e retry. |
|
||||
| `apps/mcp_gateway/config/mcp_gateway.yaml` | Novo / overlay | Configuração central do MCP Gateway: MCP servers, tools, versões, cache, timeout, retry, autorização por agente/canal e mapping BusinessContext → parâmetros. |
|
||||
| `apps/mcp_gateway/requirements.txt` | Novo / overlay | Dependências Python do MCP Gateway. |
|
||||
|
||||
### Agent Framework
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `libs/agent_framework/src/agent_framework/gateway_policy_context.py` | Novo / overlay | Helper no framework para o Runtime ler a política de modelo enviada pelo Agent Gateway em state['metadata']['model_policy']. |
|
||||
| `libs/agent_framework/src/agent_framework/gateways/__init__.py` | Novo / overlay | Inicializa o pacote de clients de gateways no framework, exportando MCPGatewayClient. |
|
||||
| `libs/agent_framework/src/agent_framework/gateways/mcp_gateway_client.py` | Novo / overlay | Client assíncrono do framework para chamar o MCP Gateway: listar tools e executar tools. |
|
||||
| `libs/agent_framework/src/agent_framework/runtime_mcp_gateway_adapter.py` | Novo / overlay | Mixin opcional para agentes/runtime chamarem tools via MCP Gateway e anexarem resultados em state['mcp_results']. |
|
||||
|
||||
### Template Backend
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `templates/agent_template_backend/app/mcp_gateway_client_factory.py` | Novo / overlay | Factory no template backend para construir MCPGatewayClient a partir de variáveis de ambiente. |
|
||||
|
||||
### MCP Server Mock
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `mcp/servers/mock_telecom_mcp/app.py` | Novo / overlay | Mock MCP Server com tools consultar_fatura e consultar_pagamentos para validar o MCP Gateway localmente. |
|
||||
| `mcp/servers/mock_telecom_mcp/requirements.txt` | Novo / overlay | Dependências do mock MCP Server de telecom usado para testes locais. |
|
||||
|
||||
### Deploy
|
||||
|
||||
| Arquivo | Tipo | Finalidade |
|
||||
|---|---|---|
|
||||
| `deploy/docker/docker-compose.mcp-gateway.yml` | Novo / overlay | Docker Compose para subir MCP Gateway e mock_telecom_mcp localmente. |
|
||||
| `deploy/k8s/mcp-gateway.yaml` | Novo / overlay | Manifest Kubernetes de Deployment e Service do MCP Gateway. |
|
||||
|
||||
## Observações de integração
|
||||
|
||||
### Agent Gateway
|
||||
|
||||
Os arquivos em `apps/agent_gateway` não criam um novo serviço. Eles evoluem o Agent Gateway existente para atuar como gateway dedicado da plataforma, centralizando:
|
||||
|
||||
- políticas de modelo/profile;
|
||||
- rate limit;
|
||||
- auditoria;
|
||||
- hooks de avaliação;
|
||||
- propagação de metadados de governança para o Runtime.
|
||||
|
||||
A rota `governed_proxy_example.py` é um exemplo de integração. O handler real do `POST /gateway/message` deve aplicar:
|
||||
|
||||
```python
|
||||
governed_body, headers = governance.prepare_backend_request(body)
|
||||
```
|
||||
|
||||
antes de chamar o backend/runtime, e:
|
||||
|
||||
```python
|
||||
return governance.process_backend_response(data)
|
||||
```
|
||||
|
||||
após receber a resposta.
|
||||
|
||||
### MCP Gateway
|
||||
|
||||
O MCP Gateway é um serviço separado. Ele centraliza:
|
||||
|
||||
- catálogo de tools;
|
||||
- autorização por agente/canal;
|
||||
- versionamento de tools;
|
||||
- mapping de BusinessContext para parâmetros;
|
||||
- cache;
|
||||
- timeout;
|
||||
- retry;
|
||||
- auditoria simples.
|
||||
|
||||
### Runtime / Backend
|
||||
|
||||
O Runtime continua responsável por:
|
||||
|
||||
- LangGraph;
|
||||
- estado;
|
||||
- memória;
|
||||
- checkpoints;
|
||||
- fluxo;
|
||||
- providers LLM existentes.
|
||||
|
||||
O Runtime passa a chamar tools via MCP Gateway usando `MCPGatewayClient` e/ou `MCPGatewayRuntimeMixin`.
|
||||
|
||||
### AI Gateway
|
||||
|
||||
Este overlay não cria `apps/ai_gateway`. A governança de modelo fica no Agent Gateway, e a execução LLM continua no Runtime/backend usando os providers já existentes.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -0,0 +1,627 @@
|
||||
# Manual de Execução Local
|
||||
## Agent Gateway + MCP Gateway + Agent Template Backend + Frontend
|
||||
|
||||
## 1. Arquitetura de execução
|
||||
|
||||
A arquitetura local fica assim:
|
||||
|
||||
```text
|
||||
Frontend
|
||||
porta 5173
|
||||
│
|
||||
▼
|
||||
Agent Gateway
|
||||
porta 9000
|
||||
│
|
||||
▼
|
||||
Agent Template Backend / Agent Runtime
|
||||
porta 8000
|
||||
│
|
||||
▼
|
||||
MCP Gateway
|
||||
porta 8300
|
||||
│
|
||||
▼
|
||||
MCP Server / Mock Telecom MCP
|
||||
porta 8001
|
||||
```
|
||||
|
||||
A governança de modelo, rate limit, auditoria e políticas ficam no **Agent Gateway**.
|
||||
|
||||
O **Agent Runtime / Agent Template Backend** continua responsável por:
|
||||
|
||||
- LangGraph;
|
||||
- estado;
|
||||
- memória;
|
||||
- checkpoints;
|
||||
- supervisor/router;
|
||||
- guardrails;
|
||||
- judges;
|
||||
- chamada LLM via providers existentes;
|
||||
- chamada de tools via MCP Gateway.
|
||||
|
||||
---
|
||||
|
||||
## 2. Portas
|
||||
|
||||
| Componente | Porta | URL |
|
||||
|---|---:|---|
|
||||
| Frontend | 5173 | `http://localhost:5173` |
|
||||
| Agent Gateway | 9000 | `http://localhost:9000` |
|
||||
| Agent Template Backend | 8000 | `http://localhost:8000` |
|
||||
| MCP Gateway | 8300 | `http://localhost:8300` |
|
||||
| MCP Server / Mock Telecom MCP | 8001 | `http://localhost:8001` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Ordem recomendada para subir
|
||||
|
||||
Subir nesta ordem:
|
||||
|
||||
1. MCP Server / Mock Telecom MCP
|
||||
2. MCP Gateway
|
||||
3. Agent Template Backend
|
||||
4. Agent Gateway
|
||||
5. Frontend
|
||||
|
||||
---
|
||||
|
||||
# 4. Terminal 1 — MCP Server / Mock Telecom MCP
|
||||
|
||||
Se estiver usando o mock incluído no overlay:
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/mcp/servers/mock_telecom_mcp
|
||||
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
uvicorn app:app --host 0.0.0.0 --port 8001 --reload
|
||||
```
|
||||
|
||||
Validar:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/health
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"service": "mock_telecom_mcp"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 5. Terminal 2 — MCP Gateway
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/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
|
||||
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8300 --reload
|
||||
```
|
||||
|
||||
Validar health:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/health
|
||||
```
|
||||
|
||||
Validar readiness:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/ready
|
||||
```
|
||||
|
||||
Listar tools:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8300/v1/tools | jq
|
||||
```
|
||||
|
||||
Executar tool:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8300/v1/tools/consultar_fatura/invoke \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"channel": "web",
|
||||
"tool_name": "consultar_fatura",
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool_name": "consultar_fatura",
|
||||
"version": "1.0.0",
|
||||
"ok": true,
|
||||
"data": {
|
||||
"invoice_id": "INV-001",
|
||||
"msisdn": "11999999999",
|
||||
"valor_total": 249.9,
|
||||
"vencimento": "2026-06-10",
|
||||
"status": "ABERTA"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 6. Terminal 3 — Agent Template Backend / Agent Runtime
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/templates/agent_template_backend
|
||||
```
|
||||
|
||||
ou, se o seu backend estiver em outra pasta:
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/templates/agent_template_backend
|
||||
```
|
||||
|
||||
Ativar ambiente:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
Se ainda não existir `.venv`:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Configurar variáveis:
|
||||
|
||||
```bash
|
||||
export MCP_GATEWAY_ENABLED=true
|
||||
export MCP_GATEWAY_URL=http://localhost:8300
|
||||
export MCP_GATEWAY_TIMEOUT_SECONDS=60
|
||||
|
||||
export AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
|
||||
```
|
||||
|
||||
Se estiver usando OCI/OpenAI-compatible, manter também as variáveis já existentes do backend:
|
||||
|
||||
```bash
|
||||
export LLM_PROVIDER=oci_openai
|
||||
export OCI_GENAI_API_KEY=<sua-chave>
|
||||
```
|
||||
|
||||
ou, para mock:
|
||||
|
||||
```bash
|
||||
export LLM_PROVIDER=mock
|
||||
```
|
||||
|
||||
Subir backend:
|
||||
|
||||
```bash
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
Validar:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
|
||||
Validar agentes:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/agents | jq
|
||||
```
|
||||
|
||||
Testar backend direto:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8000/gateway/message \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"payload": {
|
||||
"message": "Quero consultar minha fatura",
|
||||
"session_id": "session-001",
|
||||
"user_id": "user-001",
|
||||
"message_id": "msg-001",
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 7. Terminal 4 — Agent Gateway
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/apps/agent_gateway
|
||||
```
|
||||
|
||||
Ativar ambiente:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Configurar variáveis:
|
||||
|
||||
```bash
|
||||
export DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
|
||||
export AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
|
||||
```
|
||||
|
||||
Subir Agent Gateway:
|
||||
|
||||
```bash
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 9000 --reload
|
||||
```
|
||||
|
||||
Validar:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9000/health
|
||||
```
|
||||
|
||||
Se a rota governada de exemplo estiver registrada no `app.main`, testar:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:9000/gateway/message/governed \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"payload": {
|
||||
"message": "Quero consultar minha fatura",
|
||||
"session_id": "session-001",
|
||||
"user_id": "user-001",
|
||||
"message_id": "msg-001",
|
||||
"metadata": {
|
||||
"operation": "agent.final_answer"
|
||||
},
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
Se a rota real for `/gateway/message`, testar:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:9000/gateway/message \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"payload": {
|
||||
"message": "Quero consultar minha fatura",
|
||||
"session_id": "session-001",
|
||||
"user_id": "user-001",
|
||||
"message_id": "msg-001",
|
||||
"metadata": {
|
||||
"operation": "agent.final_answer"
|
||||
},
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8. Terminal 5 — Frontend
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci/agent_frontend
|
||||
```
|
||||
|
||||
ou a pasta onde estiver o frontend.
|
||||
|
||||
Instalar dependências:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Subir:
|
||||
|
||||
```bash
|
||||
npm run dev -- --host 0.0.0.0 --port 5173
|
||||
```
|
||||
|
||||
Abrir:
|
||||
|
||||
```text
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
Configurar no frontend:
|
||||
|
||||
```text
|
||||
Backend URL: http://localhost:9000
|
||||
Agent: telecom_contas
|
||||
Session ID: session-001
|
||||
Customer Key: 11999999999
|
||||
Contract Key: INV-001
|
||||
```
|
||||
|
||||
O frontend deve chamar o **Agent Gateway** na porta 9000, não o MCP Gateway.
|
||||
|
||||
---
|
||||
|
||||
# 9. Fluxo final esperado
|
||||
|
||||
```text
|
||||
Frontend 5173
|
||||
↓
|
||||
Agent Gateway 9000
|
||||
↓
|
||||
Agent Template Backend 8000
|
||||
↓
|
||||
MCP Gateway 8300
|
||||
↓
|
||||
Mock Telecom MCP 8001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 10. Docker Compose para MCP Gateway + Mock MCP
|
||||
|
||||
Também é possível subir MCP Gateway + Mock MCP com Docker Compose:
|
||||
|
||||
```bash
|
||||
cd agent_platform_oci
|
||||
|
||||
docker compose -f deploy/docker/docker-compose.mcp-gateway.yml up --build
|
||||
```
|
||||
|
||||
Isso sobe:
|
||||
|
||||
```text
|
||||
MCP Gateway http://localhost:8300
|
||||
Mock Telecom MCP http://localhost:8001
|
||||
```
|
||||
|
||||
Depois subir manualmente:
|
||||
|
||||
- Agent Template Backend na porta 8000;
|
||||
- Agent Gateway na porta 9000;
|
||||
- Frontend na porta 5173.
|
||||
|
||||
---
|
||||
|
||||
# 11. Checklist de validação
|
||||
|
||||
## MCP Server
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/health
|
||||
```
|
||||
|
||||
## MCP Gateway
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/health
|
||||
curl http://localhost:8300/v1/tools
|
||||
```
|
||||
|
||||
## Backend Runtime
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
curl http://localhost:8000/agents
|
||||
```
|
||||
|
||||
## Agent Gateway
|
||||
|
||||
```bash
|
||||
curl http://localhost:9000/health
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
```text
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 12. Erros comuns
|
||||
|
||||
## 12.1. Frontend chamando porta errada
|
||||
|
||||
Errado:
|
||||
|
||||
```text
|
||||
Frontend → http://localhost:8000
|
||||
```
|
||||
|
||||
Correto:
|
||||
|
||||
```text
|
||||
Frontend → http://localhost:9000
|
||||
```
|
||||
|
||||
Se você quiser testar sem Agent Gateway, pode apontar temporariamente para 8000. Mas no modelo final, o frontend deve usar o Agent Gateway.
|
||||
|
||||
---
|
||||
|
||||
## 12.2. MCP Gateway sem MCP Server
|
||||
|
||||
Sintoma:
|
||||
|
||||
```text
|
||||
MCP server unavailable
|
||||
```
|
||||
|
||||
Correção:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8001/health
|
||||
```
|
||||
|
||||
Se falhar, subir o mock MCP server.
|
||||
|
||||
---
|
||||
|
||||
## 12.3. Tool sem BusinessContext
|
||||
|
||||
Sintoma:
|
||||
|
||||
```json
|
||||
{
|
||||
"missing_business_keys": ["customer_key", "contract_key"]
|
||||
}
|
||||
```
|
||||
|
||||
Correção:
|
||||
|
||||
enviar:
|
||||
|
||||
```json
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12.4. Agent Gateway não encontra backend
|
||||
|
||||
Sintoma:
|
||||
|
||||
```text
|
||||
Connection refused http://localhost:8000
|
||||
```
|
||||
|
||||
Correção:
|
||||
|
||||
validar:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
```
|
||||
|
||||
e configurar:
|
||||
|
||||
```bash
|
||||
export DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12.5. Rota governada não registrada
|
||||
|
||||
Se `/gateway/message/governed` retornar 404, significa que o arquivo de exemplo ainda não foi incluído no `app.main`.
|
||||
|
||||
Nesse caso, use a rota real `/gateway/message` ou registre no `main.py`:
|
||||
|
||||
```python
|
||||
from app.routes.governed_proxy_example import router as governed_router
|
||||
|
||||
app.include_router(governed_router)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 13. Variáveis consolidadas
|
||||
|
||||
## Agent Gateway
|
||||
|
||||
```env
|
||||
DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
|
||||
AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
|
||||
```
|
||||
|
||||
## Agent Template Backend
|
||||
|
||||
```env
|
||||
MCP_GATEWAY_ENABLED=true
|
||||
MCP_GATEWAY_URL=http://localhost:8300
|
||||
MCP_GATEWAY_TIMEOUT_SECONDS=60
|
||||
LLM_PROVIDER=mock
|
||||
```
|
||||
|
||||
## MCP Gateway
|
||||
|
||||
```env
|
||||
MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 14. Resumo rápido
|
||||
|
||||
Em cinco terminais:
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
cd mcp/servers/mock_telecom_mcp
|
||||
source .venv/bin/activate
|
||||
uvicorn app:app --host 0.0.0.0 --port 8001 --reload
|
||||
|
||||
# Terminal 2
|
||||
cd apps/mcp_gateway
|
||||
source .venv/bin/activate
|
||||
export MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8300 --reload
|
||||
|
||||
# Terminal 3
|
||||
cd templates/agent_template_backend
|
||||
source .venv/bin/activate
|
||||
export MCP_GATEWAY_ENABLED=true
|
||||
export MCP_GATEWAY_URL=http://localhost:8300
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
|
||||
# Terminal 4
|
||||
cd apps/agent_gateway
|
||||
source .venv/bin/activate
|
||||
export DEFAULT_AGENT_BACKEND_URL=http://localhost:8000
|
||||
export AGENT_GATEWAY_GOVERNANCE_CONFIG=config/gateway_governance.yaml
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 9000 --reload
|
||||
|
||||
# Terminal 5
|
||||
cd agent_frontend
|
||||
npm install
|
||||
npm run dev -- --host 0.0.0.0 --port 5173
|
||||
```
|
||||
93
agent_framework_oci/Documentacao/MCP_GATEWAY_RUNBOOK.md
Normal file
93
agent_framework_oci/Documentacao/MCP_GATEWAY_RUNBOOK.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# MCP Gateway Runbook
|
||||
|
||||
## Arquitetura corrigida
|
||||
|
||||
O backend/agente não deve chamar diretamente os MCP servers finais. O fluxo correto é:
|
||||
|
||||
```text
|
||||
agent_template_backend / agent_framework
|
||||
-> MCP Gateway Client
|
||||
-> apps/mcp_gateway
|
||||
-> mcp/servers/telecom_mcp_server ou mcp/servers/retail_mcp_server
|
||||
```
|
||||
|
||||
## Subir localmente
|
||||
|
||||
A partir da raiz do projeto:
|
||||
|
||||
### Terminal 1 - Telecom MCP Server
|
||||
|
||||
```bash
|
||||
cd mcp/servers/telecom_mcp_server
|
||||
python -m uvicorn main:app --host 0.0.0.0 --port 8100 --reload
|
||||
```
|
||||
|
||||
### Terminal 2 - Retail MCP Server
|
||||
|
||||
```bash
|
||||
cd mcp/servers/retail_mcp_server
|
||||
python -m uvicorn main:app --host 0.0.0.0 --port 8200 --reload
|
||||
```
|
||||
|
||||
### Terminal 3 - MCP Gateway
|
||||
|
||||
```bash
|
||||
cd apps/mcp_gateway
|
||||
export MCP_GATEWAY_CONFIG_PATH=config/mcp_gateway.yaml
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8300 --reload
|
||||
```
|
||||
|
||||
### Terminal 4 - Backend/agente
|
||||
|
||||
No `.env` do backend/agente ou do runtime que usa o `agent_framework`, habilite:
|
||||
|
||||
```env
|
||||
ENABLE_MCP_TOOLS=true
|
||||
MCP_GATEWAY_ENABLED=true
|
||||
MCP_GATEWAY_URL=http://localhost:8300
|
||||
MCP_GATEWAY_AGENT_ID=telecom_contas
|
||||
MCP_GATEWAY_TENANT_ID=default
|
||||
```
|
||||
|
||||
## Testes rápidos
|
||||
|
||||
### Health do gateway
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/health
|
||||
```
|
||||
|
||||
### Lista de tools expostas pelo gateway
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/v1/tools
|
||||
```
|
||||
|
||||
### Chamada de tool via gateway
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8300/v1/tools/consultar_fatura/invoke \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"channel": "web",
|
||||
"tool_name": "consultar_fatura",
|
||||
"arguments": {
|
||||
"msisdn": "11999999999",
|
||||
"invoice_id": "INV-123"
|
||||
},
|
||||
"business_context": {},
|
||||
"metadata": {"session_id": "local-test"}
|
||||
}'
|
||||
```
|
||||
|
||||
Resposta esperada: `ok: true`, `data.invoice_id`, `data.msisdn`, `metadata.server: telecom`.
|
||||
|
||||
## O que foi corrigido
|
||||
|
||||
- `apps/mcp_gateway/config/mcp_gateway.yaml` agora aponta para os MCP servers reais nas portas `8100` e `8200`.
|
||||
- O MCP Gateway agora suporta o contrato legado dos MCP servers: `POST /mcp/tools/call` com `{tool_name, arguments}`.
|
||||
- O `agent_framework` ganhou flags `MCP_GATEWAY_ENABLED`, `MCP_GATEWAY_URL`, `MCP_GATEWAY_TOKEN`, `MCP_GATEWAY_AGENT_ID` e `MCP_GATEWAY_TENANT_ID`.
|
||||
- O `MCPToolRouter` passa a chamar o MCP Gateway quando `MCP_GATEWAY_ENABLED=true`.
|
||||
- `libs/agent_framework/config/mcp_servers.yaml` foi mantido como registry lógico/fallback, não como caminho principal quando o gateway está ativo.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
520
agent_framework_oci/Documentacao/Manual_Long_Term_Memory_PT.md
Normal file
520
agent_framework_oci/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.
|
||||
@@ -0,0 +1,129 @@
|
||||
# Agent Platform OCI — Agent Gateway + MCP Gateway Evolution
|
||||
|
||||
Este overlay remove o conceito de `AI Gateway` separado.
|
||||
|
||||
## Arquitetura
|
||||
|
||||
```text
|
||||
Frontend
|
||||
↓
|
||||
Agent Gateway
|
||||
├── governance
|
||||
├── model policies
|
||||
├── rate limit
|
||||
├── audit
|
||||
└── evaluation hooks
|
||||
↓
|
||||
Agent Backend / Runtime
|
||||
├── LangGraph
|
||||
├── state
|
||||
├── memory
|
||||
├── checkpoints
|
||||
└── LLM providers via profiles existentes
|
||||
↓
|
||||
MCP Gateway
|
||||
↓
|
||||
MCP Servers
|
||||
```
|
||||
|
||||
## O que entra no Agent Gateway
|
||||
|
||||
```text
|
||||
apps/agent_gateway/app/governance/
|
||||
apps/agent_gateway/app/governance_middleware.py
|
||||
apps/agent_gateway/app/routes/governed_proxy_example.py
|
||||
apps/agent_gateway/config/gateway_governance.yaml
|
||||
```
|
||||
|
||||
## O que entra no MCP Gateway
|
||||
|
||||
```text
|
||||
apps/mcp_gateway/
|
||||
libs/agent_framework/src/agent_framework/gateways/mcp_gateway_client.py
|
||||
libs/agent_framework/src/agent_framework/runtime_mcp_gateway_adapter.py
|
||||
```
|
||||
|
||||
## Aplicar overlay
|
||||
|
||||
```bash
|
||||
unzip agent_platform_agent_gateway_mcp_gateway_overlay.zip -d /tmp/overlay
|
||||
rsync -av /tmp/overlay/ ./
|
||||
```
|
||||
|
||||
## Subir MCP Gateway local
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker/docker-compose.mcp-gateway.yml up --build
|
||||
```
|
||||
|
||||
Serviços:
|
||||
|
||||
```text
|
||||
MCP Gateway http://localhost:8300
|
||||
Mock Telecom MCP http://localhost:8001
|
||||
```
|
||||
|
||||
## Testar MCP Gateway
|
||||
|
||||
```bash
|
||||
curl http://localhost:8300/health
|
||||
curl http://localhost:8300/v1/tools
|
||||
```
|
||||
|
||||
Executar tool:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8300/v1/tools/consultar_fatura/invoke \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tenant_id": "default",
|
||||
"agent_id": "telecom_contas",
|
||||
"channel": "web",
|
||||
"tool_name": "consultar_fatura",
|
||||
"business_context": {
|
||||
"customer_key": "11999999999",
|
||||
"contract_key": "INV-001",
|
||||
"session_key": "session-001"
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
## Como plugar no Agent Gateway
|
||||
|
||||
No handler real do `POST /gateway/message`, antes de encaminhar ao backend/runtime:
|
||||
|
||||
```python
|
||||
governed_body, headers = governance.prepare_backend_request(body)
|
||||
```
|
||||
|
||||
Ao receber resposta do backend:
|
||||
|
||||
```python
|
||||
return governance.process_backend_response(data)
|
||||
```
|
||||
|
||||
O arquivo abaixo mostra um exemplo completo:
|
||||
|
||||
```text
|
||||
apps/agent_gateway/app/routes/governed_proxy_example.py
|
||||
```
|
||||
|
||||
## Variáveis do Runtime
|
||||
|
||||
```env
|
||||
MCP_GATEWAY_ENABLED=true
|
||||
MCP_GATEWAY_URL=http://localhost:8300
|
||||
MCP_GATEWAY_TIMEOUT_SECONDS=60
|
||||
```
|
||||
|
||||
## Importante
|
||||
|
||||
Não existe `apps/ai_gateway`.
|
||||
|
||||
A governança de modelo fica no Agent Gateway como policy/metadados.
|
||||
|
||||
O Runtime continua usando os LLM providers existentes, podendo ler a política enviada pelo Gateway em:
|
||||
|
||||
```python
|
||||
state["metadata"]["model_policy"]
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
# Checkpoint Enterprise no Agent Framework OCI
|
||||
|
||||
Esta versão adiciona quatro capacidades ao checkpointer do LangGraph usado pelo framework:
|
||||
|
||||
1. **Checkpoint Integrity**: cada checkpoint é salvo dentro de um envelope com `schema_version`, `checkpoint_id`, `payload_hash` SHA-256 e `created_at`. Na leitura, o hash é recalculado. Se o payload foi truncado, alterado ou corrompido, o checkpoint é ignorado no recovery.
|
||||
2. **Checkpoint Compaction**: checkpoints antigos são removidos automaticamente conforme a configuração `CHECKPOINT_COMPACT_EVERY` e `CHECKPOINT_KEEP_LAST`. Isso evita crescimento infinito da tabela `workflow_checkpoints`.
|
||||
3. **Resilient Checkpointer**: gravações e leituras usam retry com backoff e jitter. A camada resiliente funciona sobre memory, SQLite e Oracle/Autonomous Database.
|
||||
4. **Checkpoint Recovery**: ao recuperar o estado, o framework varre os últimos checkpoints e retorna o mais recente válido, pulando checkpoints corrompidos.
|
||||
|
||||
## Configuração
|
||||
|
||||
No `.env`:
|
||||
|
||||
```env
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
ENABLE_RESILIENT_CHECKPOINTER=true
|
||||
ENABLE_CHECKPOINT_INTEGRITY=true
|
||||
ENABLE_CHECKPOINT_COMPACTION=true
|
||||
CHECKPOINT_COMPACT_EVERY=50
|
||||
CHECKPOINT_KEEP_LAST=20
|
||||
CHECKPOINT_RECOVERY_SCAN_LIMIT=25
|
||||
CHECKPOINT_RETRY_MAX_ATTEMPTS=3
|
||||
CHECKPOINT_RETRY_BASE_DELAY_SECONDS=0.05
|
||||
CHECKPOINT_RETRY_MAX_DELAY_SECONDS=1.0
|
||||
CHECKPOINT_RETRY_JITTER_SECONDS=0.05
|
||||
```
|
||||
|
||||
Para produção com múltiplos pods, prefira:
|
||||
|
||||
```env
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
||||
ADB_USER=...
|
||||
ADB_PASSWORD=...
|
||||
ADB_DSN=...
|
||||
ADB_WALLET_LOCATION=...
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
## Uso no LangGraph
|
||||
|
||||
```python
|
||||
from agent_framework.checkpoints import create_langgraph_checkpointer
|
||||
|
||||
checkpointer = create_langgraph_checkpointer(settings)
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
|
||||
config = {"configurable": {"thread_id": session_id}}
|
||||
result = graph.invoke(input_state, config=config)
|
||||
```
|
||||
|
||||
O `thread_id` continua sendo a chave de recuperação da conversa. Em ambiente com Load Balancer, qualquer pod consegue retomar a execução se usar o mesmo repositório persistente.
|
||||
|
||||
## Arquivos alterados
|
||||
|
||||
- `agent_framework/src/agent_framework/checkpoints/checkpoint_repository.py`
|
||||
- `agent_framework/src/agent_framework/checkpoints/langgraph_saver.py`
|
||||
- `agent_framework/src/agent_framework/checkpoints/__init__.py`
|
||||
- `agent_framework/src/agent_framework/config/settings.py`
|
||||
- `tests/unit/test_resilient_checkpointer.py`
|
||||
|
||||
## Observação importante
|
||||
|
||||
O provider `memory` agora também usa o `RepositoryCheckpointSaver` quando `ENABLE_RESILIENT_CHECKPOINTER=true`. Para voltar ao `MemorySaver` puro do LangGraph em testes locais, configure:
|
||||
|
||||
```env
|
||||
ENABLE_RESILIENT_CHECKPOINTER=false
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=memory
|
||||
```
|
||||
114
agent_framework_oci/Documentacao/README_ENTERPRISE_ROUTING.md
Normal file
114
agent_framework_oci/Documentacao/README_ENTERPRISE_ROUTING.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# AI Agent Platform — Enterprise Routing Edition
|
||||
|
||||
Esta versão inclui o projeto completo com:
|
||||
|
||||
- `agent_framework`: framework reutilizável.
|
||||
- `agent_template_backend`: backend FastAPI com LangGraph, OCI Generative AI, Langfuse, guardrails, judges, supervisor e roteamento enterprise.
|
||||
- `agent_frontend`: frontend web independente.
|
||||
- `templates/template_telecom_billing_product`: template de exemplo para telecom com agentes de Fatura e Produto.
|
||||
- `templates/template_retail_orders_support`: template de exemplo para e-commerce com agentes de Pedido e Suporte.
|
||||
|
||||
## Roteamento enterprise
|
||||
|
||||
O roteamento fica em:
|
||||
|
||||
```text
|
||||
agent_framework/src/agent_framework/routing/
|
||||
```
|
||||
|
||||
Componentes principais:
|
||||
|
||||
- `models.py`: modelos `IntentDefinition`, `RouterStatePolicy`, `RouteDecision`.
|
||||
- `config_loader.py`: carrega o YAML de intents e políticas.
|
||||
- `enterprise_router.py`: decide o agente de destino por estado, keyword, LLM ou fallback.
|
||||
|
||||
O template usa:
|
||||
|
||||
```text
|
||||
agent_template_backend/config/routing.yaml
|
||||
```
|
||||
|
||||
## Ordem de decisão
|
||||
|
||||
1. Estado conversacional (`state_policies`).
|
||||
2. Keywords/intents configuráveis.
|
||||
3. LLM Router opcional (`ENABLE_LLM_ROUTER=true`).
|
||||
4. Fallback (`router.fallback_agent`).
|
||||
|
||||
## Como testar roteamento sem chamar o agente final
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/debug/route \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"payload": {
|
||||
"text": "Minha fatura veio alta",
|
||||
"user_id": "u1",
|
||||
"channel_id": "browser-1",
|
||||
"context": {"msisdn": "5511999999999"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Resposta esperada:
|
||||
|
||||
```json
|
||||
{
|
||||
"route": "billing_agent",
|
||||
"agent": "billing_agent",
|
||||
"intent": "billing_invoice_explanation",
|
||||
"method": "keyword"
|
||||
}
|
||||
```
|
||||
|
||||
## Como habilitar roteamento por LLM
|
||||
|
||||
No `.env` do backend:
|
||||
|
||||
```env
|
||||
LLM_PROVIDER=oci_openai
|
||||
OCI_GENAI_API_KEY=...
|
||||
OCI_GENAI_BASE_URL=https://inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com/openai/v1
|
||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
||||
ENABLE_LLM_ROUTER=true
|
||||
ROUTING_CONFIG_PATH=./config/routing.yaml
|
||||
```
|
||||
|
||||
## Como adicionar novo agente
|
||||
|
||||
1. Criar classe do agente em `agent_template_backend/app/agents/`.
|
||||
2. Instanciar o agente em `AgentWorkflow.__init__`.
|
||||
3. Adicionar node no LangGraph.
|
||||
4. Adicionar a rota no `add_conditional_edges`.
|
||||
5. Criar intent no `config/routing.yaml` apontando `agent: nome_do_agente`.
|
||||
|
||||
## Templates incluídos
|
||||
|
||||
### Template 1 — Telecom
|
||||
|
||||
Diretório:
|
||||
|
||||
```text
|
||||
templates/template_telecom_billing_product
|
||||
```
|
||||
|
||||
Agentes:
|
||||
|
||||
- BillingAgent
|
||||
- ProductAgent
|
||||
|
||||
### Template 2 — Retail/E-commerce
|
||||
|
||||
Diretório:
|
||||
|
||||
```text
|
||||
templates/template_retail_orders_support
|
||||
```
|
||||
|
||||
Agentes:
|
||||
|
||||
- OrdersAgent
|
||||
- SupportAgent
|
||||
|
||||
Este segundo template mostra como reutilizar a mesma arquitetura para outro domínio de negócio.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Delta Implementado para Padrão FIRST
|
||||
|
||||
Esta versão corrige as prioridades levantadas na comparação com o FIRST:
|
||||
|
||||
1. Oracle Session Repository real
|
||||
2. Oracle Message History real
|
||||
3. Oracle LangGraph Checkpoint Repository real
|
||||
4. LangGraph Deep Telemetry
|
||||
5. Token Accounting
|
||||
6. Cost Accounting
|
||||
7. Session Lock SSE
|
||||
8. Replay Buffer SSE
|
||||
9. KeepAlive SSE
|
||||
10. Recovery por Last-Event-ID
|
||||
11. Redis Provider e Distributed Cache
|
||||
12. Oracle Vector Provider
|
||||
13. Oracle Graph Provider
|
||||
14. RAG Telemetry
|
||||
15. Langfuse Generation Tracking
|
||||
16. OpenTelemetry/Event Bus compatível
|
||||
17. OCI Streaming Exporter preservado
|
||||
|
||||
A lógica de domínio continua genérica; o framework não copia regras específicas de cobrança do FIRST.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Agent Framework FIRST Enterprise Plus
|
||||
|
||||
Esta versão evolui o framework nos quatro blocos solicitados:
|
||||
|
||||
1. **Langfuse Enterprise completo**
|
||||
- `Telemetry.span()` com trace/session/user/metadata/tags.
|
||||
- `Telemetry.generation()` com `usage`, token/cost metadata e compatibilidade Langfuse v2/v3.
|
||||
- `Telemetry.score()` para judges/avaliações.
|
||||
- Eventos arbitrários são registrados como spans seguros para evitar `Unknown observation type` no Langfuse.
|
||||
|
||||
2. **Token/Cost Accounting completo**
|
||||
- `TokenUsageCollector` suporta `prompt_tokens`, `completion_tokens`, `cached_tokens`, `reasoning_tokens` e `total_tokens`.
|
||||
- Tabela de preços por modelo via `MODEL_PRICES_JSON`.
|
||||
- Conversão USD→BRL via `USD_BRL_RATE`.
|
||||
- Persistência em `UsageRepository` e endpoint `/debug/usage`.
|
||||
|
||||
3. **Redis distribuído**
|
||||
- `DistributedCache`: L1 memória + L2 Redis/SQLite/Oracle.
|
||||
- `RedisCache` com `redis.asyncio` quando disponível e fallback sync.
|
||||
- Namespace por `CACHE_KEY_PREFIX`.
|
||||
- Telemetria de cache hit/miss/set/delete.
|
||||
|
||||
4. **Oracle Vector + PGQL reais**
|
||||
- `OracleVectorStore` usa `VECTOR_DISTANCE(..., COSINE)` e `TO_VECTOR()` no Oracle 23ai.
|
||||
- Tentativa automática de criar vector index quando suportado.
|
||||
- `OracleGraphStore` usa tabelas `GRAPH_NODE` e `GRAPH_EDGE`.
|
||||
- Suporte a criação de Property Graph e consulta por `GRAPH_TABLE`/PGQL, com fallback SQL.
|
||||
|
||||
Também foi corrigido o problema de duplicação SSE por replay + fila live usando controle de `max_replayed_id` no `SSEHub.subscribe()`.
|
||||
|
||||
## Testes
|
||||
|
||||
```bash
|
||||
PYTHONPATH=agent_framework/src pytest -q tests/unit
|
||||
```
|
||||
|
||||
Resultado validado nesta geração:
|
||||
|
||||
```text
|
||||
17 passed
|
||||
```
|
||||
|
||||
## Segurança
|
||||
|
||||
Os arquivos `.env` foram higienizados para não conter chaves reais. Configure suas credenciais localmente antes de usar OCI/Langfuse.
|
||||
@@ -0,0 +1,105 @@
|
||||
# Ajustes operacionais finais — padrão FIRST
|
||||
|
||||
Esta versão corrige os gaps identificados na comparação contra o FIRST.
|
||||
|
||||
## Correções aplicadas
|
||||
|
||||
### 1. Checkpoint LangGraph operacional
|
||||
|
||||
O workflow não compila mais com `MemorySaver()` diretamente. Foi criado o adaptador:
|
||||
|
||||
```text
|
||||
agent_framework/checkpoints/langgraph_saver.py
|
||||
```
|
||||
|
||||
Ele conecta o LangGraph ao repository configurado do framework:
|
||||
|
||||
- `memory`
|
||||
- `sqlite`
|
||||
- `oracle` / `autonomous`
|
||||
|
||||
No workflow:
|
||||
|
||||
```python
|
||||
builder.compile(checkpointer=create_langgraph_checkpointer(self.settings))
|
||||
```
|
||||
|
||||
### 2. Telemetria LangGraph envolvendo a execução real
|
||||
|
||||
Foi adicionado wrapper de nó no workflow:
|
||||
|
||||
```python
|
||||
self._node("billing_agent", self.billing_agent)
|
||||
```
|
||||
|
||||
Assim o span/evento `langgraph.node.*` envolve a execução real do nó, não apenas um bloco vazio.
|
||||
|
||||
Eventos emitidos:
|
||||
|
||||
- `langgraph.node.started`
|
||||
- `langgraph.node.completed`
|
||||
- `langgraph.node.failed`
|
||||
- `langgraph.edge.selected`
|
||||
|
||||
### 3. RAG integrado aos agentes
|
||||
|
||||
Os agentes agora recebem `RagService` e usam o contexto recuperado no prompt:
|
||||
|
||||
- BillingAgent
|
||||
- ProductAgent
|
||||
- OrdersAgent
|
||||
- SupportAgent
|
||||
|
||||
O RAG usa:
|
||||
|
||||
- `VECTOR_STORE_PROVIDER=memory|sqlite|oracle|autonomous`
|
||||
- `GRAPH_STORE_PROVIDER=memory|oracle|autonomous`
|
||||
- `RAG_TOP_K`
|
||||
|
||||
### 4. Cache integrado ao runtime dos agentes
|
||||
|
||||
Criado mixin:
|
||||
|
||||
```text
|
||||
agent_template_backend/app/agents/runtime.py
|
||||
```
|
||||
|
||||
Ele adiciona:
|
||||
|
||||
- busca RAG padronizada;
|
||||
- chave de cache para chamada LLM;
|
||||
- hit/miss com telemetria;
|
||||
- cache distribuído via `create_cache(settings)`.
|
||||
|
||||
### 5. Testes unitários
|
||||
|
||||
Criada pasta:
|
||||
|
||||
```text
|
||||
tests/unit
|
||||
```
|
||||
|
||||
Cobertura inicial:
|
||||
|
||||
- cache;
|
||||
- SSE;
|
||||
- RAG;
|
||||
- checkpoint saver;
|
||||
- telemetria LangGraph;
|
||||
- runtime dos agentes;
|
||||
- verificação estática do workflow;
|
||||
- imports principais.
|
||||
|
||||
Validação local executada:
|
||||
|
||||
```text
|
||||
12 passed
|
||||
```
|
||||
|
||||
## Como testar
|
||||
|
||||
```bash
|
||||
cd projeto_agent_framework_first_ready
|
||||
pip install -r agent_template_backend/requirements.txt
|
||||
pytest -q tests/unit
|
||||
```
|
||||
379
agent_framework_oci/Documentacao/README_FIRST_READY.md
Normal file
379
agent_framework_oci/Documentacao/README_FIRST_READY.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# Projeto Agent Framework FIRST-ready
|
||||
|
||||
Esta versão mantém a arquitetura do `meu_projeto_agent_framework` e adiciona os padrões operacionais encontrados no projeto FIRST.
|
||||
|
||||
## Recursos adicionados
|
||||
|
||||
1. **SSE no padrão FIRST**
|
||||
- `GET /gateway/events/{session_id}` para stream `text/event-stream`.
|
||||
- `POST /gateway/message/sse` para processar mensagem emitindo eventos SSE.
|
||||
- Eventos: `connected`, `flow.start`, `session.upserted`, `message.received`, `workflow.started`, `workflow.completed`, `message.responded`, `flow.end`.
|
||||
- Keepalive configurável por `SSE_KEEPALIVE_SECONDS`.
|
||||
- Lock por sessão para evitar concorrência dentro da mesma conversa.
|
||||
- Replay de eventos via `Last-Event-ID` ou query param `last_event_id`.
|
||||
|
||||
2. **Persistência de sessão e mensagens**
|
||||
- Implementado provider `sqlite`, executável localmente.
|
||||
- `SESSION_REPOSITORY_PROVIDER=sqlite`.
|
||||
- `MEMORY_REPOSITORY_PROVIDER=sqlite`.
|
||||
- Tabelas locais: `agent_sessions`, `agent_messages`.
|
||||
- Idempotência por `message_id`.
|
||||
|
||||
3. **Checkpoint persistente**
|
||||
- Implementado provider `sqlite` para checkpoint final do workflow.
|
||||
- `CHECKPOINT_REPOSITORY_PROVIDER=sqlite`.
|
||||
- Endpoint de leitura: `GET /sessions/{session_id}/checkpoint`.
|
||||
|
||||
4. **Histórico de mensagens**
|
||||
- Endpoint: `GET /sessions/{session_id}/messages`.
|
||||
- Histórico usado como memória conversacional antes de chamar o LangGraph.
|
||||
|
||||
5. **Cache**
|
||||
- Novo módulo `agent_framework.cache.cache`.
|
||||
- Suporta cache local em memória e Redis se `ENABLE_REDIS_CACHE=true`.
|
||||
|
||||
6. **RAG / Vector Store**
|
||||
- `agent_framework.rag.vector_store` agora possui `InMemoryVectorStore`, `SQLiteVectorStore` e contrato `AutonomousVectorStore`.
|
||||
- A versão SQLite usa busca lexical local para desenvolvimento.
|
||||
- O contrato permite trocar por Oracle Vector Search sem alterar a camada de aplicação.
|
||||
|
||||
7. **Observabilidade**
|
||||
- Mantém Langfuse existente.
|
||||
- Acrescenta eventos de gateway/SSE/workflow com `session_id`, `agent_id`, `tenant_id`, `message_id`, rota e intenção.
|
||||
|
||||
## Arquitetura resultante
|
||||
|
||||
```text
|
||||
Browser
|
||||
|-- POST /gateway/message/sse
|
||||
|-- GET /gateway/events/{session_id}
|
||||
|
|
||||
FastAPI Template Backend
|
||||
|
|
||||
ChannelGateway
|
||||
|
|
||||
SessionRepository + MessageHistory + CheckpointRepository
|
||||
|
|
||||
LangGraph AgentWorkflow
|
||||
|
|
||||
Guardrails -> Router/Supervisor -> Agent -> Output Guardrails -> Judges
|
||||
|
|
||||
Telemetry / Langfuse / OCI Streaming
|
||||
```
|
||||
|
||||
## Como rodar localmente
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
pip install -e ../agent_framework
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd agent_frontend
|
||||
python -m http.server 3000
|
||||
```
|
||||
|
||||
Abra:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## Variáveis principais
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
SQLITE_DB_PATH=./data/agent_framework.db
|
||||
ENABLE_SSE=true
|
||||
SSE_KEEPALIVE_SECONDS=15
|
||||
ENABLE_MESSAGE_IDEMPOTENCY=true
|
||||
```
|
||||
|
||||
## Teste via curl
|
||||
|
||||
Mensagem normal:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","message":"teste","session_id":"s1","user_id":"u1","message_id":"m1"}}'
|
||||
```
|
||||
|
||||
Mensagem com SSE:
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:8000/gateway/events/s1
|
||||
```
|
||||
|
||||
Em outro terminal:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message/sse \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","message":"teste","session_id":"s1","user_id":"u1","message_id":"m2"}}'
|
||||
```
|
||||
|
||||
Histórico:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/sessions/s1/messages
|
||||
```
|
||||
|
||||
Checkpoint:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/sessions/s1/checkpoint
|
||||
```
|
||||
|
||||
## Observação importante
|
||||
|
||||
A versão adicionada é executável localmente com SQLite. As classes `AutonomousSessionRepository`, `DatabaseMessageHistory`, `AutonomousCheckpointRepository` e `AutonomousVectorStore` mantêm o contrato para Oracle Autonomous Database, mas nesta entrega usam SQLite como backend local para permitir rodar e testar sem infraestrutura Oracle.
|
||||
|
||||
## Evolução de Observabilidade no padrão FIRST
|
||||
|
||||
Esta versão adiciona uma camada corporativa de observabilidade ao framework, mantendo os componentes reutilizáveis dentro de `agent_framework`.
|
||||
|
||||
### Componentes adicionados
|
||||
|
||||
```text
|
||||
agent_framework/observability/
|
||||
├── context.py # ContextVar: request_id, session_id, user_id, tenant_id, agent_id, channel, ura_call_id, workflow_id, message_id
|
||||
├── telemetry.py # Facade central: span, event, generation, rag_event, cache_event, checkpoint_event
|
||||
├── event_bus.py # Event bus interno para plugar logs, SSE, OCI Streaming, Elastic, Phoenix etc.
|
||||
├── otel.py # OpenTelemetry opcional via OTLP
|
||||
├── workflow_events.py # workflow.started, node.started, node.completed, edge.selected, workflow.failed
|
||||
├── guardrail_events.py # guardrail.<CODE>.evaluated e guardrail.<CODE>.blocked
|
||||
├── judge_events.py # judge.<NAME>.evaluated
|
||||
├── streaming_events.py # sse.connected, sse.keepalive, sse.event.emitted
|
||||
└── decorators.py # decorator @traced para classes do framework
|
||||
```
|
||||
|
||||
### Correlação ponta-a-ponta
|
||||
|
||||
Cada chamada HTTP cria ou propaga `x-request-id` e o fluxo de mensagem vincula:
|
||||
|
||||
```text
|
||||
request_id → tenant_id → agent_id → session_id → user_id → channel → message_id → workflow_id
|
||||
```
|
||||
|
||||
O contexto usa `ContextVar`, portanto funciona em chamadas assíncronas, FastAPI, LangGraph e providers LLM.
|
||||
|
||||
### Langfuse
|
||||
|
||||
Ative no `.env`:
|
||||
|
||||
```env
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_PUBLIC_KEY=pk-lf-...
|
||||
LANGFUSE_SECRET_KEY=sk-lf-...
|
||||
LANGFUSE_HOST=http://localhost:3000
|
||||
```
|
||||
|
||||
O framework registra:
|
||||
|
||||
```text
|
||||
Trace de conversa
|
||||
├── http.request
|
||||
├── agent.gateway_message
|
||||
├── workflow.langgraph.ainvoke
|
||||
├── workflow.input_guardrails
|
||||
│ └── guardrail.<CODE>.evaluated / blocked
|
||||
├── workflow.routing_decision
|
||||
├── workflow.agent.<agent>
|
||||
│ └── generation.<model>
|
||||
├── workflow.output_guardrails
|
||||
├── workflow.judge
|
||||
│ └── judge.<NAME>.evaluated
|
||||
├── workflow.supervisor_review
|
||||
├── workflow.persist
|
||||
└── sse.event.emitted / sse.keepalive
|
||||
```
|
||||
|
||||
### OpenTelemetry
|
||||
|
||||
Ative no `.env`:
|
||||
|
||||
```env
|
||||
ENABLE_OTEL=true
|
||||
OTEL_SERVICE_NAME=agent-framework-template
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
|
||||
```
|
||||
|
||||
Com isso, os mesmos spans são exportados via OTLP para Elastic, Grafana Tempo, Jaeger, Collector ou outro backend compatível.
|
||||
|
||||
### SSE observável
|
||||
|
||||
O `SSEHub` agora registra eventos de:
|
||||
|
||||
- conexão aberta;
|
||||
- replay de eventos;
|
||||
- evento emitido;
|
||||
- keepalive;
|
||||
- lock por sessão no processamento de mensagem.
|
||||
|
||||
### Guardrails e Judges
|
||||
|
||||
Além dos eventos agregados (`guardrails.input.completed`, `judges.completed`), cada decisão individual gera telemetria própria:
|
||||
|
||||
```text
|
||||
guardrail.MSK.evaluated
|
||||
guardrail.OOS.blocked
|
||||
judge.response_quality.evaluated
|
||||
judge.groundedness.evaluated
|
||||
```
|
||||
|
||||
### Extensão para outros backends
|
||||
|
||||
A classe `Telemetry.event_bus` permite plugar novos handlers sem alterar o workflow. Exemplo:
|
||||
|
||||
```python
|
||||
async def enviar_para_elastic(event):
|
||||
...
|
||||
|
||||
telemetry.event_bus.subscribe(enviar_para_elastic)
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Evolução FIRST Enterprise Completa
|
||||
|
||||
Esta versão recebeu os componentes que faltavam para aproximar o framework do padrão operacional do projeto FIRST:
|
||||
|
||||
### Persistência Oracle Autonomous Database
|
||||
|
||||
Foram adicionados providers reais Oracle:
|
||||
|
||||
- `OracleSessionRepository`
|
||||
- `OracleMessageHistory`
|
||||
- `OracleCheckpointRepository`
|
||||
- `OracleCache`
|
||||
- `OracleVectorStore`
|
||||
- `OracleGraphStore`
|
||||
- `OracleStore`
|
||||
|
||||
Tabelas criadas automaticamente com prefixo configurável `ADB_TABLE_PREFIX`:
|
||||
|
||||
- `<PREFIX>_AGENT_SESSION`
|
||||
- `<PREFIX>_AGENT_MESSAGE`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT_WRITE`
|
||||
- `<PREFIX>_WORKFLOW_CHECKPOINT_BLOB`
|
||||
- `<PREFIX>_SSE_EVENT`
|
||||
- `<PREFIX>_CACHE_ENTRY`
|
||||
- `<PREFIX>_RAG_DOCUMENT`
|
||||
- `<PREFIX>_GRAPH_EDGE`
|
||||
|
||||
### Configuração Oracle
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=oracle
|
||||
MEMORY_REPOSITORY_PROVIDER=oracle
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=oracle
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
VECTOR_STORE_PROVIDER=oracle
|
||||
GRAPH_STORE_PROVIDER=oracle
|
||||
SSE_STORE_PROVIDER=oracle
|
||||
|
||||
ADB_USER=ADMIN
|
||||
ADB_PASSWORD=***
|
||||
ADB_DSN=meu_adb_high
|
||||
ADB_WALLET_LOCATION=/path/wallet
|
||||
ADB_WALLET_PASSWORD=***
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
|
||||
### SSE Enterprise
|
||||
|
||||
O SSE agora possui:
|
||||
|
||||
- lock por sessão (`SessionLockManager`)
|
||||
- keepalive configurável
|
||||
- replay por `Last-Event-ID`
|
||||
- persistência de eventos em SQLite ou Oracle
|
||||
- telemetria de conexão, replay, keepalive e desconexão
|
||||
|
||||
Endpoint:
|
||||
|
||||
```text
|
||||
GET /gateway/events/{session_id}?last_event_id=123
|
||||
```
|
||||
|
||||
### LangGraph Deep Telemetry
|
||||
|
||||
Foi adicionado `LangGraphDeepTelemetry` com eventos:
|
||||
|
||||
- `langgraph.node.started`
|
||||
- `langgraph.node.completed`
|
||||
- `langgraph.node.failed`
|
||||
- `langgraph.edge.selected`
|
||||
|
||||
Esses eventos são enviados para o Event Bus, Langfuse e OpenTelemetry quando habilitados.
|
||||
|
||||
### Token e Cost Accounting
|
||||
|
||||
Foi adicionado:
|
||||
|
||||
- `TokenUsageCollector`
|
||||
- `CostTracker`
|
||||
- cálculo de `prompt_tokens`, `completion_tokens`, `cached_tokens`, `total_tokens`
|
||||
- cálculo de `cost_usd` e `cost_brl`
|
||||
|
||||
Configuração opcional:
|
||||
|
||||
```env
|
||||
USD_BRL_RATE=5.0
|
||||
MODEL_PRICES_JSON={"openai.gpt-4.1":{"input_per_1m":"2.00","output_per_1m":"8.00"}}
|
||||
```
|
||||
|
||||
### Cache Enterprise
|
||||
|
||||
O cache agora é em cascata:
|
||||
|
||||
```text
|
||||
L1: InMemory
|
||||
L2: Redis, SQLite ou Oracle
|
||||
```
|
||||
|
||||
Configuração:
|
||||
|
||||
```env
|
||||
ENABLE_REDIS_CACHE=true
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
```
|
||||
|
||||
ou:
|
||||
|
||||
```env
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
```
|
||||
|
||||
### RAG Oracle 23ai
|
||||
|
||||
Foi adicionado `OracleVectorStore`, com suporte a coluna `VECTOR` e `VECTOR_DISTANCE()` quando um embedding provider for conectado.
|
||||
Sem embedding provider, mantém fallback lexical para desenvolvimento local.
|
||||
|
||||
Também foi adicionado `OracleGraphStore` com tabela de arestas, pronto para evoluir para PGQL/Property Graph.
|
||||
|
||||
### Langfuse
|
||||
|
||||
Cada chamada LLM agora gera `generation` com:
|
||||
|
||||
- input
|
||||
- output
|
||||
- model
|
||||
- provider
|
||||
- token usage
|
||||
- cost metadata
|
||||
|
||||
Além disso, spans de workflow, guardrails, judges, RAG, cache, checkpoint, SSE e LangGraph são publicados pelo mesmo Event Bus.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Guardrails implementados no framework
|
||||
|
||||
Esta versão adiciona uma camada pragmática de guardrails ao `agent_framework`, inspirada na separação de rails por estágio: input, output, retrieval e execução/tool.
|
||||
|
||||
## Rails de input
|
||||
|
||||
- `MSIZE` — bloqueia mensagens excessivamente grandes.
|
||||
- `MSK` — mascara CPF, CNPJ, telefone, e-mail, cartão, CEP, RG, tokens e chaves.
|
||||
- `TOX` — detecta toxicidade e registra severidade sem bloquear por padrão.
|
||||
- `PINJ` — detecta prompt injection e registra score.
|
||||
- `JBRK` — detecta jailbreak/roleplay de burla e registra score.
|
||||
- `VLOOP` — bloqueia loop conversacional repetitivo.
|
||||
|
||||
## Rails de output
|
||||
|
||||
- `PII_OUT` — mascara PII na resposta do agente.
|
||||
- `CMP` — suaviza promessas absolutas e linguagem de garantia excessiva.
|
||||
- `REVPREC` — bloqueia verbalização de ação operacional sem confirmação de tool.
|
||||
- `GND` — sinaliza groundedness/risco quando há resposta específica sem evidência.
|
||||
- `ALUC_RISK` — marca risco de alucinação para telemetria e judges.
|
||||
|
||||
## Rails opcionais
|
||||
|
||||
- `RET_REL` — valida relevância de chunks de retrieval por score mínimo.
|
||||
- `TOOL_VAL` — valida ferramenta MCP/tool, argumentos obrigatórios, valores negativos e allowlist.
|
||||
|
||||
## Arquivos alterados
|
||||
|
||||
- `agent_framework/src/agent_framework/guardrails/rails.py`
|
||||
- `agent_framework/src/agent_framework/guardrails/pipeline.py`
|
||||
- `agent_framework/src/agent_framework/guardrails/__init__.py`
|
||||
|
||||
## Uso rápido
|
||||
|
||||
```python
|
||||
from agent_framework.guardrails.pipeline import GuardrailPipeline
|
||||
|
||||
pipeline = GuardrailPipeline()
|
||||
|
||||
sanitized_input, input_decisions = await pipeline.run_input(
|
||||
user_text,
|
||||
{"history_texts": history_texts},
|
||||
)
|
||||
|
||||
final_answer, output_decisions = await pipeline.run_output(
|
||||
answer,
|
||||
context,
|
||||
)
|
||||
```
|
||||
|
||||
Para tools/MCP:
|
||||
|
||||
```python
|
||||
_, decisions = await pipeline.run_tool(
|
||||
"cancelar_produto",
|
||||
{"produto": "VAS", "valor": 0},
|
||||
{
|
||||
"required_args": ["produto"],
|
||||
"allowed_tools": ["cancelar_produto", "consultar_fatura"],
|
||||
},
|
||||
)
|
||||
```
|
||||
139
agent_framework_oci/Documentacao/README_MAX_OPERACIONAL.md
Normal file
139
agent_framework_oci/Documentacao/README_MAX_OPERACIONAL.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Projeto Agent Framework — FIRST Operational Max
|
||||
|
||||
Esta versão adiciona os ajustes operacionais que faltavam para aproximar o framework do padrão FIRST em produção.
|
||||
|
||||
## Ajustes incluídos nesta versão
|
||||
|
||||
### 1. Langfuse Enterprise Adapter
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/observability/langfuse_enterprise.py
|
||||
```
|
||||
|
||||
Inclui adaptador compatível com SDKs Langfuse v2/v3 para:
|
||||
|
||||
- atualização de trace;
|
||||
- score/avaliação de trace;
|
||||
- prompt registry quando suportado pelo SDK;
|
||||
- isolamento das diferenças de API do Langfuse.
|
||||
|
||||
### 2. Token e Cost Accounting persistente
|
||||
Novo pacote:
|
||||
|
||||
```text
|
||||
agent_framework/billing/
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `UsageRecord`
|
||||
- `SQLiteUsageRepository`
|
||||
- `OracleUsageRepository`
|
||||
- `create_usage_repository(settings)`
|
||||
|
||||
O provider LLM agora registra automaticamente:
|
||||
|
||||
- `prompt_tokens`
|
||||
- `completion_tokens`
|
||||
- `cached_tokens`
|
||||
- `total_tokens`
|
||||
- `cost_usd`
|
||||
- `cost_brl`
|
||||
- `tenant_id`
|
||||
- `agent_id`
|
||||
- `session_id`
|
||||
- `message_id`
|
||||
|
||||
Novo endpoint:
|
||||
|
||||
```http
|
||||
GET /debug/usage
|
||||
GET /debug/usage?tenant_id=default
|
||||
GET /debug/usage?session_id=<id>
|
||||
```
|
||||
|
||||
### 3. RAG Service operacional
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
agent_framework/rag/rag_service.py
|
||||
```
|
||||
|
||||
Inclui:
|
||||
|
||||
- `RagService.add_documents()`
|
||||
- `RagService.retrieve()`
|
||||
- `RagResult.as_prompt_context()`
|
||||
- telemetria de latência, quantidade de documentos, top scores e grafo.
|
||||
|
||||
### 4. Configuração nova
|
||||
Variável adicionada:
|
||||
|
||||
```env
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
```
|
||||
|
||||
Valores:
|
||||
|
||||
```text
|
||||
sqlite
|
||||
oracle
|
||||
autonomous
|
||||
```
|
||||
|
||||
### 5. Compatibilidade operacional local
|
||||
Por padrão, a contabilização de uso usa SQLite mesmo que o restante esteja em memória. Assim é possível testar localmente sem Oracle.
|
||||
|
||||
## Teste rápido
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Teste uma mensagem:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"teste","user_id":"u1","session_id":"s1"}}'
|
||||
```
|
||||
|
||||
Verifique uso/custo:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/debug/usage
|
||||
```
|
||||
|
||||
## Para rodar com padrão mais próximo de produção
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
CACHE_BACKEND_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_HOST=http://localhost:3000
|
||||
LANGFUSE_PUBLIC_KEY=...
|
||||
LANGFUSE_SECRET_KEY=...
|
||||
```
|
||||
|
||||
Para Autonomous Database:
|
||||
|
||||
```env
|
||||
SESSION_REPOSITORY_PROVIDER=oracle
|
||||
MEMORY_REPOSITORY_PROVIDER=oracle
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=oracle
|
||||
USAGE_REPOSITORY_PROVIDER=oracle
|
||||
CACHE_BACKEND_PROVIDER=oracle
|
||||
VECTOR_STORE_PROVIDER=oracle
|
||||
GRAPH_STORE_PROVIDER=oracle
|
||||
ADB_USER=...
|
||||
ADB_PASSWORD=...
|
||||
ADB_DSN=...
|
||||
ADB_WALLET_LOCATION=...
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
```
|
||||
79
agent_framework_oci/Documentacao/README_MCP.md
Normal file
79
agent_framework_oci/Documentacao/README_MCP.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# AI Agent Platform com MCP Tools
|
||||
|
||||
Esta versão adiciona uma camada MCP ao framework:
|
||||
|
||||
- `agent_framework.mcp.MCPToolRouter`
|
||||
- `agent_template_backend/config/mcp_servers.yaml`
|
||||
- `agent_template_backend/config/tools.yaml`
|
||||
- `mcp_servers/telecom_mcp_server`
|
||||
- `mcp_servers/retail_mcp_server`
|
||||
|
||||
## Subir localmente
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
bash ./scripts/run_mcp_servers.sh
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
uvicorn app.main:app --reload --reload-dir app --reload-dir config --port 8000
|
||||
```
|
||||
|
||||
Terminal 3:
|
||||
|
||||
```bash
|
||||
cd agent_frontend
|
||||
python -m http.server 5173
|
||||
```
|
||||
|
||||
## Testes rápidos
|
||||
|
||||
Listar tools MCP carregadas pelo backend:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/debug/mcp/tools
|
||||
```
|
||||
|
||||
Chamar tool diretamente via backend:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/debug/mcp/call/consultar_fatura \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"msisdn":"11999999999","invoice_id":"INV-001"}'
|
||||
```
|
||||
|
||||
Roteamento Telecom + MCP:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"session_id":"sess-tel-1","message":"Minha fatura veio alta","context":{"msisdn":"11999999999","invoice_id":"INV-001"}}}'
|
||||
```
|
||||
|
||||
Roteamento Retail + MCP:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"session_id":"sess-ret-1","message":"Meu pedido não chegou","context":{"order_id":"PED-1001","customer_id":"C-001"}}}'
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
```bash
|
||||
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).
|
||||
108
agent_framework_oci/Documentacao/README_MULTI_AGENT_ISOLATION.md
Normal file
108
agent_framework_oci/Documentacao/README_MULTI_AGENT_ISOLATION.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Multi-agent isolation
|
||||
|
||||
Esta versão permite subir mais de um `agent_template` no mesmo backend e chavear por `agent_id` sem misturar estado.
|
||||
|
||||
## O que ficou isolado
|
||||
|
||||
A chave lógica usada pelo backend é:
|
||||
|
||||
```text
|
||||
tenant_id:agent_id:session_id
|
||||
```
|
||||
|
||||
Com isso ficam isolados:
|
||||
|
||||
- memória conversacional;
|
||||
- checkpoints do LangGraph (`thread_id`);
|
||||
- telemetria/tags;
|
||||
- prompts por perfil de agente;
|
||||
- configuração de guardrails por agente;
|
||||
- configuração de judges por agente;
|
||||
- metadados de sessão.
|
||||
|
||||
## Arquivo principal
|
||||
|
||||
```text
|
||||
agent_template_backend/config/agents.yaml
|
||||
```
|
||||
|
||||
Exemplo:
|
||||
|
||||
```yaml
|
||||
default_agent_id: telecom_contas
|
||||
agents:
|
||||
- agent_id: telecom_contas
|
||||
prompt_policy_path: ./config/agents/telecom_contas/prompt_policy.yaml
|
||||
guardrails_config_path: ./config/agents/telecom_contas/guardrails.yaml
|
||||
judges_config_path: ./config/agents/telecom_contas/judges.yaml
|
||||
|
||||
- agent_id: retail_orders
|
||||
prompt_policy_path: ./config/agents/retail_orders/prompt_policy.yaml
|
||||
guardrails_config_path: ./config/agents/retail_orders/guardrails.yaml
|
||||
judges_config_path: ./config/agents/retail_orders/judges.yaml
|
||||
```
|
||||
|
||||
## Como escolher o agente na chamada
|
||||
|
||||
### Telecom
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"agent_id": "telecom_contas",
|
||||
"tenant_id": "tim",
|
||||
"payload": {
|
||||
"session_id": "sessao-123",
|
||||
"user_id": "cliente-1",
|
||||
"message": "Quero entender minha fatura",
|
||||
"context": {"invoice_id": "FAT-001"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Retail
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/gateway/message \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"channel": "web",
|
||||
"agent_id": "retail_orders",
|
||||
"tenant_id": "loja",
|
||||
"payload": {
|
||||
"session_id": "sessao-123",
|
||||
"user_id": "cliente-1",
|
||||
"message": "Onde está meu pedido?",
|
||||
"context": {"order_id": "PED-001"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Mesmo usando o mesmo `session_id`, as conversas ficam separadas porque as chaves finais serão:
|
||||
|
||||
```text
|
||||
tim:telecom_contas:sessao-123
|
||||
loja:retail_orders:sessao-123
|
||||
```
|
||||
|
||||
## Endpoints úteis
|
||||
|
||||
```text
|
||||
GET /agents
|
||||
GET /health
|
||||
POST /debug/route
|
||||
POST /gateway/message
|
||||
```
|
||||
|
||||
## Como adicionar um novo agent_template
|
||||
|
||||
1. Crie uma pasta em `agent_template_backend/config/agents/<novo_agent_id>/`.
|
||||
2. Adicione `prompt_policy.yaml`, `guardrails.yaml` e `judges.yaml`.
|
||||
3. Registre o agente em `config/agents.yaml`.
|
||||
4. Chame `/gateway/message` usando `agent_id=<novo_agent_id>`.
|
||||
|
||||
## Observação arquitetural
|
||||
|
||||
O backend continua usando um único processo FastAPI e um único framework instalado, mas o estado persistido não usa mais `session_id` sozinho. Isso evita que dois agentes compartilhem memória, checkpoints ou decisões de governança acidentalmente.
|
||||
@@ -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.
|
||||
281
agent_framework_oci/Documentacao/README_ROUTING_MODES.md
Normal file
281
agent_framework_oci/Documentacao/README_ROUTING_MODES.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Modos de roteamento multi-agent: Enterprise Router e Supervisor
|
||||
|
||||
Este projeto suporta dois desenhos arquiteturais para roteamento entre agentes, sem precisar criar dois frameworks diferentes.
|
||||
|
||||
## Modos disponíveis
|
||||
|
||||
Configure por variável de ambiente:
|
||||
|
||||
```bash
|
||||
ROUTING_MODE=router
|
||||
```
|
||||
|
||||
ou:
|
||||
|
||||
```bash
|
||||
ROUTING_MODE=supervisor
|
||||
```
|
||||
|
||||
Também existe a chave documental em `agent_template_backend/config/routing.yaml`:
|
||||
|
||||
```yaml
|
||||
router:
|
||||
mode: router
|
||||
```
|
||||
|
||||
A variável de ambiente `ROUTING_MODE` é a forma recomendada para ativar um modo em runtime, especialmente em Docker, Kubernetes ou OCI.
|
||||
|
||||
---
|
||||
|
||||
## Opção 1: Enterprise Router
|
||||
|
||||
Fluxo:
|
||||
|
||||
```text
|
||||
Usuário
|
||||
-> Input Guardrails
|
||||
-> EnterpriseRouter
|
||||
-> AgentRegistry
|
||||
-> 1 agente especialista
|
||||
-> Output Guardrails
|
||||
-> Judges
|
||||
-> Supervisor Review
|
||||
-> Persistência/eventos
|
||||
```
|
||||
|
||||
Uso recomendado quando cada mensagem deve ser atendida por um único agente especialista.
|
||||
|
||||
Exemplos:
|
||||
|
||||
- `Minha fatura veio alta` -> `billing_agent`
|
||||
- `Onde está meu pedido?` -> `orders_agent`
|
||||
- `Quero trocar um produto com defeito` -> `support_agent`
|
||||
|
||||
Vantagens:
|
||||
|
||||
- Menor latência.
|
||||
- Menor custo de tokens.
|
||||
- Debug mais simples.
|
||||
- Mais fácil de operar em produção.
|
||||
|
||||
Limitação:
|
||||
|
||||
- Uma mensagem com múltiplos assuntos precisa ser roteada para um agente principal ou tratada por handoff.
|
||||
|
||||
---
|
||||
|
||||
## Opção 2: Supervisor
|
||||
|
||||
Fluxo:
|
||||
|
||||
```text
|
||||
Usuário
|
||||
-> Input Guardrails
|
||||
-> Supervisor.route_plan
|
||||
-> supervisor_agent
|
||||
-> billing_agent opcional
|
||||
-> orders_agent opcional
|
||||
-> product_agent opcional
|
||||
-> support_agent opcional
|
||||
-> Consolidação
|
||||
-> Output Guardrails
|
||||
-> Judges
|
||||
-> Supervisor Review
|
||||
-> Persistência/eventos
|
||||
```
|
||||
|
||||
Uso recomendado quando uma única mensagem pode envolver vários agentes.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```text
|
||||
Meu pedido não chegou e também fui cobrado duas vezes.
|
||||
```
|
||||
|
||||
Neste caso, o supervisor pode acionar:
|
||||
|
||||
- `orders_agent`
|
||||
- `billing_agent`
|
||||
|
||||
Vantagens:
|
||||
|
||||
- Suporta múltiplas intenções na mesma mensagem.
|
||||
- Permite consolidação de respostas.
|
||||
- Facilita cenários enterprise com vários domínios.
|
||||
|
||||
Custos:
|
||||
|
||||
- Maior latência.
|
||||
- Maior consumo de tokens.
|
||||
- Mais complexidade operacional.
|
||||
|
||||
---
|
||||
|
||||
## O que foi alterado no código
|
||||
|
||||
### 1. Configuração
|
||||
|
||||
Arquivo:
|
||||
|
||||
```text
|
||||
agent_framework/src/agent_framework/config/settings.py
|
||||
```
|
||||
|
||||
Foi adicionada a configuração:
|
||||
|
||||
```python
|
||||
ROUTING_MODE: Literal['router','supervisor'] = 'router'
|
||||
```
|
||||
|
||||
### 2. Workflow LangGraph
|
||||
|
||||
Arquivo:
|
||||
|
||||
```text
|
||||
agent_template_backend/app/workflows/agent_graph.py
|
||||
```
|
||||
|
||||
O nó `enterprise_route` foi substituído por um nó genérico:
|
||||
|
||||
```text
|
||||
routing_decision
|
||||
```
|
||||
|
||||
Esse nó decide o caminho com base em `ROUTING_MODE`:
|
||||
|
||||
- `router` usa `EnterpriseRouter`.
|
||||
- `supervisor` usa `Supervisor.route_plan`.
|
||||
|
||||
Também foi adicionado o nó:
|
||||
|
||||
```text
|
||||
supervisor_agent
|
||||
```
|
||||
|
||||
Ele executa um ou mais agentes e consolida o resultado.
|
||||
|
||||
### 3. Supervisor
|
||||
|
||||
Arquivo:
|
||||
|
||||
```text
|
||||
agent_framework/src/agent_framework/supervisor/supervisor.py
|
||||
```
|
||||
|
||||
Foi adicionada a estrutura:
|
||||
|
||||
```python
|
||||
SupervisorPlan
|
||||
```
|
||||
|
||||
E o método:
|
||||
|
||||
```python
|
||||
route_plan(state)
|
||||
```
|
||||
|
||||
Esse método retorna uma lista de agentes a executar.
|
||||
|
||||
### 4. Debug
|
||||
|
||||
Endpoint:
|
||||
|
||||
```text
|
||||
POST /debug/route
|
||||
```
|
||||
|
||||
Agora respeita `ROUTING_MODE` e permite verificar rapidamente como uma mensagem será roteada.
|
||||
|
||||
---
|
||||
|
||||
## Como testar localmente
|
||||
|
||||
### Instalação
|
||||
|
||||
```bash
|
||||
cd agent_template_backend
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -U pip setuptools wheel
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Modo Router
|
||||
|
||||
```bash
|
||||
export ROUTING_MODE=router
|
||||
uvicorn app.main:app --reload --port 8000
|
||||
```
|
||||
|
||||
Teste:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/debug/route \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"Onde está meu pedido?","session_id":"s1"}}'
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "router",
|
||||
"route": "orders_agent"
|
||||
}
|
||||
```
|
||||
|
||||
### Modo Supervisor
|
||||
|
||||
```bash
|
||||
export ROUTING_MODE=supervisor
|
||||
uvicorn app.main:app --reload --port 8000
|
||||
```
|
||||
|
||||
Teste:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/debug/route \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"channel":"web","payload":{"text":"Meu pedido atrasou e minha fatura veio duplicada","session_id":"s2"}}'
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "supervisor",
|
||||
"route": "supervisor_agent",
|
||||
"agents": ["billing_agent", "orders_agent"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Isolamento
|
||||
|
||||
A chave lógica de isolamento permanece:
|
||||
|
||||
```text
|
||||
tenant_id:agent_id:session_id
|
||||
```
|
||||
|
||||
Use essa chave para memória, sessão, checkpoint e telemetria. Em produção, recomenda-se padronizar `agent_id` por agente especialista ou por template, dependendo do nível de isolamento desejado.
|
||||
|
||||
---
|
||||
|
||||
## Recomendação
|
||||
|
||||
Comece em produção com:
|
||||
|
||||
```bash
|
||||
ROUTING_MODE=router
|
||||
```
|
||||
|
||||
Ative:
|
||||
|
||||
```bash
|
||||
ROUTING_MODE=supervisor
|
||||
```
|
||||
|
||||
quando houver necessidade real de múltiplos agentes na mesma mensagem.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Template Backend/Frontend alinhado ao BusinessContext v2
|
||||
|
||||
Este pacote atualiza o `agent_template_backend` e o `agent_frontend` para refletir o framework novo, onde as chaves vindas do canal/front-end são resolvidas uma vez como chaves canônicas e propagadas pelas camadas até o MCP Server.
|
||||
|
||||
## Fluxo implementado
|
||||
|
||||
1. O front-end envia `tenant_id`, `agent_id`, `session_id` e `business_context`.
|
||||
2. O backend normaliza a mensagem via `ChannelGateway` preservando todo o payload no `context`.
|
||||
3. O backend usa `IdentityResolver` com `config/identity.yaml` para gerar `BusinessContext`:
|
||||
- `customer_key`
|
||||
- `contract_key`
|
||||
- `interaction_key`
|
||||
- `account_key`
|
||||
- `resource_key`
|
||||
- `session_key`
|
||||
4. O workflow recebe `context.business_context`.
|
||||
5. Os agentes de exemplo não montam mais argumentos específicos como `msisdn`, `invoice_id` ou `order_id` diretamente.
|
||||
6. O `MCPToolRouter` usa `config/mcp_parameter_mapping.yaml` para converter chaves canônicas em parâmetros reais de cada tool MCP.
|
||||
|
||||
## Arquivos principais ajustados
|
||||
|
||||
- `agent_template_backend/app/main.py`
|
||||
- carrega `IdentityResolver`;
|
||||
- resolve `BusinessContext` por mensagem;
|
||||
- persiste as chaves na sessão/memória/metadata/SSE;
|
||||
- adiciona `/debug/identity`.
|
||||
|
||||
- `agent_template_backend/app/agents/runtime.py`
|
||||
- adiciona `_collect_mcp_context()` centralizado;
|
||||
- repassa `business_context` e `original_context` para o MCP Router.
|
||||
|
||||
- `agent_template_backend/app/agents/*_agent.py`
|
||||
- agentes passam a usar `_collect_mcp_context()` em vez de montar argumentos específicos.
|
||||
|
||||
- `agent_template_backend/config/identity.yaml`
|
||||
- define como campos do canal/front-end alimentam as chaves canônicas.
|
||||
|
||||
- `agent_template_backend/config/mcp_parameter_mapping.yaml`
|
||||
- define como chaves canônicas viram parâmetros reais por tool MCP.
|
||||
|
||||
- `agent_frontend/index.html` e `agent_frontend/app.js`
|
||||
- adicionam campos de `tenant`, `agent` e chaves canônicas;
|
||||
- enviam `business_context` no payload;
|
||||
- mantêm aliases de domínio para compatibilidade (`msisdn`, `invoice_id`, `order_id`, etc.).
|
||||
|
||||
## Teste rápido
|
||||
|
||||
Suba backend, frontend e MCP servers. Depois teste:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8000/health | jq
|
||||
|
||||
curl -s -X POST http://localhost:8000/debug/identity \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"channel":"web",
|
||||
"tenant_id":"default",
|
||||
"agent_id":"telecom_contas",
|
||||
"payload":{
|
||||
"message":"Minha fatura veio alta",
|
||||
"session_id":"teste-001",
|
||||
"msisdn":"11999999999",
|
||||
"invoice_id":"3000131180",
|
||||
"ura_call_id":"URA-123",
|
||||
"business_context":{
|
||||
"customer_key":"11999999999",
|
||||
"contract_key":"3000131180",
|
||||
"interaction_key":"URA-123",
|
||||
"session_key":"teste-001"
|
||||
}
|
||||
}
|
||||
}' | jq
|
||||
|
||||
curl -s -X POST http://localhost:8000/debug/mcp/call/consultar_fatura \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"business_context": {
|
||||
"customer_key":"11999999999",
|
||||
"contract_key":"3000131180",
|
||||
"interaction_key":"URA-123",
|
||||
"session_key":"teste-001"
|
||||
}
|
||||
}' | jq
|
||||
```
|
||||
|
||||
No log do backend, procure por `mcp.tool.mapped`. Ele deve indicar as chaves mapeadas e `has_msisdn=true`, `has_invoice_id=true` para o domínio telecom.
|
||||
28
agent_framework_oci/Documentacao/README_TESTES_UNITARIOS.md
Normal file
28
agent_framework_oci/Documentacao/README_TESTES_UNITARIOS.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Testes unitários do framework
|
||||
|
||||
Esta versão inclui uma pasta `tests/unit` cobrindo os componentes principais:
|
||||
|
||||
- cache local e distribuído;
|
||||
- SSE com encode, persistência e replay;
|
||||
- RAG com busca vetorial em memória;
|
||||
- checkpoint saver compatível com LangGraph;
|
||||
- telemetria profunda de LangGraph;
|
||||
- runtime dos agentes com cache/RAG;
|
||||
- verificação estática do workflow para garantir que não usa mais `MemorySaver()` diretamente.
|
||||
|
||||
## Como executar
|
||||
|
||||
```bash
|
||||
cd projeto_agent_framework_first_ready
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r agent_template_backend/requirements.txt
|
||||
pip install pytest pytest-asyncio
|
||||
pytest -q
|
||||
```
|
||||
|
||||
Para rodar apenas os testes unitários:
|
||||
|
||||
```bash
|
||||
pytest -q tests/unit
|
||||
```
|
||||
90
agent_framework_oci/Documentacao/README_TOOL_POLICIES.md
Normal file
90
agent_framework_oci/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.
|
||||
113
agent_framework_oci/Documentacao/README_old.md
Normal file
113
agent_framework_oci/Documentacao/README_old.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# AI Agent Platform — LangGraph + OCI
|
||||
|
||||
Monorepo com três projetos independentes:
|
||||
|
||||
- `agent_framework/`: biblioteca reutilizável para agentes escaláveis.
|
||||
- `agent_template_backend/`: backend FastAPI usando o framework, com dois agentes, roteador, máquina de estados, sessão persistente e gateway de canais.
|
||||
- `agent_frontend/`: frontend web simples e independente para conversar com o backend via gateway HTTP.
|
||||
|
||||
## Visão de arquitetura
|
||||
|
||||
```text
|
||||
Frontend Web / WhatsApp / Voz / Texto
|
||||
↓
|
||||
Channel Gateway + Adapters
|
||||
↓
|
||||
SessionRepository persistente
|
||||
↓
|
||||
Supervisor / Router Agent
|
||||
↓
|
||||
LangGraph StateGraph
|
||||
↓
|
||||
Agent A Agent B
|
||||
↓ ↓
|
||||
Guardrails → LLM OCI Generative AI → Output Guardrails → Judges
|
||||
↓
|
||||
Memory / RAG / Vector / Graph / Telemetry / Streaming
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Quickstart local
|
||||
|
||||
Suba a estrutura de Langfuse, MongoDB, REDIS para seu ambiente de desenvolvimento:
|
||||
|
||||
Vá até o folder ./agent_framework/Infrastructure_Langfuse/, onde existe o docker-compose.yml e execute:
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
O langfuse estará em:
|
||||
|
||||
```bash
|
||||
http://localhost:3005
|
||||
```
|
||||
Crie sua Organização e seu projeto
|
||||
|
||||

|
||||
|
||||
Será criado também um MongoDB e um REDIS, logo seu .env terá a configuração para apontar para estes recursos conteinerizados.
|
||||
Você pode também apontar para um banco de dados Autonomous Oracle, basta configurar no arquivo .env.
|
||||
|
||||
Depois compile do Agent Framework dentro do agent_template_backend (agente template que se utiliza do Framework):
|
||||
|
||||
Obs: configure o arquivo .env.
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
cd agent_framework_oci
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
cd agent_template_backend
|
||||
pip install -e ../agent_framework
|
||||
pip install -r requirements.txt
|
||||
uvicorn app.main:app --reload --reload-dir app --reload-dir config --port 8000
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
cd agent_framework_oci
|
||||
bash ./scripts/run_mcp_servers.sh
|
||||
```
|
||||
|
||||
Terminal 3:
|
||||
|
||||
```bash
|
||||
cd agent_framework_oci
|
||||
cd agent_frontend
|
||||
python -m http.server 5173
|
||||
```
|
||||
|
||||
Abra `http://localhost:5173`.
|
||||
|
||||
## OCI LLM
|
||||
|
||||
Configure no `.env`:
|
||||
|
||||
```env
|
||||
LLM_PROVIDER=oci_openai
|
||||
OCI_GENAI_BASE_URL=https://inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com/openai/v1
|
||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
||||
OCI_GENAI_API_KEY=...
|
||||
```
|
||||
|
||||
Para rodar sem credenciais, use:
|
||||
|
||||
```env
|
||||
LLM_PROVIDER=mock
|
||||
```
|
||||
|
||||
## Estado do projeto
|
||||
|
||||
Este é um template de referência funcional/simulável. Conectores reais de Autonomous Database, MongoDB, Redis, Langfuse, OCI Streaming e OCI GenAI estão isolados por interfaces/adapters para facilitar evolução e deploy.
|
||||
|
||||
## Enterprise Routing Edition
|
||||
|
||||
Esta versão também possui `README_ENTERPRISE_ROUTING.md`, com detalhes sobre roteamento por estado, intents configuráveis, LLM Router opcional e dois templates de exemplo.
|
||||
|
||||
## Multi-agent isolation
|
||||
|
||||
Esta distribuição inclui suporte para múltiplos `agent_template` no mesmo backend.
|
||||
Consulte `README_MULTI_AGENT_ISOLATION.md`.
|
||||
1545
agent_framework_oci/Documentacao/README_old2.md
Normal file
1545
agent_framework_oci/Documentacao/README_old2.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -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.
|
||||
Binary file not shown.
Binary file not shown.
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
VALIDAÇÃO - GLOBAL SUPERVISOR
|
||||
|
||||
Alterações implementadas:
|
||||
|
||||
1. Framework
|
||||
- agent_framework.global_supervisor.models
|
||||
- agent_framework.global_supervisor.config
|
||||
- agent_framework.global_supervisor.session_store
|
||||
- agent_framework.global_supervisor.router
|
||||
- agent_framework.global_supervisor.client
|
||||
|
||||
2. Novo serviço
|
||||
- agent_gateway/app/main.py
|
||||
- agent_gateway/app/settings.py
|
||||
- agent_gateway/config/backends.yaml
|
||||
- agent_gateway/README.md
|
||||
- agent_gateway/Dockerfile
|
||||
- agent_gateway/docs/ARQUITETURA_GLOBAL_SUPERVISOR.md
|
||||
|
||||
3. Docker Compose
|
||||
- serviço agent-gateway adicionado na porta 8010.
|
||||
|
||||
Validações executadas:
|
||||
|
||||
- python3 -m compileall -q agent_framework/src/agent_framework/global_supervisor agent_gateway/app
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test do roteamento híbrido:
|
||||
Entrada 1: "Minha fatura veio alta" -> contas
|
||||
Entrada 2: "e esse valor?" na mesma session_id -> contas por active_backend
|
||||
Resultado: OK
|
||||
|
||||
- Smoke test de import do app FastAPI:
|
||||
from app.main import app, registry, router
|
||||
Resultado: OK
|
||||
|
||||
Observação:
|
||||
- O proxy SSE do gateway foi deixado como etapa futura. O endpoint /gateway/message/sse já roteia e encaminha como mensagem normal; para SSE fim-a-fim, pode-se implementar proxy de /gateway/events/{session_id} para o backend ativo.
|
||||
@@ -0,0 +1,5 @@
|
||||
VALIDATION REPORT - guardrails parallel fail-fast + observer IC
|
||||
Date: 2026-06-03
|
||||
|
||||
compileall: OK
|
||||
smoke-tests: OK
|
||||
BIN
agent_framework_oci/Documentacao/img.png
Normal file
BIN
agent_framework_oci/Documentacao/img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
Reference in New Issue
Block a user