Files
agent_platform_oci/Documentacao/README_old2.md
2026-06-19 22:17:09 -03:00

36 KiB

Tutorial — Implementação de um Agente usando agent_template_backend

Este tutorial mostra como criar um novo agente a partir do modelo agent_template_backend, mantendo o padrão corporativo do framework: LangGraph, roteamento enterprise, supervisor, guardrails, judges, memória, checkpoint, observabilidade, analytics IC/NOC/GRL e integração com MCP Tools.

img_1.png

O objetivo é que cada novo agente implemente apenas sua lógica de domínio — prompts, regras de negócio, ferramentas, schemas e nós específicos — sem recriar motores que já pertencem ao framework.


1. Visão geral da arquitetura

O template segue esta divisão:

agent_template_backend/
├── app/
│   ├── main.py                    # API FastAPI, gateway, SSE, sessão, memória e chamada do workflow
│   ├── state.py                   # Estado compartilhado do LangGraph
│   ├── workflows/
│   │   └── agent_graph.py          # Workflow LangGraph com guardrails, router, agentes, judges e persistência
│   ├── agents/
│   │   ├── runtime.py              # Mixin comum para MCP, RAG, cache e emissão de IC/GRL
│   │   ├── billing_agent.py        # Exemplo de agente de faturas
│   │   ├── product_agent.py        # Exemplo de agente de produtos
│   │   ├── orders_agent.py         # Exemplo de agente de pedidos
│   │   └── support_agent.py        # Exemplo de agente de suporte
│   └── examples/                  # Exemplos de IC, NOC, GRL, MCP e observer
├── config/
│   ├── agents.yaml                # Registro de agentes disponíveis
│   ├── routing.yaml               # Intents, keywords, fallback e roteamento
│   ├── tools.yaml                 # Catálogo de tools MCP
│   ├── mcp_servers.yaml           # Endpoints MCP locais
│   ├── mcp_servers.docker.yaml    # Endpoints MCP em Docker Compose
│   ├── mcp_parameter_mapping.yaml # Mapeamento de chaves canônicas para parâmetros das tools
│   ├── identity.yaml              # Resolução de identidade de negócio
│   ├── guardrails.yaml            # Guardrails globais
│   ├── judges.yaml                # Judges globais
│   ├── prompt_policy.yaml         # Política global de prompt
│   └── agents/<agent_id>/         # Configurações isoladas por agente
├── data/
│   └── agent_framework.db         # Banco local de exemplo, quando aplicável
├── Dockerfile
├── requirements.txt
└── .env                           # Configuração local

Responsabilidade do framework

O framework deve concentrar os motores genéricos:

  • LangGraph e montagem do workflow.
  • Checkpoint.
  • Memória.
  • Session repository.
  • Channel gateway.
  • Enterprise Router.
  • Supervisor.
  • Guardrails.
  • Output Supervisor.
  • Judges.
  • Telemetria Langfuse/OpenTelemetry.
  • Analytics IC/NOC/GRL.
  • MCP Tool Router.
  • Cache.
  • RAG genérico.

Responsabilidade do agente

O agente deve concentrar apenas customizações de domínio:

  • Prompts específicos.
  • Regras de negócio.
  • Schemas próprios.
  • Tools específicas.
  • Clients de sistemas externos.
  • Mapeamento de parâmetros.
  • Nós especializados, se houver.
  • ICs de negócio da jornada.

Essa separação evita que cada agente recrie seu próprio motor de execução.


2. Fluxo de execução do template

O fluxo principal começa em app/main.py, no endpoint /gateway/message.

Canal / Frontend / API
  ↓
POST /gateway/message
  ↓
ChannelGateway.normalize()
  ↓
IdentityResolver
  ↓
SessionRepository
  ↓
MemoryRepository
  ↓
AgentWorkflow.ainvoke()
  ↓
LangGraph
  ↓
Input Guardrails
  ↓
Enterprise Router ou Supervisor
  ↓
Agente especializado
  ↓
MCP Tool Router / RAG / Cache / LLM
  ↓
Output Supervisor
  ↓
Output Guardrails
  ↓
Judges
  ↓
Supervisor Review
  ↓
Persistência / Checkpoint / Memória
  ↓
Resposta

O AgentWorkflow, em app/workflows/agent_graph.py, já contém os nós corporativos:

input_guardrails
routing_decision
billing_agent
product_agent
orders_agent
support_agent
handoff
supervisor_agent
output_supervisor
output_guardrails
judge
supervisor_review
persist

