34 KiB
Executable File
Agent Framework OCI Evaluator — LLM-as-a-Judge
Idioma: Português | English
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 é:
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, dotrace.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:
agentId:reference_date
Exemplo:
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:
D - daysBack
usando explicitamente:
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:
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:
billing
billing
billing
payment
payment
Resultado:
loop = 3
4. Fluxo completo de execução
O fluxo produtivo é executado pelo comando:
python -m evaluator.cli run-agents
ou pelo entrypoint instalado:
af-evaluator run-agents
Etapas:
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:
1 trace
-> GET trace detail
-> GET observations
repetido para cada trace.
Agora a coleta é paginada em lote:
/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:
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
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:
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:
EVALUATION_RUN
EVALUATION_PROGRESS_EVENT
EVALUATION_ITEM
EVALUATION_RESULT
EVALUATION_METRIC
EVALUATION_FINDING
O prefixo é configurável por:
ADB_TABLE_PREFIX=AGENTFW
5.4. Publishers
gcs_export.py— upload do artefato oficial;langfuse_job_audit.py— auditoriajudge-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 globaisLANGFUSE_HOST/PUBLIC_KEY/SECRET_KEY; deixe-a desabilitada quando não houver um projeto global apropriado.
6. Configuração de agentes
Arquivo padrão:
configs/judge/agents.yaml
Exemplo:
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:
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:
LANGFUSE_BILLING_*
LANGFUSE_SALES_*
O evaluator lê uma definição declarativa:
LANGFUSE_CREDENTIALS_CONFIG_PATH=configs/langfuse/langfuse_credentials.yaml
Crie o arquivo a partir do exemplo:
cp configs/langfuse/langfuse_credentials.example.yaml \
configs/langfuse/langfuse_credentials.yaml
Exemplo:
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:
langfuse:
agents:
- agentId: billing
hostEnv: BILLING_LF_HOST
publicKeyEnv: BILLING_LF_PUBLIC
secretKeyFile: /var/secrets/langfuse/billing-secret-key
Campos suportados:
host / hostEnv / hostFile
publicKey / publicKeyEnv / publicKeyFile
secretKey / secretKeyEnv / secretKeyFile
Ordem de resolução de cada campo:
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:
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:
mock
oci_openai
oci_sdk
openai_compatible
A recomendação para os modelos OCI dedicados é:
LLM_PROVIDER=oci_sdk
8.1. OCI SDK
Exemplo local com ~/.oci/config:
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:
config_file
instance_principal
resource_principal
oke_workload_identity
workload_identity
8.2. Dedicated endpoint
Para endpoint dedicado:
OCI_GENAI_BASE_URL=https://<private-endpoint>.inference.generativeai.<region>.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:
configs/llm_profiles/llm_profiles.yaml
Exemplo:
profiles:
judge:
temperature: 0
max_tokens: 900
Selecionado por:
LLM_PROFILE=judge
9. Prompts e métricas
9.1. Trace metrics
Arquivo:
configs/judge/trace_metrics.yaml
O judge deve retornar JSON com:
{
"judgeScore": 0.0,
"accuracyScore": 0.0,
"alucinationScore": 0.0,
"rationale": "..."
}
Se o parse falhar:
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:
configs/judge/session_metrics.yaml
Resultado esperado:
{
"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:
configs/identity.yaml
A camada de identidade converte identificadores específicos de canais/domínios para chaves canônicas como:
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:
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:
configs/judge/storage.yaml
Exemplo:
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:
APP_ENV=dev
STORAGE_CONFIG_PATH=configs/judge/storage.yaml
Override de emergência/operação:
JUDGE_GCS_BUCKET=meu-bucket
Para habilitar o fluxo produtivo:
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:
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:
AGENTE_{agentId}_LLM_JUDGE_{YYYYMMDD}_{HHMMSS}.TXT.GZ
Exemplo:
AGENTE_billing_LLM_JUDGE_20260814_020015.TXT.GZ
Cabeçalho:
"judgeScore"|;"accuracyScore"|;"alucinationScore"|;"promptLength"|;"loop"|;"inferredCsiScore"|;"resolution"|;"conversationPrecision"|;"uraCallId"|;"channelId"|;"sessionId"|;"messageId"
Linha final:
"TOTAL"|;"<n_traces>"
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:
python -m venv .venv
source .venv/bin/activate
Windows PowerShell:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
13.3. Instalar
Modo recomendado durante desenvolvimento:
python -m pip install --upgrade pip setuptools wheel
pip install -e .
Validar:
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
cp .env.example .env
Edite os valores reais.
Configuração mínima típica:
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
cp configs/langfuse/langfuse_credentials.example.yaml \
configs/langfuse/langfuse_credentials.yaml
Declare o agente que será executado.
Passo 3 — agents.yaml
Ajuste:
configs/judge/agents.yaml
Para uma instância dedicada, deixe somente o agente daquela squad habilitado.
Passo 4 — validar configuração
python -m evaluator.cli show-config
Passo 5 — inicializar banco
python -m evaluator.cli init-db
reset-dbapaga e recria o schema do evaluator. Use apenas em ambiente controlado.
15. CLI
A ajuda real da versão atual é:
python -m evaluator.cli --help
Comandos disponíveis:
init-db
reset-db
show-config
run
run-agents
progress
runs
15.1. run — execução manual/diagnóstica
Sintaxe:
python -m evaluator.cli run PERIOD_START PERIOD_END [OPTIONS]
Exemplo:
python -m evaluator.cli run \
2026-08-12T00:00:00 \
2026-08-12T23:59:59 \
--source langfuse \
--limit 10
Opções:
--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
python -m evaluator.cli run-agents
Um agente:
python -m evaluator.cli run-agents \
--agent-id billing \
--source langfuse
Teste limitado:
python -m evaluator.cli run-agents \
--agent-id billing \
--limit 10
Opções:
--source TEXT default: langfuse
--agent-id TEXT
--limit INTEGER
run-agents usa daysBack, percentage, credenciais e gcs_prefix do agente.
15.3. Listar runs
python -m evaluator.cli runs
ou:
python -m evaluator.cli runs --limit 50
15.4. Progresso de um run
python -m evaluator.cli progress <RUN_ID>
Com mais eventos:
python -m evaluator.cli progress <RUN_ID> --events 100
Eventos típicos do fluxo produtivo:
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:
pip install pytest
16.1. Todos os testes
python -m pytest -v
16.2. Conformidade TIM
python -m pytest tests/test_tim_spec.py -v
16.3. Conformidade ampliada
python -m pytest tests/test_full_tim_compliance.py -v
16.4. Regressão de hardcodes
python -m pytest tests/test_no_product_hardcodes.py -v
16.5. Suite recomendada antes de um release
python -m pytest \
tests/test_tim_spec.py \
tests/test_full_tim_compliance.py \
tests/test_no_product_hardcodes.py \
-v
Validar sintaxe/imports:
python -m compileall evaluator
17. Teste funcional local recomendado
17.1. Smoke test com mock
Configure:
LLM_PROVIDER=mock
E rode um período pequeno com source mock quando desejado:
python -m evaluator.cli run \
2026-08-12T00:00:00 \
2026-08-12T23:59:59 \
--source mock
17.2. Teste Langfuse sem volume grande
python -m evaluator.cli run-agents \
--agent-id billing \
--limit 5
Depois:
python -m evaluator.cli runs --limit 5
python -m evaluator.cli progress <RUN_ID> --events 100
Confirme:
COLLECTED> 0;SAMPLEDmostra sessions selecionadas;- itens concluem sem
ITEM_FAILED; EXPORTEDaparece;GCS_UPLOADEDcontém URIgs://...;LOCAL_FILE_DELETEDaparece;- auditoria
completedfoi confirmada.
18. Docker
Build local:
docker build -t agent-framework-evaluator:local .
O runtime produtivo é batch. O container não precisa expor porta HTTP.
Para Kubernetes, o CronJob chama:
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.
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:
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:
schedule: "0 2 * * *"
timeZone: "America/Sao_Paulo"
suspend: false
concurrencyPolicy: Forbid
19.1. Preparar arquivos de deploy
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:
deploy/wallet/
Credencial GCP:
deploy/gcp/GCP_ACCESS_KEY.json
19.2. Criar secrets
DEV:
./deploy/scripts/create-secrets.sh dev
HML:
./deploy/scripts/create-secrets.sh hml
PROD:
./deploy/scripts/create-secrets.sh prod
São criados/atualizados:
evaluator-secrets
evaluator-wallet
evaluator-langfuse
evaluator-gcp (quando o arquivo GCP existir)
19.3. Build e push
export REGISTRY=tim.azurecr.io
export IMAGE_TAG=<version>
./deploy/scripts/build-push.sh prod
19.4. Deploy
export REGISTRY=tim.azurecr.io
export IMAGE_TAG=<version>
./deploy/scripts/deploy.sh prod
Por default o script também executa o job de inicialização do banco.
Para não executar:
export RUN_INIT_DB=false
19.5. Status
./deploy/scripts/status.sh prod
ou diretamente:
kubectl -n agent-evaluator-prod get cronjob,jobs,pods
19.6. Execução imediata fora do horário
./deploy/scripts/run-now.sh prod
O script cria um Job baseado no CronJob.
Acompanhar:
kubectl -n agent-evaluator-prod get jobs,pods
kubectl -n agent-evaluator-prod logs -f job/<nome-do-job>
19.7. Interface web de acompanhamento
A UI é servida pelo componente independente evaluator-monitor:
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:
kubectl -n agent-evaluator-prod port-forward svc/evaluator-monitor 8080:8080
Depois abra:
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:
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
python -m pytest -v
python -m compileall evaluator
3. Testar localmente com limite
python -m evaluator.cli run-agents \
--agent-id <agentId> \
--limit 5
4. Validar o run
python -m evaluator.cli runs --limit 10
python -m evaluator.cli progress <RUN_ID> --events 100
5. Confirmar GCS
O evento deve conter:
GCS_UPLOADED
com uma URI como:
gs://<bucket>/<prefix>/AGENTE_<agent>_LLM_JUDGE_<timestamp>.TXT.GZ
6. Atualizar secrets/configuração do ambiente
./deploy/scripts/create-secrets.sh <dev|hml|prod>
7. Build/push com uma tag nova
export REGISTRY=<registry>
export IMAGE_TAG=<nova-versao>
./deploy/scripts/build-push.sh <ambiente>
8. Deploy
./deploy/scripts/deploy.sh <ambiente>
9. Rodar manualmente para homologação
./deploy/scripts/run-now.sh <ambiente>
10. Verificar logs e artefatos
kubectl -n agent-evaluator-<ambiente> logs -f job/<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:
- data processada (
daysBackusa BRT); - credencial Langfuse do
agentIdcorreto; hostdo projeto Langfuse;langfuse_agent_aliases;- existência de sessions/traces naquele período;
- se o
agentIdlógico é diferente dometadata.agent_iddos traces.
Exemplo:
- 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:
LANGFUSE_CREDENTIALS_CONFIG_PATH
Adicione uma entrada com o mesmo agentId.
Langfuse credentials definition file not found
Crie/mapeie o arquivo:
configs/langfuse/langfuse_credentials.yaml
ou configure outro path.
TIM specification requires GCS upload...
run-agents foi executado com:
ENABLE_GCS_UPLOAD=false
Altere para true e configure a credencial GCP.
GCS falha
Verifique:
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:
LLM_PROVIDER=oci_sdk
OCI_GENAI_BASE_URL=https://<endpoint-nativo-sem-openai-v1>
OCI_GENAI_ENDPOINT_ID=ocid1.generativeaiendpoint...
OCI_COMPARTMENT_ID=...
Para oci_sdk, não use /openai/v1 no endpoint.
Banco não inicializado
python -m evaluator.cli init-db
Ver progresso pela CLI
O run-agents agora imprime o RUN_ID assim que a execução é criada:
[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:
python -m evaluator.cli runs
python -m evaluator.cli progress <RUN_ID> --events 100
Ver progresso pela interface web
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
.envreal; - não versionar
langfuse_credentials.yamlreal; - não versionar GCP service account JSON;
- não versionar wallet Oracle;
- em OKE prefira
oke_workload_identityquando aplicável; - monte credenciais como Kubernetes Secrets;
- use
readOnlyRootFilesystemno CronJob; - mantenha
concurrencyPolicy: Forbidpara evitar dois D+1 concorrentes do mesmo deployment; - nunca use
reset-dbem produção sem plano explícito de restauração.
24. Checklist de homologação
Antes de promover uma versão:
pip checksem erro;python -m pytest -vpassando;python -m compileall evaluatorsem erro;show-configaponta para ADB/OCI/configs corretos;agents.yamlcontém apenas os agentes esperados para aquele deployment;- credencial Langfuse existe para cada
agentIdhabilitado; run-agents --limit 5encontra 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_UPLOADEDconfirmado; - arquivo local removido depois do upload;
- auditoria Langfuse
created/completedconfirmada; - 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:
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:
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.