mirror of
https://github.com/hoshikawa2/compass_backoffice.git
synced 2026-07-09 13:54:20 +00:00
bugfix
This commit is contained in:
240
.env
240
.env
@@ -1,240 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# AI AGENT PLATFORM - CONFIGURAÇÃO ÚNICA
|
|
||||||
# Este arquivo é lido por Pydantic Settings no framework e no backend template.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
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 - OCI Generative AI como provider principal
|
|
||||||
###############################################################################
|
|
||||||
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
|
||||||
LLM_PROVIDER=oci_openai
|
|
||||||
LLM_TEMPERATURE=0.2
|
|
||||||
LLM_MAX_TOKENS=2048
|
|
||||||
LLM_TIMEOUT_SECONDS=120
|
|
||||||
|
|
||||||
# OCI OpenAI-compatible endpoint
|
|
||||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
|
|
||||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
|
||||||
OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
|
||||||
OCI_GENAI_PROJECT_OCID=
|
|
||||||
|
|
||||||
# OCI SDK / signer / profiles
|
|
||||||
OCI_CONFIG_FILE=~/.oci/config
|
|
||||||
OCI_PROFILE=LATINOAMERICA-Chicago
|
|
||||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
|
||||||
OCI_REGION=us-chicago-1
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Persistência
|
|
||||||
###############################################################################
|
|
||||||
# Opções: memory, autonomous, mongodb
|
|
||||||
SESSION_REPOSITORY_PROVIDER=autonomous
|
|
||||||
MEMORY_REPOSITORY_PROVIDER=autonomous
|
|
||||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
|
||||||
|
|
||||||
# Autonomous Database
|
|
||||||
ADB_USER=admin
|
|
||||||
ADB_PASSWORD=Moniquinha1972
|
|
||||||
ADB_DSN=oradb23ai_high
|
|
||||||
ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai
|
|
||||||
ADB_WALLET_PASSWORD=Moniquinha1972
|
|
||||||
ADB_TABLE_PREFIX=AGENTFW
|
|
||||||
|
|
||||||
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
|
||||||
MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017
|
|
||||||
MONGODB_DATABASE=agent_platform
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_URL=redis://localhost:6379/0
|
|
||||||
ENABLE_REDIS_CACHE=false
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# RAG / Vector / Graph
|
|
||||||
###############################################################################
|
|
||||||
VECTOR_STORE_PROVIDER=memory
|
|
||||||
GRAPH_STORE_PROVIDER=memory
|
|
||||||
RAG_TOP_K=5
|
|
||||||
EMBEDDING_PROVIDER=mock
|
|
||||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Observabilidade
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_LANGFUSE=true
|
|
||||||
LANGFUSE_PUBLIC_KEY=pk-lf-bd9b0c7e-2b8b-4e5b-a382-284a9b4413b3
|
|
||||||
LANGFUSE_SECRET_KEY=sk-lf-5f5cc18d-0bb5-424e-b5d0-cb3664d58c20
|
|
||||||
LANGFUSE_HOST=http://localhost:3005
|
|
||||||
ENABLE_OTEL=false
|
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=
|
|
||||||
OTEL_SERVICE_NAME=ai-agent-template
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Analytics / Observer corporativo
|
|
||||||
###############################################################################
|
|
||||||
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
|
||||||
ENABLE_ANALYTICS=false
|
|
||||||
# Providers aceitos: oci_streaming,pubsub,noop
|
|
||||||
ANALYTICS_PROVIDERS=oci_streaming
|
|
||||||
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
|
||||||
AGENT_PUBSUB_TOPIC=
|
|
||||||
GCP_PUBSUB_TOPIC_PATH=
|
|
||||||
GCP_PROJECT_ID=
|
|
||||||
GCP_PUBSUB_TOPIC=
|
|
||||||
GCP_PUBSUB_TIMEOUT_SECONDS=30
|
|
||||||
# Credencial GCP segue padrão Google:
|
|
||||||
# GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OCI Streaming
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_OCI_STREAMING=false
|
|
||||||
OCI_STREAM_ENDPOINT=
|
|
||||||
OCI_STREAM_OCID=
|
|
||||||
OCI_STREAM_PARTITION_KEY=agent-events
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Guardrails, Judges, Supervisor
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_INPUT_GUARDRAILS=true
|
|
||||||
ENABLE_OUTPUT_GUARDRAILS=true
|
|
||||||
ENABLE_JUDGES=true
|
|
||||||
ENABLE_SUPERVISOR=true
|
|
||||||
ENABLE_OUTPUT_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
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Gateway de canais
|
|
||||||
###############################################################################
|
|
||||||
DEFAULT_CHANNEL=web
|
|
||||||
ENABLE_VOICE_ADAPTER=true
|
|
||||||
ENABLE_WHATSAPP_ADAPTER=true
|
|
||||||
ENABLE_TEXT_ADAPTER=true
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
# ENTERPRISE ROUTING
|
|
||||||
#################################################
|
|
||||||
# Arquivo YAML com intents, keywords, políticas de estado e fallback.
|
|
||||||
ROUTING_CONFIG_PATH=./config/routing.yaml
|
|
||||||
# true = usa LLM para classificar quando keywords/estado não resolverem.
|
|
||||||
# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência.
|
|
||||||
ENABLE_LLM_ROUTER=true
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# MCP / Tools
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_MCP_TOOLS=true
|
|
||||||
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
|
||||||
TOOLS_CONFIG_PATH=./config/tools.yaml
|
|
||||||
MCP_TOOL_TIMEOUT_SECONDS=30
|
|
||||||
|
|
||||||
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
|
||||||
ROUTING_MODE=router
|
|
||||||
|
|
||||||
# Usage/cost accounting
|
|
||||||
USAGE_REPOSITORY_PROVIDER=autonomous
|
|
||||||
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
|
||||||
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# BACKOFFICE CONVERTIDO - CHAVES ADICIONADAS A PARTIR DO .env.example
|
|
||||||
# Valores existentes no .env original foram preservados. Revise placeholders.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
# Framework
|
|
||||||
DEFAULT_AGENT_ID=backoffice_anatel
|
|
||||||
ENABLE_SSE=true
|
|
||||||
SQLITE_DB_PATH=.local/backoffice_framework.db
|
|
||||||
LLM_MODEL=cohere.command-r-08-2024
|
|
||||||
# OCI OpenAI-compatible endpoint/credentials. Preencha conforme seu Agent Framework.
|
|
||||||
OCI_OPENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/chat
|
|
||||||
OCI_OPENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
|
||||||
# OPENAI_API_KEY=<quando o provider oci_openai usa contrato OpenAI-compatible>
|
|
||||||
OCI_CONFIG_PROFILE=LATINOAMERICA-Chicago
|
|
||||||
# OCI_REGION=us-chicago-1
|
|
||||||
# OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
|
||||||
|
|
||||||
# MCP
|
|
||||||
MCP_ENABLED=true
|
|
||||||
DEBUG=true
|
|
||||||
# IMDB_API_HOST=
|
|
||||||
# SIEBEL_API_HOST=
|
|
||||||
# SIEBEL_API_ROUTE=
|
|
||||||
# SIEBEL_STATUS_API_HOST=
|
|
||||||
# SIEBEL_PREPAGO_STATUS_API_HOST=
|
|
||||||
# SPEECH_ANALYTICS_API_HOST=
|
|
||||||
# TAIS_KB_DB_USER=
|
|
||||||
# TAIS_KB_DB_PASSWORD=
|
|
||||||
# TAIS_KB_DSN=
|
|
||||||
# OCI_REQUEST_STREAM_OCID=
|
|
||||||
# OCI_RESPONSE_STREAM_OCID=
|
|
||||||
# OCI_CONSUMER_GROUP_NAME=
|
|
||||||
|
|
||||||
# MCP Server local do backoffice
|
|
||||||
# O backend principal chama esse endpoint pelo MCPToolRouter do framework.
|
|
||||||
BACKOFFICE_MCP_BASE_URL=http://localhost:8010
|
|
||||||
BACKOFFICE_MCP_USE_MOCK=false
|
|
||||||
BACKOFFICE_MCP_BACKEND_TYPE=tim_clients
|
|
||||||
BACKOFFICE_MCP_FAIL_OPEN_ON_BACKEND_ERROR=true
|
|
||||||
|
|
||||||
# TIM/develop original - preencha no .env real, nunca versionar segredos.
|
|
||||||
PMID_API_HOST=http://localhost:8011/access/v1/info
|
|
||||||
PMID_API_BASIC_TOKEN=Basic dGlteDpAckp1SkpAcFJPUiM=
|
|
||||||
PMID_API_TIMEOUT=10
|
|
||||||
PMID_API_CLIENT_ID=AIAGENTCR
|
|
||||||
SIEBEL_API_HOST=http://localhost:8011
|
|
||||||
SIEBEL_API_ROUTE=/customers/v1/backOfficeSRopening
|
|
||||||
SIEBEL_API_TIMEOUT=10
|
|
||||||
SIEBEL_API_USERNAME=aiagentcr
|
|
||||||
SIEBEL_API_PASSWORD=AiAgentCR#FQA#
|
|
||||||
SIEBEL_API_CLIENT_ID=AIAGENTCR
|
|
||||||
SIEBEL_STATUS_API_HOST=https://locahost:8011
|
|
||||||
SIEBEL_STATUS_API_ROUTE=/interactions/v1/statusServiceRequest
|
|
||||||
SIEBEL_PREPAGO_STATUS_API_HOST=https://localhost:8011
|
|
||||||
SIEBEL_PREPAGO_STATUS_API_ROUTE=/customers/v1/serviceRequest
|
|
||||||
VERIFY_SSL=false
|
|
||||||
|
|
||||||
SPEECH_PREDICTION_BASE_URL=https://apigatewayfqa1.tim.com.br
|
|
||||||
SPEECH_PREDICTION_CLIENT_ID=TzlzCbMGNKpGXl7oUDAlE66eL3ZGmWBtuHMjgsClMoDNdmLz
|
|
||||||
SPEECH_PREDICTION_CLIENT_SECRET=ZIgIIxsn3CG7ra9HiOLSLygRzcgcivIMsoo4kwqLo9MtQBtNnmUadNpUx81ABkEm
|
|
||||||
SPEECH_HISTORY_BASE_URL=https://run-external-speech-analytics-audio-toxico-49911170294.us-east1.run.app
|
|
||||||
SPEECH_HISTORY_AUDIENCE=https://run-external-speech-analytics-audio-toxico-49911170294.us-east1.run.app
|
|
||||||
GOOGLE_APPLICATION_CREDENTIALS=/Users/cristianohoshikawa/Dropbox/ORACLE/TIM/compass/lab_backoffice/config/gcp-credentials-local.json
|
|
||||||
SPEECH_TIMEOUT=30
|
|
||||||
SPEECH_SIMILARITY_THRESHOLD=70
|
|
||||||
|
|
||||||
TAIS_DB_USER=admin
|
|
||||||
TAIS_DB_PASSWORD=Moniquinha1972
|
|
||||||
TAIS_DB_DSN=" (description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=adb.sa-saopaulo-1.oraclecloud.com))(connect_data=(service_name=jy2otyfomimhaoc_oradb23ai_high.adb.oraclecloud.com))(security=(ssl_server_dn_match=yes)))"
|
|
||||||
TAIS_DB_TIMEOUT=30
|
|
||||||
TAIS_GENAI_ENDPOINT=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
|
|
||||||
TAIS_GENAI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
|
||||||
TAIS_GENAI_EMBED_MODEL_ID=cohere.embed-multilingual-v3.0
|
|
||||||
TAIS_TABLE_CHUNKS=CHUNKS_CHAR_COHERE_3
|
|
||||||
TAIS_TOP_K=3
|
|
||||||
|
|
||||||
# LLM CLASSIFICATION
|
|
||||||
CLASSIFICATION_LLM_MODEL=openai.gpt-4.1
|
|
||||||
CLASSIFICATION_LLM_TEMPERATURE=0.3
|
|
||||||
CLASSIFICATION_LLM_MAX_TOKENS=2000
|
|
||||||
CLASSIFICATION_LLM_TOP_P=0.9
|
|
||||||
CLASSIFICATION_LLM_TOP_K=250
|
|
||||||
|
|
||||||
CLASSIFICATION_LARGE_LLM_MODEL=openai.gpt-4.1
|
|
||||||
CLASSIFICATION_LARGE_LLM_TEMPERATURE=0.3
|
|
||||||
CLASSIFICATION_LARGE_LLM_MAX_TOKENS=4000
|
|
||||||
CLASSIFICATION_LARGE_LLM_TOP_P=0.9
|
|
||||||
CLASSIFICATION_LARGE_LLM_TOP_K=250
|
|
||||||
|
|
||||||
USE_FULL_ANATEL_DICT=true
|
|
||||||
31
FRAMEWORK_NATIVE_CORRECTIONS.md
Normal file
31
FRAMEWORK_NATIVE_CORRECTIONS.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Correções framework-native aplicadas no Compass Backoffice
|
||||||
|
|
||||||
|
Este pacote foi corrigido para reduzir forks locais e usar o máximo possível do `agent_framework_oci`.
|
||||||
|
|
||||||
|
## Correções principais
|
||||||
|
|
||||||
|
1. `app/agents/runtime.py` deixou de manter um runtime próprio.
|
||||||
|
- Agora reexporta `AgentRuntimeMixin`, `RuntimeContext` e `MessageBuilder` diretamente de `agent_framework.runtime.agent_runtime`.
|
||||||
|
- Isso ativa o comportamento nativo do framework para MCP, cache MCP por `args_schema`, deduplicação intra-turno, RAG, memória, cache LLM e eventos IC/NOC/GRL.
|
||||||
|
|
||||||
|
2. `BackofficeWorkflowExecutor` passou a carregar judges pelo profile do agente.
|
||||||
|
- Antes: `JudgePipeline()` usava o caminho global por padrão.
|
||||||
|
- Agora: `JudgePipeline(settings=settings, config_path=self.agent_profile.get("judges_config_path"))`.
|
||||||
|
- Com isso o agente `backoffice_anatel` usa `config/agents/backoffice_anatel/judges.yaml`.
|
||||||
|
|
||||||
|
3. `config/guardrails.yaml` foi alinhado com `config/agents/backoffice_anatel/guardrails.yaml`.
|
||||||
|
- Evita que estágios do framework que leem o arquivo global executem uma política mais fraca.
|
||||||
|
- Guardrails ativos: `INPUT_SIZE`, `MSK`, `PINJ`, `TOX`, `DLEX_IN`, `RAGSEC`, `VLOOP`, `REVPREC`, `DLEX_OUT`, `OOS`, `CMP`, `AOFERTA`.
|
||||||
|
|
||||||
|
4. `config/judges.yaml` foi alinhado com `config/agents/backoffice_anatel/judges.yaml`.
|
||||||
|
- Evita divergência entre execução global e execução por agente.
|
||||||
|
- Inclui judges de qualidade, groundedness e políticas específicas do domínio backoffice.
|
||||||
|
|
||||||
|
5. `config/tools.yaml` recebeu política declarativa de MCP.
|
||||||
|
- Tools de consulta: `tool_type: read` e `cache.enabled: true`.
|
||||||
|
- Tools mutáveis/de ação: `tool_type: action`, `requires` e `cache.enabled: false`.
|
||||||
|
- A chave de cache passa a ser controlada pelo runtime do framework usando `args_schema`.
|
||||||
|
|
||||||
|
## Regra arquitetural preservada
|
||||||
|
|
||||||
|
As regras genéricas de segurança, qualidade, vazamento, repetição, escopo, groundedness e revisão de resposta ficam no framework. As regras específicas do domínio ANATEL/Backoffice permanecem como workflow de domínio, porque representam etapas de negócio e não guardrails reutilizáveis.
|
||||||
889
README.md
Normal file
889
README.md
Normal file
@@ -0,0 +1,889 @@
|
|||||||
|
# compass_backoffice — Manual de execução e testes
|
||||||
|
|
||||||
|
## 1. O que é o `compass_backoffice`
|
||||||
|
|
||||||
|
O `compass_backoffice` é um backend de agente para atendimento operacional de Backoffice/ANATEL, migrado para utilizar o `agent_framework_oci` como motor principal de execução.
|
||||||
|
|
||||||
|
Ele foi construído para receber solicitações de backoffice, reclamações ANATEL, protocolos, casos de cliente e demandas operacionais, acionar ferramentas externas por MCP, consultar contexto via RAG, aplicar guardrails, registrar observabilidade IC/NOC/GRL e produzir uma resposta controlada pelo workflow corporativo do framework.
|
||||||
|
|
||||||
|
Em termos práticos, o projeto permite testar um agente de backoffice com:
|
||||||
|
|
||||||
|
- entrada conversacional via `/gateway/message`;
|
||||||
|
- entrada REST compatível com o contrato legado via `/agent/process-and-stream` e rotas relacionadas;
|
||||||
|
- roteamento por intents configuradas em YAML;
|
||||||
|
- execução de ferramentas pelo `MCPToolRouter` do framework;
|
||||||
|
- isolamento por `tenant_id`, `agent_id`, `session_id` e `BusinessContext`;
|
||||||
|
- RAG, memória, checkpoints, cache, telemetria, guardrails, judges e output supervisor do `agent_framework_oci`.
|
||||||
|
|
||||||
|
## 2. Finalidade da aplicação
|
||||||
|
|
||||||
|
O agente foi criado para apoiar fluxos como:
|
||||||
|
|
||||||
|
- triagem de reclamações ANATEL;
|
||||||
|
- consulta de protocolo, chamado, cliente, contrato, linha, CPF/CNPJ e contexto operacional;
|
||||||
|
- enriquecimento de caso usando IMDB/PMID, Siebel, Speech Analytics, TAIS KB, ABRT e Portabilidade;
|
||||||
|
- geração de rascunho ou resposta para emulador ANATEL;
|
||||||
|
- registro controlado de ação operacional, parecer, conclusão, encaminhamento ou atualização de demanda;
|
||||||
|
- validação de segurança, conformidade, precisão e aderência ao domínio antes da resposta final.
|
||||||
|
|
||||||
|
O objetivo da migração é evitar que o agente tenha um grafo paralelo ou regras “quebra-galho”. A aplicação deve delegar ao framework tudo que o framework já oferece: workflow, roteamento, identidade, MCP, RAG, guardrails, judges, observabilidade, memória, checkpoint e cache.
|
||||||
|
|
||||||
|
## 3. Arquitetura resumida
|
||||||
|
|
||||||
|
Fluxo principal do agente conversacional:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HTTP /gateway/message ou /gateway/message/sse
|
||||||
|
-> ChannelGateway
|
||||||
|
-> IdentityResolver + BusinessContext
|
||||||
|
-> SessionRepository / Memory / Checkpoint
|
||||||
|
-> AgentWorkflow LangGraph
|
||||||
|
-> input_guardrails
|
||||||
|
-> routing_decision
|
||||||
|
-> backoffice_agent
|
||||||
|
-> output_supervisor
|
||||||
|
-> output_guardrails
|
||||||
|
-> judge
|
||||||
|
-> supervisor_review
|
||||||
|
-> persist
|
||||||
|
-> ChannelResponse
|
||||||
|
```
|
||||||
|
|
||||||
|
Fluxo REST legado adaptado ao framework:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HTTP /agent/process-and-stream
|
||||||
|
-> BackofficeRestChannelAdapter
|
||||||
|
-> BackofficeWorkflowDispatcher
|
||||||
|
-> BackofficeWorkflowExecutor
|
||||||
|
-> etapas framework-native de guardrails, output supervisor, judges, telemetry e persistência
|
||||||
|
```
|
||||||
|
|
||||||
|
Fluxo de ferramentas:
|
||||||
|
|
||||||
|
```text
|
||||||
|
BackofficeAgent
|
||||||
|
-> MCPToolRouter do agent_framework_oci
|
||||||
|
-> config/tools.yaml
|
||||||
|
-> config/mcp_parameter_mapping.yaml
|
||||||
|
-> config/mcp_servers.yaml
|
||||||
|
-> Backoffice MCP Server HTTP na porta 8010
|
||||||
|
-> backend mock, REST, Oracle ou clientes TIM
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Componentes principais do projeto
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
API FastAPI principal. Inicializa LLM, memória, summary memory, sessão, checkpoint, cache, ChannelGateway, analytics, observer, MCPToolRouter, IdentityResolver, AgentWorkflow, executor REST e rotas de debug/teste.
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/workflows/agent_graph.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Workflow LangGraph principal. Usa componentes do framework para guardrails, roteamento, supervisor, output supervisor, judges, RAG, telemetria profunda de LangGraph e persistência.
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/agents/backoffice_agent.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Agente de domínio Backoffice/ANATEL. Ele não deve recriar capacidades do framework. Recebe `intent`, `route`, `mcp_tools` e contexto do workflow; consulta RAG; aciona ferramentas MCP; emite IC/NOC/GRL; gera a resposta final.
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/workflows/backoffice_workflow_executor.py
|
||||||
|
app/workflows/backoffice_workflow_dispatcher.py
|
||||||
|
app/channels/backoffice_rest_adapter.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Camada de compatibilidade para contratos REST de backoffice. Preserva endpoints antigos, mas executa o fluxo dentro do runtime do framework.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/agents.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Registra o agente `backoffice_anatel`, define o agent default e aponta configs específicas de prompt, guardrails, judges, MCP e tools.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/routing.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Define intents, palavras-chave, exemplos, tools por intent e state policies. O roteamento ativo padrão é `router`, com fallback para `backoffice_agent`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/agents/backoffice_anatel/guardrails.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuração de guardrails específica do agente. Ativa rails de entrada e saída como `INPUT_SIZE`, `MSK`, `PINJ`, `TOX`, `DLEX_IN`, `RAGSEC`, `VLOOP`, `REVPREC`, `DLEX_OUT`, `OOS`, `CMP` e `AOFERTA`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/agents/backoffice_anatel/judges.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuração de judges pós-resposta para avaliação de qualidade, precisão e aderência.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/tools.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Contrato das ferramentas disponíveis para o agente. Exemplos: `consultar_reclamacao`, `consultar_cliente_backoffice`, `consultar_siebel_caso`, `consultar_imdb_cliente`, `consultar_speech_analytics`, `consultar_tais_kb`, `consultar_abrt`, `consultar_portabilidade`, `registrar_acao_backoffice` e ferramentas do emulador.
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/mcp_parameter_mapping.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Mapeia `BusinessContext` canônico para argumentos das tools MCP. Exemplo: `interaction_key -> protocol_id`, `customer_key -> customer_key`, `contract_key -> contract_key`, `session_key -> operator_session`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
mcp_servers/backoffice_mcp_server/
|
||||||
|
```
|
||||||
|
|
||||||
|
Servidor MCP HTTP compatível com o `MCPToolRouter` do framework. Expõe `/health`, `/tools/list` e `/tools/call`.
|
||||||
|
|
||||||
|
## 5. O que o projeto utiliza do `agent_framework_oci`
|
||||||
|
|
||||||
|
O `compass_backoffice` utiliza as seguintes capacidades do framework:
|
||||||
|
|
||||||
|
| Capacidade do framework | Uso no projeto |
|
||||||
|
|---|---|
|
||||||
|
| LangGraph / workflow corporativo | `app/workflows/agent_graph.py` |
|
||||||
|
| ChannelGateway | Normalização/renderização de mensagens por canal |
|
||||||
|
| IdentityResolver | Resolução de identidade técnica e `BusinessContext` |
|
||||||
|
| BusinessContext | Chaves canônicas para MCP e sessão |
|
||||||
|
| AgentProfileRegistry | Carregamento de `config/agents.yaml` |
|
||||||
|
| EnterpriseRouter | Roteamento por intents em `config/routing.yaml` |
|
||||||
|
| Supervisor | Modo alternativo quando `ROUTING_MODE=supervisor` |
|
||||||
|
| GuardrailPipeline | Guardrails de entrada e saída |
|
||||||
|
| OutputSupervisor | Revisão/retry/sanitize/block de resposta |
|
||||||
|
| JudgePipeline | Avaliação pós-resposta |
|
||||||
|
| MCPToolRouter | Chamada de tools via servidor MCP HTTP |
|
||||||
|
| RagService | Consulta RAG nativa do framework |
|
||||||
|
| Cache | Cache criado por `create_cache(settings)` |
|
||||||
|
| Memory | Histórico conversacional |
|
||||||
|
| ConversationSummaryMemory | Resumo de conversa |
|
||||||
|
| SessionRepository | Sessões persistentes |
|
||||||
|
| CheckpointRepository | Checkpoints por sessão |
|
||||||
|
| Telemetry / Langfuse | Spans, eventos, usage e debug |
|
||||||
|
| AgentObserver / Analytics | Emissão de IC, NOC e GRL |
|
||||||
|
| SSEHub | Eventos SSE por sessão |
|
||||||
|
|
||||||
|
## 6. Pré-requisitos
|
||||||
|
|
||||||
|
### 6.1 Python
|
||||||
|
|
||||||
|
O `pyproject.toml` declara:
|
||||||
|
|
||||||
|
```text
|
||||||
|
requires-python = >=3.11
|
||||||
|
```
|
||||||
|
|
||||||
|
Recomendação prática:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Python 3.11 ou 3.12
|
||||||
|
```
|
||||||
|
|
||||||
|
O `Dockerfile` usa `python:3.12-slim`.
|
||||||
|
|
||||||
|
### 6.2 Dependências Python
|
||||||
|
|
||||||
|
O projeto possui `requirements.txt` com dependências como:
|
||||||
|
|
||||||
|
- `fastapi`
|
||||||
|
- `uvicorn[standard]`
|
||||||
|
- `pydantic`
|
||||||
|
- `pydantic-settings`
|
||||||
|
- `python-dotenv`
|
||||||
|
- `langgraph`
|
||||||
|
- `langchain-core`
|
||||||
|
- `openai`
|
||||||
|
- `oci`
|
||||||
|
- `oracledb`
|
||||||
|
- `pymongo`
|
||||||
|
- `redis`
|
||||||
|
- `PyYAML`
|
||||||
|
- `langfuse`
|
||||||
|
- `httpx`
|
||||||
|
- `opentelemetry-*`
|
||||||
|
- `pytest`
|
||||||
|
- `pytest-asyncio`
|
||||||
|
- `motor`
|
||||||
|
- `aiokafka`
|
||||||
|
- `google-auth`
|
||||||
|
- `requests`
|
||||||
|
- `tenacity`
|
||||||
|
- `python-json-logger`
|
||||||
|
|
||||||
|
### 6.3 `agent_framework_oci`
|
||||||
|
|
||||||
|
O backend depende do pacote `agent_framework_oci`, importado como `agent_framework`.
|
||||||
|
|
||||||
|
Existem duas formas comuns de disponibilizar o framework:
|
||||||
|
|
||||||
|
#### Opção A — instalação editável local
|
||||||
|
|
||||||
|
Estrutura sugerida:
|
||||||
|
|
||||||
|
```text
|
||||||
|
workspace/
|
||||||
|
agent_framework_oci/
|
||||||
|
compass_backoffice/
|
||||||
|
```
|
||||||
|
|
||||||
|
Instalação:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd workspace/compass_backoffice
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install -e ../agent_framework_oci
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Se o diretório do framework tiver outro nome, ajuste o caminho do `pip install -e`.
|
||||||
|
|
||||||
|
#### Opção B — Docker
|
||||||
|
|
||||||
|
O `Dockerfile` espera copiar o framework para `/agent_framework`. Em builds Docker, garanta que o contexto inclua o diretório do framework conforme esperado pelo arquivo.
|
||||||
|
|
||||||
|
### 6.4 Serviços opcionais
|
||||||
|
|
||||||
|
Para testes locais mínimos, é possível usar SQLite e MCP mock.
|
||||||
|
|
||||||
|
Para execução integrada, podem ser necessários:
|
||||||
|
|
||||||
|
- Redis, se o cache estiver configurado para Redis;
|
||||||
|
- MongoDB, se algum repositório estiver configurado para Mongo;
|
||||||
|
- Oracle Autonomous DB, se memória/checkpoint/RAG/TAIS real forem usados;
|
||||||
|
- Langfuse, se telemetria estiver habilitada;
|
||||||
|
- credenciais OCI Generative AI;
|
||||||
|
- credenciais TIM/Siebel/PMID/Speech/TAIS, se o MCP estiver em modo real.
|
||||||
|
|
||||||
|
## 7. Configuração local
|
||||||
|
|
||||||
|
Crie o `.env` principal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Principais variáveis para teste local:
|
||||||
|
|
||||||
|
```env
|
||||||
|
APP_ENV=local
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
CORS_ORIGINS=*
|
||||||
|
|
||||||
|
DEFAULT_AGENT_ID=backoffice_anatel
|
||||||
|
ROUTING_MODE=router
|
||||||
|
ENABLE_SSE=true
|
||||||
|
ENABLE_PARALLEL_GUARDRAILS=true
|
||||||
|
GUARDRAILS_FAIL_FAST=true
|
||||||
|
|
||||||
|
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||||
|
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||||
|
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||||
|
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||||
|
SQLITE_DB_PATH=.local/backoffice_framework.db
|
||||||
|
|
||||||
|
LLM_PROVIDER=oci_openai
|
||||||
|
LLM_MODEL=cohere.command-r-08-2024
|
||||||
|
|
||||||
|
MCP_ENABLED=true
|
||||||
|
MCP_SERVERS_CONFIG_PATH=config/mcp_servers.yaml
|
||||||
|
MCP_PARAMETER_MAPPING_PATH=config/mcp_parameter_mapping.yaml
|
||||||
|
BACKOFFICE_MCP_BASE_URL=http://localhost:8010
|
||||||
|
```
|
||||||
|
|
||||||
|
Para rodar com LLM real, preencha também as variáveis do provider OCI/OpenAI-compatible exigidas pelo seu `agent_framework_oci`, por exemplo endpoint, chave, profile, região e compartment.
|
||||||
|
|
||||||
|
Para testes sem integrações reais de backoffice, configure o MCP em modo mock.
|
||||||
|
|
||||||
|
## 8. Como subir o Legacy Backoffice MCP Server
|
||||||
|
|
||||||
|
Crie o arquivo de ambiente do MCP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.backoffice_mcp.example .env.backoffice_mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
Para teste local com dados simulados:
|
||||||
|
|
||||||
|
```env
|
||||||
|
BACKOFFICE_MCP_HOST=0.0.0.0
|
||||||
|
BACKOFFICE_MCP_PORT=8010
|
||||||
|
BACKOFFICE_MCP_USE_MOCK=true
|
||||||
|
BACKOFFICE_MCP_BACKEND_TYPE=mock
|
||||||
|
```
|
||||||
|
|
||||||
|
Suba o servidor MCP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source .venv/bin/activate
|
||||||
|
./scripts/run_backoffice_mcp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste o health:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8010/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Liste as tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8010/tools/list
|
||||||
|
```
|
||||||
|
|
||||||
|
Chamada direta de tool:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8010/tools/call' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"name": "consultar_reclamacao",
|
||||||
|
"arguments": {
|
||||||
|
"protocol_id": "202603279001551",
|
||||||
|
"customer_key": "06252533106"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modos de backend do MCP
|
||||||
|
|
||||||
|
O MCP server suporta os modos abaixo:
|
||||||
|
|
||||||
|
| Modo | Uso |
|
||||||
|
|---|---|
|
||||||
|
| `mock` | Teste local com dados simulados em memória |
|
||||||
|
| `rest` | Chamada para backend REST configurado por `BACKOFFICE_MCP_REST_BASE_URL` |
|
||||||
|
| `oracle` | Placeholder para integração Oracle real |
|
||||||
|
| `tim_clients` | Uso de clientes TIM/develop configurados no `.env` |
|
||||||
|
|
||||||
|
Para usar integrações reais, desative mock e preencha as credenciais correspondentes:
|
||||||
|
|
||||||
|
```env
|
||||||
|
BACKOFFICE_MCP_USE_MOCK=false
|
||||||
|
BACKOFFICE_MCP_BACKEND_TYPE=tim_clients
|
||||||
|
BACKOFFICE_MCP_FAIL_OPEN_ON_BACKEND_ERROR=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Como subir a aplicação principal
|
||||||
|
|
||||||
|
Em outro terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd compass_backoffice
|
||||||
|
source .venv/bin/activate
|
||||||
|
mkdir -p .local
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste o health:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Endpoints úteis de diagnóstico:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/agents
|
||||||
|
curl http://localhost:8000/debug/env
|
||||||
|
curl http://localhost:8000/debug/mcp/tools
|
||||||
|
curl http://localhost:8000/test-llm
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste de identidade:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/debug/identity' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"channel": "web",
|
||||||
|
"agent_id": "backoffice_anatel",
|
||||||
|
"tenant_id": "default",
|
||||||
|
"payload": {
|
||||||
|
"message": "Consultar protocolo 202603279001551 do CPF 06252533106",
|
||||||
|
"session_id": "teste-readme-001",
|
||||||
|
"user_id": "operador"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste de roteamento:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/debug/route' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"channel": "web",
|
||||||
|
"agent_id": "backoffice_anatel",
|
||||||
|
"tenant_id": "default",
|
||||||
|
"payload": {
|
||||||
|
"message": "Preciso analisar uma reclamação da ANATEL do protocolo 202603279001551",
|
||||||
|
"session_id": "teste-readme-001",
|
||||||
|
"user_id": "operador"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Teste conversacional pelo gateway do framework
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/gateway/message' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"channel": "web",
|
||||||
|
"agent_id": "backoffice_anatel",
|
||||||
|
"tenant_id": "default",
|
||||||
|
"payload": {
|
||||||
|
"message": "Preciso analisar a reclamação ANATEL protocolo 202603279001551 do CPF 06252533106. A TIM está ligando cobrando planos que o cliente diz não ter assinado.",
|
||||||
|
"session_id": "teste-backoffice-001",
|
||||||
|
"user_id": "operador-bko",
|
||||||
|
"business_context": {
|
||||||
|
"customer_key": "06252533106",
|
||||||
|
"interaction_key": "202603279001551",
|
||||||
|
"session_key": "teste-backoffice-001"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Resposta esperada em alto nível:
|
||||||
|
|
||||||
|
- `text` com resposta do agente;
|
||||||
|
- `metadata.agent_id = backoffice_anatel`;
|
||||||
|
- `metadata.route` e/ou `metadata.intent`;
|
||||||
|
- `metadata.business_context` resolvido;
|
||||||
|
- `metadata.mcp_results`, quando tools forem acionadas;
|
||||||
|
- `metadata.guardrails` e `metadata.judges`, quando habilitados/retornados pelo pipeline.
|
||||||
|
|
||||||
|
## 11. Teste com SSE
|
||||||
|
|
||||||
|
Primeiro envie uma mensagem com SSE habilitado:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/gateway/message/sse' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"channel": "web",
|
||||||
|
"agent_id": "backoffice_anatel",
|
||||||
|
"tenant_id": "default",
|
||||||
|
"payload": {
|
||||||
|
"message": "Consultar protocolo ANATEL 202603279001551",
|
||||||
|
"session_id": "teste-sse-001",
|
||||||
|
"user_id": "operador-bko",
|
||||||
|
"business_context": {
|
||||||
|
"interaction_key": "202603279001551",
|
||||||
|
"customer_key": "06252533106"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Depois acompanhe os eventos usando o `conversation_key` retornado na resposta. Em geral, o formato é:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tenant_id:agent_id:session_id
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemplo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -N 'http://localhost:8000/gateway/events/default:backoffice_anatel:teste-sse-001'
|
||||||
|
```
|
||||||
|
|
||||||
|
Eventos esperados:
|
||||||
|
|
||||||
|
- `flow.start`
|
||||||
|
- `session.upserted`
|
||||||
|
- `message.received`
|
||||||
|
- `workflow.started`
|
||||||
|
- `workflow.completed`
|
||||||
|
- `message.responded`
|
||||||
|
- `flow.end`
|
||||||
|
|
||||||
|
## 12. Teste com o curl anexado
|
||||||
|
|
||||||
|
O arquivo anexado `curl_test.txt` usa a rota:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /agent/process-and-stream
|
||||||
|
```
|
||||||
|
|
||||||
|
Esse endpoint preserva o contrato REST do backoffice e passa pelo adapter framework-native.
|
||||||
|
|
||||||
|
Com a aplicação na porta 8000, execute o conteúdo do arquivo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash curl_test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Ou copie o curl abaixo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/agent/process-and-stream' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data-raw '{
|
||||||
|
"caseType": "anatel",
|
||||||
|
"origin": {
|
||||||
|
"sourceSystem": "turbina_odc",
|
||||||
|
"submittedBy": {
|
||||||
|
"userId": "f8052701",
|
||||||
|
"name": "Nicolas Silva",
|
||||||
|
"email": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"crmProtocol": "DS-987654321",
|
||||||
|
"ticketId": "ert",
|
||||||
|
"customer": {
|
||||||
|
"cpfCnpj": "06252533106",
|
||||||
|
"phones": ["62981152324"],
|
||||||
|
"msisdn": "62981152324",
|
||||||
|
"name": "Nicolas Ferreira da Silva",
|
||||||
|
"email": "nicolas.silva@exemplo.com.br",
|
||||||
|
"govBrSeal": null,
|
||||||
|
"odcCustomer": false,
|
||||||
|
"contumazCustomer": false,
|
||||||
|
"address": {
|
||||||
|
"cep": "01001-000",
|
||||||
|
"street": "Praca da Se",
|
||||||
|
"neighborhood": "Se",
|
||||||
|
"city": "Sao Paulo",
|
||||||
|
"state": "SP"
|
||||||
|
},
|
||||||
|
"subscriber": {
|
||||||
|
"cpfCnpj": "06252533106",
|
||||||
|
"subscriberName": "Assinante Exemplo",
|
||||||
|
"contactPhone": "62981152324",
|
||||||
|
"contactName": "Nicolas Ferreira da Silva"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"complaint": {
|
||||||
|
"complaintProtocol": "202603279001551",
|
||||||
|
"actionType": "nova",
|
||||||
|
"providerProtocol": null,
|
||||||
|
"inputChannel": "Anatel",
|
||||||
|
"service": "Celular Pós-pago",
|
||||||
|
"firstService": "Celular Pós-pago",
|
||||||
|
"modality": "Cobrança",
|
||||||
|
"motive": "Operadora liga ou envia mensagens indevidas de cobrança",
|
||||||
|
"description": "A TIM está me ligando cobrando planos que eu não assinei. Quero que parem",
|
||||||
|
"openedAt": "2026-03-27T11:45:00-03:00",
|
||||||
|
"dueAt": null
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 13. Teste de MCP via backend principal
|
||||||
|
|
||||||
|
O backend principal também permite chamar tools usando o router do framework:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location 'http://localhost:8000/debug/mcp/call/consultar_reclamacao' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data '{
|
||||||
|
"business_context": {
|
||||||
|
"interaction_key": "202603279001551",
|
||||||
|
"customer_key": "06252533106",
|
||||||
|
"session_key": "teste-mcp-001"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Esse teste é importante porque valida o caminho correto:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FastAPI principal -> MCPToolRouter -> mcp_parameter_mapping.yaml -> Backoffice MCP Server
|
||||||
|
```
|
||||||
|
|
||||||
|
Não é recomendado chamar clientes TIM/Siebel diretamente dentro do agente. O caminho correto é via MCP.
|
||||||
|
|
||||||
|
## 14. Como subir com Docker Compose
|
||||||
|
|
||||||
|
O projeto inclui `docker-compose.yml` com dois serviços:
|
||||||
|
|
||||||
|
- `backoffice-api`, porta `8000`;
|
||||||
|
- `backoffice-mcp`, porta `8010`.
|
||||||
|
|
||||||
|
Execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
cp .env.backoffice_mcp.example .env.backoffice_mcp
|
||||||
|
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/health
|
||||||
|
curl http://localhost:8010/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Observação: confira o `Dockerfile` e o contexto de build. Ele espera que o framework esteja disponível no contexto como `agent_framework` e a aplicação como `agent_template_backend`. Se a pasta local tiver outro nome, ajuste o Dockerfile ou o contexto antes do build.
|
||||||
|
|
||||||
|
## 15. Guardrails, judges e output supervisor
|
||||||
|
|
||||||
|
O agente usa duas camadas de configuração:
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/guardrails.yaml
|
||||||
|
config/agents/backoffice_anatel/guardrails.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
A configuração específica do agente é a mais importante para o `backoffice_anatel`.
|
||||||
|
|
||||||
|
Guardrails de entrada configurados:
|
||||||
|
|
||||||
|
- `INPUT_SIZE`
|
||||||
|
- `MSK`
|
||||||
|
- `PINJ`
|
||||||
|
- `TOX`
|
||||||
|
- `DLEX_IN`
|
||||||
|
- `RAGSEC`
|
||||||
|
- `VLOOP`
|
||||||
|
|
||||||
|
Guardrails de saída configurados:
|
||||||
|
|
||||||
|
- `REVPREC`
|
||||||
|
- `DLEX_OUT`
|
||||||
|
- `OOS`
|
||||||
|
- `CMP`
|
||||||
|
- `AOFERTA`
|
||||||
|
|
||||||
|
O workflow também usa:
|
||||||
|
|
||||||
|
- `OutputSupervisor`, para revisão, retry, sanitização ou bloqueio;
|
||||||
|
- `JudgePipeline`, para avaliação pós-resposta;
|
||||||
|
- `supervisor_review`, para revisão final do fluxo.
|
||||||
|
|
||||||
|
## 16. IC, NOC e GRL
|
||||||
|
|
||||||
|
A emissão de observabilidade passa por:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AgentObserver
|
||||||
|
create_analytics_publisher(settings)
|
||||||
|
src.compat.framework_observer.configure(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
No agente de backoffice, os eventos principais aparecem em `app/agents/backoffice_agent.py`.
|
||||||
|
|
||||||
|
Exemplos de ICs usados no agente:
|
||||||
|
|
||||||
|
- `AGA.001`: entrada recebida pelo agente nativo;
|
||||||
|
- `AGA.018`: contexto canônico validado;
|
||||||
|
- `AGA.012`: RAG/TAIS KB consultado;
|
||||||
|
- `AGA.014`: ferramentas MCP selecionadas;
|
||||||
|
- `AGA.008`: registro operacional ignorado por ausência de `action_text`;
|
||||||
|
- `AGA.010`: Speech Analytics consultado;
|
||||||
|
- `AGA.011`: cliente/IMDB consultado;
|
||||||
|
- `AGA.020`: templates/TAIS consultados;
|
||||||
|
- `AGA.006`: ação operacional solicitada;
|
||||||
|
- `AGA.043`: resposta produzida.
|
||||||
|
|
||||||
|
Exemplo de NOC:
|
||||||
|
|
||||||
|
- `NOC.001`: conclusão do agente nativo de backoffice.
|
||||||
|
|
||||||
|
Os GRLs são esperados principalmente nas etapas de guardrails/output supervisor/judges do framework e nos pontos de observabilidade plugados pelo `AgentObserver`.
|
||||||
|
|
||||||
|
## 17. RAG
|
||||||
|
|
||||||
|
O workflow inicializa:
|
||||||
|
|
||||||
|
```text
|
||||||
|
create_embedding_provider(settings)
|
||||||
|
RagService(settings, telemetry=telemetry)
|
||||||
|
```
|
||||||
|
|
||||||
|
O agente usa RAG para enriquecer a resposta quando configurado. O namespace de referência do agente aparece em `config/agents.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
rag_namespace: backoffice_anatel
|
||||||
|
```
|
||||||
|
|
||||||
|
Para uso real do RAG, configure no `.env` os providers e stores exigidos pelo `agent_framework_oci`, como embeddings OCI, vector store e credenciais de banco quando aplicável.
|
||||||
|
|
||||||
|
## 18. Rotas disponíveis
|
||||||
|
|
||||||
|
### Framework/gateway
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /health
|
||||||
|
GET /agents
|
||||||
|
GET /debug/env
|
||||||
|
GET /test-llm
|
||||||
|
POST /debug/route
|
||||||
|
POST /debug/identity
|
||||||
|
GET /debug/usage
|
||||||
|
GET /debug/mcp/tools
|
||||||
|
POST /debug/mcp/call/{tool_name}
|
||||||
|
POST /gateway/message
|
||||||
|
POST /gateway/message/sse
|
||||||
|
GET /gateway/events/{session_id}
|
||||||
|
GET /sessions/{session_id}/messages
|
||||||
|
GET /sessions/{session_id}/checkpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contratos REST de backoffice preservados
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /agent/process-ticket
|
||||||
|
POST /agent/execute
|
||||||
|
POST /agent/process-and-stream
|
||||||
|
POST /agent/search-tais-kb
|
||||||
|
POST /case/{transaction_id}/response-emulator/generate
|
||||||
|
POST /case/{transaction_id}/response-emulator/finalize
|
||||||
|
GET /case/{transaction_id}/response-emulator
|
||||||
|
POST /emulator-rag/search
|
||||||
|
GET /health/live
|
||||||
|
GET /health/ready
|
||||||
|
GET /debug/backoffice/parity
|
||||||
|
```
|
||||||
|
|
||||||
|
### MCP server
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /health
|
||||||
|
GET /tools/list
|
||||||
|
POST /tools/call
|
||||||
|
```
|
||||||
|
|
||||||
|
## 19. Validação estrutural
|
||||||
|
|
||||||
|
Antes de executar testes funcionais, valide sintaxe e imports básicos:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m compileall -q app src tools mcp_servers
|
||||||
|
python tools/validate_parity.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 20. Troubleshooting
|
||||||
|
|
||||||
|
### `ModuleNotFoundError: agent_framework`
|
||||||
|
|
||||||
|
Instale o framework em modo editável:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e ../agent_framework_oci
|
||||||
|
```
|
||||||
|
|
||||||
|
Ou ajuste o `PYTHONPATH` temporariamente:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PYTHONPATH="../agent_framework_oci:$PYTHONPATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `/debug/mcp/tools` sem tools
|
||||||
|
|
||||||
|
Verifique:
|
||||||
|
|
||||||
|
```env
|
||||||
|
MCP_ENABLED=true
|
||||||
|
MCP_SERVERS_CONFIG_PATH=config/mcp_servers.yaml
|
||||||
|
MCP_PARAMETER_MAPPING_PATH=config/mcp_parameter_mapping.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
E confirme se o MCP está ativo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8010/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### MCP retorna erro de conexão
|
||||||
|
|
||||||
|
Confirme se `config/mcp_servers.yaml` aponta para:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
endpoint: http://localhost:8010
|
||||||
|
```
|
||||||
|
|
||||||
|
No Docker, o endpoint deve ser o nome do serviço:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
endpoint: http://backoffice-mcp:8010
|
||||||
|
```
|
||||||
|
|
||||||
|
### LLM não responde
|
||||||
|
|
||||||
|
Teste:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/test-llm
|
||||||
|
```
|
||||||
|
|
||||||
|
Se falhar, revise as variáveis do provider OCI/OpenAI-compatible.
|
||||||
|
|
||||||
|
### SQLite não cria banco
|
||||||
|
|
||||||
|
Crie a pasta local:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .local
|
||||||
|
```
|
||||||
|
|
||||||
|
### Endpoint legado funciona, mas gateway não aciona tools
|
||||||
|
|
||||||
|
Teste o roteamento:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/debug/mcp/tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Depois teste `/debug/route` e confirme se a intent retorna `mcp_tools`.
|
||||||
|
|
||||||
|
## 21. Sequência recomendada de teste local
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Terminal 1
|
||||||
|
cd compass_backoffice
|
||||||
|
source .venv/bin/activate
|
||||||
|
cp .env.backoffice_mcp.example .env.backoffice_mcp
|
||||||
|
./scripts/run_backoffice_mcp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Terminal 2
|
||||||
|
cd compass_backoffice
|
||||||
|
source .venv/bin/activate
|
||||||
|
cp .env.example .env
|
||||||
|
mkdir -p .local
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
# Terminal 3
|
||||||
|
cd compass_backoffice
|
||||||
|
source .venv/bin/activate
|
||||||
|
cd legacy_backend
|
||||||
|
uvicorn mock_imdb_server:app --port 8011
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Terminal 4
|
||||||
|
curl http://localhost:8010/health
|
||||||
|
curl http://localhost:8000/health
|
||||||
|
curl http://localhost:8000/debug/mcp/tools
|
||||||
|
bash curl_test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 22. Diretriz de evolução
|
||||||
|
|
||||||
|
Ao evoluir o `compass_backoffice`, mantenha a regra arquitetural:
|
||||||
|
|
||||||
|
- identidade e chaves de negócio: `IdentityResolver` e `BusinessContext`;
|
||||||
|
- roteamento: `config/routing.yaml` e `EnterpriseRouter`;
|
||||||
|
- ferramentas externas: `MCPToolRouter` + MCP server;
|
||||||
|
- conhecimento: `RagService` do framework;
|
||||||
|
- segurança e conformidade: guardrails, output supervisor e judges do framework;
|
||||||
|
- observabilidade: `AgentObserver`, IC, NOC, GRL, Langfuse e telemetry;
|
||||||
|
- persistência: memória, sessão e checkpoint do framework.
|
||||||
|
|
||||||
|
Só implemente código de domínio fora do framework quando a capacidade realmente não existir no `agent_framework_oci`.
|
||||||
@@ -1,267 +1,12 @@
|
|||||||
from __future__ import annotations
|
"""Framework-native agent runtime facade.
|
||||||
|
|
||||||
import hashlib
|
Compass Backoffice must not maintain a forked runtime for MCP, RAG, cache,
|
||||||
from typing import Any
|
telemetry, memory or IC/NOC/GRL behavior. The application imports the runtime
|
||||||
|
mixin directly from agent_framework_oci so fixes in the framework (for example
|
||||||
|
MCP cache using args_schema, BusinessContext mapping and tool deduplication) are
|
||||||
class AgentRuntimeMixin:
|
picked up by this backend without duplicating code here.
|
||||||
"""Mixin operacional para agentes.
|
|
||||||
|
|
||||||
Integra RAG, cache, telemetria e chamadas MCP usando BusinessContext.
|
|
||||||
Os agentes não precisam conhecer nomes reais de parâmetros do domínio
|
|
||||||
(msisdn, invoice_id, order_id etc.); eles repassam as chaves canônicas e
|
|
||||||
o MCPParameterMapper converte para cada tool configurada.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from agent_framework.runtime.agent_runtime import AgentRuntimeMixin, MessageBuilder, RuntimeContext
|
||||||
|
|
||||||
async def _emit_ic(self, code: str, state: dict[str, Any], payload: dict[str, Any] | None = None, component: str | None = None) -> None:
|
__all__ = ["AgentRuntimeMixin", "MessageBuilder", "RuntimeContext"]
|
||||||
"""Emite Item de Controle (IC) sem impactar a execução do agente.
|
|
||||||
|
|
||||||
Este helper é intencionalmente fail-open: erro de observabilidade não
|
|
||||||
pode quebrar a jornada de negócio do agente. O desenvolvedor pode usar
|
|
||||||
o mesmo padrão para ICs específicos da sua squad.
|
|
||||||
"""
|
|
||||||
observer = getattr(self, "observer", None)
|
|
||||||
if not observer:
|
|
||||||
return
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
base = {
|
|
||||||
"session_id": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
"tenant_id": state.get("tenant_id"),
|
|
||||||
"agent_id": state.get("agent_id"),
|
|
||||||
"route": state.get("route"),
|
|
||||||
"intent": state.get("intent"),
|
|
||||||
"message_id": ctx.get("message_id"),
|
|
||||||
"channel_id": ctx.get("channel"),
|
|
||||||
}
|
|
||||||
base.update(payload or {})
|
|
||||||
try:
|
|
||||||
await observer.emit_ic(code, base, component=component or f"agent.{getattr(self, 'name', 'unknown')}")
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
async def _emit_noc(self, code: str, state: dict[str, Any], payload: dict[str, Any] | None = None, component: str | None = None) -> None:
|
|
||||||
"""Emite evento NOC sem acoplar a lógica de negócio à observabilidade."""
|
|
||||||
observer = getattr(self, "observer", None)
|
|
||||||
if not observer:
|
|
||||||
return
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
base = {
|
|
||||||
"session_id": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
"tenant_id": state.get("tenant_id"),
|
|
||||||
"agent_id": state.get("agent_id"),
|
|
||||||
"route": state.get("route"),
|
|
||||||
"intent": state.get("intent"),
|
|
||||||
"message_id": ctx.get("message_id"),
|
|
||||||
"channel_id": ctx.get("channel"),
|
|
||||||
}
|
|
||||||
base.update(payload or {})
|
|
||||||
try:
|
|
||||||
await observer.emit_noc(code, base, component=component or f"agent.{getattr(self, 'name', 'unknown')}")
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
async def _emit_grl(self, code: str, state: dict[str, Any], payload: dict[str, Any] | None = None, component: str | None = None) -> None:
|
|
||||||
"""Emite evento GRL opcional para custom rails implementados no backend."""
|
|
||||||
observer = getattr(self, "observer", None)
|
|
||||||
if not observer:
|
|
||||||
return
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
base = {
|
|
||||||
"session_id": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
"tenant_id": state.get("tenant_id"),
|
|
||||||
"agent_id": state.get("agent_id"),
|
|
||||||
"route": state.get("route"),
|
|
||||||
"intent": state.get("intent"),
|
|
||||||
"message_id": ctx.get("message_id"),
|
|
||||||
"channel_id": ctx.get("channel"),
|
|
||||||
}
|
|
||||||
base.update(payload or {})
|
|
||||||
try:
|
|
||||||
await observer.emit_grl(code, base, component=component or f"agent.{getattr(self, 'name', 'unknown')}")
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
async def _retrieve_rag_context(self, state: dict[str, Any]) -> tuple[str, dict[str, Any]]:
|
|
||||||
rag_service = getattr(self, "rag_service", None)
|
|
||||||
if not rag_service:
|
|
||||||
return "", {"enabled": False}
|
|
||||||
text = state.get("sanitized_input") or state.get("user_text") or ""
|
|
||||||
namespace = (
|
|
||||||
(state.get("agent_profile") or {}).get("rag_namespace")
|
|
||||||
or state.get("agent_id")
|
|
||||||
or state.get("route")
|
|
||||||
or "default"
|
|
||||||
)
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
business_context = ctx.get("business_context") or {}
|
|
||||||
graph_node = (
|
|
||||||
ctx.get("graph_node")
|
|
||||||
or business_context.get("customer_key")
|
|
||||||
or business_context.get("contract_key")
|
|
||||||
or ctx.get("customer_id")
|
|
||||||
)
|
|
||||||
result = await rag_service.retrieve(text, namespace=namespace, graph_node=graph_node)
|
|
||||||
context = result.as_prompt_context()
|
|
||||||
return context, {
|
|
||||||
"enabled": True,
|
|
||||||
"namespace": namespace,
|
|
||||||
"latency_ms": result.latency_ms,
|
|
||||||
"document_count": len(result.documents),
|
|
||||||
"graph_neighbors": len(result.graph_neighbors),
|
|
||||||
"top_document_ids": [d.id for d in result.documents[:5]],
|
|
||||||
"top_scores": [d.score for d in result.documents[:5]],
|
|
||||||
}
|
|
||||||
|
|
||||||
async def _call_mcp_tool(self, tool: str, arguments: dict[str, Any] | None, state: dict[str, Any]) -> dict[str, Any]:
|
|
||||||
"""Chama uma ferramenta via MCPToolRouter usando o contrato canônico do framework.
|
|
||||||
|
|
||||||
Use este helper quando o agente precisa passar argumentos específicos
|
|
||||||
além do BusinessContext mapeado em mcp_parameter_mapping.yaml.
|
|
||||||
Observabilidade IC.MCP_TOOL_CALLED/IC.TOOL_CALLED permanece uniforme.
|
|
||||||
"""
|
|
||||||
if not getattr(self, "tool_router", None):
|
|
||||||
return {"ok": False, "tool_name": tool, "error": "tool_router não configurado"}
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
business_context = ctx.get("business_context") or state.get("business_context") or {}
|
|
||||||
original_context = {
|
|
||||||
**ctx,
|
|
||||||
"tenant_id": state.get("tenant_id"),
|
|
||||||
"agent_id": state.get("agent_id"),
|
|
||||||
"session_id": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
"conversation_key": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
}
|
|
||||||
observer = getattr(self, "observer", None)
|
|
||||||
if observer:
|
|
||||||
await observer.emit_ic(
|
|
||||||
"IC.MCP_TOOL_CALLED",
|
|
||||||
{
|
|
||||||
"session_id": original_context.get("conversation_key") or original_context.get("session_id"),
|
|
||||||
"tenant_id": original_context.get("tenant_id"),
|
|
||||||
"agent_id": original_context.get("agent_id"),
|
|
||||||
"tool_name": tool,
|
|
||||||
"framework_native": True,
|
|
||||||
},
|
|
||||||
component="agent_runtime.native_mcp",
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
res = await self.tool_router.call(
|
|
||||||
tool,
|
|
||||||
arguments or {},
|
|
||||||
business_context=business_context,
|
|
||||||
original_context=original_context,
|
|
||||||
)
|
|
||||||
result_payload = res.model_dump(mode="json") if hasattr(res, "model_dump") else dict(res)
|
|
||||||
except Exception as exc:
|
|
||||||
result_payload = {"ok": False, "tool_name": tool, "error": str(exc), "error_type": type(exc).__name__}
|
|
||||||
result_payload.setdefault("tool_name", tool)
|
|
||||||
if observer:
|
|
||||||
await observer.emit_ic(
|
|
||||||
"IC.TOOL_CALLED",
|
|
||||||
{
|
|
||||||
"session_id": original_context.get("conversation_key") or original_context.get("session_id"),
|
|
||||||
"tenant_id": original_context.get("tenant_id"),
|
|
||||||
"agent_id": original_context.get("agent_id"),
|
|
||||||
"tool_name": tool,
|
|
||||||
"ok": result_payload.get("ok"),
|
|
||||||
"server_name": result_payload.get("server_name"),
|
|
||||||
"error": result_payload.get("error"),
|
|
||||||
"framework_native": True,
|
|
||||||
},
|
|
||||||
component="agent_runtime.native_mcp",
|
|
||||||
)
|
|
||||||
return result_payload
|
|
||||||
|
|
||||||
async def _collect_mcp_context(self, state: dict[str, Any]) -> list[dict[str, Any]]:
|
|
||||||
results: list[dict[str, Any]] = []
|
|
||||||
if not getattr(self, "tool_router", None):
|
|
||||||
return results
|
|
||||||
tools = state.get("mcp_tools") or []
|
|
||||||
ctx = state.get("context") or {}
|
|
||||||
business_context = ctx.get("business_context") or state.get("business_context") or {}
|
|
||||||
original_context = {
|
|
||||||
**ctx,
|
|
||||||
"tenant_id": state.get("tenant_id"),
|
|
||||||
"agent_id": state.get("agent_id"),
|
|
||||||
"session_id": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
"conversation_key": state.get("conversation_key") or state.get("session_id"),
|
|
||||||
}
|
|
||||||
for tool in tools:
|
|
||||||
observer = getattr(self, "observer", None)
|
|
||||||
if observer:
|
|
||||||
await observer.emit_ic(
|
|
||||||
"IC.MCP_TOOL_CALLED",
|
|
||||||
{
|
|
||||||
"session_id": original_context.get("conversation_key") or original_context.get("session_id"),
|
|
||||||
"tenant_id": original_context.get("tenant_id"),
|
|
||||||
"agent_id": original_context.get("agent_id"),
|
|
||||||
"tool_name": tool,
|
|
||||||
},
|
|
||||||
component="agent_runtime",
|
|
||||||
)
|
|
||||||
res = await self.tool_router.call(
|
|
||||||
tool,
|
|
||||||
{},
|
|
||||||
business_context=business_context,
|
|
||||||
original_context=original_context,
|
|
||||||
)
|
|
||||||
result_payload = res.model_dump(mode="json")
|
|
||||||
if observer:
|
|
||||||
await observer.emit_ic(
|
|
||||||
"IC.TOOL_CALLED",
|
|
||||||
{
|
|
||||||
"session_id": original_context.get("conversation_key") or original_context.get("session_id"),
|
|
||||||
"tenant_id": original_context.get("tenant_id"),
|
|
||||||
"agent_id": original_context.get("agent_id"),
|
|
||||||
"tool_name": tool,
|
|
||||||
"ok": result_payload.get("ok"),
|
|
||||||
"server_name": result_payload.get("server_name"),
|
|
||||||
"error": result_payload.get("error"),
|
|
||||||
},
|
|
||||||
component="agent_runtime",
|
|
||||||
)
|
|
||||||
results.append(result_payload)
|
|
||||||
return results
|
|
||||||
|
|
||||||
async def _cache_get(self, key: str):
|
|
||||||
cache = getattr(self, "cache", None)
|
|
||||||
if not cache:
|
|
||||||
return None
|
|
||||||
return await cache.get(key)
|
|
||||||
|
|
||||||
async def _cache_set(self, key: str, value: Any, ttl_seconds: int | None = None):
|
|
||||||
cache = getattr(self, "cache", None)
|
|
||||||
if not cache:
|
|
||||||
return
|
|
||||||
await cache.set(key, value, ttl_seconds)
|
|
||||||
|
|
||||||
def _llm_cache_key(self, state: dict[str, Any], agent_name: str, prompt_parts: list[Any]) -> str:
|
|
||||||
business_context = (state.get("context") or {}).get("business_context") or {}
|
|
||||||
raw = "|".join([
|
|
||||||
agent_name,
|
|
||||||
state.get("tenant_id") or "",
|
|
||||||
state.get("agent_id") or "",
|
|
||||||
state.get("intent") or "",
|
|
||||||
business_context.get("customer_key") or "",
|
|
||||||
business_context.get("contract_key") or "",
|
|
||||||
business_context.get("interaction_key") or "",
|
|
||||||
state.get("sanitized_input") or state.get("user_text") or "",
|
|
||||||
repr(prompt_parts),
|
|
||||||
])
|
|
||||||
return "llm:" + hashlib.sha256(raw.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
async def _invoke_llm_cached(self, state: dict[str, Any], agent_name: str, messages: list[dict[str, str]]):
|
|
||||||
ttl = int(getattr(getattr(self, "settings", None), "CACHE_TTL_SECONDS", 300) or 300)
|
|
||||||
key = self._llm_cache_key(state, agent_name, messages)
|
|
||||||
cached = await self._cache_get(key)
|
|
||||||
if cached is not None:
|
|
||||||
telemetry = getattr(self, "telemetry", None)
|
|
||||||
if telemetry:
|
|
||||||
await telemetry.event("cache.llm.hit", {"agent": agent_name, "key": key}, kind="cache")
|
|
||||||
return cached
|
|
||||||
telemetry = getattr(self, "telemetry", None)
|
|
||||||
if telemetry:
|
|
||||||
await telemetry.event("cache.llm.miss", {"agent": agent_name, "key": key}, kind="cache")
|
|
||||||
answer = await self.llm.ainvoke(messages)
|
|
||||||
await self._cache_set(key, answer, ttl)
|
|
||||||
return answer
|
|
||||||
|
|||||||
@@ -220,7 +220,10 @@ class BackofficeWorkflowExecutor:
|
|||||||
enable_parallel=bool(getattr(settings, "ENABLE_PARALLEL_GUARDRAILS", True)),
|
enable_parallel=bool(getattr(settings, "ENABLE_PARALLEL_GUARDRAILS", True)),
|
||||||
fail_fast=bool(getattr(settings, "GUARDRAILS_FAIL_FAST", True)),
|
fail_fast=bool(getattr(settings, "GUARDRAILS_FAIL_FAST", True)),
|
||||||
)
|
)
|
||||||
self.judges = JudgePipeline()
|
self.judges = JudgePipeline(
|
||||||
|
settings=settings,
|
||||||
|
config_path=self.agent_profile.get("judges_config_path"),
|
||||||
|
)
|
||||||
self.supervisor = Supervisor()
|
self.supervisor = Supervisor()
|
||||||
self.workflow_telemetry = WorkflowTelemetry(telemetry)
|
self.workflow_telemetry = WorkflowTelemetry(telemetry)
|
||||||
self.guardrail_telemetry = GuardrailTelemetry(telemetry)
|
self.guardrail_telemetry = GuardrailTelemetry(telemetry)
|
||||||
|
|||||||
@@ -1,54 +1,34 @@
|
|||||||
|
# Source of truth for global/default guardrails in Compass Backoffice.
|
||||||
|
# This file is intentionally aligned with config/agents/backoffice_anatel/guardrails.yaml
|
||||||
|
# so framework stages that read the global path and stages that read the agent profile
|
||||||
|
# apply the same policy set.
|
||||||
agent_id: backoffice_anatel
|
agent_id: backoffice_anatel
|
||||||
profile: backoffice_anatel_enterprise
|
profile: backoffice_anatel
|
||||||
|
|
||||||
input:
|
input:
|
||||||
- code: INPUT_SIZE
|
- code: INPUT_SIZE
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
max_chars: 12000
|
|
||||||
- code: MSK
|
- code: MSK
|
||||||
enabled: true
|
enabled: true
|
||||||
action: sanitize
|
|
||||||
- code: PINJ
|
- code: PINJ
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
- code: TOX
|
- code: TOX
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
- code: DLEX_IN
|
- code: DLEX_IN
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
- code: RAGSEC
|
- code: RAGSEC
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
- code: VLOOP
|
- code: VLOOP
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
output:
|
output:
|
||||||
- code: REVPREC
|
- code: REVPREC
|
||||||
enabled: true
|
enabled: true
|
||||||
action: sanitize
|
|
||||||
- code: DLEX_OUT
|
- code: DLEX_OUT
|
||||||
enabled: true
|
enabled: true
|
||||||
action: sanitize
|
|
||||||
- code: OOS
|
- code: OOS
|
||||||
enabled: true
|
enabled: true
|
||||||
action: review
|
|
||||||
- code: CMP
|
- code: CMP
|
||||||
enabled: true
|
enabled: true
|
||||||
action: review
|
|
||||||
- code: AOFERTA
|
- code: AOFERTA
|
||||||
enabled: true
|
enabled: true
|
||||||
action: block
|
|
||||||
business_rules:
|
|
||||||
require_evidence_for:
|
|
||||||
- protocolo
|
|
||||||
- cliente
|
|
||||||
- contrato
|
|
||||||
- status_siebel
|
|
||||||
- acao_operacional
|
|
||||||
forbid_without_tool_ok:
|
|
||||||
- registrar_acao_backoffice
|
|
||||||
- registrar_acao_siebel
|
|
||||||
domain_workflows:
|
|
||||||
checklist: src.agent.graphs.main_graph.create_main_agent_graph
|
|
||||||
response_emulator: src.agent.graphs.emulator_graph.create_emulator_graph
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
# Source of truth for global/default judges in Compass Backoffice.
|
||||||
|
# Aligned with config/agents/backoffice_anatel/judges.yaml to avoid running a
|
||||||
|
# weaker judge set when the framework is configured with the global path.
|
||||||
agent_id: backoffice_anatel
|
agent_id: backoffice_anatel
|
||||||
|
profile: judge
|
||||||
|
fail_closed: true
|
||||||
judges:
|
judges:
|
||||||
- name: response_quality
|
- name: response_quality
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -8,13 +13,21 @@ judges:
|
|||||||
threshold: 0.80
|
threshold: 0.80
|
||||||
- name: backoffice_no_fabricated_protocol
|
- name: backoffice_no_fabricated_protocol
|
||||||
enabled: true
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
description: Verifica se a resposta não inventa protocolo, cliente, contrato, SLA ou status operacional.
|
description: Verifica se a resposta não inventa protocolo, cliente, contrato, SLA ou status operacional.
|
||||||
- name: siebel_action_requires_tool_ok
|
- name: siebel_action_requires_tool_ok
|
||||||
enabled: true
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.85
|
||||||
description: Bloqueia confirmação de registro/fechamento Siebel sem evidência ok/registered.
|
description: Bloqueia confirmação de registro/fechamento Siebel sem evidência ok/registered.
|
||||||
- name: anatel_domain_traceability
|
- name: anatel_domain_traceability
|
||||||
enabled: true
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
description: Exige rastreabilidade para decisão de cancelamento, reclassificação, tratamento ou encaminhamento.
|
description: Exige rastreabilidade para decisão de cancelamento, reclassificação, tratamento ou encaminhamento.
|
||||||
- name: response_emulator_policy
|
- name: response_emulator_policy
|
||||||
enabled: true
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
description: Valida resposta formal ANATEL gerada pelo emulador antes de persistir/aprovar/fechar.
|
description: Valida resposta formal ANATEL gerada pelo emulador antes de persistir/aprovar/fechar.
|
||||||
|
|||||||
@@ -1,8 +1,34 @@
|
|||||||
|
# Source of truth for global/default guardrails in Compass Backoffice.
|
||||||
|
# This file is intentionally aligned with config/agents/backoffice_anatel/guardrails.yaml
|
||||||
|
# so framework stages that read the global path and stages that read the agent profile
|
||||||
|
# apply the same policy set.
|
||||||
|
agent_id: backoffice_anatel
|
||||||
|
profile: backoffice_anatel
|
||||||
|
|
||||||
input:
|
input:
|
||||||
|
- code: INPUT_SIZE
|
||||||
|
enabled: true
|
||||||
- code: MSK
|
- code: MSK
|
||||||
enabled: true
|
enabled: true
|
||||||
|
- code: PINJ
|
||||||
|
enabled: true
|
||||||
|
- code: TOX
|
||||||
|
enabled: true
|
||||||
|
- code: DLEX_IN
|
||||||
|
enabled: true
|
||||||
|
- code: RAGSEC
|
||||||
|
enabled: true
|
||||||
- code: VLOOP
|
- code: VLOOP
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- code: REVPREC
|
- code: REVPREC
|
||||||
enabled: true
|
enabled: true
|
||||||
|
- code: DLEX_OUT
|
||||||
|
enabled: true
|
||||||
|
- code: OOS
|
||||||
|
enabled: true
|
||||||
|
- code: CMP
|
||||||
|
enabled: true
|
||||||
|
- code: AOFERTA
|
||||||
|
enabled: true
|
||||||
|
|||||||
@@ -1,7 +1,33 @@
|
|||||||
|
# Source of truth for global/default judges in Compass Backoffice.
|
||||||
|
# Aligned with config/agents/backoffice_anatel/judges.yaml to avoid running a
|
||||||
|
# weaker judge set when the framework is configured with the global path.
|
||||||
|
agent_id: backoffice_anatel
|
||||||
|
profile: judge
|
||||||
|
fail_closed: true
|
||||||
judges:
|
judges:
|
||||||
- name: response_quality
|
- name: response_quality
|
||||||
enabled: true
|
enabled: true
|
||||||
threshold: 0.7
|
threshold: 0.75
|
||||||
- name: groundedness
|
- name: groundedness
|
||||||
enabled: true
|
enabled: true
|
||||||
threshold: 0.6
|
threshold: 0.80
|
||||||
|
- name: backoffice_no_fabricated_protocol
|
||||||
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
|
description: Verifica se a resposta não inventa protocolo, cliente, contrato, SLA ou status operacional.
|
||||||
|
- name: siebel_action_requires_tool_ok
|
||||||
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.85
|
||||||
|
description: Bloqueia confirmação de registro/fechamento Siebel sem evidência ok/registered.
|
||||||
|
- name: anatel_domain_traceability
|
||||||
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
|
description: Exige rastreabilidade para decisão de cancelamento, reclassificação, tratamento ou encaminhamento.
|
||||||
|
- name: response_emulator_policy
|
||||||
|
enabled: true
|
||||||
|
type: llm
|
||||||
|
threshold: 0.80
|
||||||
|
description: Valida resposta formal ANATEL gerada pelo emulador antes de persistir/aprovar/fechar.
|
||||||
|
|||||||
@@ -3,60 +3,110 @@ tools:
|
|||||||
description: Consulta reclamação/protocolo de backoffice/ANATEL.
|
description: Consulta reclamação/protocolo de backoffice/ANATEL.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 300
|
||||||
args_schema: { protocol_id: string, customer_key: string, interaction_key: string }
|
args_schema: { protocol_id: string, customer_key: string, interaction_key: string }
|
||||||
consultar_cliente_backoffice:
|
consultar_cliente_backoffice:
|
||||||
description: Consulta contexto operacional do cliente para backoffice.
|
description: Consulta contexto operacional do cliente para backoffice.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 600
|
||||||
args_schema: { customer_key: string, contract_key: string, session_key: string }
|
args_schema: { customer_key: string, contract_key: string, session_key: string }
|
||||||
registrar_acao_backoffice:
|
registrar_acao_backoffice:
|
||||||
description: Registra ação operacional, parecer ou encaminhamento no sistema de backoffice.
|
description: Registra ação operacional, parecer ou encaminhamento no sistema de backoffice.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: action
|
||||||
|
requires: [protocol_id, action_text, operator_session]
|
||||||
|
confirmation_required: false
|
||||||
|
cache:
|
||||||
|
enabled: false
|
||||||
args_schema: { protocol_id: string, action_text: string, operator_session: string }
|
args_schema: { protocol_id: string, action_text: string, operator_session: string }
|
||||||
|
|
||||||
consultar_siebel_caso:
|
consultar_siebel_caso:
|
||||||
description: Consulta caso/SR no Siebel.
|
description: Consulta caso/SR no Siebel.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 300
|
||||||
args_schema: { protocol_id: string, interaction_key: string, customer_key: string }
|
args_schema: { protocol_id: string, interaction_key: string, customer_key: string }
|
||||||
registrar_acao_siebel:
|
registrar_acao_siebel:
|
||||||
description: Registra ação, reclassificação ou fechamento no Siebel.
|
description: Registra ação, reclassificação ou fechamento no Siebel.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: action
|
||||||
|
requires: [protocol_id, action_text, operator_session]
|
||||||
|
confirmation_required: false
|
||||||
|
cache:
|
||||||
|
enabled: false
|
||||||
args_schema: { protocol_id: string, action_text: string, operator_session: string }
|
args_schema: { protocol_id: string, action_text: string, operator_session: string }
|
||||||
consultar_imdb_cliente:
|
consultar_imdb_cliente:
|
||||||
description: Consulta enriquecimento IMDB/PMID do cliente.
|
description: Consulta enriquecimento IMDB/PMID do cliente.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 600
|
||||||
args_schema: { customer_key: string, contract_key: string, session_key: string }
|
args_schema: { customer_key: string, contract_key: string, session_key: string }
|
||||||
consultar_speech_analytics:
|
consultar_speech_analytics:
|
||||||
description: Consulta histórico/resumo do Speech Analytics.
|
description: Consulta histórico/resumo do Speech Analytics.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 600
|
||||||
args_schema: { protocol_id: string, customer_key: string, interaction_key: string }
|
args_schema: { protocol_id: string, customer_key: string, interaction_key: string }
|
||||||
consultar_tais_kb:
|
consultar_tais_kb:
|
||||||
description: Consulta TAIS Knowledge Base/RAG e templates.
|
description: Consulta TAIS Knowledge Base/RAG e templates.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 900
|
||||||
args_schema: { query: string, protocol_id: string, customer_key: string }
|
args_schema: { query: string, protocol_id: string, customer_key: string }
|
||||||
consultar_abrt:
|
consultar_abrt:
|
||||||
description: Consulta ABRT associado ao cliente/caso.
|
description: Consulta ABRT associado ao cliente/caso.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 600
|
||||||
args_schema: { customer_key: string, protocol_id: string }
|
args_schema: { customer_key: string, protocol_id: string }
|
||||||
consultar_portabilidade:
|
consultar_portabilidade:
|
||||||
description: Consulta status de portabilidade.
|
description: Consulta status de portabilidade.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 600
|
||||||
args_schema: { customer_key: string, contract_key: string }
|
args_schema: { customer_key: string, contract_key: string }
|
||||||
buscar_templates_emulador:
|
buscar_templates_emulador:
|
||||||
description: Busca templates/documentos para Response Emulator.
|
description: Busca templates/documentos para Response Emulator.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: read
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
ttl_seconds: 900
|
||||||
args_schema: { protocol_id: string, query: string }
|
args_schema: { protocol_id: string, query: string }
|
||||||
gerar_rascunho_emulador:
|
gerar_rascunho_emulador:
|
||||||
description: Gera rascunho de resposta do Response Emulator.
|
description: Gera rascunho de resposta do Response Emulator.
|
||||||
mcp_server: backoffice
|
mcp_server: backoffice
|
||||||
enabled: true
|
enabled: true
|
||||||
|
tool_type: action
|
||||||
|
requires: [protocol_id]
|
||||||
|
cache:
|
||||||
|
enabled: false
|
||||||
args_schema: { protocol_id: string, selected_actions: array, operator_instructions: string }
|
args_schema: { protocol_id: string, selected_actions: array, operator_instructions: string }
|
||||||
|
|||||||
Reference in New Issue
Block a user