19 KiB
19 KiB
OCI CIS Agent — Lições Aprendidas (Erros & Acertos)
Documento de referência para uso como prompt em projetos futuros. Projeto: FastAPI + React 19 SPA + OCI GenAI + MCP + Terraform Agent Evolução: v1.0 → v2.8 (~4 meses de desenvolvimento iterativo)
1. Arquitetura & Decisões Fundamentais
ACERTOS
1.1 Chat Assíncrono desde o início (v2.0)
- Padrão: POST
/api/chatretorna imediatamente commessage_id; frontend faz polling em/api/chat/{mid}/status - Resultado: Eliminou 504 timeouts em respostas lentas do GenAI
- Regra: Para qualquer endpoint que dependa de LLM, adote async-first desde o dia 1. Polling a cada 2s é aceitável
1.2 SQLite como banco principal para monolito
- Decisão: SQLite com WAL mode + busy_timeout=30s para estado compartilhado
- Resultado: Rápido (<5ms leitura), ACID, zero containers extras, funciona até ~12 chats simultâneos
- Regra: Use SQLite para monolitos. Migre para banco dedicado quando atingir ~50 requests concorrentes
1.3 Multi-worker com estado no banco (não em memória)
- Problema: 8 workers uvicorn = 8 processos separados. Dicts em memória NÃO são compartilhados
- Solução: Todo estado compartilhado vai para SQLite (sessões, cache MCP, workspaces Terraform)
- Regra: Ao escalar workers, assuma que memória é local. Banco de dados é a fonte de verdade
1.4 Cache agressivo para APIs externas
- Padrão: MCP sessions cacheadas por 2h; Terraform resource docs cacheados no SQLite
- Resultado: Scans OCI caíram de ~3min para <10s no cache hit
- Regra: Cache tudo que vem de API externa. TTL de 2h é prático para OCI; ajuste por serviço
1.5 System prompts compactos para geração de código
- Evolução: Prompt do Terraform foi de ~200 linhas → ~30 linhas (~2.3K chars)
- Descoberta: GPT-5.2/o3 via OCI funcionam melhor com regras concisas que com exemplos verbosos
- Regra: Para agentes de código, priorize regras > exemplos. Alvo: <3K chars no system prompt. Use exemplos apenas para casos ambíguos
1.6 Validação de tipos com fuzzy matching
- Implementação:
terraform providers schema -json→ SQLite (~937 tipos OCI) +difflib.get_close_matches - Resultado: Modelo gera
oci_core_vcn_x→ sistema sugereoci_core_vcn - Regra: Para geração de código, valide contra catálogo estático. Gere na startup do container, cache no banco
1.7 Geração de referência no startup (não no build)
- Problema: Arquivo gerado no build → perdido quando volume Docker sobrescreve o diretório
- Solução: Gerar no entrypoint do container, salvar em
/data/ - Regra: Docker volume mounts sobrescrevem artefatos de build. Dados dinâmicos devem ser gerados em runtime
1.8 RAG separado por contexto de agente
- Problema: Terraform Agent tentava buscar RAG no ADB (banco vetorial externo) que ficava parado, travando o chat
- Solução: Terraform usa apenas SQLite local (resource reference + docs oficiais). ADB só para Chat Agent
- Regra: Cada agente deve ter suas fontes de dados bem definidas. Não compartilhe pipelines de RAG entre agentes com necessidades diferentes
ERROS
1.9 Provider region hardcoded no Terraform
- Bug:
provider.tfera gerado comregion = "us-ashburn-1"(string fixa da OCI config) em vez devar.region - Impacto: Recursos provisionados em Ashburn ao invés de Madrid → erro de quota → recursos órfãos para limpar manualmente
- Correção: Provider sempre usa
var.region. Se modelo não declaravariable "region", plan é bloqueado com erro explicativo - Regra: NUNCA hardcode valores de infraestrutura. Sempre use variáveis. Implemente guardrails que bloqueiem antes de provisionar
1.10 Estado travado após restart do container
- Bug: Workspace com status
planning/applyingfica preso se container reinicia — nenhum botão aparece no frontend - Correção: Frontend mostra botão Plan para todos os estados (incluindo os "travados")
- Regra: Sempre considere o cenário de restart. Status intermediários devem ter recovery path
1.11 HCL single-line blocks inválidos
- Bug: Modelos geram
variable "x" { type = string, default = "y" }(inválido em HCL) - Correção: Regex auto-fix em frontend E backend para expandir para multi-line
- Regra: Para geração de código, implemente auto-correção de sintaxe em múltiplas camadas
1.12 Modelo descarta arquivos na correção
- Bug: Ao corrigir erros, modelo retorna APENAS arquivos corrigidos, descartando os não alterados
- Correção: Merge-based updates: arquivos existentes com mesmo nome → substituídos; sem match → preservados
- Regra: Workflows de correção AI precisam de semântica de merge, não de substituição
1.13 sqlite3.Row não suporta .get()
- Bug:
row.get("column")crasha porque sqlite3.Row não tem método.get() - Correção: Usar
try/exceptcom acesso diretorow["column"]ou converter paradict(row) - Regra: Conheça as limitações do seu ORM/driver. sqlite3.Row é dict-like mas não é dict
1.14 reasoning_effort case-sensitive no OCI SDK
- Bug: OCI SDK requer
"HIGH"(uppercase), não"high" - Regra: Sempre consulte a documentação do SDK para formatos exatos. Normalize inputs com
.upper()antes de enviar
1.15 Index.html monolítico
- Status: ~2820 linhas de vanilla JS — manutenção cada vez mais difícil
- Limite prático: ~2000 linhas para SPA vanilla. Acima disso, framework de componentes se paga
- Plano: Migração para React (Vite + TypeScript), mantendo FastAPI backend
2. Pipeline de Geração de Código (Terraform)
ACERTO: Pipeline 3 camadas
- Frontend (
_splitTfMonolith): Detecta arquivos monolíticos (≤2 blocos + >2000 chars) e divide - Backend (
_split_tf_monolith): Validação Python, split por categoria (networking.tf, compute.tf) - Deduplicação (
_write_tf_files): Remove declarações duplicadas de recursos
Regra: Para geração de código, implemente validação + auto-correção em camadas (client + server). Belt-and-suspenders funciona.
ACERTO: Checklist de validação no prompt
14 pontos obrigatórios no system prompt:
- Cross-references entre recursos
- Validação de CIDRs
- Security lists + route tables
- DRG attachments + RPC peering
- Sintaxe HCL (blocos multi-line)
- Tipos de recursos válidos
- Declaração de variáveis
Regra: Checklists no prompt funcionam melhor que exemplos para evitar erros comuns
ACERTO: Auto-inject de terraform.tfvars
- Scan de variáveis declaradas nos
.tffiles - Map automático:
tenancy_ocid,user_ocid,fingerprint,compartment_id,ssh_public_key - Valores vêm da OCI config ativa (criptografada no banco)
Regra: Automatize tudo que pode ser derivado de configuração existente. Menos inputs manuais = menos erros
ERRO: Timeout de polling insuficiente
- Bug: Frontend tinha 1h de timeout para polling, mas operações grandes (multi-region) podiam levar mais
- Regra: Timeout de polling deve ser generoso. 1h para plan, 2h para apply. Melhor sobrar que faltar
3. Integração com OCI
ACERTOS
- OCID-based model resolution: Catálogo mapeia model IDs para OCIDs por região
- 16 regiões OCI: Endpoints auto-gerados (
https://inference.generativeai.{region}.oci.oraclecloud.com) - Wallet auto-parse: Upload de .zip → extrai password + DSN automaticamente
- GenAI config auto-resolve: Detecta config a partir de credenciais OCI (menos setup manual)
ERROS
- VCN deletion em cascata: VCNs não podem ser deletadas com dependências (subnets, route tables, security lists, DRG attachments, RPCs). Ordem: RPCs → DRG Attachments → Subnets → SGWs → non-default RTs/SLs → VCNs → DRGs
- Default resources não deletáveis: Route tables e security lists default são tied ao lifecycle da VCN — não tente deletar independentemente
- SSH key ≠ API key: SSH public key (para compute) é diferente da private key OCI API. Armazene separadamente na config
4. Frontend / UX
ACERTOS
- SVG icons (Lucide): Consistência cross-OS, themeable via CSS, zero CDN
- CSS variables para tema: Dark/light mode com toggle simples (
:root { --bg: ... }) - Oracle Dark Premium: Palette documentada (
#0D0F12background,#C74634accent) - Chart.js para dashboards: Leve, sem dependência de React, gauge + donut + bar
- Confirmation modals: Digitar "DESTROY"/"ROLLBACK" para ações destrutivas
ERROS
- Emojis inconsistentes: ✅ ❌ ⚠️ renderizam diferente em cada OS → SVG resolveu
- Layout complexo sem framework: Resize handles, split panels, tabs — tudo manual em vanilla JS. Funciona mas é frágil
- Scroll references quebradas: Renomear CSS classes quebra scroll automático silenciosamente
5. Operacional & Deploy
Padrão de Deploy
docker compose up -d --build backend && docker compose restart frontend
- Backend: rebuild necessário (código Python)
- Frontend: restart necessário (nginx serve index.html via bind mount, mas precisa reload)
RCP (Shortcut do usuário)
- README: atualizar changelog
- Commit: mensagem descritiva
- Push: enviar para remote
Versionamento
- Bump de versão no badge do README + entrada no changelog
- Versão semântica simplificada (v2.1, v2.2, etc.)
6. Regras para AI Coding Assistant
Fazer
- Ler arquivo antes de editar (entender contexto)
- Preferir editar existente vs criar novo (evitar file bloat)
- Deploy combo sempre (backend build + frontend restart)
- Testar cenários de restart/recovery
- Validar inputs em múltiplas camadas
Não fazer
- Incluir atribuição AI em commits/código
- Hardcode valores de infraestrutura
- Compartilhar pipeline de RAG entre agentes diferentes
- Injetar variáveis automaticamente quando o modelo esquece (forçar correção)
- Usar
.get()em sqlite3.Row - Confiar que status intermediários vão se resolver sozinhos
7. Stack & Trade-offs
| Componente | Escolha | Por quê | Limite |
|---|---|---|---|
| Backend | FastAPI | Python-first (OCI SDK), async nativo | Mais lento que Go/Rust |
| Frontend | Vanilla JS → React | Zero build step inicialmente | ~2000 linhas é o limite prático |
| Banco | SQLite → Oracle ATP | Simples, ACID, sem container extra | Single-machine, ~50 req/s |
| Cache | In-memory → Redis | Implícito; migrar quando distribuir | Não compartilhado entre workers |
| IaC | Terraform CLI (subprocess) | Testado, familiar | Overhead de processo; file locking |
| Charts | Chart.js | Leve, funciona sem React | Customização limitada vs D3 |
| Icons | SVG inline (Lucide) | Themeable, consistente | Cherry-picking manual |
8. Métricas de Referência
| Arquivo | Linhas | Propósito |
|---|---|---|
| backend/app.py | ~7200 | FastAPI server, todos endpoints, state |
| frontend-react/src/ | ~15500 | React 19 SPA (42 source files) |
| backend/mcp_cis_server.py | ~700 | MCP server, 12 tools CIS |
| backend/gen_tf_reference.py | ~70 | Gerador de catálogo Terraform |
9. ADB Oracle — Embedding & Vector Search (v2.8+)
ACERTOS
9.1 Auto-mapeamento CSV → tabela ADB
- Cada CSV do CIS report mapeia automaticamente para sua tabela:
cis_Identity_*.csv→identityandaccess,cis_Networking_*.csv→networking, etc. - Regra: Quando possível, derive o destino do nome do arquivo. Menos configuração manual = menos erro
9.2 Purge antes de re-embed
- Deletar dados antigos do mesmo tenancy + extract_date antes de inserir novos
- Usa
JSON_VALUE(METADATA, '$.tenancy')para filtrar - Regra: Sempre limpe antes de re-inserir. Duplicatas em bases vetoriais poluem resultados
9.3 Auto-detect dimensão de embedding por tabela
_get_table_embedding_dim()→_DIM_TO_MODEL = {1536: "text-embedding-3-small", 3072: "text-embedding-3-large"}- Cache de embeddings por modelo: se 5 tabelas usam o mesmo modelo, gera 1 embedding só
- Regra: Nunca assuma que todas as tabelas têm a mesma dimensão. Detecte dinamicamente
9.4 Chunking com header de contexto
- Textos >8000 chars divididos em partes, cada parte repete header (Tenancy, Resource, ID)
- Cada chunk é auto-suficiente para busca vetorial
- Regra: Chunks sem contexto são inúteis. Sempre repita metadados essenciais
9.5 Tabelas globais vs tenant-specific
cisrecomeengineerknowledgebase: busca SEM filtro de tenancy (dados genéricos)- Tabelas de findings: busca COM filtro de tenancy (dados específicos)
- Regra: Separe dados genéricos de dados específicos. Filtrar dados genéricos por tenancy retorna zero resultados
9.6 Hierarquia de fontes no RAG
- Findings (dados reais) > cisrecom (remediação oficial) > engineerknowledgebase (complementar)
- RAG vs MCP Tools: system prompt diferencia dados armazenados vs scan em tempo real
- Regra: O modelo precisa saber a confiabilidade relativa de cada fonte
ERROS
9.7 ID RAW(16) no ADB
- Tabelas usam
ID RAW(16), não VARCHAR. Inserts devem usarHEXTORAW(:1)comuuid.uuid4().hex.upper() - Erro:
ORA-01465: invalid hex number - Regra: Verifique o schema da tabela ANTES de implementar inserts
9.8 METADATA tipo JSON, não CLOB
LIKE '%..%'não funciona com colunas JSON. UsarJSON_VALUE(METADATA, '$.field') = :1- Erro:
ORA-01722: unable to convert string containing '%' to a number - Regra: Cada tipo Oracle tem suas funções. JSON → JSON_VALUE/JSON_EXISTS. CLOB → LIKE
9.9 FETCH FIRST bind variable
- Oracle não aceita bind variable no
FETCH FIRST :1 ROWS ONLY - Usar f-string:
FETCH FIRST {int(n)} ROWS ONLY(safe pois n é int do Python) - Regra: Nem toda cláusula SQL aceita bind variables. Teste cada uso
9.10 Token limit do modelo de embedding
text-embedding-3-largeaceita max 8192 tokens.truncate="END"do OCI não funciona com modelos OpenAI- Solução: truncar no código Python (8000 chars max) + chunking (8000 chars por parte)
- Regra: Não confie em flags do SDK para limites. Implemente truncation no seu código
9.11 Dados legados sem metadata estruturado
- Embeddings antigos tinham metadata em formato diferente (
source: blob, blobType, doc_id), sem campotenancy - Filtro
JSON_VALUE(METADATA, '$.tenancy') IS NULLincluía esses dados indevidamente - Solução: remover fallback IS NULL, aceitar que dados legados precisam ser re-embedados
- Regra: Ao mudar schema de metadata, considere migração dos dados existentes
10. React Migration & State Management (v2.7→v2.8)
ACERTOS
10.1 Zustand para persistência entre abas
- Todas as configurações (model, params, tools, sessions, messages) no Zustand store
- Sobrevive navegação entre páginas sem re-fetch
- Regra: Estado que o usuário espera manter ao voltar → Zustand. Estado transiente (loading, dropdown open) → useState local
10.2 Code splitting com React.lazy
- Bundle de 1.3MB → ~550KB initial load (15 chunks por página)
- Suspense fallback com spinner
- Regra: Implementar lazy loading desde o início. Custo zero de implementação, ganho significativo
10.3 Background tasks com polling persistente
- task_id salvo no Zustand store → polling retoma ao voltar para a página
- Status em arquivo no disco (não dict em memória) → visível entre workers
- Regra: Todo task longo precisa: 1) backend salvar status em disco/DB, 2) frontend salvar task_id no store, 3) retomar polling ao montar
ERROS
10.4 useCallback com closure stale
sessionIdcapturado na closure do useCallback ficava desatualizado- Solução: usar
useAppStore.getState()para ler valor atual no momento da execução - Regra: Em callbacks assíncronos, sempre leia state do store diretamente, nunca da closure
10.5 setInterval inline no onClick
- Não limpa no unmount, pode duplicar. Race condition com estado
- Solução: useEffect com cleanup, ou usePolling hook customizado
- Regra: Nunca crie timers/intervals dentro de event handlers. Sempre use useEffect com cleanup
10.6 X-Frame-Options DENY bloqueando iframes internos
- Security header bloqueava compliance report e HTML report renderizados em iframes
- Solução: SAMEORIGIN no bloco /api/ do nginx
- Regra: Ao adicionar security headers, teste TODOS os fluxos que usam iframes
11. Security & Docker Hardening (v2.8)
ACERTOS
- Non-root container: gosu entrypoint para chown do volume + exec como user
agent - Security headers nginx: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy
- Rate limiting: 10 tentativas/5min por IP com threading.Lock
- CORS restritivo: métodos/headers explícitos, origins configurável via env
- .gitignore + .dockerignore: prevenir leak de secrets e reduzir build context
- Graceful shutdown: terminar subprocesses + executor no shutdown event
- File upload validation: 50MB max, extensão whitelist por endpoint
- 16 SQLite indexes: em foreign keys e colunas frequentemente consultadas
ERROS
- CORS allow_origins=["*"]: acidentalmente permitia CSRF. Sempre restringir
- Container rodando como root: padrão do Docker se não especificar USER
- Bare except clauses: 13 instâncias engolindo erros silenciosamente. Sempre logar
- Sem timeout em chamadas externas: GenAI e ADB podiam travar indefinidamente. Sempre definir timeout
12. Gaps Conhecidos (para endereçar em projetos futuros)
- Testes automatizados: Sem cobertura. Pré-requisito para migração PostgreSQL
- PostgreSQL + Redis: Planejado para escalar (multi-container, concorrência real)
- Dashboard real: Painel com compliance score, atividade recente, status ADB
- Notificações: Toast/badge quando tasks longos completam
- Busca cross-report: Comparar compliance entre datas
- Drift detection: Terraform não detecta mudanças manuais
13. Recomendações para Próximo Projeto
- Async-first para qualquer coisa que dependa de LLM
- SQLite para começar, migrar quando necessário
- System prompts compactos (<3K chars, regras > exemplos)
- Validação em 3 camadas para geração de código
- Cache agressivo para APIs externas (TTL 2h)
- CSS variables para tema desde o dia 1
- SVG icons em vez de emojis
- Framework de componentes quando ultrapassar ~2K linhas de frontend
- Auto-update de ferramentas terceiras no startup do container
- Guardrails de segurança (bloquear antes de provisionar, não corrigir silenciosamente)
- Recovery paths para todos os estados intermediários
- Separar fontes de RAG por agente/contexto
- Nunca hardcode valores de infraestrutura — sempre variáveis
- Documentar deploy patterns desde o início