Para criar um agente novo, normalmente você altera:

  1. app/agents/<novo_agente>.py
  2. app/workflows/agent_graph.py
  3. app/state.py, se precisar de campos novos
  4. config/agents.yaml
  5. config/routing.yaml
  6. config/tools.yaml
  7. config/mcp_servers.yaml
  8. config/mcp_parameter_mapping.yaml
  9. config/identity.yaml, se houver novas chaves de negócio
  10. config/agents/<agent_id>/prompt_policy.yaml
  11. config/agents/<agent_id>/guardrails.yaml
  12. config/agents/<agent_id>/judges.yaml
  13. .env

3. Pré-requisitos

3.1. Requisitos locais

  • Python 3.12 ou 3.13.
  • pip ou uv.
  • Projeto agent_framework disponível no mesmo workspace, pois o Dockerfile espera algo como:
workspace/
├── agent_framework/
└── agent_template_backend/
  • Servidores MCP, se o agente usar tools.
  • Redis, Oracle Autonomous Database, MongoDB e Langfuse são opcionais conforme configuração.

3.2. Instalação local

Dentro do diretório agent_template_backend:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Se o agent_framework estiver em desenvolvimento local:

pip install -e ../agent_framework

Em Windows PowerShell:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
pip install -e ..\agent_framework

4. Configuração do .env

O arquivo .env controla o comportamento do backend. Não versionar credenciais reais.

Exemplo seguro para desenvolvimento local:

APP_NAME=ai-agent-template
APP_ENV=local
LOG_LEVEL=INFO
API_HOST=0.0.0.0
API_PORT=8000
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173

# LLM
# Opções comuns: mock, oci_openai, oci_sdk, openai_compatible
LLM_PROVIDER=mock
LLM_TEMPERATURE=0.2
LLM_MAX_TOKENS=2048
LLM_TIMEOUT_SECONDS=120

# OCI/OpenAI-compatible, se usar OCI GenAI
OCI_GENAI_BASE_URL=https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1
OCI_GENAI_MODEL=<modelo>
OCI_GENAI_API_KEY=<api-key-ou-token>
OCI_CONFIG_FILE=~/.oci/config
OCI_PROFILE=DEFAULT
OCI_COMPARTMENT_ID=<ocid-do-compartment>
OCI_REGION=<region>

# Persistência local simples
SESSION_REPOSITORY_PROVIDER=memory
MEMORY_REPOSITORY_PROVIDER=memory
CHECKPOINT_REPOSITORY_PROVIDER=memory
USAGE_REPOSITORY_PROVIDER=memory

# Redis/cache
ENABLE_REDIS_CACHE=false
REDIS_URL=redis://localhost:6379/0
CACHE_TTL_SECONDS=300

# RAG
VECTOR_STORE_PROVIDER=memory
GRAPH_STORE_PROVIDER=memory
RAG_TOP_K=5
EMBEDDING_PROVIDER=mock

# Observabilidade
ENABLE_LANGFUSE=false
LANGFUSE_HOST=http://localhost:3005
ENABLE_OTEL=false
OTEL_SERVICE_NAME=ai-agent-template

# Analytics IC/NOC/GRL
ENABLE_ANALYTICS=false
ANALYTICS_PROVIDERS=noop
ENABLE_OCI_STREAMING=false
OCI_STREAM_ENDPOINT=
OCI_STREAM_OCID=
OCI_STREAM_PARTITION_KEY=agent-events

# Guardrails, judges e supervisor
ENABLE_INPUT_GUARDRAILS=true
ENABLE_OUTPUT_GUARDRAILS=true
ENABLE_OUTPUT_SUPERVISOR=true
ENABLE_JUDGES=true
ENABLE_SUPERVISOR=true
ENABLE_PARALLEL_GUARDRAILS=true
GUARDRAILS_FAIL_FAST=true
OUTPUT_SUPERVISOR_MAX_RETRIES=3
GUARDRAILS_CONFIG_PATH=./config/guardrails.yaml
JUDGES_CONFIG_PATH=./config/judges.yaml
PROMPT_POLICY_PATH=./config/prompt_policy.yaml

# Roteamento
ROUTING_CONFIG_PATH=./config/routing.yaml
ROUTING_MODE=router
ENABLE_LLM_ROUTER=false

# MCP
ENABLE_MCP_TOOLS=true
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
TOOLS_CONFIG_PATH=./config/tools.yaml
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
MCP_TOOL_TIMEOUT_SECONDS=30

# Identidade
IDENTITY_CONFIG_PATH=./config/identity.yaml

Para usar Oracle Autonomous Database, ajuste:

SESSION_REPOSITORY_PROVIDER=autonomous
MEMORY_REPOSITORY_PROVIDER=autonomous
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
USAGE_REPOSITORY_PROVIDER=autonomous

