# Agent Framework OCI Evaluator — LLM-as-a-Judge **Idioma:** Português | [English](README.en-US.md) ## 1. Visão geral O **Agent Framework OCI Evaluator** é o componente batch responsável por avaliar, de forma assíncrona, a qualidade das conversas produzidas pelos agentes construídos sobre o Agent Framework OCI. O serviço lê sessões e traces do **Langfuse v3**, seleciona uma amostra determinística de sessões, calcula métricas de qualidade com um **LLM-as-a-Judge**, persiste resultados em **Oracle Autonomous Database**, gera o arquivo oficial `.TXT.GZ`, publica esse arquivo em **Google Cloud Storage (GCS)** e registra a execução do próprio job no Langfuse. Ele foi desenhado para execução **D+1**, como um **Kubernetes CronJob**, sem endpoint HTTP/gRPC de runtime. > **Modelo de implantação recomendado:** uma instância do evaluator por agente/squad. O código é genérico e suporta múltiplos agentes na configuração, mas em produção a recomendação é cada deployment carregar apenas o agente de sua responsabilidade. Isso mantém credenciais, ownership, scheduling e troubleshooting isolados. --- ## 2. Por que este componente existe Avaliar respostas generativas em tempo real dentro do fluxo síncrono do agente aumenta latência, custo e acoplamento. O evaluator separa essa responsabilidade do atendimento online. O desenho é: ```text Atendimento online | v Agent Framework | +--------------------> Langfuse v3 | | traces / sessions v Evaluator D+1 (CronJob) | +------------------+------------------+ | | | v v v OCI GenAI ADB GCS LLM-as-a-Judge estado/resultados .TXT.GZ oficial | +-----------------------> Langfuse auditoria / scores opcionais ``` Essa separação traz alguns benefícios: - não adiciona latência ao atendimento; - permite reprocessamento e auditoria; - permite amostragem configurável por agente; - isola falhas de avaliação do agente online; - permite utilizar modelos dedicados do OCI GenAI; - mantém um artefato oficial para a camada analítica da TIM; - evita acoplamento do código a nomes específicos de agentes, buckets ou credenciais. --- ## 3. Conceitos principais ### 3.1. Trace Um **trace** representa uma interação avaliada individualmente. Para cada trace são calculadas métricas trace-level: - `judgeScore` — qualidade global da resposta; - `accuracyScore` — aderência/factualidade em relação ao contexto; - `alucinationScore` — grau de afirmações não suportadas pelo contexto; - `promptLength` — tamanho bruto, em caracteres, do `trace.input`. ### 3.2. Session Uma **session** representa a conversa completa. Uma sessão normalmente possui vários traces. As métricas session-level são calculadas uma vez e replicadas nas linhas dos traces daquela sessão: - `inferredCsiScore`; - `resolution`; - `conversationPrecision`; - `loop`. ### 3.3. Amostragem por sessão A amostragem é feita sobre **sessions**, nunca sobre traces isolados. A seed é determinística: ```text agentId:reference_date ``` Exemplo: ```text billing:2026-08-13 ``` Com isso, duas execuções para o mesmo agente, data e percentual selecionam as mesmas sessões. Depois que uma sessão é selecionada, **todos os traces daquela sessão** são mantidos. ### 3.4. Janela BRT `run-agents` calcula a data de referência com base em: ```text D - daysBack ``` usando explicitamente: ```text America/Sao_Paulo ``` Com `daysBack: 1`, uma execução em 14/08 avalia o dia 13/08 completo, de 00:00:00 até 23:59:59.999999 BRT. ### 3.5. CONTEXT_TURN_LIMIT `CONTEXT_TURN_LIMIT` controla quantos turnos anteriores são enviados ao judge trace-level. Default: ```env CONTEXT_TURN_LIMIT=6 ``` Ele existe porque uma resposta não deve ser julgada fora do contexto da conversa. Aumentar o valor fornece mais contexto, mas também aumenta tokens, custo e latência do judge. ### 3.6. Loop A métrica `loop` não mede repetição textual simples. Ela representa o maior **streak consecutivo de `metadata.intent`** dentro da sessão. Exemplo: ```text billing billing billing payment payment ``` Resultado: ```text loop = 3 ``` --- ## 4. Fluxo completo de execução O fluxo produtivo é executado pelo comando: ```bash python -m evaluator.cli run-agents ``` ou pelo entrypoint instalado: ```bash af-evaluator run-agents ``` Etapas: ```text 1. Carrega agents.yaml 2. Calcula D-daysBack em BRT 3. Resolve credenciais Langfuse do agente 4. Busca TODAS as sessions nativas do período, com paginação 5. Busca TODOS os traces do período, com paginação 6. Busca observations em bulk/paginado 7. Recupera session_id real via metadata quando o trace legado não possui sessionId nativo 8. Filtra traces do agente/aliases 9. Faz sampling determinístico das sessions lógicas 10. Mantém todos os traces das sessions selecionadas 11. Persiste itens no ADB 12. Executa judge trace-level 13. Executa judge session-level 14. Calcula loop local 15. Gera .TXT.GZ 16. Faz upload para GCS 17. Remove o arquivo local após sucesso 18. Atualiza auditoria judge-job-{agentId} no Langfuse ``` ### 4.1. Por que a coleta não usa N+1 A implementação evita o padrão antigo: ```text 1 trace -> GET trace detail -> GET observations ``` repetido para cada trace. Agora a coleta é paginada em lote: ```text /api/public/sessions page 1..N /api/public/traces page 1..N /api/public/observations page 1..N ``` Assim, 10.000 traces não produzem ~20.000 chamadas adicionais. O número de requests cresce com o número de páginas da API. O `PAGE_SIZE` atual é 100. ### 4.2. Compatibilidade com traces sem `sessionId` nativo Algumas instrumentações históricas registram a sessão no metadata do trace/observation, mas não promovem o valor para o campo nativo `trace.sessionId` do Langfuse. O evaluator recupera a sessão real com a seguinte precedência: ```text trace.sessionId / trace.session_id -> trace.metadata.session_id / sessionId / conversation_key -> observation.metadata.session_id / sessionId / conversation_key -> business_context.session_key (último fallback de metadata) ``` O `trace.id` **nunca** é usado como `session_id`. Assim, o evaluator preserva sampling e métricas de session mesmo para traces históricos, sem transformar cada trace em uma sessão artificial. As estatísticas de coleta expõem `native_sessions_read`, `metadata_sessions_recovered` e `traces_without_session`. --- ## 5. Arquitetura de componentes ```text agent_framework_oci_evaluator/ ├── evaluator/ │ ├── analytics/ │ │ └── vloop.py │ ├── collectors/ │ │ ├── langfuse.py │ │ ├── langfuse_credentials.py │ │ ├── agent_framework.py │ │ └── mock.py │ ├── config/ │ │ ├── agents.py │ │ └── settings.py │ ├── core/ │ │ └── models.py │ ├── identity/ │ │ └── resolver.py │ ├── judges/ │ │ └── llm_judge.py │ ├── llm/ │ │ ├── client.py │ │ └── profile_resolver.py │ ├── output/ │ │ └── legacy_exporter.py │ ├── persistence/ │ │ ├── oracle_store.py │ │ └── repository.py │ ├── prompts/ │ │ └── loader.py │ ├── publishers/ │ │ ├── gcs_export.py │ │ ├── langfuse_job_audit.py │ │ └── langfuse_scores.py │ ├── cli.py │ ├── engine.py │ └── time_utils.py ├── configs/ │ ├── identity.yaml │ ├── judge/ │ │ ├── agents.yaml │ │ ├── storage.yaml │ │ ├── trace_metrics.yaml │ │ └── session_metrics.yaml │ ├── langfuse/ │ │ └── langfuse_credentials.example.yaml │ └── llm_profiles/ │ └── llm_profiles.yaml ├── deploy/ ├── tests/ ├── .env.example ├── Dockerfile └── pyproject.toml ``` ### 5.1. Collectors Transformam fontes externas em `ConversationRecord`. Fontes disponíveis: ```text langfuse agent_framework mock ``` `langfuse` é a fonte produtiva padrão. ### 5.2. EvaluationEngine `evaluator/engine.py` coordena o pipeline: - criação do run; - coleta; - amostragem; - persistência; - judge; - export; - upload; - auditoria. ### 5.3. Persistence Os estados e resultados são persistidos em Oracle Database. O schema é criado automaticamente pelo comando `init-db`. As principais tabelas são: ```text EVALUATION_RUN EVALUATION_PROGRESS_EVENT EVALUATION_ITEM EVALUATION_RESULT EVALUATION_METRIC EVALUATION_FINDING ``` O prefixo é configurável por: ```env ADB_TABLE_PREFIX=AGENTFW ``` ### 5.4. Publishers - `gcs_export.py` — upload do artefato oficial; - `langfuse_job_audit.py` — auditoria `judge-job-{agentId}`; - `langfuse_scores.py` — publicação opcional de scores de trace. > **Nota:** a auditoria produtiva do job usa as credenciais dinâmicas do agente. A publicação opcional de scores (`PUBLISH_LANGFUSE_SCORES=true`) ainda utiliza as credenciais globais `LANGFUSE_HOST/PUBLIC_KEY/SECRET_KEY`; deixe-a desabilitada quando não houver um projeto global apropriado. --- ## 6. Configuração de agentes Arquivo padrão: ```text configs/judge/agents.yaml ``` Exemplo: ```yaml agents: - agentId: billing enabled: true daysBack: 1 percentage: 1.0 langfuse_agent_aliases: - telecom_contas gcs_prefix: agnt_ai_contas ``` Campos: | Campo | Descrição | |---|---| | `agentId` | identidade lógica oficial do agente | | `enabled` | habilita/desabilita o agente naquele deployment | | `daysBack` | quantos dias voltar a partir do dia atual BRT | | `percentage` | percentual de sessions avaliadas, de 0.0 a 1.0 | | `langfuse_agent_aliases` | nomes técnicos aceitos para o agente nos traces; quando preenchido, funciona como filtro explícito independentemente da origem das credenciais Langfuse | | `gcs_prefix` | prefixo do agente dentro do bucket | Também são aceitos `agent_id` e `days_back` por compatibilidade, mas o formato recomendado é `agentId` / `daysBack`. **Semântica de `langfuse_agent_aliases`:** quando a lista possui valores, somente traces cujo identificador de agente corresponda a um dos aliases são elegíveis para sampling e avaliação. Quando a lista está vazia, o evaluator não aplica filtro por identidade de agente e considera o projeto/endpoint Langfuse configurado exclusivo para aquela entrada. ### 6.1. Um evaluator por agente Para o modelo recomendado de uma instância por agente, mantenha apenas uma entrada habilitada naquele deployment: ```yaml agents: - agentId: billing enabled: true daysBack: 1 percentage: 1.0 langfuse_agent_aliases: - telecom_contas - billing_agent gcs_prefix: agnt_ai_contas ``` O mesmo código pode ser usado em outro repositório/deployment apenas trocando os arquivos de configuração. --- ## 7. Credenciais Langfuse dinâmicas Não existem variáveis hardcoded do tipo: ```text LANGFUSE_BILLING_* LANGFUSE_SALES_* ``` O evaluator lê uma definição declarativa: ```env LANGFUSE_CREDENTIALS_CONFIG_PATH=configs/langfuse/langfuse_credentials.yaml ``` Crie o arquivo a partir do exemplo: ```bash cp configs/langfuse/langfuse_credentials.example.yaml \ configs/langfuse/langfuse_credentials.yaml ``` Exemplo: ```yaml langfuse: agents: - agentId: billing host: https://langfuse.example.internal publicKey: pk-... secretKey: sk-... - agentId: outro-agente host: https://outro-langfuse.internal publicKey: pk-... secretKey: sk-... ``` O resolver percorre todas as entradas. Adicionar um agente não exige alteração Python. ### 7.1. Referências a env ou arquivos Para não manter secrets no Git: ```yaml langfuse: agents: - agentId: billing hostEnv: BILLING_LF_HOST publicKeyEnv: BILLING_LF_PUBLIC secretKeyFile: /var/secrets/langfuse/billing-secret-key ``` Campos suportados: ```text host / hostEnv / hostFile publicKey / publicKeyEnv / publicKeyFile secretKey / secretKeyEnv / secretKeyFile ``` Ordem de resolução de cada campo: ```text valor direto -> variável de ambiente declarada -> arquivo declarado ``` ### 7.2. Credenciais globais Estas variáveis continuam disponíveis apenas para o comando manual genérico `run`: ```env LANGFUSE_HOST=http://localhost:3005 LANGFUSE_PUBLIC_KEY=... LANGFUSE_SECRET_KEY=... ``` O fluxo produtivo `run-agents` resolve credenciais pelo arquivo declarativo por agente. --- ## 8. Configuração do LLM Providers aceitos: ```text mock oci_openai oci_sdk openai_compatible ``` A recomendação para os modelos OCI dedicados é: ```env LLM_PROVIDER=oci_sdk ``` ### 8.1. OCI SDK Exemplo local com `~/.oci/config`: ```env LLM_PROVIDER=oci_sdk OCI_AUTH_MODE=config_file OCI_CONFIG_FILE=~/.oci/config OCI_PROFILE=DEFAULT OCI_REGION=sa-saopaulo-1 OCI_COMPARTMENT_ID=ocid1.compartment... OCI_GENAI_BASE_URL=https://inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com OCI_GENAI_MODEL=meta.llama-3.3-70b-instruct ``` Modos de autenticação suportados pelo código: ```text config_file instance_principal resource_principal oke_workload_identity workload_identity ``` ### 8.2. Dedicated endpoint Para endpoint dedicado: ```env OCI_GENAI_BASE_URL=https://.inference.generativeai..oci.oraclecloud.com OCI_GENAI_ENDPOINT_ID=ocid1.generativeaiendpoint... ``` Também é aceito o OCID do dedicated endpoint em `OCI_GENAI_MODEL`. O provider seleciona `DedicatedServingMode` quando há endpoint OCID; caso contrário usa `OnDemandServingMode`. ### 8.3. Perfis do judge Arquivo: ```text configs/llm_profiles/llm_profiles.yaml ``` Exemplo: ```yaml profiles: judge: temperature: 0 max_tokens: 900 ``` Selecionado por: ```env LLM_PROFILE=judge ``` --- ## 9. Prompts e métricas ### 9.1. Trace metrics Arquivo: ```text configs/judge/trace_metrics.yaml ``` O judge deve retornar JSON com: ```json { "judgeScore": 0.0, "accuracyScore": 0.0, "alucinationScore": 0.0, "rationale": "..." } ``` Se o parse falhar: ```text 1ª tentativa -> parse falha 2ª tentativa com retry-hint -> parse falha novamente métricas trace-level = None -> exportadas como "0.00" ``` ### 9.2. Session metrics Arquivo: ```text configs/judge/session_metrics.yaml ``` Resultado esperado: ```json { "inferredCsiScore": 0.5, "resolution": 1, "conversationPrecision": 1, "rationale": "..." } ``` Os valores session-level são replicados no arquivo para todos os traces da mesma session. --- ## 10. Identity normalization Arquivo: ```text configs/identity.yaml ``` A camada de identidade converte identificadores específicos de canais/domínios para chaves canônicas como: ```text customer_key contract_key interaction_key account_key resource_key session_key ``` Isso evita espalhar lógica como `cpf`, `msisdn`, `invoice_id`, `order_id`, `ura_call_id` etc. pelo código do evaluator. A ordem das fontes funciona como fallback. Exemplo: ```yaml session_key: sources: - business_context.session_key - session_key - conversation_key - session_id ``` --- ## 11. Configuração de GCS Buckets ficam fora do código em: ```text configs/judge/storage.yaml ``` Exemplo: ```yaml storage: buckets: dev: tim-bigdata-dev-ca1f-raw-standard-agnt-ai-atendimento fqa: tim-bigdata-fqa-60ce-raw-standard-agnt-ai-atendimento prd: tim-bigdata-prod-e305-raw-standard-agnt-ai-atendimento ``` Seleção: ```env APP_ENV=dev STORAGE_CONFIG_PATH=configs/judge/storage.yaml ``` Override de emergência/operação: ```env JUDGE_GCS_BUCKET=meu-bucket ``` Para habilitar o fluxo produtivo: ```env ENABLE_GCS_UPLOAD=true GOOGLE_APPLICATION_CREDENTIALS=/path/GCP_ACCESS_KEY.json ``` `run-agents` considera GCS obrigatório. Se `ENABLE_GCS_UPLOAD=false` ou o upload falhar, o run do agente termina como `FAILED`. --- ## 12. Arquivo de saída O nome é configurado diretamente no `.env`: ```env OUTPUT_FILENAME_PATTERN=AGENTE_{agent_id}_LLM_JUDGE_{timestamp}.TXT.GZ ``` Os placeholders disponíveis são `{agent_id}`, `{run_id}` e `{timestamp}`. O `timestamp` é gerado no formato `YYYYMMDD_HHMMSS`, no fuso de São Paulo. Padrão resultante: ```text AGENTE_{agentId}_LLM_JUDGE_{YYYYMMDD}_{HHMMSS}.TXT.GZ ``` Exemplo: ```text AGENTE_billing_LLM_JUDGE_20260814_020015.TXT.GZ ``` Cabeçalho: ```text "judgeScore"|;"accuracyScore"|;"alucinationScore"|;"promptLength"|;"loop"|;"inferredCsiScore"|;"resolution"|;"conversationPrecision"|;"uraCallId"|;"channelId"|;"sessionId"|;"messageId" ``` Linha final: ```text "TOTAL"|;"" ``` Após upload bem-sucedido para GCS, o arquivo local é apagado. --- ## 13. Instalação local ### 13.1. Pré-requisitos - Python 3.11+; - acesso ao Oracle Database/Autonomous Database; - Oracle Wallet quando aplicável; - acesso ao Langfuse v3; - acesso ao OCI Generative AI para execução real do judge; - credencial GCP para upload produtivo. ### 13.2. Criar venv Linux/WSL/macOS: ```bash python -m venv .venv source .venv/bin/activate ``` Windows PowerShell: ```powershell python -m venv .venv .\.venv\Scripts\Activate.ps1 ``` ### 13.3. Instalar Modo recomendado durante desenvolvimento: ```bash python -m pip install --upgrade pip setuptools wheel pip install -e . ``` Validar: ```bash pip check python -c "import oci, httpx, oracledb; import google.cloud.storage; print('Dependencias OK')" ``` --- ## 14. Configuração local passo a passo ### Passo 1 — `.env` ```bash cp .env.example .env ``` Edite os valores reais. Configuração mínima típica: ```env APP_ENV=dev ADB_USER=admin ADB_PASSWORD=... ADB_DSN=... ADB_WALLET_LOCATION=/path/to/wallet ADB_WALLET_PASSWORD=... ADB_TABLE_PREFIX=AGENTFW ENABLE_LANGFUSE=true LANGFUSE_CREDENTIALS_CONFIG_PATH=configs/langfuse/langfuse_credentials.yaml LLM_PROVIDER=oci_sdk LLM_PROFILE=judge OCI_AUTH_MODE=config_file OCI_CONFIG_FILE=~/.oci/config OCI_PROFILE=DEFAULT OCI_REGION=sa-saopaulo-1 OCI_COMPARTMENT_ID=... OCI_GENAI_BASE_URL=https://... OCI_GENAI_ENDPOINT_ID=ocid1.generativeaiendpoint... CONTEXT_TURN_LIMIT=6 BATCH_SIZE=50 MAX_ATTEMPTS=3 ENABLE_GCS_UPLOAD=true STORAGE_CONFIG_PATH=configs/judge/storage.yaml GOOGLE_APPLICATION_CREDENTIALS=/path/GCP_ACCESS_KEY.json ``` ### Passo 2 — Langfuse ```bash cp configs/langfuse/langfuse_credentials.example.yaml \ configs/langfuse/langfuse_credentials.yaml ``` Declare o agente que será executado. ### Passo 3 — agents.yaml Ajuste: ```text configs/judge/agents.yaml ``` Para uma instância dedicada, deixe somente o agente daquela squad habilitado. ### Passo 4 — validar configuração ```bash python -m evaluator.cli show-config ``` ### Passo 5 — inicializar banco ```bash python -m evaluator.cli init-db ``` > `reset-db` apaga e recria o schema do evaluator. Use apenas em ambiente controlado. --- ## 15. CLI A ajuda real da versão atual é: ```bash python -m evaluator.cli --help ``` Comandos disponíveis: ```text init-db reset-db show-config run run-agents progress runs ``` ### 15.1. `run` — execução manual/diagnóstica Sintaxe: ```bash python -m evaluator.cli run PERIOD_START PERIOD_END [OPTIONS] ``` Exemplo: ```bash python -m evaluator.cli run \ 2026-08-12T00:00:00 \ 2026-08-12T23:59:59 \ --source langfuse \ --limit 10 ``` Opções: ```text --source TEXT default: langfuse --limit INTEGER --show-progress / --no-show-progress ``` **Importante:** `run` é um caminho manual genérico. Ele coleta, avalia e persiste resultados, mas não executa o contrato produtivo completo por agente (sampling configurado, export GCS e auditoria de job). Para homologação/produção use `run-agents`. ### 15.2. `run-agents` — fluxo produtivo ```bash python -m evaluator.cli run-agents ``` Um agente: ```bash python -m evaluator.cli run-agents \ --agent-id billing \ --source langfuse ``` Teste limitado: ```bash python -m evaluator.cli run-agents \ --agent-id billing \ --limit 10 ``` Opções: ```text --source TEXT default: langfuse --agent-id TEXT --limit INTEGER ``` `run-agents` usa `daysBack`, `percentage`, credenciais e `gcs_prefix` do agente. ### 15.3. Listar runs ```bash python -m evaluator.cli runs ``` ou: ```bash python -m evaluator.cli runs --limit 50 ``` ### 15.4. Progresso de um run ```bash python -m evaluator.cli progress ``` Com mais eventos: ```bash python -m evaluator.cli progress --events 100 ``` Eventos típicos do fluxo produtivo: ```text LANGFUSE_JOB_AUDIT RUN_CREATED COLLECTING COLLECTED SAMPLED ITEMS_INSERTED BATCH_STARTED ITEM_COMPLETED SESSION_JUDGE_COMPLETED COMPLETED EXPORTED GCS_UPLOADED LOCAL_FILE_DELETED LANGFUSE_JOB_AUDIT ``` --- ## 16. Testes Instale `pytest` caso necessário: ```bash pip install pytest ``` ### 16.1. Todos os testes ```bash python -m pytest -v ``` ### 16.2. Conformidade TIM ```bash python -m pytest tests/test_tim_spec.py -v ``` ### 16.3. Conformidade ampliada ```bash python -m pytest tests/test_full_tim_compliance.py -v ``` ### 16.4. Regressão de hardcodes ```bash python -m pytest tests/test_no_product_hardcodes.py -v ``` ### 16.5. Suite recomendada antes de um release ```bash python -m pytest \ tests/test_tim_spec.py \ tests/test_full_tim_compliance.py \ tests/test_no_product_hardcodes.py \ -v ``` Validar sintaxe/imports: ```bash python -m compileall evaluator ``` --- ## 17. Teste funcional local recomendado ### 17.1. Smoke test com mock Configure: ```env LLM_PROVIDER=mock ``` E rode um período pequeno com source mock quando desejado: ```bash python -m evaluator.cli run \ 2026-08-12T00:00:00 \ 2026-08-12T23:59:59 \ --source mock ``` ### 17.2. Teste Langfuse sem volume grande ```bash python -m evaluator.cli run-agents \ --agent-id billing \ --limit 5 ``` Depois: ```bash python -m evaluator.cli runs --limit 5 python -m evaluator.cli progress --events 100 ``` Confirme: - `COLLECTED` > 0; - `SAMPLED` mostra sessions selecionadas; - itens concluem sem `ITEM_FAILED`; - `EXPORTED` aparece; - `GCS_UPLOADED` contém URI `gs://...`; - `LOCAL_FILE_DELETED` aparece; - auditoria `completed` foi confirmada. --- ## 18. Docker Build local: ```bash docker build -t agent-framework-evaluator:local . ``` O runtime produtivo é batch. O container não precisa expor porta HTTP. Para Kubernetes, o `CronJob` chama: ```text python -m evaluator.cli run-agents --source langfuse ``` --- ## 19. Kubernetes / OKE O **evaluator propriamente dito continua CronJob-only**, conforme a especificação: ele não abre porta HTTP/gRPC e o processamento continua sendo executado exclusivamente pelo `evaluator-batch`. A interface web foi reintroduzida como um **componente separado e somente leitura**, `evaluator-monitor`. Ela não executa avaliação, não altera runs e não muda o contrato batch do evaluator. O monitor apenas consulta as tabelas de execução/progresso no Autonomous Database. ```text Evaluator batch (evaluator-batch CronJob) │ ├── cria/atualiza EVALUATION_RUN ├── grava EVALUATION_PROGRESS_EVENT └── grava EVALUATION_RESULT │ ▼ Autonomous Database ▲ │ somente leitura Evaluator Monitor (Deployment HTTP) │ └── /ui + APIs de acompanhamento ``` Recursos Kustomize: ```text Namespace ServiceAccount ConfigMap CronJob evaluator-batch Deployment evaluator-monitor Service evaluator-monitor (ClusterIP) ``` Não existe Ingress por default. Assim, a interface não é exposta para fora do cluster automaticamente. CronJob atual: ```yaml schedule: "0 2 * * *" timeZone: "America/Sao_Paulo" suspend: false concurrencyPolicy: Forbid ``` ### 19.1. Preparar arquivos de deploy ```bash cp deploy/env/evaluator.env.example deploy/env/evaluator.env cp deploy/langfuse/langfuse_credentials.example.yaml \ deploy/langfuse/langfuse_credentials.yaml ``` Coloque o wallet em: ```text deploy/wallet/ ``` Credencial GCP: ```text deploy/gcp/GCP_ACCESS_KEY.json ``` ### 19.2. Criar secrets DEV: ```bash ./deploy/scripts/create-secrets.sh dev ``` HML: ```bash ./deploy/scripts/create-secrets.sh hml ``` PROD: ```bash ./deploy/scripts/create-secrets.sh prod ``` São criados/atualizados: ```text evaluator-secrets evaluator-wallet evaluator-langfuse evaluator-gcp (quando o arquivo GCP existir) ``` ### 19.3. Build e push ```bash export REGISTRY=tim.azurecr.io export IMAGE_TAG= ./deploy/scripts/build-push.sh prod ``` ### 19.4. Deploy ```bash export REGISTRY=tim.azurecr.io export IMAGE_TAG= ./deploy/scripts/deploy.sh prod ``` Por default o script também executa o job de inicialização do banco. Para não executar: ```bash export RUN_INIT_DB=false ``` ### 19.5. Status ```bash ./deploy/scripts/status.sh prod ``` ou diretamente: ```bash kubectl -n agent-evaluator-prod get cronjob,jobs,pods ``` ### 19.6. Execução imediata fora do horário ```bash ./deploy/scripts/run-now.sh prod ``` O script cria um Job baseado no CronJob. Acompanhar: ```bash kubectl -n agent-evaluator-prod get jobs,pods kubectl -n agent-evaluator-prod logs -f job/ ``` ### 19.7. Interface web de acompanhamento A UI é servida pelo componente independente `evaluator-monitor`: ```text GET /ui GET /health GET /api/runs GET /api/runs/{run_id} GET /api/runs/{run_id}/results ``` Para acessar localmente um monitor implantado no Kubernetes sem criar Ingress: ```bash kubectl -n agent-evaluator-prod port-forward svc/evaluator-monitor 8080:8080 ``` Depois abra: ```text http://localhost:8080/ui ``` A tela mostra runs recentes, `RUN_ID`, agente, status, progresso, eventos e resultados. O browser faz auto-refresh a cada 5 segundos. Para executar o monitor localmente: ```bash python -m uvicorn evaluator_monitor.app:app --host 0.0.0.0 --port 8080 ``` O monitor utiliza as mesmas configurações `ADB_*`/wallet do evaluator. Ele é **read-only** do ponto de vista funcional: não há endpoint para iniciar, cancelar, apagar ou alterar runs. > Recomendação de segurança: mantenha o Service como `ClusterIP`. Se futuramente houver necessidade de exposição externa, coloque autenticação/autorização no Ingress/API Gateway corporativo; não exponha o monitor diretamente à Internet. --- ## 20. Como subir uma nova versão Fluxo recomendado: ### 1. Atualizar código/configuração Não coloque credenciais reais no Git. ### 2. Rodar testes ```bash python -m pytest -v python -m compileall evaluator ``` ### 3. Testar localmente com limite ```bash python -m evaluator.cli run-agents \ --agent-id \ --limit 5 ``` ### 4. Validar o run ```bash python -m evaluator.cli runs --limit 10 python -m evaluator.cli progress --events 100 ``` ### 5. Confirmar GCS O evento deve conter: ```text GCS_UPLOADED ``` com uma URI como: ```text gs:////AGENTE__LLM_JUDGE_.TXT.GZ ``` ### 6. Atualizar secrets/configuração do ambiente ```bash ./deploy/scripts/create-secrets.sh ``` ### 7. Build/push com uma tag nova ```bash export REGISTRY= export IMAGE_TAG= ./deploy/scripts/build-push.sh ``` ### 8. Deploy ```bash ./deploy/scripts/deploy.sh ``` ### 9. Rodar manualmente para homologação ```bash ./deploy/scripts/run-now.sh ``` ### 10. Verificar logs e artefatos ```bash kubectl -n agent-evaluator- logs -f job/ ``` Confirme também o objeto no bucket GCS e a auditoria `judge-job-{agentId}` no Langfuse. --- ## 21. Troubleshooting ### `Collected 0 traces` Verifique nesta ordem: 1. data processada (`daysBack` usa BRT); 2. credencial Langfuse do `agentId` correto; 3. `host` do projeto Langfuse; 4. `langfuse_agent_aliases`; 5. existência de sessions/traces naquele período; 6. se o `agentId` lógico é diferente do `metadata.agent_id` dos traces. Exemplo: ```yaml - agentId: billing langfuse_agent_aliases: - telecom_contas - billing_agent ``` ### `No Langfuse credentials configured for agent ...` O agente existe em `agents.yaml`, mas não no arquivo apontado por: ```env LANGFUSE_CREDENTIALS_CONFIG_PATH ``` Adicione uma entrada com o mesmo `agentId`. ### `Langfuse credentials definition file not found` Crie/mapeie o arquivo: ```text configs/langfuse/langfuse_credentials.yaml ``` ou configure outro path. ### `TIM specification requires GCS upload...` `run-agents` foi executado com: ```env ENABLE_GCS_UPLOAD=false ``` Altere para `true` e configure a credencial GCP. ### GCS falha Verifique: ```bash echo "$GOOGLE_APPLICATION_CREDENTIALS" ls -l "$GOOGLE_APPLICATION_CREDENTIALS" ``` E valide o bucket resolvido por `APP_ENV`/`storage.yaml`. ### OCI SDK falha em dedicated endpoint Confira: ```env LLM_PROVIDER=oci_sdk OCI_GENAI_BASE_URL=https:// OCI_GENAI_ENDPOINT_ID=ocid1.generativeaiendpoint... OCI_COMPARTMENT_ID=... ``` Para `oci_sdk`, não use `/openai/v1` no endpoint. ### Banco não inicializado ```bash python -m evaluator.cli init-db ``` ### Ver progresso pela CLI O `run-agents` agora imprime o `RUN_ID` assim que a execução é criada: ```text [telecom_contas] RUN_ID=8d9a2d1e-... progress: python -m evaluator.cli progress 8d9a2d1e-... --events 100 ``` Também é possível listar runs e consultar qualquer execução: ```bash python -m evaluator.cli runs python -m evaluator.cli progress --events 100 ``` ### Ver progresso pela interface web ```bash python -m uvicorn evaluator_monitor.app:app --host 0.0.0.0 --port 8080 ``` Abra `http://localhost:8080/ui`. Em Kubernetes, prefira `kubectl port-forward svc/evaluator-monitor 8080:8080`. --- ## 22. Variáveis de ambiente principais | Variável | Uso | |---|---| | `APP_ENV` | ambiente usado para resolver storage | | `ADB_*` | conexão e wallet Oracle | | `ENABLE_LANGFUSE` | habilita coleta Langfuse | | `LANGFUSE_CREDENTIALS_CONFIG_PATH` | YAML declarativo por agente | | `LANGFUSE_*` | fallback global apenas para `run` manual | | `PUBLISH_LANGFUSE_SCORES` | publica scores de trace de volta no Langfuse | | `LLM_PROVIDER` | `mock`, `oci_openai`, `oci_sdk`, `openai_compatible` | | `LLM_PROFILE` | perfil de judge | | `OCI_AUTH_MODE` | autenticação OCI SDK | | `OCI_GENAI_BASE_URL` | endpoint OCI GenAI | | `OCI_GENAI_MODEL` | model ID/nome ou compatibilidade com endpoint OCID | | `OCI_GENAI_ENDPOINT_ID` | dedicated endpoint OCID | | `OCI_COMPARTMENT_ID` | compartment OCI | | `AGENTS_CONFIG_PATH` | arquivo de agentes | | `TRACE_PROMPT_PATH` | prompt trace-level | | `SESSION_PROMPT_PATH` | prompt session-level | | `BATCH_SIZE` | quantidade processada por lote interno | | `MAX_ATTEMPTS` | limite configurável de tentativas | | `CONTEXT_TURN_LIMIT` | últimos N turnos enviados ao judge trace | | `ENABLE_GCS_UPLOAD` | exige upload no fluxo produtivo | | `STORAGE_CONFIG_PATH` | mapping de buckets por ambiente | | `JUDGE_GCS_BUCKET` | override de bucket | | `GOOGLE_APPLICATION_CREDENTIALS` | credencial GCP | | `OUTPUT_DIR` | diretório temporário de export | | `OUTPUT_FILENAME_PATTERN` | template configurável do nome do arquivo de saída | --- ## 23. Segurança e operação - não versionar `.env` real; - não versionar `langfuse_credentials.yaml` real; - não versionar GCP service account JSON; - não versionar wallet Oracle; - em OKE prefira `oke_workload_identity` quando aplicável; - monte credenciais como Kubernetes Secrets; - use `readOnlyRootFilesystem` no CronJob; - mantenha `concurrencyPolicy: Forbid` para evitar dois D+1 concorrentes do mesmo deployment; - nunca use `reset-db` em produção sem plano explícito de restauração. --- ## 24. Checklist de homologação Antes de promover uma versão: - [ ] `pip check` sem erro; - [ ] `python -m pytest -v` passando; - [ ] `python -m compileall evaluator` sem erro; - [ ] `show-config` aponta para ADB/OCI/configs corretos; - [ ] `agents.yaml` contém apenas os agentes esperados para aquele deployment; - [ ] credencial Langfuse existe para cada `agentId` habilitado; - [ ] `run-agents --limit 5` encontra traces; - [ ] sampling seleciona sessions, não traces individuais; - [ ] judge trace/session conclui; - [ ] arquivo possui cabeçalho de 12 colunas; - [ ] nome segue `AGENTE_{agentId}_LLM_JUDGE_{YYYYMMDD}_{HHMMSS}.TXT.GZ`; - [ ] evento `GCS_UPLOADED` confirmado; - [ ] arquivo local removido depois do upload; - [ ] auditoria Langfuse `created/completed` confirmada; - [ ] CronJob está `suspend: false`; - [ ] timezone do CronJob é `America/Sao_Paulo`; - [ ] nenhuma credencial real está no repositório. --- ## 25. Resumo para quem está chegando no projeto Se você precisa entender o projeto em poucos minutos, pense assim: ```text Langfuse guarda as conversas ↓ Evaluator escolhe sessions D+1 ↓ OCI GenAI julga traces e sessions ↓ ADB guarda execução e resultados ↓ GCS recebe o arquivo oficial ↓ Langfuse registra a auditoria do batch ``` Os pontos de extensão ficam em configuração: ```text Qual agente? -> configs/judge/agents.yaml Qual projeto Langfuse?-> langfuse_credentials.yaml Qual bucket? -> configs/judge/storage.yaml Qual prompt? -> trace_metrics.yaml / session_metrics.yaml Qual modelo? -> .env + llm_profiles.yaml Qual identidade? -> configs/identity.yaml ``` A regra arquitetural mais importante é: **o código do evaluator deve permanecer genérico; detalhes de agentes, ambientes, credenciais e storage pertencem à configuração.**