setup models

This commit is contained in:
Marcos Lohmann
2026-10-07 13:43:09 -03:00
parent aeee6c7de1
commit 84ea9a5307
75 changed files with 5093 additions and 58 deletions

View File

@@ -1,5 +1,7 @@
# 02 — Desenho experimental
> **Escopo do primeiro ciclo (revisão de 2026-10-07).** Não há agentes implementados. Por isso o ciclo avalia só a **classificação de intenções (D1)** no domínio de **atendimento de telefonia móvel**, com a taxonomia `data/taxonomy/intents-v1.yaml` (42 intenções em 13 domínios). Pedidos fora de escopo, enunciados emocionais e casos com várias intenções entram como exemplos de D1, com rótulo `sem_correspondencia` ou a intenção principal. D2 a D5 ficam descritas abaixo para os próximos ciclos. Os dados são **100% sintéticos** e a anotação é feita por **três modelos generativos** (ver §4).
## 1. Hipóteses
- **H1 (acurácia).** Pelo menos um candidato atinge, em PT-BR, uma acurácia de roteamento (D1/D2) compatível com o uso no orquestrador. O critério é a não inferioridade em relação ao baseline LLM (margem de 3 p.p.); ver [04, Q5](04-questoes-em-aberto.md#q5--critérios-de-sucesso-pré-registrados).
@@ -8,12 +10,12 @@
## 2. Tarefas de decisão
Cada tarefa tem **pergunta**, **opções com descrição** e **regra de decisão** (tarefa 21). No primeiro ciclo, cada questão tem **no máximo 20 opções**, incluindo `sem_correspondencia` (tarefas 22–23).
Cada tarefa tem **pergunta**, **opções com descrição** e **regra de decisão** (tarefa 21). No primeiro ciclo, cada questão tem **no máximo 16 opções**, incluindo `sem_correspondencia` (tarefas 22–23; limite do SemIf, ver Q2).
### D1 — Classificação de intenção
- **Pergunta:** "Qual intenção do usuário este enunciado expressa?"
- **Opções:** as intenções elegíveis do exemplo, de 2 a 19, mais `sem_correspondencia`. Cada opção tem uma descrição curta, do mesmo jeito que viria do catálogo de agentes ou intenções.
- **Opções:** as intenções elegíveis do exemplo, de 1 a 15, mais `sem_correspondencia`. Cada opção tem uma descrição curta, do mesmo jeito que viria do catálogo de agentes ou intenções.
- **Regra:** escolher a intenção principal que o próximo agente precisa atender. Se nenhuma se aplica, escolher `sem_correspondencia`.
### D2 — Continuidade de sessão
@@ -83,6 +85,18 @@ Cada tarefa tem **pergunta**, **opções com descrição** e **regra de decisão
## 4. Dados
**Primeiro ciclo: pipeline sintético (`datagen/`), executado na VM.**
| Etapa | Implementação | Tarefas |
|-------|---------------|---------|
| Geração | `datagen.generate`: 42 intenções × 7 perfis + 5 categorias fora de escopo × 9 lotes; 12 enunciados por lote. Geradores alternados `meta.llama-4-maverick` e `cohere.command-a` (fornecedores distintos dos anotadores). Perfis: curto/neutro, informal/abreviado, erros de digitação, regionalismo, emocional, longo com contexto, indireto ou com várias intenções | 24, 27 |
| Preparação | `datagen.prepare`: mascaramento de dados pessoais (CPF, CNPJ, cartão, telefone, e-mail, CEP), deduplicação exata e aproximada (Jaccard ≥ 0,85 em 5-gramas), limites de comprimento | 25, 28 |
| Anotação | `datagen.annotate`: três modelos (`openai.gpt-5.5`, `google.gemini-2.5-pro`, `xai.grok-4.3`) em rodízio por lote; dois anotam de forma independente, sem ver o rótulo de geração, e o terceiro desempata. Três rótulos distintos → exemplo excluído. Instruções: [06-guia-de-anotacao.md](06-guia-de-anotacao.md) | 29–32 |
| Montagem | `datagen.build`: partição por lote de geração (3/2/2 lotes por intenção para teste/calibração/dev), conjuntos de 4, 8, 12 ou 16 opções simulando a elegibilidade (10% sem a intenção correta → `sem_correspondencia`), lint e congelamento com SHA-256 em `dmb-entrada` e `dmb-logs-imutaveis` | 33–36 |
Itens abaixo descrevem o desenho geral (válido também para dados reais em ciclos futuros).
- **Unidade de amostragem:** conversa (`group_id`). Cada turno anotado gera um ou mais exemplos (um por tarefa de decisão).
- **Fontes:** enunciados representativos do domínio de destino, anonimizados (tarefas 24–25). Se houver dados sintéticos, eles ficam identificados em `metadata` e são reportados em separado.
- **Estratificação (tarefa 26):** domínio, comprimento, ambiguidade, classe esperada, posição do turno e presença de agente ativo ou de sessões suspensas.

View File

@@ -45,39 +45,48 @@ As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
- [ ] **9.** Criar uma rota temporária de saída por NAT Gateway, só para a aquisição inicial de dependências e pesos.
- ↳ Remover a rota logo após a tarefa 16 e passar pelo gate de egresso antes de qualquer execução. *(Q3)*
- ↳ Criar e ativar **só quando necessário**, com `infra/oci/nat.py on`. Para desligar: `nat.py off`, que fecha a rota e bloqueia o NAT. Na tarefa 78: `nat.py off --delete`.
- [ ] **10.** Validar que os jobs acessam os buckets sem internet pública.
- ↳ Adiado para quando o primeiro job existir (depois da tarefa 11).
- [x] **10.** Validar que os jobs acessam os buckets sem internet pública.
- ↳ Validado em 2026-10-07 com jobs do Data Science em `dmb-subnet-jobs`. Os pesos do Rizzo (4,4 GB) desceram pela Service Gateway em 28 s, com SHA-256 conferido.
## F1 — Ambiente e artefatos
- [ ] **11.** Criar uma imagem de contêiner base com Python, CUDA, PyTorch, Transformers, Hugging Face Hub e ferramentas de medição.
- [x] **11.** Criar uma imagem de contêiner base com Python, CUDA, PyTorch, Transformers, Hugging Face Hub e ferramentas de medição.
- ↳ Incluir um exportador OpenTelemetry, para que a telemetria do harness tenha o mesmo formato da observabilidade do orquestrador.
- [ ] **12.** Fixar num manifesto as versões do sistema, dos drivers CUDA, do Python, das bibliotecas e da imagem de contêiner.
- [ ] **13.** Baixar os pesos, tokenizadores, revisões e licenças aprovados de cada candidato. *(Q4)*
- ↳ **Substituído na prática por ambientes Python publicados no bucket** (`infra/ds/build_env.sh` → `dmb-artefatos/envs/<candidato>/`), porque os jobs do Data Science dispensam contêiner próprio. O CUDA vem das wheels `cu128` do PyTorch e o Python é o 3.11.9 base dos jobs.
- ↳ `env/Dockerfile` com `--build-arg CANDIDATE=...`: **uma imagem por candidato**, porque o SemIf exige `transformers==5.17.0` e o GLiNER exige `transformers<5`. Os pesos ficam fora da imagem. Falta fazer o build e o push para o OCIR, na VM.
- [x] **12.** Fixar num manifesto as versões do sistema, dos drivers CUDA, do Python, das bibliotecas e da imagem de contêiner.
- [x] **13.** Baixar os pesos, tokenizadores, revisões e licenças aprovados de cada candidato. *(Q4)*
- ↳ `env/acquire.py`: todas as licenças são Apache-2.0 ou MIT (incluindo o Qwen3.5-4B). Rizzo: GGUF Q8_0 (padrão do projeto) + llama.cpp b11081. **Não foi preciso NAT**: a aquisição rodou fora da VCN e enviou direto ao bucket.
- ↳ A aquisição falha se a licença estiver fora da allowlist de `config/benchmark.yaml`. O contexto efetivo de cada candidato é medido e registrado.
- ↳ Incluir as dependências específicas de cada candidato (pacote GLiNER, runtime SemIf, interface Rizzo Flow) e a revisão fixada de `Qwen/Qwen3.5-4B`.
- [ ] **14.** Calcular o SHA-256 de cada artefato baixado.
- [ ] **15.** Copiar os pesos e dependências aprovados para o bucket `artefatos`.
- [ ] **16.** Registrar no manifesto a origem, a revisão, a licença, o hash, a data de aquisição e o tamanho de cada artefato.
- [ ] **17.** Desabilitar a saída pública dos jobs de benchmark.
- [ ] **18.** Carregar cada candidato só a partir dos artefatos locais.
- [x] **14.** Calcular o SHA-256 de cada artefato baixado.
- [x] **15.** Copiar os pesos e dependências aprovados para o bucket `artefatos`.
- [x] **16.** Registrar no manifesto a origem, a revisão, a licença, o hash, a data de aquisição e o tamanho de cada artefato.
- [x] **17.** Desabilitar a saída pública dos jobs de benchmark.
- ↳ O gate de egresso foi aprovado nas execuções em sub-rede privada: huggingface.co, pypi.org e github.com ficaram inacessíveis.
- [x] **18.** Carregar cada candidato só a partir dos artefatos locais.
- ↳ Usar `HF_HUB_OFFLINE=1`, `TRANSFORMERS_OFFLINE=1` e um cache apontado para os artefatos verificados.
- [ ] **19.** Fazer o job falhar se algum candidato tentar baixar arquivos durante a inferência.
- ↳ `harness/job.py` baixa do bucket só o que está no manifesto e confere o SHA-256. Os quatro candidatos já carregaram offline, a partir dos artefatos locais.
- [x] **19.** Fazer o job falhar se algum candidato tentar baixar arquivos durante a inferência.
- ↳ Duas camadas: as variáveis de ambiente offline e o bloqueio de rede da VCN. Um teste negativo prova que a falha ocorre.
- ↳ Camada de aplicação em `dmb/offline.py` (bloqueia conexões fora do loopback), com teste em `tests/test_offline.py`. O gate de egresso de `harness/job.py` cobre a camada de rede.
## F2 — Tarefas de decisão e taxonomia
- [ ] **20.** Definir a taxonomia de decisões a avaliar em PT-BR.
- [x] **20.** Definir a taxonomia de decisões a avaliar em PT-BR.
- ↳ **Primeiro ciclo: só D1 (classificação de intenções), atendimento de telefonia móvel.** `data/taxonomy/intents-v1.yaml`: 42 intenções em 13 domínios + 5 categorias fora de escopo.
- ↳ Taxonomia de **intenções** (D1), ligadas aos **agentes de destino** do catálogo A2A, mais as decisões de **continuidade de sessão** (D2), **desambiguação** (D3) e **várias intenções** (D4). D5 é opcional. Ver [02 §2](02-desenho-experimental.md#2-tarefas-de-decisão).
- [ ] **21.** Escrever a pergunta, as opções, as descrições das opções e a regra de decisão de cada tarefa.
- [x] **21.** Escrever a pergunta, as opções, as descrições das opções e a regra de decisão de cada tarefa.
- ↳ As descrições das opções seguem o estilo de um *agent card*: curtas e orientadas à capacidade do agente.
- [ ] **22.** Limitar o primeiro ciclo a no máximo 20 opções por questão, para garantir compatibilidade com o Laya. *(Q2)*
- [x] **22.** Limitar o primeiro ciclo a no máximo 20 opções por questão, para garantir compatibilidade com o Laya. *(Q2)*
- ↳ Os subconjuntos de opções por exemplo simulam o serviço de elegibilidade de intenções.
- [ ] **23.** Incluir uma opção explícita de `outro` ou `sem correspondência` quando a taxonomia não for exaustiva.
- ↳ **Limite efetivo: 16 opções**, por causa do SemIf (Q2).
- [x] **23.** Incluir uma opção explícita de `outro` ou `sem correspondência` quando a taxonomia não for exaustiva.
## F3 — Dados e anotação
- [ ] **24.** Coletar textos PT-BR representativos do domínio de destino. *(Q7)*
- [ ] **24.** Coletar textos PT-BR representativos do domínio de destino. *(Q7, Q15)*
- ↳ **100% sintéticos** (`datagen.generate`, na VM).
- ↳ A unidade é a **conversa com vários turnos**, não o enunciado isolado. Cada turno guarda o estado (agente ativo e sessões suspensas).
- [ ] **25.** Remover ou mascarar dados pessoais, segredos e identificadores que o benchmark não precisa.
- [ ] **26.** Separar os textos por domínio, comprimento, grau de ambiguidade e classe esperada.
@@ -85,9 +94,11 @@ As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
- [ ] **27.** Criar exemplos adversariais com abreviações, erros de ortografia, regionalismos, textos curtos e várias intenções.
- ↳ Acrescentar respostas curtas que dependem do contexto ("sim", "o segundo"), troca de assunto, retorno a um assunto anterior e pedidos fora de escopo.
- [ ] **28.** Definir os critérios de inclusão e exclusão dos exemplos.
- [ ] **29.** Produzir o guia de anotação humana.
- [x] **29.** Produzir o guia de anotação humana.
- ↳ [06-guia-de-anotacao.md](06-guia-de-anotacao.md), usado literalmente pelos anotadores.
- ↳ O guia cobre D1–D4 com exemplos-limite (por exemplo, quando um enunciado curto continua a tarefa do agente ativo e quando inicia um novo agente).
- [ ] **30.** Anotar cada exemplo com dois avaliadores independentes.
- ↳ Avaliadores = modelos generativos em rodízio (`openai.gpt-5.5`, `google.gemini-2.5-pro`, `xai.grok-4.3`). *(Q15)*
- [ ] **31.** Resolver as discordâncias com a revisão de um terceiro avaliador.
- [ ] **32.** Registrar, para cada exemplo, a resposta de referência, a justificativa e a versão da taxonomia.
- ↳ Reportar também a concordância entre anotadores.
@@ -99,27 +110,29 @@ As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
## F4 — Contrato JEV e adaptadores
- [ ] **36.** Criar um formato canônico de entrada com `id`, `state`, `question`, `options`, `gold_label` e metadados.
- [x] **36.** Criar um formato canônico de entrada com `id`, `state`, `question`, `options`, `gold_label` e metadados.
- ↳ Ver [02 §3](02-desenho-experimental.md#3-formato-canônico-de-entrada-tarefa-36), que inclui a **regra única de serialização** de `state`.
- [ ] **37.** Definir a versão do contrato JEV compatível a usar no benchmark: `POST /v1/systemone`, esquema de requisição, esquema de resposta e códigos de erro. *(Q1)*
- [x] **37.** Definir a versão do contrato JEV compatível a usar no benchmark: `POST /v1/systemone`, esquema de requisição, esquema de resposta e códigos de erro. *(Q1)*
- ↳ Versão `jev-compat-v1`. Os esquemas já estão em `contracts/jev/schemas/`; falta revisar os códigos de erro no código dos adaptadores.
- [ ] **38.** Definir o mapeamento obrigatório entre o contrato JEV e os tipos internos `choice`, `noul` e `score`.
- [ ] **39.** Definir a regra de conversão de probabilidades, confiança, níveis ordinais, opções descritas e campos de uso.
- [ ] **40.** Definir o comportamento padronizado para entradas inválidas, limite de contexto, timeout, opção desconhecida e indisponibilidade do modelo.
- [x] **38.** Definir o mapeamento obrigatório entre o contrato JEV e os tipos internos `choice`, `noul` e `score`.
- [x] **39.** Definir a regra de conversão de probabilidades, confiança, níveis ordinais, opções descritas e campos de uso.
- [x] **40.** Definir o comportamento padronizado para entradas inválidas, limite de contexto, timeout, opção desconhecida e indisponibilidade do modelo.
- ↳ Na prática, uma falha do modelo de decisão vira desambiguação ou fallback no orquestrador. No benchmark, essas falhas contam como erro e são reportadas à parte.
- [ ] **41.** Criar testes de conformidade com requisições e respostas JEV congeladas.
- [ ] **42.** Criar um adaptador JEV local para o Laya com o checkpoint multilíngue fixado.
- [ ] **43.** Criar um adaptador JEV local para o SemIf em modo `direct`, com a revisão do Qwen fixada.
- [ ] **44.** Criar um adaptador JEV local para o GLiNER Decide usando `classify_text`.
- [ ] **45.** Criar um adaptador JEV local para o Rizzo Flow usando sua interface de decisões. *(Q4)*
- [ ] **46.** Expor cada adaptador no mesmo caminho local `POST /v1/systemone`, sem dependência de rede externa.
- [ ] **47.** Garantir que cada adaptador devolva a resposta JEV completa, incluindo escolha, distribuição de probabilidades, confiança e metadados de uso, quando disponíveis.
- [ ] **48.** Normalizar as saídas adicionais de execução num registro interno com `latency_ms`, `model_id`, `run_id` e versão do adaptador.
- [x] **41.** Criar testes de conformidade com requisições e respostas JEV congeladas.
- ↳ `contracts/jev/fixtures/conformance.json` + `tests/` (35 testes).
- [x] **42.** Criar um adaptador JEV local para o Laya com o checkpoint multilíngue fixado.
- [x] **43.** Criar um adaptador JEV local para o SemIf em modo `direct`, com a revisão do Qwen fixada.
- [x] **44.** Criar um adaptador JEV local para o GLiNER Decide usando `classify_text`.
- [x] **45.** Criar um adaptador JEV local para o Rizzo Flow usando sua interface de decisões. *(Q4)*
- [x] **46.** Expor cada adaptador no mesmo caminho local `POST /v1/systemone`, sem dependência de rede externa.
- [x] **47.** Garantir que cada adaptador devolva a resposta JEV completa, incluindo escolha, distribuição de probabilidades, confiança e metadados de uso, quando disponíveis.
- [x] **48.** Normalizar as saídas adicionais de execução num registro interno com `latency_ms`, `model_id`, `run_id` e versão do adaptador.
- ↳ O registro é emitido como span OpenTelemetry e também gravado em arquivo de resultados.
- [ ] **49.** Validar que os adaptadores preservam a mesma ordem e a mesma descrição das opções.
- [ ] **50.** Validar que trocar de candidato não altera a requisição JEV nem o formato da resposta JEV.
- [ ] **51.** Implementar testes de sanidade com decisões triviais, opções permutadas e os três tipos de pergunta.
- [x] **49.** Validar que os adaptadores preservam a mesma ordem e a mesma descrição das opções.
- [x] **50.** Validar que trocar de candidato não altera a requisição JEV nem o formato da resposta JEV.
- [x] **51.** Implementar testes de sanidade com decisões triviais, opções permutadas e os três tipos de pergunta.
- ↳ Incluir um teste de invariância à permutação das opções em D1 e D2 (a mesma escolha com outra ordem).
- ↳ `harness/sanity.py`. Primeira rodada em CPU (2026-10-07): sem violações de contrato nos 4 candidatos. Acertos: SemIf 8/8, Laya 7/8, Rizzo 7/8, GLiNER 5/8. Invariância à permutação: 4/4 em todos. Os próximos testes rodam na OCI.
## F5 — Controle de execução
@@ -132,6 +145,7 @@ As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
- [ ] **55.** Executar um piloto de 50 exemplos por candidato em CPU. *(Q11)*
- [ ] **56.** Executar um piloto de 50 exemplos por candidato em GPU.
- ↳ GPU em **sa-saopaulo-1, A10.1**, uma por vez *(Q14)*. Sanidade em GPU já em andamento.
- [ ] **57.** Medir memória de GPU, memória RAM, tempo de carga, tempo de aquecimento e falhas do piloto.
- [ ] **58.** Ajustar apenas os parâmetros operacionais necessários para eliminar falhas de execução.

View File

@@ -7,7 +7,7 @@ Todas as questões foram resolvidas em 2026-10-07. O critério foi preferir a **
| # | Questão | Decisão | Mecanismo automatizado |
|---|---------|---------|------------------------|
| Q1 | Contrato JEV | Esquema público do `POST /v1/systemone` (`state` + `questions` tipadas → `answers` + `usage`), congelado como `jev-compat-v1` | JSON Schema em `contracts/jev/schemas/` e testes de conformidade |
| Q2 | 20 opções incluem o fallback? | Sim: no máximo 19 opções de domínio + `sem_correspondencia` | Validação de esquema (`maxProperties: 20`) e lint do dataset |
| Q2 | 20 opções incluem o fallback? | Sim, e o limite comum cai para **16**: até 15 opções de domínio + `sem_correspondencia` (revisado: o SemIf aceita só 16) | Validação de esquema (`maxProperties: 16`) e lint do dataset |
| Q3 | NAT × isolamento | Desassociar a rota NAT logo após a tarefa 16 | Gate de egresso: o pipeline só segue se a sonda de saída **falhar** |
| Q4 | Interfaces e licenças | Interfaces mapeadas por candidato (abaixo). Só licenças da allowlist | O script de aquisição lê a licença nos metadados do Hub e falha fora da allowlist |
| Q5 | Critérios de sucesso | SLO de latência fixo + não inferioridade relativa ao baseline | Avaliação automática dos critérios em `config/benchmark.yaml` |
@@ -18,10 +18,25 @@ Todas as questões foram resolvidas em 2026-10-07. O critério foi preferir a **
| Q10 | Calibração | Temperature scaling por candidato × tarefa, só na partição de calibração | Ajuste por NLL e congelamento com hash |
| Q11 | Piloto em CPU | Mantido, com timeout. Inviabilidade declarada automaticamente | Regra p95 > timeout → "inviável em CPU" |
| Q12 | Tamanho do teste | ≥ 1.300 decisões D1 no teste final (análise de poder) | `analysis/sample_size.py`, com recálculo do MDE |
| Q14 | GPU | A10.1 em **sa-saopaulo-1**, um job por vez (limite da região). Sem VCN disponível lá: rede gerenciada + isolamento no processo (`DMB_ISOLATION=app`) | `infra/ds/gpu_queue.py`, `infra/oci/replicate.py`, autoteste `dmb.offline.self_test` |
| Q15 | Dados e anotação | 100% sintéticos; anotação por três modelos generativos em rodízio | `datagen/` (ver [02 §4](02-desenho-experimental.md#4-dados)) |
| Q13 | Centro de custo e orçamento | **Orçamento em aberto**: o uso está coberto por cota interna de engenharia. A estimativa de custo continua no relatório | A estimativa é calculada por script (tarefa 70) |
---
## Q14 — GPU em São Paulo (revisão de 2026-10-07)
- Em us-chicago-1 não há capacidade de A10, e A100 e V100 têm limite zero. Os jobs de GPU rodam em **sa-saopaulo-1, `VM.GPU.A10.1`**, com limite de **uma GPU por vez** (fila em `infra/ds/gpu_queue.py`).
- Pesos e ambientes são replicados do lado do servidor para `dmb-artefatos-gru` (`infra/oci/replicate.py`); os resultados vão para `dmb-resultados-gru`.
- O limite de VCNs da região está esgotado. Os jobs de GPU usam **rede gerenciada**, e o isolamento passa a valer no processo: variáveis offline, bloqueio de sockets fora do loopback e um **autoteste a cada execução** (`process_guard`). O gate de egresso registra que há saída de rede, mas não aborta (`DMB_ISOLATION=app`). É um desvio documentado em relação à tarefa 17. Se uma VCN for liberada em São Paulo, os jobs voltam para uma sub-rede privada.
- Todas as medições de GPU usam o mesmo shape e a mesma região. As de CPU continuam em us-chicago-1.
## Q15 — Dados sintéticos e anotação por LLMs (revisão de 2026-10-07)
- Substitui Q7: os dados são 100% sintéticos, incluindo fora de escopo e enunciados emocionais. Não há dados reais neste ciclo.
- A anotação humana (tarefas 30–31) é substituída por três modelos generativos de fornecedores distintos, em rodízio de papéis. O gerador nunca é um dos anotadores.
- Limitação para o relatório: os rótulos de referência refletem o consenso de LLMs, não de pessoas. A concordância entre anotadores (kappa por par) e com o rótulo de geração é reportada.
## Q1 — Contrato JEV `jev-compat-v1`
Base: a forma pública do `POST /v1/systemone`, a mesma reproduzida pelas implementações abertas (OpenJev/SemIf).
@@ -76,7 +91,7 @@ Base: a forma pública do `POST /v1/systemone`, a mesma reproduzida pelas implem
## Q2 — Limite de opções
O limite de 20 opções inclui `sem_correspondencia`: no máximo 19 opções de domínio + 1. O dataset é validado por esquema. Um exemplo com mais de 20 opções reprova o lint e impede o congelamento (tarefa 35).
O limite inclui `sem_correspondencia`. **Revisão (2026-10-07):** o código do SemIf (`semif_phase1.core.LETTERS = "A…P"`) aceita no máximo 16 opções. O Laya recomenda até ~20 e o Rizzo aceita 26. O limite comum passa a ser **16: até 15 opções de domínio + 1**. O dataset é validado por esquema. Um exemplo com mais de 16 opções reprova o lint e impede o congelamento (tarefa 35).
## Q3 — NAT e isolamento de rede

View File

@@ -0,0 +1,60 @@
# 05 — Ambiente de execução
A máquina local é **só IDE e repositório**. Todo script de benchmark roda na OCI (região `us-chicago-1`, compartment `decision-models`), no OCI Data Science.
## Componentes
| Componente | Rede | Uso |
|------------|------|-----|
| **Jobs de build** (`infra/ds/build_env.sh`) | Rede gerenciada do serviço, com internet | Criam o ambiente Python de cada candidato com as versões fixadas em `env/requirements/` e o publicam em `dmb-artefatos/envs/<candidato>/` (pacote + `latest.json` com SHA-256 + manifesto de versões) |
| **Jobs de benchmark** (`infra/ds/run_job.sh` → `harness/job.py`) | Sub-rede privada `dmb-subnet-jobs`, **sem internet**, só Service Gateway | Gate de egresso, download dos pesos e do ambiente do bucket (SHA-256 conferido), inferência com bloqueio de rede no processo e envio dos resultados para `dmb-resultados/runs/<run_id>/` |
| **Notebook `dmb-notebook-cpu`** | Rede gerenciada | Análise interativa dos resultados. Um notebook de GPU é criado só quando houver trabalho interativo em GPU |
| Máquina local | — | Edição, testes unitários rápidos e disparo dos jobs (`infra/ds/jobs.py`) |
O código vai para cada job como artefato zip, gerado na hora a partir do repositório local, sem `.secrets/`, `models/` e venvs. Dentro do job, o processo pai usa o Python base do serviço (3.11.9, com SDK OCI e resource principal). A inferência roda no Python do ambiente do candidato.
## Comandos
```bash
# job genérico (CPU flex ou GPU), com espera e logs
PYTHONPATH=infra/oci .venv/bin/python infra/ds/jobs.py run --name <nome> --entrypoint <script> \
[--shape VM.Standard.E4.Flex --ocpus 4 --memory 32 | --shape VM.GPU.A10.1] [--private] \
[--env DMB_CANDIDATE=laya DMB_MODE=sanity DMB_DEVICE=auto] [--wait]
# build do ambiente de um candidato
... jobs.py run --name build-laya --entrypoint infra/ds/build_env.sh --ocpus 4 --memory 32 --env DMB_CANDIDATE=laya
# sanidade de um candidato na sub-rede privada (CPU ou GPU)
... jobs.py run --name sanity-laya --entrypoint infra/ds/run_job.sh --private \
--env DMB_CANDIDATE=laya DMB_MODE=sanity --wait
... jobs.py run --name sanity-laya-gpu --entrypoint infra/ds/run_job.sh --private --shape VM.GPU.A10.1 \
--env DMB_CANDIDATE=laya DMB_MODE=sanity --wait
```
## VM de trabalho
| Item | Valor |
|------|-------|
| Instância | `instance-20261007-1206`: `VM.Standard.E6.Flex`, 8 OCPU (16 vCPU), 88 GB, Oracle Linux 9, sub-rede `dmb-subnet-dev` (SSH só do IP autorizado) |
| Disco | Boot volume de 200 GB (raiz `/`) + block volume `dmb-compute-vol` de 1 TB (iSCSI, XFS) montado em **`/data`**: `models/`, `envs/`, `runs/`, `repo/` |
| Acesso à OCI | Instance principal (política `dmb-workloads`), sem chave de API na VM |
| Preparação | `infra/vm/bootstrap.sh` (Python 3.12, git, rsync, SDK OCI em `~/.venv-oci`) |
A VM roda o trabalho de CPU que não precisa do isolamento de rede dos jobs: preparação de dados, análise e orquestração. Do lado local:
```bash
infra/vm/remote.sh sync # envia o repositório para /data/repo (sem .secrets, models, venvs)
infra/vm/remote.sh run '<comando>' # sincroniza e executa na VM
infra/vm/remote.sh ssh # shell na VM
```
A VM foi criada pelo console: o lançamento de instâncias pela API, com a chave do usuário, é negado neste compartment (`404` em `LaunchInstance`). Por isso `infra/oci/provision.py` só registra a VM e seus volumes no catálogo, sem tentar criá-la. O `env/Dockerfile` (uma imagem por candidato) fica como alternativa caso se queira contêineres próprios (BYOC/OCIR).
## Divisão do trabalho
| Trabalho | Onde |
|----------|------|
| Execuções que valem como medição (piloto, dev, calibração, teste final) | Jobs do Data Science na sub-rede privada, em CPU (`VM.Standard.E4.Flex`) ou GPU (`VM.GPU.A10.1`) |
| Builds de ambiente | Jobs com rede gerenciada |
| Preparação de dados, análise, relatórios | VM (`/data`) ou notebook |
| Edição e disparo | Máquina local |

View File

@@ -0,0 +1,49 @@
# 06 — Guia de anotação (tarefa 29)
Este guia é usado **literalmente** como instrução dos anotadores. São três modelos generativos de fornecedores distintos, em rodízio entre os papéis de anotador e de desempate (ver [02-desenho-experimental.md §4](02-desenho-experimental.md#4-dados)). As intenções e suas descrições estão em `data/taxonomy/intents-v1.yaml`.
<!-- GUIA:INICIO -->
## Tarefa
Você recebe mensagens que clientes de uma operadora de telefonia móvel enviaram ao canal de atendimento digital (chat ou aplicativo de mensagens). Para cada mensagem, escolha **uma intenção principal** da taxonomia: o que o cliente quer que o atendimento faça **agora**. Se nenhuma intenção se aplicar, use `sem_correspondencia`.
## Regras
1. **Intenção principal = ação pedida.** Rotule pelo que o cliente quer que aconteça, não pelo assunto mencionado de passagem.
2. **Tom não muda a intenção.** Irritação, ansiedade, ironia ou xingamentos não alteram o rótulo. "Que absurdo, cobraram um serviço que eu nunca pedi, quero meu dinheiro de volta" → `conta_contestacao`.
3. **Desabafo sem pedido** (só reclamação emocional, ofensa ou xingamento sem ação identificável) → `sem_correspondencia`.
4. **Reclamação do próprio atendimento** (demora, protocolo sem solução, pedido de ouvidoria) → `reclamacao_atendimento`. Pedido explícito para falar com uma pessoa → `atendimento_humano`, mesmo que o motivo seja citado.
5. **Várias intenções na mesma mensagem:** escolha a que o cliente pede primeiro ou com mais ênfase e liste as demais em `secundarias`.
6. **Saudação + pedido:** ignore a saudação. Saudação, agradecimento ou teste isolado → `sem_correspondencia`.
7. **Fora do domínio** (outro tipo de empresa, assuntos gerais, texto ininteligível) → `sem_correspondencia`.
8. **Mensagem vaga demais** para identificar qualquer intenção ("preciso resolver um problema", "me ajuda") → `sem_correspondencia` com `ambiguidade` alta.
## Pares difíceis
| Situação | Rótulo |
|----------|--------|
| Diz que a cobrança é indevida e/ou pede estorno | `conta_contestacao` |
| Só quer entender a fatura ou o aumento | `conta_explicacao` |
| Cobrança indevida de uso no exterior | `roaming_cobranca` |
| Quer cancelar um serviço adicional ou assinatura (mesmo citando a cobrança) | `vas_cancelamento` |
| Linha bloqueada por falta de pagamento e quer reativar | `negociacao_religacao` |
| Já pagou e quer confirmar se o pagamento caiu | `conta_pagamento_confirmacao` |
| Quer negociar ou parcelar o que está atrasado | `negociacao_divida` |
| Problema só com internet móvel (dados) | `internet_problema_conexao` |
| Sem sinal ou sem conseguir ligar ou receber chamadas e SMS | `suporte_sem_sinal` |
| Recarga paga que não caiu | `recarga_nao_creditada` |
| Bônus prometido que não veio | `bonus_nao_recebido` |
| Quer pagar menos para continuar ou ameaça sair pedindo condição melhor | `desconto_retencao` |
| Quer encerrar a linha sem pedir condição | `cancelamento_linha` |
| Levar ou trazer o número de outra operadora | `portabilidade` |
| Perdeu ou teve o aparelho roubado | `seguranca_perda_roubo` |
| Linha clonada, golpe ou contratação que não reconhece | `seguranca_fraude` |
## Resposta
Para cada mensagem, devolva:
- `rotulo`: um id da taxonomia ou `sem_correspondencia`;
- `secundarias`: lista (possivelmente vazia) de outros ids presentes;
- `ambiguidade`: `baixa`, `media` ou `alta`;
- `justificativa`: uma frase curta.
<!-- GUIA:FIM -->