ADB_USER=<usuario>
ADB_PASSWORD=<senha>
ADB_DSN=<dsn>
ADB_WALLET_LOCATION=<caminho-wallet>
ADB_WALLET_PASSWORD=<senha-wallet>
ADB_TABLE_PREFIX=AGENTFW

Para usar Langfuse:

ENABLE_LANGFUSE=true
LANGFUSE_PUBLIC_KEY=<public-key>
LANGFUSE_SECRET_KEY=<secret-key>
LANGFUSE_HOST=http://localhost:3005

5. Criando um novo agente

Neste exemplo, vamos criar um agente chamado financeiro_agent para atendimento financeiro genérico.

5.1. Criar o arquivo do agente

Crie:

app/agents/financeiro_agent.py

Código-base:

from app.agents.prompting import apply_agent_profile_prompt
from app.agents.runtime import AgentRuntimeMixin


class FinanceiroAgent(AgentRuntimeMixin):
    name = "financeiro_agent"

    def __init__(self, llm, telemetry=None, tool_router=None, rag_service=None, cache=None, settings=None, observer=None):
        self.llm = llm
        self.telemetry = telemetry
        self.tool_router = tool_router
        self.rag_service = rag_service
        self.cache = cache
        self.settings = settings
        self.observer = observer

    async def run(self, state):
        await self._emit_ic(
            "IC.FINANCEIRO_AGENT_STARTED",
            state,
            {"business_component": "financeiro"},
            component="agent.financeiro.start",
        )

        session = (state.get("context") or {}).get("session", {})
        tool_context = await self._collect_tool_context(state)

        if tool_context:
            await self._emit_ic(
                "IC.FINANCEIRO_MCP_CONTEXT_COLLECTED",
                state,
                {"tool_result_count": len(tool_context)},
                component="agent.financeiro.mcp",
            )

        rag_context, rag_metadata = await self._retrieve_rag_context(state)

        messages = [
            {
                "role": "system",
                "content": apply_agent_profile_prompt(
                    state,
                    "Você é um agente financeiro. Responda com clareza, usando dados das ferramentas quando disponíveis. Não confirme ações financeiras sem evidência e confirmação explícita."
                ),
            },
            {
                "role": "user",
                "content": (
                    f"Mensagem: {state.get('sanitized_input') or state['user_text']}\n"
                    f"Sessão: {session}\n"
                    f"Intent: {state.get('intent')}\n"
                    f"Dados MCP: {tool_context}\n"
                    f"Contexto RAG: {rag_context}"
                ),
            },
        ]

        answer = await self._invoke_llm_cached(state, "FinanceiroAgent", messages)

        result = {
            "answer": f"[FinanceiroAgent] {answer}",
            "next_state": "FINANCEIRO_ACTIVE",
            "mcp_results": tool_context,
            "rag": rag_metadata,
        }

        await self._emit_ic(
            "IC.FINANCEIRO_AGENT_COMPLETED",
            state,
            {
                "answer_chars": len(result.get("answer") or ""),
                "has_mcp_results": bool(tool_context),
                "rag_enabled": bool(rag_metadata.get("enabled")),
            },
            component="agent.financeiro.completed",
        )

        return result

    async def _collect_tool_context(self, state):
        return await self._collect_mcp_context(state)

Esse agente usa recursos já existentes no AgentRuntimeMixin:

  • _emit_ic() para eventos de negócio.
  • _collect_mcp_context() para chamar tools selecionadas pelo roteador.
  • _retrieve_rag_context() para recuperar contexto RAG.
  • _invoke_llm_cached() para chamada ao LLM com cache.

6. Registrando o agente no workflow

Edite:

app/workflows/agent_graph.py

6.1. Importar o agente

Adicione:

from app.agents.financeiro_agent import FinanceiroAgent

6.2. Instanciar o agente

No __init__ da classe AgentWorkflow, depois de agent_kwargs:

self.financeiro = FinanceiroAgent(llm, **agent_kwargs)

6.3. Criar o nó do LangGraph

Em _build_graph():

builder.add_node("financeiro_agent", self._node("financeiro_agent", self.financeiro_agent))

6.4. Adicionar rota condicional

No dicionário de builder.add_conditional_edges("routing_decision", ...), inclua:

"financeiro_agent": "financeiro_agent",

Exemplo:

builder.add_conditional_edges(
    "routing_decision",
    lambda s: s.get("route", "billing_agent"),
    {
        "billing_agent": "billing_agent",
        "product_agent": "product_agent",
        "orders_agent": "orders_agent",
        "support_agent": "support_agent",
        "financeiro_agent": "financeiro_agent",
        "handoff": "handoff",
        "supervisor_agent": "supervisor_agent",
    },
)

