Atualiza README para refletir a V2 em producao (V1 vira referencia legado)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9475e68cb1
commit
24f0476dd7
@@ -1,8 +1,117 @@
|
|||||||
# Resumo Diário GLPI - MVP
|
# Resumo Operacional GLPI (V2) — em produção
|
||||||
|
|
||||||
Workflow N8N inicial para gerar um resumo diário de chamados usando dados simulados.
|
Automação n8n que gera e envia ao Slack um resumo diário/semanal de chamados do GLPI, com números 100% determinísticos (SQL) e uma camada opcional de interpretação por IA (insights, nunca números).
|
||||||
|
|
||||||
## Subir o N8N com Docker
|
**Status atual: V2 ativada em produção** na instância `devn8n.gtigrupo.com.br` (não existe uma segunda instância n8n de produção — esta é a instância operacional até uma futura migração). V1 é mantida só como referência local (nunca foi implantada nesta instância).
|
||||||
|
|
||||||
|
## Princípio central
|
||||||
|
|
||||||
|
> **SQL conta. IA entende.**
|
||||||
|
|
||||||
|
Todo número no relatório (abertos, fechados, pendentes, saldo, alta prioridade) vem de query SQL determinística. A IA só recebe esses números prontos e uma lista limitada/redigida de chamados, e só pode produzir **insights interpretativos** (texto), nunca alterar ou inventar contagens. Se a IA falhar, atrasar ou responder algo inválido, o relatório continua com fallback determinístico — nunca quebra por causa da IA.
|
||||||
|
|
||||||
|
## Arquitetura (fonte de verdade local)
|
||||||
|
|
||||||
|
```
|
||||||
|
src/v2/
|
||||||
|
config.js — configuração central (thresholds, flags de ambiente)
|
||||||
|
period.js — calendário operacional e janelas de baseline histórico
|
||||||
|
queries.js — todas as queries SQL (somente leitura, geradas, nunca hardcoded)
|
||||||
|
sqlGuard.js — bloqueia qualquer verbo de escrita numa query antes de rodar
|
||||||
|
baseline.js — pivotamento de séries diárias para detecção de baseline
|
||||||
|
anomaly.js — detecção determinística de spike/concentração/tendência
|
||||||
|
insights.js — validação do output da IA, fingerprint, memória de insights
|
||||||
|
aiSchema.js — contrato JSON schema + prompt da IA + contexto minimizado/redigido
|
||||||
|
format.js — montagem da mensagem final (fallback e curta)
|
||||||
|
sendGuard.js — guarda de envio real + idempotência (ver abaixo)
|
||||||
|
pipeline.js — orquestra tudo (buildReport) — mesma lógica testada localmente
|
||||||
|
e colada nos Code nodes do n8n (não há import no n8n remoto)
|
||||||
|
|
||||||
|
tools/
|
||||||
|
build_v2_workflow.js — gera os Code nodes a partir de src/v2/*.js
|
||||||
|
build_v2_workflow_assemble.js — monta o workflow n8n completo (30 nodes)
|
||||||
|
smoke_test_generated_nodes.js — simula os Code nodes gerados via `vm`, localmente,
|
||||||
|
antes de qualquer deploy
|
||||||
|
|
||||||
|
test/ — 133 testes (`node:test`), cobrindo cada módulo + regressões
|
||||||
|
estruturais do workflow gerado (executeOnce, sendGuard concatenado)
|
||||||
|
|
||||||
|
Resumo Operacional GLPI v2 - IA.n8n.json — workflow gerado (saída de build_v2_workflow.js)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fluxo de mudança obrigatório:** editar `src/v2/*.js` → `node tools/build_v2_workflow.js` → `npm test` → `node tools/smoke_test_generated_nodes.js` → deploy no n8n (`n8n_update_partial_workflow`/`n8n_update_full_workflow`) → `n8n_validate_workflow`. Nunca editar o Code node direto no n8n sem refletir a mudança em `src/v2/`.
|
||||||
|
|
||||||
|
## Guarda de envio real e idempotência (`sendGuard.js`)
|
||||||
|
|
||||||
|
```
|
||||||
|
podeEnviar =
|
||||||
|
SHADOW_MODE === false (absoluto)
|
||||||
|
&& DEV_MODE === false (absoluto)
|
||||||
|
&& SEND_ENABLED === true (absoluto)
|
||||||
|
&& (isScheduled || FORCE_SEND === true) (manual só envia com override explícito)
|
||||||
|
```
|
||||||
|
|
||||||
|
Identidade de um relatório para fins de duplicata = **`report_type + period_start + period_end + destination`** (sem `message_hash` — a IA não é determinística; incluir o hash na chave já causou envio duplicado real em produção, corrigido em 2026-08-25). O `message_hash` continua calculado e armazenado só para auditoria.
|
||||||
|
|
||||||
|
Registro de sucesso na Data Table `dev_glpi_v2_send_log` só acontece **depois** da confirmação real do Slack (`ok:true`). Retry do mesmo período é sempre bloqueado, mesmo que a IA gere texto diferente.
|
||||||
|
|
||||||
|
## Configuração operacional atual (node `Config - V2`)
|
||||||
|
|
||||||
|
| Flag | Valor | Efeito |
|
||||||
|
|---|---|---|
|
||||||
|
| `DEV_MODE` | `false` | produção ativa |
|
||||||
|
| `SHADOW_MODE` | `false` | envio real habilitado |
|
||||||
|
| `SEND_ENABLED` | `true` | chave mestra de envio |
|
||||||
|
| `FORCE_SEND` | `false` (padrão) | execução manual não envia a menos que setado explicitamente |
|
||||||
|
| `SLACK_DESTINATION` | `C0BJNBTPFT5` | canal onde o bot já é membro confirmado |
|
||||||
|
| `EXTENDED_METRICS_ENABLED` | `false` | fora de escopo desta versão |
|
||||||
|
|
||||||
|
Credenciais reaproveitadas (nenhuma criada nova): MySQL `GLPI` (`dsVj1nmOIXRvix9n`), OpenAI `OpenAI - Coordenador GLPI` (`WUGriAn5FgCiLza2`), Slack `Slack - Bot Coordenador (Chamados Suporte)` (`lNqwBBrtLfkRqw2X`, mesma credencial do workflow irmão "[DEV] Coordenador GLPI - Chamados Abertos v2 (Shadow)").
|
||||||
|
|
||||||
|
## Calendário e agendamento
|
||||||
|
|
||||||
|
Schedule Trigger: `0 8 * * 1-5`, timezone da instância (`America/Bahia`, sem conversão manual para UTC).
|
||||||
|
|
||||||
|
- Segunda 08:00 → semana anterior completa (segunda a domingo)
|
||||||
|
- Terça 08:00 → relatório de segunda-feira
|
||||||
|
- Quarta 08:00 → relatório de terça-feira
|
||||||
|
- Quinta 08:00 → relatório de quarta-feira
|
||||||
|
- Sexta 08:00 → relatório de quinta-feira
|
||||||
|
- `data_alvo` manual (campo em `Config - V2`) força um dia específico, ex.: `2026-08-20`
|
||||||
|
|
||||||
|
## GLPI
|
||||||
|
|
||||||
|
Acesso **estritamente somente leitura** — `sqlGuard.js` bloqueia qualquer verbo de escrita (`INSERT/UPDATE/DELETE/ALTER/DROP/CREATE/TRUNCATE/REPLACE/GRANT/REVOKE`) antes de qualquer query chegar ao node MySQL. Filtro de categorias pelas árvores `7`, `16`, `17`, `18` via `ancestors_cache`.
|
||||||
|
|
||||||
|
## Rodando os testes
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
133/133 devem passar. Após qualquer alteração em `src/v2/*.js`, rodar também:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
node tools/build_v2_workflow.js
|
||||||
|
node tools/smoke_test_generated_nodes.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fallback
|
||||||
|
|
||||||
|
- IA indisponível/timeout/JSON inválido → indicadores determinísticos seguem no relatório, insights de IA são omitidos (nunca derruba a automação).
|
||||||
|
- Baseline histórico indisponível → insight correspondente é omitido, resto do relatório segue normal.
|
||||||
|
|
||||||
|
## Workflows V1 (legado, referência local — nunca implantados nesta instância n8n)
|
||||||
|
|
||||||
|
Mantidos apenas como referência histórica da lógica SQL original (a V2 herda a mesma lógica de `abertos`/`fechados`/filtro de categorias, validada linha a linha contra a V1):
|
||||||
|
|
||||||
|
- `Resumo Diario GLPI - SQL Slack.n8n.json` — lógica de período diário/semanal automático que a V2 herda
|
||||||
|
- `Resumo Semanal GLPI - SQL Slack.n8n.json`
|
||||||
|
- `sql/indicadores_diario_glpi.sql`, `sql/indicadores_semana_glpi.sql`
|
||||||
|
|
||||||
|
Não editar nem reativar sem necessidade comprovada — a V2 é a versão operacional.
|
||||||
|
|
||||||
|
## Subir o N8N localmente com Docker (ambiente de desenvolvimento)
|
||||||
|
|
||||||
1. Copie `.env.example` para `.env`.
|
1. Copie `.env.example` para `.env`.
|
||||||
2. Edite `.env` e troque `N8N_BASIC_AUTH_PASSWORD` e `N8N_ENCRYPTION_KEY`.
|
2. Edite `.env` e troque `N8N_BASIC_AUTH_PASSWORD` e `N8N_ENCRYPTION_KEY`.
|
||||||
@@ -12,237 +121,7 @@ Workflow N8N inicial para gerar um resumo diário de chamados usando dados simul
|
|||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Acesse:
|
4. Acesse `http://localhost:5678`.
|
||||||
|
5. Para parar: `docker compose down`.
|
||||||
|
|
||||||
```text
|
Isso é independente da instância operacional (`devn8n.gtigrupo.com.br`), que já está ativada e não depende deste Docker local.
|
||||||
http://localhost:5678
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Para parar:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
docker compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
## Arquivo
|
|
||||||
|
|
||||||
- `Resumo Diario GLPI - MVP.n8n.json`
|
|
||||||
- `Resumo Diario GLPI - Real Slack.n8n.json`
|
|
||||||
- `Resumo Diario GLPI - SQL Slack.n8n.json`
|
|
||||||
- `Resumo Semanal GLPI - SQL Slack.n8n.json`
|
|
||||||
- `sql/indicadores_diario_glpi.sql`
|
|
||||||
- `sql/indicadores_semana_glpi.sql`
|
|
||||||
|
|
||||||
## Workflow Recomendado
|
|
||||||
|
|
||||||
Use este workflow para o relatório automatico correto:
|
|
||||||
|
|
||||||
- `Resumo Diario GLPI - SQL Slack.n8n.json`
|
|
||||||
|
|
||||||
Essa versão roda de segunda a sexta as 08:00 e escolhe o periodo automaticamente:
|
|
||||||
|
|
||||||
- terca-feira: envia o relatorio da segunda-feira
|
|
||||||
- quarta-feira: envia o relatorio da terca-feira
|
|
||||||
- quinta-feira: envia o relatorio da quarta-feira
|
|
||||||
- sexta-feira: envia o relatorio da quinta-feira
|
|
||||||
- segunda-feira: envia o relatorio da semana anterior inteira, de segunda a domingo
|
|
||||||
|
|
||||||
Essa versão usa a regra aprendida com a query semanal, aplicada ao periodo selecionado:
|
|
||||||
|
|
||||||
- chamados abertos no periodo: `glpi_tickets.date` entre `data_inicio` e `data_fim`
|
|
||||||
- chamados fechados no periodo: `glpi_tickets.closedate` entre `data_inicio` e `data_fim`
|
|
||||||
- chamados fechados no periodo podem ter sido abertos em qualquer data anterior
|
|
||||||
- filtro `is_deleted = false`
|
|
||||||
- filtro de categorias pelas árvores `7`, `16`, `17` e `18` usando `ancestors_cache`
|
|
||||||
|
|
||||||
### Como configurar o SQL automatico
|
|
||||||
|
|
||||||
1. Importe `Resumo Diario GLPI - SQL Slack.n8n.json`.
|
|
||||||
2. Configure a credencial MySQL no node `MySQL - Indicadores diario GLPI`.
|
|
||||||
3. Configure a credencial de IA/OpenAI no node `Z IA Chat Model`.
|
|
||||||
4. Abra `Config - Diario SQL` e preencha:
|
|
||||||
|
|
||||||
- `data_alvo`: deixe vazio para o modo automatico ou use `2026-07-03` para teste manual de um dia especifico
|
|
||||||
- `slack_webhook_url`
|
|
||||||
- `openai_model`, padrão `gpt-5.5`
|
|
||||||
|
|
||||||
5. Execute pelo `Manual Trigger` para testar.
|
|
||||||
6. Ative o workflow para usar o `Schedule Trigger - Dias uteis 08h`.
|
|
||||||
|
|
||||||
Se o agente de IA falhar, o workflow envia o resumo fallback sem IA com os números SQL.
|
|
||||||
|
|
||||||
### Agente de IA
|
|
||||||
|
|
||||||
O workflow usa os nodes nativos de IA do N8N:
|
|
||||||
|
|
||||||
- `AI Agent - Coordenador GLPI`: melhora o texto do relatorio antes do Slack
|
|
||||||
- `Z IA Chat Model`: chat model conectado ao agente pela entrada `Chat Model`
|
|
||||||
|
|
||||||
O agente recebe o resumo tecnico gerado pelo SQL e deve apenas melhorar a comunicacao. Ele foi instruido a nao inventar numeros.
|
|
||||||
|
|
||||||
## Workflow SQL Recomendado
|
|
||||||
|
|
||||||
Para bater exatamente com os indicadores do GLPI, use:
|
|
||||||
|
|
||||||
- `Resumo Semanal GLPI - SQL Slack.n8n.json`
|
|
||||||
|
|
||||||
Essa versão consulta diretamente o banco MySQL/MariaDB do GLPI e executa a mesma lógica da query de referência:
|
|
||||||
|
|
||||||
- abertos pela coluna `glpi_tickets.date`
|
|
||||||
- fechados pela coluna `glpi_tickets.closedate`
|
|
||||||
- filtro `is_deleted = false`
|
|
||||||
- filtro de categorias pelas árvores `7`, `16`, `17` e `18` usando `ancestors_cache`
|
|
||||||
|
|
||||||
Isso é diferente da API REST, que pode não trazer todos os chamados necessários para reproduzir o indicador semanal, principalmente porque chamados fechados na semana podem ter sido abertos em semanas anteriores.
|
|
||||||
|
|
||||||
### Como configurar
|
|
||||||
|
|
||||||
1. Importe `Resumo Semanal GLPI - SQL Slack.n8n.json`.
|
|
||||||
2. Configure a credencial MySQL no node `MySQL - Indicadores semana GLPI`.
|
|
||||||
3. Abra `Config - Slack e OpenAI` e preencha:
|
|
||||||
|
|
||||||
- `slack_webhook_url`
|
|
||||||
- `openai_api_key`
|
|
||||||
- `openai_model`, padrão `gpt-5.5`
|
|
||||||
|
|
||||||
4. Execute pelo `Manual Trigger`.
|
|
||||||
|
|
||||||
Se a OpenAI falhar, o workflow envia um resumo fallback sem IA com os números da query.
|
|
||||||
|
|
||||||
## Como usar
|
|
||||||
|
|
||||||
1. No N8N, vá em `Workflows`.
|
|
||||||
2. Clique em `Import from File`.
|
|
||||||
3. Selecione `Resumo Diario GLPI - MVP.n8n.json`.
|
|
||||||
4. Abra o node `OpenAI - Gerar resumo`.
|
|
||||||
5. Selecione sua credencial da OpenAI.
|
|
||||||
6. Execute o workflow pelo `Manual Trigger`.
|
|
||||||
|
|
||||||
## Fluxo
|
|
||||||
|
|
||||||
```text
|
|
||||||
Manual Trigger
|
|
||||||
↓
|
|
||||||
Set - Dados simulados
|
|
||||||
↓
|
|
||||||
Code - Calcular indicadores
|
|
||||||
↓
|
|
||||||
OpenAI - Gerar resumo
|
|
||||||
```
|
|
||||||
|
|
||||||
## Próxima etapa
|
|
||||||
|
|
||||||
Depois que o MVP estiver gerando o texto corretamente, substitua o node `Set - Dados simulados` pelos nodes HTTP Request do GLPI.
|
|
||||||
|
|
||||||
## Workflow Real com GLPI e Slack
|
|
||||||
|
|
||||||
Importe o arquivo:
|
|
||||||
|
|
||||||
- `Resumo Diario GLPI - Real Slack.n8n.json`
|
|
||||||
|
|
||||||
Depois da importação, abra o node `Config - GLPI e Slack` e preencha:
|
|
||||||
|
|
||||||
- `glpi_url`: URL base do GLPI, exemplo `https://glpi.suaempresa.com.br`
|
|
||||||
- `glpi_app_token`: App Token do GLPI
|
|
||||||
- `glpi_user_token`: User Token do usuário de integração
|
|
||||||
- `slack_webhook_url`: URL do Incoming Webhook do Slack
|
|
||||||
- `range_tickets`: quantidade buscada na API, exemplo `0-999`
|
|
||||||
- `page_size`: tamanho de cada pagina buscada no GLPI, padrao `1000`
|
|
||||||
- `page_count`: quantidade de paginas por ordenacao, padrao `2`
|
|
||||||
- `data_alvo`: data do relatório. Deixe vazio para usar o dia atual, ou informe uma data como `2026-07-03` ou `03/07/26`
|
|
||||||
- `openai_api_key`: chave da OpenAI usada pelo node `OpenAI - Gerar resumo`
|
|
||||||
- `openai_model`: modelo usado na OpenAI, padrão `gpt-5.5`
|
|
||||||
|
|
||||||
O node `OpenAI - Gerar resumo` usa `HTTP Request` direto para `https://api.openai.com/v1/chat/completions`.
|
|
||||||
|
|
||||||
O workflow busca tickets em paginas e combina duas ordenacoes:
|
|
||||||
|
|
||||||
- `id DESC`, para capturar chamados abertos recentemente
|
|
||||||
- `date_mod DESC`, para capturar chamados antigos que foram modificados, solucionados ou fechados recentemente
|
|
||||||
|
|
||||||
Depois ele remove duplicados por `id` e filtra no N8N pela `data_alvo`. O cálculo separa:
|
|
||||||
|
|
||||||
- chamados abertos na data, usando `date_creation`, `date` ou `data_abertura`
|
|
||||||
- chamados fechados na data, usando `solvedate`, `closedate` ou `data_fechamento`
|
|
||||||
- pendentes, usando o status atual do ticket filtrado
|
|
||||||
|
|
||||||
Na base atual, o GLPI informou `1216` tickets. Por isso o padrão recomendado é `page_size=1000` e `page_count=2`, cobrindo `0-1999` por ordenação sem estourar o total. Se aumentar `page_count`, o GLPI pode retornar `ERROR_RANGE_EXCEED_TOTAL` para páginas acima do total existente.
|
|
||||||
|
|
||||||
Execute pelo `Manual Trigger` primeiro. Depois que validar, troque por `Schedule Trigger`.
|
|
||||||
|
|
||||||
Para gerar o relatório da sexta-feira `03/07/2026`, use no campo `data_alvo`:
|
|
||||||
|
|
||||||
```text
|
|
||||||
2026-07-03
|
|
||||||
```
|
|
||||||
|
|
||||||
### Criar Webhook no Slack
|
|
||||||
|
|
||||||
1. Acesse `https://api.slack.com/apps`.
|
|
||||||
2. Crie ou abra um app do Slack.
|
|
||||||
3. Ative `Incoming Webhooks`.
|
|
||||||
4. Crie um webhook para o canal desejado.
|
|
||||||
5. Copie a URL para o campo `slack_webhook_url`.
|
|
||||||
|
|
||||||
### Fluxo Real
|
|
||||||
|
|
||||||
```text
|
|
||||||
Manual Trigger
|
|
||||||
↓
|
|
||||||
Config - GLPI e Slack
|
|
||||||
↓
|
|
||||||
HTTP - GLPI initSession
|
|
||||||
↓
|
|
||||||
HTTP - GLPI buscar tickets
|
|
||||||
↓
|
|
||||||
Code - Filtrar chamados de hoje
|
|
||||||
↓
|
|
||||||
Code - Calcular indicadores
|
|
||||||
↓
|
|
||||||
OpenAI - Gerar resumo
|
|
||||||
↓
|
|
||||||
HTTP - Slack enviar mensagem
|
|
||||||
↓
|
|
||||||
HTTP - GLPI killSession
|
|
||||||
```
|
|
||||||
|
|
||||||
### Observação
|
|
||||||
|
|
||||||
Os campos retornados pelo GLPI podem variar conforme versão e configuração. Se o node `Code - Filtrar chamados de hoje` retornar categoria ou cliente como ID em vez de nome, ajuste o mapeamento depois de ver o retorno real da API.
|
|
||||||
|
|
||||||
### Erro Slack `400 - no_text`
|
|
||||||
|
|
||||||
Se o node `HTTP - Slack enviar mensagem` retornar `400 - no_text`, o corpo da requisição foi enviado sem o campo `text`.
|
|
||||||
|
|
||||||
No node `HTTP - Slack enviar mensagem`, configure:
|
|
||||||
|
|
||||||
- `Method`: `POST`
|
|
||||||
- `Send Body`: ativo
|
|
||||||
- `Body Content Type`: `JSON`
|
|
||||||
- `Specify Body`: `Using JSON`
|
|
||||||
- `JSON Body`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"text": "{{ $json.text }}"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Erro OpenAI `too many requests`
|
|
||||||
|
|
||||||
Se o node `OpenAI - Gerar resumo` retornar `The service is receiving too many requests from you`, a OpenAI respondeu com limite de uso/rate limit.
|
|
||||||
|
|
||||||
Verifique:
|
|
||||||
|
|
||||||
- Se a chave `openai_api_key` está correta
|
|
||||||
- Se a conta OpenAI tem crédito/billing ativo
|
|
||||||
- Se houve muitas execuções seguidas em pouco tempo
|
|
||||||
- Se o modelo `gpt-4o-mini` está disponível para a chave usada
|
|
||||||
|
|
||||||
O workflow importável já está configurado com retry:
|
|
||||||
|
|
||||||
- 3 tentativas
|
|
||||||
- 30 segundos entre tentativas
|
|
||||||
- limite de resposta de 700 tokens
|
|
||||||
|
|
||||||
Se a OpenAI continuar falhando, o workflow continua a execução e envia ao Slack um resumo técnico gerado pelo node `Code - Preparar mensagem Slack`, sem IA.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user