Files

Transaction Pre-Validation / Pré-validação Transacional

Esta variante demonstra a capability genérica de pré-validação MCP antes da confirmação.

A regra de negócio continua no MCP. O framework apenas orquestra o contrato genérico:

parâmetros completos
      ↓
MCP validator (read-only / side-effect-free)
      ↓
eligible?
 ├─ false → OUT_OF_SCOPE / NOT_ELIGIBLE → responde sem pedir confirmação
 └─ true  → AWAITING_CONFIRMATION → usuário confirma → tool transacional executa

Nenhum LLM adicional é usado pela pré-validação.

Por que existe

Sem pre-validation, uma aplicação pode pedir confirmação para uma operação que o domínio já sabe que é inválida. Exemplo: tentar cancelar um pedido já entregue ou contestar uma categoria que não é elegível para contestação.

O framework não contém essas regras de negócio. Ele apenas consulta uma tool MCP declarada na policy e interpreta o campo genérico eligible.

Policy

config/tool_policies.yaml:

tool_policies:
  cancelar_pedido:
    operation_type: transactional
    require_confirmation: true
    requires: [order_id]
    pre_validation:
      enabled: true
      tool: validar_cancelamento_pedido
      fail_open: false

A tool validar_cancelamento_pedido é read-only/internal e deve ser side-effect-free.

Contrato MCP esperado

Elegível:

{
  "eligible": true,
  "status": "ELIGIBLE",
  "order_id": "PED-1001"
}

Não elegível:

{
  "eligible": false,
  "status": "NOT_ELIGIBLE",
  "order_id": "PED-ENTREGUE",
  "reason": "Pedido já entregue não pode ser cancelado por esta operação."
}

Cenário de teste

Suba o Retail MCP de exemplo:

cd agent_framework_oci/mcp/servers/retail_mcp_server
uvicorn main:app --host 0.0.0.0 --port 8200

Em outro terminal:

cd agent_framework_oci/Tuning-Performance/Transaction_Pre_Validation/agent_template_backend
pip install -e ../../../libs/agent_framework
pip install -r requirements.txt
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Caso elegível

quero cancelar o pedido PED-1001

Esperado:

validar_cancelamento_pedido(PED-1001)
→ eligible=true
→ AWAITING_CONFIRMATION
→ nenhuma execução de cancelar_pedido ainda

Após sim, cancelar_pedido é executada.

Caso não elegível

quero cancelar o pedido PED-ENTREGUE

Esperado:

validar_cancelamento_pedido(PED-ENTREGUE)
→ eligible=false
→ OUT_OF_SCOPE
→ NÃO entra em AWAITING_CONFIRMATION
→ cancelar_pedido NÃO é executada

Telemetria

Procure pelos eventos:

  • IC.TRANSACTION_PREVALIDATION_REQUESTED
  • IC.TRANSACTION_PREVALIDATION_PASSED
  • IC.TRANSACTION_PREVALIDATION_REJECTED
  • IC.TRANSACTION_CONFIRMATION_REQUIRED somente após pre-validation aprovada.

No estado/metadata, transaction_pre_validation registra o validator utilizado e o resultado.

Fail-open x fail-closed

fail_open: false é o padrão recomendado para operações sensíveis: se o validator estiver indisponível, a transação não avança para confirmação.

fail_open: true deve ser usado apenas quando o domínio aceitar explicitamente prosseguir sem a pré-validação.

Separação de responsabilidades

Framework: ordem das etapas, estado, confirmação, idempotência e telemetria.

MCP/domínio: regra de elegibilidade, consulta aos sistemas de registro e motivo da rejeição.

A capability é genérica: pode ser usada para cancelamento, contestação, devolução, troca, suspensão ou qualquer outra operação transacional que possua uma precondição de domínio.

Rejeição encerra o estado transacional

Quando o validator retorna eligible: false, o framework encerra imediatamente o latch transacional. Isso significa que selected_tool_call, pending_tool_call e missing_parameters são limpos, confirmation_required=false, next_state=null e transaction_status=OUT_OF_SCOPE.

O resultado da pré-validação também é propagado no estado/metadata como transaction_pre_validation, por exemplo:

{
  "transaction_pre_validation": {
    "tool_name": "contestar_cobranca",
    "validator_tool": "validar_contestacao",
    "eligible": false,
    "status": "OUT_OF_SCOPE",
    "terminal": true
  }
}

Assim, o turno seguinte volta ao roteamento normal e não permanece preso em COLLECTING_* ou WAITING_*. A rejeição não vira transaction_evidence, pois a operação de negócio não foi executada.