6.5. Conectar o nó ao Output Supervisor

builder.add_edge("financeiro_agent", "output_supervisor")

6.6. Criar o método wrapper

Na classe AgentWorkflow:

async def financeiro_agent(self, state):
    async with self.langgraph_telemetry.node("financeiro_agent", state):
        async with self.telemetry.span(
            "workflow.agent.financeiro",
            session_id=state.get("conversation_key") or state.get("session_id"),
            input={"intent": state.get("intent")},
        ):
            return await self.financeiro.run(state)

6.7. Adicionar ao modo supervisor

No método supervisor_agent(), ajuste o mapa de handlers:

handlers = {
    "billing_agent": self.billing.run,
    "product_agent": self.product.run,
    "orders_agent": self.orders.run,
    "support_agent": self.support.run,
    "financeiro_agent": self.financeiro.run,
}

7. Ajustando o estado do agente

Edite:

app/state.py

Adicione novos campos apenas se o agente precisar guardar algo específico no estado do LangGraph.

Exemplo:

class AgentState(TypedDict, total=False):
    # campos existentes...
    financial_context: dict[str, Any]
    financial_decision: dict[str, Any]

Evite colocar dados grandes no estado. Para histórico longo, use memória. Para evidências externas, use RAG, banco ou cache.


8. Registrando o agente em config/agents.yaml

Edite:

config/agents.yaml

Adicione um novo item:

agents:
  - agent_id: financeiro_agent
    name: Financeiro Agent
    description: Agente para dúvidas financeiras, pagamentos, saldos, acordos e segunda via.
    prompt_policy_path: ./config/agents/financeiro_agent/prompt_policy.yaml
    routing_config_path: ./config/routing.yaml
    guardrails_config_path: ./config/agents/financeiro_agent/guardrails.yaml
    judges_config_path: ./config/agents/financeiro_agent/judges.yaml
    mcp_servers_config_path: ./config/mcp_servers.yaml
    tools_config_path: ./config/tools.yaml
    metadata:
      domain: financeiro
      system_prefix: |
        Você está executando o financeiro_agent.
        Use somente políticas, memória, checkpoints, guardrails e judges deste agent_id.
        Não misture histórico ou decisões de outros agentes.

Se quiser que este seja o agente padrão, ajuste o campo de agente default conforme o formato atual do seu agents.yaml.


9. Criando configurações isoladas do agente

Crie a pasta:

config/agents/financeiro_agent/

9.1. prompt_policy.yaml

id: financeiro_agent_prompt_policy
version: 1
description: Prompt base isolado do agente financeiro.
system_prefix: |
  Você é um agente corporativo especializado em atendimento financeiro.
  Seja claro, objetivo, auditável e não invente dados.
  Quando precisar executar uma ação, use ferramentas configuradas.
  Quando faltar informação obrigatória, peça apenas o dado necessário.

9.2. guardrails.yaml

input:
  - code: MSK
    enabled: true
  - code: VLOOP
    enabled: true
  - code: PINJ
    enabled: true
output:
  - code: REVPREC
    enabled: true
  - code: CMP
    enabled: true

9.3. judges.yaml

judges:
  - name: response_quality
    enabled: true
    threshold: 0.7
  - name: groundedness
    enabled: true
    threshold: 0.6

10. Configurando roteamento em config/routing.yaml

Adicione uma intent para o novo agente:

intents:
  - name: financeiro_pagamentos
    domain: financeiro
    agent: financeiro_agent
    description: Dúvidas sobre pagamento, saldo, fatura, boleto, acordo, contestação e segunda via.
    priority: 15
    mcp_tools:
      - consultar_titulo_financeiro
      - consultar_pagamentos_financeiro
    keywords:
      - pagamento
      - boleto
      - saldo
      - acordo
      - financeiro
      - segunda via
      - vencimento
      - cobrança
      - contestação
    examples:
      - Quero consultar meu pagamento.
      - Preciso da segunda via do boleto.
      - Meu pagamento ainda não foi baixado.

Se estiver usando políticas de estado, adicione:

state_policies:
  - state: WAITING_FINANCEIRO_CONFIRMATION
    agent: financeiro_agent
    description: Mantém confirmações curtas no fluxo financeiro.

O roteador suporta dois modos:

ROUTING_MODE=router

ou:

ROUTING_MODE=supervisor

No modo router, uma intent aponta para um agente. No modo supervisor, o supervisor pode acionar um ou mais agentes.


11. Configurando tools em config/tools.yaml

Adicione as tools necessárias:

