Encerramento do ciclo d1-v1 (tarefas 75–78): README executivo, arquivamento e desligamento

- README: apresentação, resultados e recomendação, método e detalhes.
- Arquivo em dmb-logs-imutaveis com retenção de 365 dias.
- Notebook desativado; sem jobs, NAT ou endpoints ativos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Lohmann
2026-10-07 20:57:10 -03:00
parent 98f042c68a
commit 9b6c4c499a
2 changed files with 133 additions and 59 deletions

169
README.md
View File

@@ -1,74 +1,145 @@
# Benchmark de modelos de decisão em PT-BR para roteamento em orquestradores multiagente
Benchmark reprodutível de modelos de decisão *open source*, do tipo "System 1" e semelhantes ao JEV (`POST /v1/systemone`), aplicados ao **roteamento e à orquestração de enunciados de usuário** (*user utterances*) num sistema multiagente com **A2A Gateway** e **MCP Gateway**.
## Apresentação
O modelo de decisão é avaliado como um componente do **Agente Orquestrador**. A cada turno, ele toma decisões rápidas e estruturadas: identifica a intenção, escolhe o agente de destino, decide entre continuar, alternar ou retomar uma sessão e decide quando é preciso desambiguar. Essas decisões substituem ou complementam chamadas a LLMs generalistas.
Num sistema multiagente, o **orquestrador** decide a cada turno para onde vai a mensagem do usuário: qual intenção ela expressa, qual agente deve atendê-la e quando é melhor perguntar antes de agir. Essas decisões ficam no caminho síncrono de toda conversa e precisam ser rápidas, baratas e confiáveis.
## Objetivo
Este benchmark avalia se **modelos de decisão *open source***, do tipo "System 1", podem cumprir esse papel em português do Brasil, no lugar de chamadas a LLMs generalistas. São modelos que respondem a uma pergunta com opções definidas em tempo de execução e devolvem uma distribuição de probabilidades, sem gerar texto, no padrão do contrato JEV `POST /v1/systemone`.
Comparar os candidatos em duas dimensões, nas mesmas condições:
**Candidatos**
- **Acurácia e calibração** das decisões de roteamento em PT-BR, incluindo casos ambíguos, adversariais e com várias intenções.
- **Desempenho operacional**: latência por turno (p50/p95/p99), vazão, memória, tempo de carga e custo por mil decisões, em CPU e GPU na OCI.
| Candidato | Modelo | Tipo |
|---|---|---|
| **SemIf** | `SemIf-OpenJev` + `Qwen/Qwen3.5-4B` (modo `direct`) | LLM de 4B, logits das opções |
| **Rizzo Flow** | `rizzoaiacademy/rizzo-flow` (Spark-X2.5 4B + LoRA, GGUF Q8_0) | LLM de 4B, logits das opções |
| **Laya** | `convaiinnovations/laya-multilingual` | Encoder de 322M com cabeça de decisão |
| **GLiNER** | `fastino/GLiNER2.5-multi-Decide` | Encoder de 287M, classificação com rótulos descritos |
O resultado é um relatório técnico com uma recomendação. Todos os resultados podem ser reproduzidos a partir dos artefatos versionados.
**Escopo deste ciclo:** classificação de intenções no atendimento de telefonia móvel (42 intenções em 13 domínios, mais "sem correspondência"), com 4.054 enunciados sintéticos anotados por três modelos generativos. As medições rodaram na OCI, em CPU e em GPU A10, com a configuração congelada antes do teste final.
## Participantes
## Resultados
| Candidato | Artefato |
|-------------|----------------------------------------------|
| Laya | `convaiinnovations/laya-multilingual` |
| SemIf | `SemIf-OpenJev` com `Qwen/Qwen3.5-4B` (modo `direct`) |
| GLiNER | `fastino/GLiNER2.5-multi-Decide` (`classify_text`) |
| Rizzo Flow | `rizzoaiacademy/rizzo-flow` (4B) |
Teste final com 1.744 exemplos, configuração congelada e três repetições. Detalhes em [reports/test](reports/test/README.md).
Cada candidato é exposto por um adaptador local com o mesmo contrato JEV (`POST /v1/systemone`). Assim, o *harness* não sabe qual modelo está respondendo.
| Candidato | Acurácia (IC 95%) | Macro F1 | Decide sozinho com risco ≤ 2% | p95 em GPU A10 | Custo por mil decisões |
|---|---|---|---|---|---|
| **SemIf** | **87,2%** (85,0–89,4) | 0,889 | 62% (97% de acerto) | 187 ms | US$ 0,071 |
| **Rizzo Flow** | **85,4%** (83,2–87,6) | 0,877 | 47% (97% de acerto) | 181 ms | US$ 0,070 |
| GLiNER | 57,0% (52,6–61,5) | 0,580 | 13% | 32 ms | US$ 0,012 |
| Laya | 56,8% (52,9–60,5) | 0,590 | 10% | 23 ms | US$ 0,012 |
## Andamento
![Qualidade × latência em GPU](reports/test/chart-qualidade-latencia.svg)
O andamento por fase fica no topo de [docs/03-plano-de-tarefas.md](docs/03-plano-de-tarefas.md), regenerado com `python tools/plan_progress.py` a cada atualização do plano.
- **Qualidade:** os modelos de 4B (SemIf e Rizzo Flow) acertam cerca de 86% e empatam estatisticamente entre si. Os encoders compactos ficam cerca de 30 p.p. abaixo.
- **Latência:** os encoders respondem em cerca de 20 ms e funcionam até em CPU. Os modelos de 4B ficam em cerca de 120 ms de mediana, mas passam do limite pré-registrado de p95 de 150 ms na A10.
- **Abstenção:** com probabilidades calibradas, o SemIf decide sozinho 62% dos casos com 97% de acerto e encaminha o resto para desambiguação. Nenhum candidato atinge a meta pré-registrada de 80% de cobertura com 2% de risco.
- **Principal fonte de erro grave:** quando a intenção correta não está entre as opções elegíveis, os modelos escolhem a opção mais próxima com alta confiança.
**Primeiro ciclo:** classificação de intenções (D1) no atendimento de telefonia móvel, com dados 100% sintéticos anotados por três modelos generativos. CPU em us-chicago-1 (sub-rede privada) e GPU A10 em sa-saopaulo-1.
**Recomendação.** Usar o **SemIf** como modelo de decisão do orquestrador, com o **Rizzo Flow** como alternativa equivalente e mais leve em memória de GPU. Junto com isso:
- classificar contra o **catálogo completo** de intenções e aplicar a elegibilidade **depois**, de forma determinística;
- usar o **limiar calibrado** para pedir esclarecimento nos casos incertos;
- buscar uma GPU mais rápida que a A10 ou otimizações de inferência para cumprir o p95 de 150 ms.
## Documentação
Encoders compactos não servem como roteador principal neste domínio.
| Critério pré-registrado | SemIf | Rizzo Flow | GLiNER | Laya |
|---|---|---|---|---|
| H1 · macro F1 ≥ 0,80 | ✅ | ✅ | ❌ | ❌ |
| H2 · cobertura ≥ 80% com risco ≤ 2% | ❌ | ❌ | ❌ | ❌ |
| H3 · p95 ≤ 150 ms em GPU | ❌ | ❌ | ✅ | ✅ |
**Etapas e relatórios**
| Etapa | Conjunto | Destaque | Relatório |
|---|---|---|---|
| Piloto | 50 exemplos de dev | Execução validada em CPU e GPU; SemIf e Rizzo inviáveis em CPU | [reports/pilot](reports/pilot/README.md) |
| Desenvolvimento | 1.183 exemplos | Dois grupos de qualidade; o Laya cai com mais opções | [reports/dev](reports/dev/README.md) |
| Calibração | 1.127 exemplos | Temperatura e limiar por candidato; ECE do Laya de 0,33 para 0,08 | [reports/calibration](reports/calibration/README.md) |
| Teste final | 1.744 exemplos × 3 repetições | Resultados acima, com IC, cortes, custos e análise de erros | [reports/test](reports/test/README.md) |
**Limitações.** Os dados são sintéticos e os rótulos de referência vêm do consenso de três modelos generativos, não de pessoas. Não houve baseline LLM neste ciclo. A latência foi medida numa única GPU (A10) e com lote 1. O Rizzo Flow roda em Q8_0, enquanto os demais rodam na precisão nativa.
## Método
1. **Contrato único.** Todos os candidatos ficam atrás do mesmo contrato `POST /v1/systemone` (`jev-compat-v1`), com adaptadores que preservam a ordem e a descrição das opções. Testes de conformidade e de sanidade garantem que trocar de candidato não muda a requisição nem a resposta.
2. **Dados sintéticos controlados.** São 42 intenções e 5 categorias fora de escopo, com 7 perfis de escrita (curto, informal, com erros de digitação, regional, emocional, longo, indireto ou com várias intenções), escritos por dois modelos geradores. Os rótulos vêm de três anotadores generativos de outros fornecedores, em rodízio, com dois anotando de forma independente e o terceiro desempatando (kappa de 0,98 a 0,99). As opções de cada exemplo simulam um serviço de elegibilidade (4, 8, 12 ou 16 opções); em 10% dos exemplos, a intenção correta fica de fora.
3. **Partições disjuntas por lote de geração:** desenvolvimento (1.183), calibração (1.127) e teste (1.744), congeladas com SHA-256.
4. **Protocolo pré-registrado.** Critérios, limites e regras ficam em `config/benchmark.yaml` antes das medições: piloto → desenvolvimento → calibração (temperatura e limiar só na partição de calibração) → congelamento da configuração com hashes (tag `congelamento-d1-v1`) → teste final, que só abre se nada tiver mudado.
5. **Execução isolada e reprodutível na OCI.** Pesos e ambientes vêm de buckets, com SHA-256 conferido; a inferência roda sem acesso a fontes externas (sub-rede privada ou bloqueio no processo, com autoteste); cada medição repete três vezes. GPU `VM.GPU.A10.1`; CPU `VM.Standard.E4.Flex` com 8 OCPU.
6. **Análise:** IC 95% por bootstrap agrupado por lote, comparações pareadas, calibração (ECE, Brier), cobertura contra risco, cortes por perfil e número de opções, classificação dos erros e custo a preço público.
Detalhes em [docs/02 — desenho experimental](docs/02-desenho-experimental.md) e [docs/04 — decisões de projeto](docs/04-decisoes-de-projeto.md).
## Detalhes do projeto
### Plano de tarefas
As 78 microtarefas, organizadas em 11 fases e com o andamento de cada uma, estão em [docs/03-plano-de-tarefas.md](docs/03-plano-de-tarefas.md). O resumo por fase é regenerado com `python tools/plan_progress.py`.
### Documentos
| Documento | Conteúdo |
|-----------|----------|
| [docs/01-contexto-arquitetural.md](docs/01-contexto-arquitetural.md) | Arquitetura de referência do orquestrador e onde o modelo de decisão atua |
| [docs/02-desenho-experimental.md](docs/02-desenho-experimental.md) | Tarefas de decisão, formato dos dados, partições, métricas e regras de isolamento |
| [docs/03-plano-de-tarefas.md](docs/03-plano-de-tarefas.md) | As 78 microtarefas organizadas em fases, com dependências e adaptações ao contexto de roteamento |
| [docs/02-desenho-experimental.md](docs/02-desenho-experimental.md) | Tarefas de decisão, formato dos dados, partições, métricas e conjunto d1-v1 |
| [docs/03-plano-de-tarefas.md](docs/03-plano-de-tarefas.md) | Plano de 78 tarefas, com o andamento |
| [docs/04-decisoes-de-projeto.md](docs/04-decisoes-de-projeto.md) | Decisões de projeto (Q1–Q14) e os mecanismos que as aplicam |
| [docs/06-guia-de-anotacao.md](docs/06-guia-de-anotacao.md) | Guia de anotação, usado literalmente pelos anotadores |
| [docs/05-cenario-de-execucao.md](docs/05-cenario-de-execucao.md) | Cenário de execução: onde cada etapa roda, isolamento e fluxo dos dados |
| [config/benchmark.yaml](config/benchmark.yaml) | Parâmetros e critérios pré-registrados, congelados na tarefa 62 |
| [docs/06-guia-de-anotacao.md](docs/06-guia-de-anotacao.md) | Guia de anotação, usado literalmente pelos anotadores |
| [reports/](reports/) | Relatórios do piloto, desenvolvimento, calibração e teste final, com dados, gráficos e análise |
## Estrutura do repositório
### Reprodução
```
config/ Configuração pré-registrada (benchmark.yaml)
dmb/ Código comum: contrato JEV, formato canônico, servidor /v1/systemone, isolamento de rede
tests/ Testes de contrato, formato canônico, servidor e isolamento
infra/oci/ Compartment, IAM, VCN, buckets, Data Science, VM e NAT (tarefas 1–10, 17, 78)
infra/ds/ Jobs do Data Science: build dos ambientes, execução do benchmark e logs
infra/vm/ remote.sh: sincroniza o repositório e executa na VM de trabalho
env/ Aquisição de artefatos, manifesto, Dockerfile por candidato e dependências fixadas (11–16)
contracts/jev/ Contrato JEV jev-compat-v1: esquemas, mapeamento de tipos, fixtures de conformidade (36–41)
data/taxonomy/ Taxonomia de intenções (intents-v1.yaml: 42 intenções, 13 domínios) (20–23)
datagen/ Pipeline sintético: geração, mascaramento/deduplicação, anotação por 3 LLMs, partições (24–36)
data/raw/ Enunciados coletados e anonimizados — não versionar dados sensíveis (24–25)
data/annotated/ Anotações, adjudicação e referência (29–32)
data/splits/ Partições dev / calibração / teste congeladas (33–35)
adapters/<modelo>/ Adaptadores JEV locais por candidato (42–51)
harness/ Execução nos jobs (job.py), sanidade, servidor, auditoria do bucket (18–19, 41, 51–66)
tools/ Utilitários do repositório (andamento do plano)
analysis/ Métricas, cortes, análise de erros e gráficos (67–74)
reports/ Relatório técnico e anexos de reprodução (75–76)
| Artefato | Onde |
|---|---|
| Manifesto de artefatos (origem, revisão, licença, SHA-256, tamanho) | [env/artifacts-manifest.json](env/artifacts-manifest.json) |
| Configuração pré-registrada, calibração e congelamento | [config/benchmark.yaml](config/benchmark.yaml), [config/calibration.json](config/calibration.json), [config/frozen.json](config/frozen.json) |
| Partições congeladas | [data/splits/](data/splits/) ([MANIFEST.json](data/splits/MANIFEST.json)) |
| Contrato e fixtures de conformidade | [contracts/jev/](contracts/jev/) |
| Predições, resumos de execução e análise | [reports/](reports/) |
| Logs de cada execução (stdout, stderr, `run.json` com gate de rede e ambiente) | Buckets `dmb-resultados` e `dmb-resultados-gru`, `runs/<run_id>/` |
```bash
# testes locais do contrato, formato, servidor, isolamento, métricas, congelamento e dados sensíveis
python -m pytest -q
# avaliação de um candidato numa partição (job do OCI Data Science)
PYTHONPATH=infra/oci .venv/bin/python infra/ds/jobs.py run --name test-laya --entrypoint infra/ds/run_job.sh \
--private --ocpus 8 --memory 64 --env DMB_CANDIDATE=laya DMB_MODE=evaluate DMB_SPLIT=test DMB_REP=1
PYTHONPATH=infra/oci:infra/ds .venv/bin/python infra/ds/gpu_queue.py evaluate semif rizzo_flow laya gliner \
--env DMB_SPLIT=test DMB_REP=1
# coleta, análise e gráficos
PYTHONPATH=infra/oci .venv/bin/python infra/ds/collect.py --split test --examples 1744 --all-reps --dest reports/test
python -m analysis.report reports/test
python -m analysis.charts reports/test --kind test --label "teste final"
```
## Princípios
### Estrutura do repositório
1. **Isolamento de rede.** Na inferência, os modelos usam só artefatos locais com hash conhecido. Qualquer tentativa de download faz o job falhar.
2. **Contrato único.** Todos os candidatos recebem a mesma requisição JEV e devolvem o mesmo formato de resposta.
3. **Teste final congelado.** Prompts, opções, pesos e limiares não mudam depois que o conjunto de teste final é aberto.
4. **Partição por conversa.** Turnos de uma mesma conversa ou caso nunca aparecem em mais de uma partição.
5. **Reprodutibilidade.** Versões, seeds, hashes, configurações e comandos ficam registrados em manifestos anexados ao relatório.
```
config/ Configuração pré-registrada, calibração, congelamento e preços
contracts/jev/ Contrato jev-compat-v1: esquemas e fixtures de conformidade
dmb/ Código comum: contrato, formato canônico, servidor /v1/systemone, isolamento de rede, cliente de LLM
adapters/<modelo>/ Adaptadores JEV por candidato
harness/ Execução nos jobs, avaliação, sanidade, servidor e auditoria do bucket
datagen/ Pipeline sintético: geração, preparação, anotação por 3 LLMs, partições
data/ Taxonomia de intenções e partições congeladas
analysis/ Métricas, calibração, análise final e gráficos
env/ Aquisição de artefatos, manifesto e dependências por candidato
infra/oci/ Compartment, IAM, rede, buckets, réplica entre regiões, NAT
infra/ds/ Jobs do OCI Data Science: builds, execuções, fila de GPU, coleta
infra/vm/ Preparação e execução remota na VM de trabalho
models/ Metadados dos modelos (cards, configs, licenças); pesos ficam no Object Storage
reports/ Relatórios por etapa
tests/ Testes automatizados
tools/ Andamento do plano e congelamento da configuração
```
### Princípios
1. **Isolamento.** Na inferência, os modelos usam só artefatos com hash conhecido, sem acesso a fontes externas.
2. **Contrato único.** Todos os candidatos recebem a mesma requisição e devolvem o mesmo formato de resposta.
3. **Teste congelado.** Nada muda depois que o teste final é aberto, e o executor confere os hashes.
4. **Partições disjuntas.** Exemplos do mesmo lote de geração nunca aparecem em mais de uma partição.
5. **Reprodutibilidade.** Versões, seeds, hashes, configurações e comandos ficam versionados neste repositório.

View File

@@ -20,11 +20,11 @@ flowchart LR
As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
<!-- PROGRESSO:INICIO -->
**Andamento em 2026-10-07:** 70 de 78 tarefas concluídas (89%), 2 em andamento.
**Andamento em 2026-10-07:** 75 de 78 tarefas concluídas (96%), 1 em andamento.
| Fase | Concluídas | Em andamento | Pendentes |
|------|-----------|--------------|-----------|
| F0 — Infraestrutura OCI | 6 | 2 | 2 |
| F0 — Infraestrutura OCI | 7 | 1 | 2 |
| F1 — Ambiente e artefatos | 9 | 0 | 0 |
| F2 — Tarefas de decisão e taxonomia | 4 | 0 | 0 |
| F3 — Dados e anotação | 12 | 0 | 0 |
@@ -34,8 +34,8 @@ As fases F0–F1 (infra) e F2–F3 (dados) podem andar em paralelo.
| F7 — Desenvolvimento e calibração | 4 | 0 | 0 |
| F8 — Teste final | 4 | 0 | 0 |
| F9 — Métricas e análise | 8 | 0 | 0 |
| F10 — Relatório e encerramento | 0 | 0 | 4 |
| **Total** | **70** | **2** | **6** |
| F10 — Relatório e encerramento | 4 | 0 | 0 |
| **Total** | **75** | **1** | **2** |
Legenda: `[x]` concluída · `[~]` em andamento ou parcial · `[ ]` pendente.
<!-- PROGRESSO:FIM -->
@@ -54,8 +54,8 @@ Legenda: `[x]` concluída · `[~]` em andamento ou parcial · `[ ]` pendente.
- ↳ Não se aplica neste ciclo (depende da tarefa 3).
- [x] **5.** Criar um projeto no OCI Data Science.
- ↳ `benchmark-decisoes-ptbr`.
- [~] **6.** Criar buckets no Object Storage para `entrada`, `artefatos`, `resultados` e `logs-imutaveis`.
- ↳ Buckets `dmb-*`, privados e com versionamento. A regra de retenção de `dmb-logs-imutaveis` é definida com a política de arquivamento (tarefa 77).
- [x] **6.** Criar buckets no Object Storage para `entrada`, `artefatos`, `resultados` e `logs-imutaveis`.
- ↳ Buckets `dmb-*`, privados e com versionamento. `dmb-logs-imutaveis` usa regra de retenção de 365 dias (tarefa 77).
- [x] **7.** Definir políticas IAM de menor privilégio para leitura dos dados, escrita dos resultados e execução dos jobs.
- ↳ Políticas no escopo do compartment, sem grupos nem dynamic groups, com principais identificados por condição.
- ↳ `dmb-usuario-manager`: o usuário (filtrado por `request.user.id`) é *manager* das famílias data-science, object, virtual-network, logging, repos e generative-ai.
@@ -226,8 +226,11 @@ Legenda: `[x]` concluída · `[~]` em andamento ou parcial · `[ ]` pendente.
## F10 — Relatório e encerramento
- [ ] **75.** Elaborar o relatório técnico com configuração, limitações, resultados e recomendação.
- [x] **75.** Elaborar o relatório técnico com configuração, limitações, resultados e recomendação.
- ↳ [`README.md`](../README.md) (apresentação, resultados, recomendação, método e detalhes) e os relatórios por etapa em [`reports/`](../reports/).
- ↳ Ao final, reestruturar o `README.md`: (1) apresentação executiva do benchmark, (2) resultados em formato executivo com charts, (3) método, (4) plano, estrutura e documentos, (5) conclusão.
- [ ] **76.** Anexar ao relatório os manifestos de artefatos, hashes, configurações, logs, testes de conformidade e comandos de reprodução.
- [ ] **77.** Arquivar os conjuntos congelados e os resultados no Object Storage com retenção definida.
- [ ] **78.** Desligar notebooks, jobs, endpoints temporários e o NAT Gateway que não sejam mais necessários.
- [x] **76.** Anexar ao relatório os manifestos de artefatos, hashes, configurações, logs, testes de conformidade e comandos de reprodução.
- [x] **77.** Arquivar os conjuntos congelados e os resultados no Object Storage com retenção definida.
- ↳ Pacote com relatórios, configuração, partições, taxonomia, manifesto, contrato e documentação em `dmb-logs-imutaveis/archive/d1-v1/` (SHA-256 `c3600792…8c1e`), com regra de retenção de 365 dias. Execuções individuais em `dmb-resultados` e `dmb-resultados-gru`.
- [x] **78.** Desligar notebooks, jobs, endpoints temporários e o NAT Gateway que não sejam mais necessários.
- ↳ Notebook desativado (reversível), nenhum job ativo, nenhum NAT criado e nenhum endpoint exposto (os adaptadores rodam dentro dos jobs). A VM de trabalho e os buckets permanecem para consulta e para os próximos ciclos.