tools:
  consultar_titulo_financeiro:
    description: Consulta um título financeiro por cliente e contrato.
    mcp_server: financeiro
    enabled: true
    args_schema:
      customer_id: string
      contract_id: string

  consultar_pagamentos_financeiro:
    description: Consulta pagamentos financeiros por cliente.
    mcp_server: financeiro
    enabled: true
    args_schema:
      customer_id: string

As tools devem existir no servidor MCP configurado. O backend não deve chamar diretamente HTTP/SOAP/DB de sistemas de negócio quando essa chamada puder ser padronizada via MCP Tool Router.


12. Configurando servidores MCP

Edite:

config/mcp_servers.yaml

Exemplo local:

servers:
  financeiro:
    transport: http
    endpoint: http://localhost:8300/mcp
    enabled: true
    description: MCP Server Financeiro local.

Para Docker Compose, edite:

config/mcp_servers.docker.yaml

Exemplo:

servers:
  financeiro:
    transport: http
    endpoint: http://financeiro-mcp:8300/mcp
    enabled: true
    description: MCP Server Financeiro em Docker.

13. Configurando mapeamento de parâmetros MCP

Edite:

config/mcp_parameter_mapping.yaml

Exemplo:

mcp_parameter_mapping:
  defaults:
    use_mock: true
  tools:
    consultar_titulo_financeiro:
      map:
        customer_key: customer_id
        contract_key: contract_id
        interaction_key: interaction_id
        session_key: session_id
    consultar_pagamentos_financeiro:
      map:
        customer_key: customer_id
        session_key: session_id

Interpretação:

customer_key  -> chave canônica no framework
customer_id   -> parâmetro esperado pela tool MCP

Assim, o agente trabalha com identidade canônica, e cada tool recebe os nomes que seu MCP Server espera.


14. Configurando identidade de negócio

Edite:

config/identity.yaml

Esse arquivo define como extrair chaves canônicas do payload, contexto, canal ou sessão.

Exemplo:

identity:
  version: "2"
  required:
    - session_key
  keys:
    customer_key:
      description: Cliente canônico.
      sources:
        - business_context.customer_key
        - customer_key
        - customer_id
        - cpf
        - cnpj
        - user_id
    contract_key:
      description: Contrato, pedido, fatura ou título principal.
      sources:
        - business_context.contract_key
        - contract_key
        - contract_id
        - invoice_id
        - order_id
    interaction_key:
      description: Chave externa da interação.
      sources:
        - business_context.interaction_key
        - interaction_key
        - call_id
        - message_id
        - protocol_id
    session_key:
      description: Sessão técnica estável.
      sources:
        - business_context.session_key
        - session_key
        - conversation_key
        - session_id

A identidade resolvida aparece em business_context dentro do state e é usada pelo MCP Tool Router.


15. Implementando ou conectando um MCP Server

O backend espera que a tool exista no MCP Server. A implementação exata depende do padrão do seu servidor MCP, mas o contrato conceitual é:

Backend Agent
  ↓
MCP Tool Router
  ↓
MCP Server financeiro
  ↓
Sistema real, mock, banco, REST, SOAP ou serviço interno

Exemplo conceitual de tools:

async def consultar_titulo_financeiro(customer_id: str, contract_id: str, session_id: str | None = None):
    return {
        "customer_id": customer_id,
        "contract_id": contract_id,
        "status": "ABERTO",
        "valor": 129.90,
        "vencimento": "2026-06-20",
    }


async def consultar_pagamentos_financeiro(customer_id: str, session_id: str | None = None):
    return {
        "customer_id": customer_id,
        "pagamentos": [
            {"data": "2026-06-01", "valor": 129.90, "status": "COMPENSADO"}
        ],
    }

Para desenvolvimento, você pode usar use_mock: true no mcp_parameter_mapping.yaml ou implementar um MCP Server local com respostas simuladas.


16. IC, NOC e GRL no novo agente

16.1. IC — eventos de negócio

Use ICs dentro do agente para registrar passos relevantes da jornada.

Exemplo:

await self._emit_ic(
    "IC.FINANCEIRO_AGENT_STARTED",
    state,
    {"business_component": "financeiro"},
    component="agent.financeiro.start",
)

Sugestão mínima por agente:

IC.<AGENTE>_AGENT_STARTED
IC.<AGENTE>_MCP_CONTEXT_COLLECTED
IC.<AGENTE>_RAG_CONTEXT_RETRIEVED
IC.<AGENTE>_AGENT_COMPLETED
IC.<AGENTE>_BUSINESS_DECISION
IC.<AGENTE>_ACTION_REQUESTED
IC.<AGENTE>_ACTION_COMPLETED

16.2. NOC — eventos operacionais

NOC deve ser usado para saúde técnica, indisponibilidade, erro, timeout, fallback e degradação.

Exemplo direto, se necessário:

await self.observer.emit_noc(
    "NOC.FINANCEIRO_TOOL_TIMEOUT",
    {
        "session_id": state.get("conversation_key") or state.get("session_id"),
        "tenant_id": state.get("tenant_id"),
        "agent_id": state.get("agent_id"),
        "tool": "consultar_titulo_financeiro",
    },
    component="agent.financeiro.tool",
)

16.3. GRL — guardrails

A maior parte dos GRLs já é emitida pelo workflow em:

input_guardrails
output_supervisor
output_guardrails

Só implemente GRL dentro do agente quando houver uma validação de domínio específica que não caiba nos guardrails globais.


17. Build e execução local

17.1. Rodar backend local

Dentro de agent_template_backend:

source .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Windows PowerShell:

.\.venv\Scripts\Activate.ps1
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Verifique saúde:

curl http://localhost:8000/health

Listar agentes:

curl http://localhost:8000/agents

Listar tools MCP conhecidas:

curl http://localhost:8000/debug/mcp/tools

18. Subindo MCP Servers

Se os MCP Servers forem processos Python separados, suba cada um em uma porta distinta.

Exemplo conceitual:

cd ../mcp_servers/financeiro_mcp_server
source .venv/bin/activate
uvicorn main:app --host 0.0.0.0 --port 8300 --reload

Depois confirme que o endpoint configurado em config/mcp_servers.yaml está correto:

servers:
  financeiro:
    endpoint: http://localhost:8300/mcp

Teste pelo backend:

curl -X POST http://localhost:8000/debug/mcp/call/consultar_titulo_financeiro \
  -H "Content-Type: application/json" \
  -d '{
    "business_context": {
      "customer_key": "12345",
      "contract_key": "ABC-999",
      "session_key": "sessao-teste"
    },
    "original_context": {
      "session_id": "sessao-teste"
    }
  }'

19. Build com Docker

O Dockerfile do template espera copiar agent_framework e agent_template_backend. Portanto, rode o build a partir do diretório pai que contém ambos.

Estrutura esperada:

workspace/
├── agent_framework/
└── agent_template_backend/

Build:

cd workspace
docker build -t agent-template-backend:local -f agent_template_backend/Dockerfile .

Run:

docker run --rm -p 8000:8000 \
  --env-file agent_template_backend/.env \
  agent-template-backend:local

Health check:

curl http://localhost:8000/health

20. Docker Compose sugerido

Crie um docker-compose.yaml no diretório pai, se quiser subir backend, Redis, Langfuse e MCP Servers juntos.

Exemplo simplificado:

services:
  backend:
    build:
      context: .
      dockerfile: agent_template_backend/Dockerfile
    env_file:
      - agent_template_backend/.env
    ports:
      - "8000:8000"
    depends_on:
      - redis
      - financeiro-mcp

  redis:
    image: redis:7
    ports:
      - "6379:6379"

  financeiro-mcp:
    build:
      context: ./mcp_servers/financeiro_mcp_server
    ports:
      - "8300:8300"

Quando estiver em Docker, use config/mcp_servers.docker.yaml e ajuste o .env:

MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.docker.yaml

21. Testando o agente pelo Gateway

21.1. Teste simples

curl -X POST http://localhost:8000/gateway/message \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "web",
    "agent_id": "financeiro_agent",
    "tenant_id": "default",
    "payload": {
      "text": "Quero consultar meu pagamento",
      "session_id": "teste-financeiro-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "contract_id": "ABC-999",
      "message_id": "msg-001"
    }
  }'

A resposta deve conter metadados como:

{
  "channel": "web",
  "session_id": "default:financeiro_agent:teste-financeiro-001",
  "text": "...",
  "metadata": {
    "route": "financeiro_agent",
    "intent": "financeiro_pagamentos",
    "mcp_results": [],
    "business_context": {
      "customer_key": "12345",
      "contract_key": "ABC-999"
    }
  }
}

21.2. Teste de roteamento sem fixar agent_id

Se quiser validar o default_agent_id e o roteamento por intenção:

curl -X POST http://localhost:8000/gateway/message \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "web",
    "tenant_id": "default",
    "payload": {
      "text": "Meu pagamento ainda não foi baixado",
      "session_id": "teste-router-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "contract_id": "ABC-999"
    }
  }'

21.3. Teste de SSE

Enviar mensagem com SSE:

curl -X POST http://localhost:8000/gateway/message/sse \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "web",
    "agent_id": "financeiro_agent",
    "tenant_id": "default",
    "payload": {
      "text": "Preciso da segunda via do boleto",
      "session_id": "teste-sse-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "contract_id": "ABC-999"
    }
  }'

Abrir stream:

curl -N http://localhost:8000/gateway/events/default:financeiro_agent:teste-sse-001

Eventos esperados:

connected
flow.start
session.upserted
message.received
workflow.started
workflow.completed
message.responded
flow.end

22. Testando debug endpoints

22.1. Roteamento

curl -X POST http://localhost:8000/debug/route \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Quero consultar meu pagamento",
    "context": {
      "agent_id": "financeiro_agent",
      "tenant_id": "default"
    }
  }'

22.2. Identidade

curl -X POST http://localhost:8000/debug/identity \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "teste-id-001",
    "customer_id": "12345",
    "contract_id": "ABC-999",
    "message_id": "msg-001"
  }'

22.3. Mensagens da sessão

curl http://localhost:8000/sessions/default:financeiro_agent:teste-financeiro-001/messages

22.4. Checkpoint

curl http://localhost:8000/sessions/default:financeiro_agent:teste-financeiro-001/checkpoint

22.5. Uso/custo

curl http://localhost:8000/debug/usage

23. Checklist de validação funcional

Use este checklist antes de considerar o agente pronto.

23.1. Configuração

  • .env sem credenciais reais versionadas.
  • LLM_PROVIDER correto.
  • ROUTING_MODE definido: router ou supervisor.
  • ENABLE_MCP_TOOLS ajustado conforme necessidade.
  • MCP_SERVERS_CONFIG_PATH aponta para o YAML correto.
  • IDENTITY_CONFIG_PATH aponta para config/identity.yaml.
  • Persistência local ou Autonomous configurada.

23.2. Agente

  • Arquivo criado em app/agents/<agent>.py.
  • Classe implementa async def run(self, state).
  • Agente herda AgentRuntimeMixin.
  • Prompts específicos aplicam apply_agent_profile_prompt().
  • Tools são chamadas via _collect_mcp_context().
  • RAG é chamado via _retrieve_rag_context(), se aplicável.
  • LLM é chamado via _invoke_llm_cached().
  • Retorno contém answer, next_state, mcp_results e, se aplicável, rag.

23.3. Workflow

  • Agente importado em agent_graph.py.
  • Agente instanciado no __init__.
  • Nó adicionado no StateGraph.
  • Rota adicionada em add_conditional_edges.
  • Edge criada para output_supervisor.
  • Handler adicionado no modo supervisor, se necessário.

23.4. Roteamento

  • Intent adicionada em config/routing.yaml.
  • Keywords suficientes.
  • Examples coerentes.
  • agent da intent bate com o nome do nó do workflow.
  • mcp_tools da intent existem em config/tools.yaml.

23.5. MCP

  • Tool declarada em config/tools.yaml.
  • MCP Server declarado em config/mcp_servers.yaml.
  • Mapeamento declarado em config/mcp_parameter_mapping.yaml.
  • Tool testada via /debug/mcp/call/{tool_name}.
  • Timeout e fallback definidos.

23.6. Observabilidade

  • ICs de início e fim emitidos.
  • ICs de coleta MCP/RAG emitidos quando aplicável.
  • NOCs emitidos em erros técnicos relevantes.
  • GRLs globais aparecem em input/output.
  • Langfuse ou outro provider recebe traces, se habilitado.

23.7. Testes

  • /health retorna status=ok.
  • /agents lista o agente novo.
  • /debug/route escolhe o agente correto.
  • /debug/identity resolve as chaves esperadas.
  • /gateway/message retorna resposta correta.
  • /gateway/message/sse publica eventos.
  • /sessions/{session_id}/messages mostra histórico.
  • /sessions/{session_id}/checkpoint mostra checkpoint.

24. Boas práticas de customização

Faça

  • Coloque regra de negócio no agente, não no framework.
  • Use MCP para acesso a sistemas externos.
  • Use identity.yaml para normalizar chaves de negócio.
  • Use mcp_parameter_mapping.yaml para adaptar nomes de parâmetros.
  • Use IC para eventos de negócio.
  • Use NOC para falhas técnicas.
  • Use GRL para decisões de segurança/validação.
  • Mantenha prompts por agente em config/agents/<agent_id>/prompt_policy.yaml.
  • Mantenha guardrails e judges isolados quando o agente tiver regras próprias.

Evite

  • Criar outro workflow fora de AgentWorkflow sem necessidade.
  • Chamar REST/DB direto dentro do agente quando a chamada deveria ser tool MCP.
  • Criar checkpointer próprio.
  • Criar memória paralela fora do framework.
  • Emitir telemetria em formato incompatível com AgentObserver.
  • Colocar regra específica de um agente dentro do framework.
  • Misturar histórico de agentes diferentes na mesma sessão.

25. Troubleshooting

25.1. /gateway/message retorna rota errada

Verifique:

curl -X POST http://localhost:8000/debug/route \
  -H "Content-Type: application/json" \
  -d '{"text":"sua frase de teste","context":{"agent_id":"financeiro_agent"}}'

Depois revise:

  • config/routing.yaml
  • keywords
  • examples
  • priority
  • ROUTING_MODE
  • ENABLE_LLM_ROUTER

25.2. Tool MCP não é chamada

Verifique:

  • A intent em routing.yaml possui mcp_tools.
  • A tool existe em tools.yaml.
  • O MCP Server está em mcp_servers.yaml.
  • ENABLE_MCP_TOOLS=true.
  • O mapeamento existe em mcp_parameter_mapping.yaml.
  • A identidade tem as chaves necessárias.

25.3. Tool recebe parâmetro errado

Revise:

config/identity.yaml
config/mcp_parameter_mapping.yaml
payload enviado ao /gateway/message

Use:

curl -X POST http://localhost:8000/debug/identity \
  -H "Content-Type: application/json" \
  -d '{"session_id":"s1","customer_id":"123","contract_id":"C1"}'

25.4. SSE dá MIME type incorreto

O endpoint correto é:

GET /gateway/events/{session_id}

O session_id precisa ser a chave canônica completa retornada pelo gateway:

tenant_id:agent_id:session_id_original

Exemplo:

default:financeiro_agent:teste-sse-001

25.5. Langfuse não mostra traces

Verifique:

ENABLE_LANGFUSE=true
LANGFUSE_PUBLIC_KEY=<public-key>
LANGFUSE_SECRET_KEY=<secret-key>
LANGFUSE_HOST=http://localhost:3005

E confira:

curl http://localhost:8000/health
curl http://localhost:8000/debug/env

25.6. Banco Autonomous não conecta

Para desenvolvimento, simplifique primeiro:

SESSION_REPOSITORY_PROVIDER=memory
MEMORY_REPOSITORY_PROVIDER=memory
CHECKPOINT_REPOSITORY_PROVIDER=memory
USAGE_REPOSITORY_PROVIDER=memory

Depois volte para autonomous quando o wallet, DSN e variáveis estiverem corretos.


26. Modelo mínimo de entrega de um novo agente

Ao finalizar uma implementação, a entrega mínima deve conter:

app/agents/<agent_name>.py
config/agents.yaml
config/routing.yaml
config/tools.yaml
config/mcp_servers.yaml
config/mcp_parameter_mapping.yaml
config/identity.yaml
config/agents/<agent_id>/prompt_policy.yaml
config/agents/<agent_id>/guardrails.yaml
config/agents/<agent_id>/judges.yaml
app/workflows/agent_graph.py
app/state.py, se necessário
.env.example ou documentação de variáveis
README.md com testes curl

27. Exemplo de teste completo

# 1. Health
curl http://localhost:8000/health

# 2. Agentes
curl http://localhost:8000/agents

# 3. Tools MCP
curl http://localhost:8000/debug/mcp/tools

# 4. Roteamento
curl -X POST http://localhost:8000/debug/route \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Quero consultar meu pagamento",
    "context": {"agent_id": "financeiro_agent", "tenant_id": "default"}
  }'

# 5. Identidade
curl -X POST http://localhost:8000/debug/identity \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "teste-final-001",
    "customer_id": "12345",
    "contract_id": "ABC-999"
  }'

# 6. Mensagem real
curl -X POST http://localhost:8000/gateway/message \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "web",
    "agent_id": "financeiro_agent",
    "tenant_id": "default",
    "payload": {
      "text": "Quero consultar meu pagamento",
      "session_id": "teste-final-001",
      "user_id": "user-001",
      "customer_id": "12345",
      "contract_id": "ABC-999",
      "message_id": "msg-final-001"
    }
  }'

# 7. Histórico
curl http://localhost:8000/sessions/default:financeiro_agent:teste-final-001/messages

# 8. Checkpoint
curl http://localhost:8000/sessions/default:financeiro_agent:teste-final-001/checkpoint

28. Conclusão

O agent_template_backend já fornece a espinha dorsal corporativa para novos agentes. A implementação de um agente novo deve se limitar ao domínio: prompts, regras, tools, clients, schemas e decisões específicas.

O padrão correto é:

Framework = motor reutilizável
Agente = customização de negócio
MCP = fronteira padronizada com sistemas externos
Config YAML = comportamento alterável sem mexer no motor
IC/NOC/GRL = rastreabilidade corporativa

Seguindo este tutorial, novos agentes podem ser criados com padronização, escalabilidade, rastreabilidade e manutenção mais simples.