docs: add AI Pipeline technical specification
Create comprehensive spec for Sprint 2 Feature 2: Content: - System prompt with Metodo Sacchi integration (Safety First, Little Often, Double Check) - OpenAI GPT-4o-mini configuration (cost: ~/bin/bash.0002/call) - Complete n8n Code Node JavaScript implementation - JSON output schema with severity mapping - Error handling with circuit breaker pattern - Security guidelines (data sanitization, rate limiting) - 10 acceptance criteria defined - 5 test scenarios with expected input/output - 5 real-world examples (OOM, disk full, connection refused, etc.) - Implementation checklist for developers Key Features: - Safe command generation (destructive commands blacklisted) - Incremental actions (Little Often principle) - Confirmation required for uncertain situations (Double Check) - Fallback when OpenAI unavailable - Response time < 5s target - Cost-optimized with GPT-4o-mini Next: @python-developer implements OpenAI integration in n8n workflow Refs: workflows/logwhisperer_ingest.json (already created)
This commit is contained in:
885
docs/specs/ai_pipeline.md
Normal file
885
docs/specs/ai_pipeline.md
Normal file
@@ -0,0 +1,885 @@
|
|||||||
|
# AI Processing Pipeline - Technical Specification
|
||||||
|
|
||||||
|
**Feature:** Sprint 2 - Feature 2
|
||||||
|
**Status:** Draft
|
||||||
|
**Last Updated:** 2025-04-02
|
||||||
|
**Author:** Tech Lead
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
### 1.1 Scopo
|
||||||
|
L'AI Processing Pipeline è il cuore intelligente di LogWhisperer AI. Trasforma log grezzi di sistema in insight azionabili utilizzando l'API OpenAI GPT-4o-mini.
|
||||||
|
|
||||||
|
### 1.2 Flusso Dati
|
||||||
|
|
||||||
|
```
|
||||||
|
+-------------+ +--------------+ +-------------+ +--------------+
|
||||||
|
| Script |---->| n8n |---->| OpenAI |---->| Notifica |
|
||||||
|
| Bash | | Webhook | | GPT-4o | | Telegram/ |
|
||||||
|
| Ingestion | | Workflow | | -mini | | Slack |
|
||||||
|
+-------------+ +--------------+ +-------------+ +--------------+
|
||||||
|
| | | |
|
||||||
|
| +----------+----------+ | |
|
||||||
|
| | Code Node (JS) |<--------+ |
|
||||||
|
| | - System Prompt | |
|
||||||
|
| | - Error Handling | |
|
||||||
|
| | - Response Parser | |
|
||||||
|
| +----------+----------+ |
|
||||||
|
| | |
|
||||||
|
+--------------------+-----------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Vincoli di Sistema
|
||||||
|
|
||||||
|
| Vincolo | Valore | Rationale |
|
||||||
|
|---------|--------|-----------|
|
||||||
|
| **Tempo risposta** | < 5s (95th percentile) | UX: notifica deve arrivare subito |
|
||||||
|
| **Costo/chiamata** | < $0.01 | Budget-friendly con GPT-4o-mini |
|
||||||
|
| **Sicurezza** | Safety-first | Metodo Sacchi: no comandi distruttivi |
|
||||||
|
| **Lingua** | Italiano | Target audience italiano |
|
||||||
|
| **Formato** | JSON strutturato | Parsabile automaticamente |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. System Prompt Design (Metodo Sacchi)
|
||||||
|
|
||||||
|
### 2.1 Principi del Metodo Sacchi
|
||||||
|
|
||||||
|
Il prompt di sistema incorpora esplicitamente i tre pilastri del Metodo Sacchi:
|
||||||
|
|
||||||
|
- **SAFETY FIRST** → Non suggerire mai comandi distruttivi
|
||||||
|
- **LITTLE OFTEN** → Azioni incrementali, verificabili
|
||||||
|
- **DOUBLE CHECK** → Conferma richiesta quando incerti
|
||||||
|
|
||||||
|
### 2.2 System Prompt Completo
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const SYSTEM_PROMPT = `Sei LogWhisperer AI, un assistente DevOps esperto specializzato nell'analisi di log di sistema.
|
||||||
|
|
||||||
|
## MISSIONE
|
||||||
|
Analizza i log ricevuti e fornisci insight azionabili in italiano, semplice e chiaro.
|
||||||
|
|
||||||
|
## PRINCIPI OBBLIGATORI (Metodo Sacchi)
|
||||||
|
|
||||||
|
### 1. SAFETY FIRST - Sicurezza Prima di Tutto
|
||||||
|
MAI suggerire comandi che possano:
|
||||||
|
- Cancellare dati (rm, del, truncate senza backup)
|
||||||
|
- Modificare configurazioni critiche senza verifica
|
||||||
|
- Riavviare servizi in produzione senza warning
|
||||||
|
- Eseguire operazioni irreversibili
|
||||||
|
|
||||||
|
REGOLE DI SICUREZZA:
|
||||||
|
- Preferisci SEMPRE comandi read-only (cat, grep, df, ps, etc.)
|
||||||
|
- Se un comando potrebbe essere distruttivo, imposta "sicuro": false
|
||||||
|
- Per operazioni rischiose, richiedi sempre conferma umana
|
||||||
|
- Non assumere mai che l'utente sappia cosa sta facendo
|
||||||
|
|
||||||
|
### 2. LITTLE OFTEN - Piccoli Passi Verificabili
|
||||||
|
- Suggerisci UN solo comando alla volta
|
||||||
|
- Ogni azione deve essere verificabile prima della prossima
|
||||||
|
- Dividi problemi complessi in step incrementali
|
||||||
|
- Preferisci diagnostica prima della mitigazione
|
||||||
|
|
||||||
|
### 3. DOUBLE CHECK - Verifica le Ipotesi
|
||||||
|
- Non assumere MAI il contesto senza verificarlo
|
||||||
|
- Se mancano informazioni, chiedi chiarimenti (richiede_conferma: true)
|
||||||
|
- Verifica sempre i presupposti prima di suggerire azioni
|
||||||
|
- Se l'errore e' ambiguo, richiedi analisi manuale
|
||||||
|
|
||||||
|
## FORMATO OUTPUT OBBLIGATORIO
|
||||||
|
|
||||||
|
Rispondi SEMPRE in formato JSON valido con questa struttura:
|
||||||
|
|
||||||
|
{
|
||||||
|
"sintesi": "Descrizione breve e chiara del problema in italiano",
|
||||||
|
"severita": "low|medium|critical",
|
||||||
|
"comando": "Comando bash esatto per diagnostica/mitigazione (o null)",
|
||||||
|
"sicuro": true|false,
|
||||||
|
"note": "Istruzioni aggiuntive, link documentazione, o 'Nessuna nota aggiuntiva'",
|
||||||
|
"richiede_conferma": true|false
|
||||||
|
}
|
||||||
|
|
||||||
|
## REGOLE JSON
|
||||||
|
|
||||||
|
1. "sintesi": Massimo 150 caratteri, linguaggio semplice
|
||||||
|
2. "severita":
|
||||||
|
- "critical": Servizio down, rischio data loss, security breach
|
||||||
|
- "medium": Degradazione performance, warning spazio disco
|
||||||
|
- "low": Messaggi informativi, warning minori
|
||||||
|
3. "comando":
|
||||||
|
- Deve essere copiabile e incollabile in terminale
|
||||||
|
- Se incerto o rischioso, usa null
|
||||||
|
- Preferisci diagnostica (ls, grep, df) a modifica (rm, kill)
|
||||||
|
4. "sicuro":
|
||||||
|
- true solo se comando e' read-only o sicuro al 100%
|
||||||
|
- false se potenzialmente distruttivo
|
||||||
|
5. "note":
|
||||||
|
- Spiega il perche' del comando
|
||||||
|
- Aggiungi link a documentazione rilevante
|
||||||
|
- Suggerisci verifiche aggiuntive
|
||||||
|
6. "richiede_conferma":
|
||||||
|
- true se serve conferma umana prima di eseguire
|
||||||
|
- true se il problema e' ambiguo o poco chiaro
|
||||||
|
|
||||||
|
## ESEMPI
|
||||||
|
|
||||||
|
Log: "OutOfMemoryError: Java heap space"
|
||||||
|
{
|
||||||
|
"sintesi": "Applicazione Java ha esaurito la memoria heap",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "ps aux | grep java | head -5 && free -h",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica processi Java attivi e memoria disponibile. Se necessario, aumentare heap size in JVM options.",
|
||||||
|
"richiede_conferma": false
|
||||||
|
}
|
||||||
|
|
||||||
|
Log: "Connection refused to database on port 5432"
|
||||||
|
{
|
||||||
|
"sintesi": "Impossibile connettersi al database PostgreSQL",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "sudo systemctl status postgresql && netstat -tlnp | grep 5432",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica stato del servizio PostgreSQL. Se stopped: 'sudo systemctl start postgresql'",
|
||||||
|
"richiede_conferma": true
|
||||||
|
}
|
||||||
|
|
||||||
|
Log: "Disk space warning: /var at 85%"
|
||||||
|
{
|
||||||
|
"sintesi": "Spazio su disco in esaurimento (85% utilizzato)",
|
||||||
|
"severita": "medium",
|
||||||
|
"comando": "df -h /var && du -sh /var/log/* | sort -hr | head -10",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Identifica file di log piu' grandi. Per pulizia sicura: usa find per eliminare log vecchi.",
|
||||||
|
"richiede_conferma": false
|
||||||
|
}
|
||||||
|
|
||||||
|
## COMANDI PROIBITI (NON MAI SUGGERIRE)
|
||||||
|
- rm -rf / o varianti
|
||||||
|
- dd if=/dev/zero (sovrascrittura dischi)
|
||||||
|
- mkfs, fdisk (formattazione)
|
||||||
|
- kill -9 senza verifica processo
|
||||||
|
- chmod 777 ricorsivo
|
||||||
|
- Qualsiasi comando con pipe a sh/bash senza verifica
|
||||||
|
|
||||||
|
## COMANDI PREFERITI (READ-ONLY)
|
||||||
|
- Diagnostica: ps, top, htop, df, du, free, iostat, netstat, ss
|
||||||
|
- Log: tail, head, grep, cat, less, journalctl
|
||||||
|
- Rete: ping, curl, netstat, ss, lsof
|
||||||
|
- Systemd: systemctl status, journalctl -u
|
||||||
|
|
||||||
|
RICORDA: L'utente potrebbe essere non-tecnico. Spiega in italiano semplice.`;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. OpenAI Integration
|
||||||
|
|
||||||
|
### 3.1 Configurazione API
|
||||||
|
|
||||||
|
| Parametro | Valore | Descrizione |
|
||||||
|
|-----------|--------|-------------|
|
||||||
|
| **Modello** | `gpt-4o-mini` | Ottimo bilancio costo/qualita' |
|
||||||
|
| **Max Tokens** | 500 | Sufficente per JSON strutturato |
|
||||||
|
| **Temperature** | 0.3 | Deterministico, meno creativo |
|
||||||
|
| **Timeout** | 10 secondi | Buffer sopra i 5s target |
|
||||||
|
| **Response Format** | `json_object` | Forza output JSON valido |
|
||||||
|
|
||||||
|
### 3.2 Payload API
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"model": "gpt-4o-mini",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "<SYSTEM_PROMPT>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "{\"timestamp\":\"2025-04-02T10:30:00Z\",\"severity\":\"ERROR\",\"source\":\"postgresql\",\"raw_log\":\"...\"}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"temperature": 0.3,
|
||||||
|
"max_tokens": 500,
|
||||||
|
"response_format": { "type": "json_object" },
|
||||||
|
"store": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Cost Management
|
||||||
|
|
||||||
|
#### Stima Costi
|
||||||
|
|
||||||
|
| Metrica | Valore | Costo stimato |
|
||||||
|
|---------|--------|---------------|
|
||||||
|
| Input tokens (avg) | ~500 | $0.000075 |
|
||||||
|
| Output tokens (avg) | ~200 | $0.00012 |
|
||||||
|
| **Totale/chiamata** | ~700 | **~$0.0002** |
|
||||||
|
| Chiamate/mese (stimato) | 1000 | **~$0.20/mese** |
|
||||||
|
|
||||||
|
#### Strategie di Contenimento
|
||||||
|
|
||||||
|
1. **Max Tokens Limit**: 500 tokens max per risposta
|
||||||
|
2. **Truncation Input**: Max 2000 caratteri per raw_log
|
||||||
|
3. **Rate Limiting**: Max 10 richieste/minuto per client
|
||||||
|
4. **Circuit Breaker**: Stop se > 50 errori/ora
|
||||||
|
5. **Fallback**: Notifica senza AI se costo eccessivo
|
||||||
|
|
||||||
|
#### Fallback Scenarios
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Se OpenAI non disponibile o costo eccessivo
|
||||||
|
const fallbackResponse = {
|
||||||
|
sintesi: "Errore rilevato (analisi AI non disponibile)",
|
||||||
|
severita: "medium",
|
||||||
|
comando: null,
|
||||||
|
sicuro: true,
|
||||||
|
note: "Controlla manualmente il log. OpenAI API non disponibile.",
|
||||||
|
richiede_conferma: true,
|
||||||
|
_fallback: true
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. n8n Integration
|
||||||
|
|
||||||
|
### 4.1 Workflow Node: Call OpenAI
|
||||||
|
|
||||||
|
Il seguente codice JavaScript deve essere inserito in un nodo **Code** nel workflow n8n:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ============================================================================
|
||||||
|
// n8n Code Node: OpenAI Processor
|
||||||
|
// ============================================================================
|
||||||
|
// Input: JSON dal nodo precedente (log data)
|
||||||
|
// Output: Oggetto con analisi AI o fallback
|
||||||
|
|
||||||
|
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
||||||
|
const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
|
||||||
|
|
||||||
|
// System Prompt (incollare qui il SYSTEM_PROMPT completo)
|
||||||
|
const SYSTEM_PROMPT = `Sei LogWhisperer AI...`;
|
||||||
|
|
||||||
|
// Input dal nodo precedente
|
||||||
|
const inputData = $input.first().json;
|
||||||
|
|
||||||
|
// Trunca raw_log se troppo lungo
|
||||||
|
const maxLogLength = 2000;
|
||||||
|
const truncatedLog = inputData.raw_log
|
||||||
|
? inputData.raw_log.substring(0, maxLogLength)
|
||||||
|
: 'Nessun log fornito';
|
||||||
|
|
||||||
|
// Prepara payload per OpenAI
|
||||||
|
const payload = {
|
||||||
|
model: "gpt-4o-mini",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "system",
|
||||||
|
content: SYSTEM_PROMPT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: JSON.stringify({
|
||||||
|
timestamp: inputData.timestamp,
|
||||||
|
severity: inputData.severity,
|
||||||
|
source: inputData.source,
|
||||||
|
hostname: inputData.hostname,
|
||||||
|
client_id: inputData.client_id,
|
||||||
|
raw_log: truncatedLog
|
||||||
|
})
|
||||||
|
}
|
||||||
|
],
|
||||||
|
temperature: 0.3,
|
||||||
|
max_tokens: 500,
|
||||||
|
response_format: { type: "json_object" },
|
||||||
|
store: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Timeout configurazione
|
||||||
|
const TIMEOUT_MS = 10000;
|
||||||
|
|
||||||
|
async function callOpenAI() {
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
||||||
|
|
||||||
|
const response = await fetch(OPENAI_API_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer ' + OPENAI_API_KEY
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('OpenAI API error: ' + response.status + ' ' + response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const aiResponse = JSON.parse(data.choices[0].message.content);
|
||||||
|
|
||||||
|
// Merge con dati originali
|
||||||
|
return {
|
||||||
|
...inputData,
|
||||||
|
ai_analysis: aiResponse,
|
||||||
|
ai_status: 'success',
|
||||||
|
ai_timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('OpenAI Error:', error.message);
|
||||||
|
|
||||||
|
// Fallback response
|
||||||
|
return {
|
||||||
|
...inputData,
|
||||||
|
ai_analysis: {
|
||||||
|
sintesi: "Errore durante analisi AI",
|
||||||
|
severita: inputData.severity || "medium",
|
||||||
|
comando: null,
|
||||||
|
sicuro: true,
|
||||||
|
note: 'Fallback: ' + error.message + '. Controlla manualmente il log.',
|
||||||
|
richiede_conferma: true
|
||||||
|
},
|
||||||
|
ai_status: 'fallback',
|
||||||
|
ai_error: error.message,
|
||||||
|
ai_timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui chiamata
|
||||||
|
return await callOpenAI();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Configurazione Environment Variables
|
||||||
|
|
||||||
|
Aggiungere in n8n (Settings -> Environment):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
OPENAI_TIMEOUT=10000
|
||||||
|
OPENAI_MAX_TOKENS=500
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Output Format
|
||||||
|
|
||||||
|
### 5.1 Schema JSON Atteso
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "PostgreSQL ha esaurito lo spazio disco",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "df -h /var/lib/postgresql && du -sh /var/log/postgresql/* | sort -hr | head -5",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica spazio disponibile. Se < 10%, pulisci log vecchi.",
|
||||||
|
"richiede_conferma": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Mapping Severita'
|
||||||
|
|
||||||
|
| Severita' | Criteri | Colore | Azione Suggerita |
|
||||||
|
|----------|---------|--------|------------------|
|
||||||
|
| **critical** | Servizio down, data loss risk, security breach, OOM | Rosso | Intervento immediato |
|
||||||
|
| **medium** | Performance degradation, disk warnings, timeout | Giallo | Monitorare e pianificare fix |
|
||||||
|
| **low** | Info messages, minor warnings, debug logs | Verde | Logging solo, nessuna azione |
|
||||||
|
|
||||||
|
### 5.3 Validazione Schema
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Validazione JSON in n8n
|
||||||
|
function validateAIResponse(response) {
|
||||||
|
const required = ['sintesi', 'severita', 'comando', 'sicuro', 'note', 'richiede_conferma'];
|
||||||
|
const validSeverities = ['low', 'medium', 'critical'];
|
||||||
|
|
||||||
|
for (const field of required) {
|
||||||
|
if (!(field in response)) {
|
||||||
|
throw new Error('Campo mancante: ' + field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validSeverities.includes(response.severita)) {
|
||||||
|
throw new Error('Severita non valida: ' + response.severita);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof response.sicuro !== 'boolean') {
|
||||||
|
throw new Error('Campo sicuro deve essere boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof response.richiede_conferma !== 'boolean') {
|
||||||
|
throw new Error('Campo richiede_conferma deve essere boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Error Handling
|
||||||
|
|
||||||
|
### 6.1 Scenari di Errore e Gestione
|
||||||
|
|
||||||
|
| Scenario | Causa | Gestione | Notifica Utente |
|
||||||
|
|----------|-------|----------|-----------------|
|
||||||
|
| **API Timeout** | OpenAI lento/non raggiungibile | Fallback a notifica base | "Analisi AI non disponibile" |
|
||||||
|
| **JSON Parse Error** | Risposta malformata | Fallback con error info | "Errore parsing risposta AI" |
|
||||||
|
| **Rate Limiting** | Troppe richieste | Queue + retry | "Analisi in coda" |
|
||||||
|
| **Invalid API Key** | Credenziali errate | Alert admin, skip AI | "Configurazione AI errata" |
|
||||||
|
| **Cost Threshold** | Budget superato | Disable AI, log only | "AI temporaneamente disabilitata" |
|
||||||
|
| **Unsafe Command Detected** | AI suggerisce comando pericoloso | Override sicuro, alert | "Comando non sicuro bloccato" |
|
||||||
|
|
||||||
|
### 6.2 Circuit Breaker Pattern
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Stato circuit breaker (persistere in n8n static data)
|
||||||
|
let circuitState = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
|
||||||
|
let failureCount = 0;
|
||||||
|
const FAILURE_THRESHOLD = 50;
|
||||||
|
const TIMEOUT_MS = 60000; // 1 minuto
|
||||||
|
let lastFailureTime = 0;
|
||||||
|
|
||||||
|
function circuitBreaker(call) {
|
||||||
|
if (circuitState === 'OPEN') {
|
||||||
|
if (Date.now() - lastFailureTime > TIMEOUT_MS) {
|
||||||
|
circuitState = 'HALF_OPEN';
|
||||||
|
} else {
|
||||||
|
throw new Error('Circuit breaker OPEN');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = call();
|
||||||
|
if (circuitState === 'HALF_OPEN') {
|
||||||
|
circuitState = 'CLOSED';
|
||||||
|
failureCount = 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
failureCount++;
|
||||||
|
if (failureCount >= FAILURE_THRESHOLD) {
|
||||||
|
circuitState = 'OPEN';
|
||||||
|
lastFailureTime = Date.now();
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Logging Errori
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Log strutturato per monitoring
|
||||||
|
const errorLog = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
level: 'ERROR',
|
||||||
|
component: 'ai_pipeline',
|
||||||
|
error_type: error.name,
|
||||||
|
error_message: error.message,
|
||||||
|
input_hash: hashInput(inputData),
|
||||||
|
retry_count: retryCount,
|
||||||
|
fallback_used: true
|
||||||
|
};
|
||||||
|
|
||||||
|
console.error(JSON.stringify(errorLog));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Security & Privacy
|
||||||
|
|
||||||
|
### 7.1 Data Protection
|
||||||
|
|
||||||
|
#### Filtro Dati Sensibili (Pre-Invio)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Pattern da rimuovere prima di inviare a OpenAI
|
||||||
|
const SENSITIVE_PATTERNS = [
|
||||||
|
/password[=:]\s*\S+/gi,
|
||||||
|
/passwd[=:]\s*\S+/gi,
|
||||||
|
/secret[=:]\s*\S+/gi,
|
||||||
|
/token[=:]\s*\S+/gi,
|
||||||
|
/key[=:]\s*\S+/gi,
|
||||||
|
/api[_-]?key[=:]\s*\S+/gi,
|
||||||
|
/-----BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY-----/gi,
|
||||||
|
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
||||||
|
/\b(?:\d{1,3}\.){3}\d{1,3}\b/g
|
||||||
|
];
|
||||||
|
|
||||||
|
function sanitizeLog(log) {
|
||||||
|
let sanitized = log;
|
||||||
|
SENSITIVE_PATTERNS.forEach(pattern => {
|
||||||
|
sanitized = sanitized.replace(pattern, '[REDACTED]');
|
||||||
|
});
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Flag API OpenAI
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"store": false,
|
||||||
|
"metadata": {
|
||||||
|
"client_id": "masked",
|
||||||
|
"environment": "production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Rate Limiting
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Rate limiting per client_id
|
||||||
|
const rateLimits = new Map();
|
||||||
|
|
||||||
|
function checkRateLimit(clientId) {
|
||||||
|
const now = Date.now();
|
||||||
|
const windowMs = 60000; // 1 minuto
|
||||||
|
const maxRequests = 10;
|
||||||
|
|
||||||
|
const clientData = rateLimits.get(clientId) || { count: 0, resetTime: now + windowMs };
|
||||||
|
|
||||||
|
if (now > clientData.resetTime) {
|
||||||
|
clientData.count = 0;
|
||||||
|
clientData.resetTime = now + windowMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientData.count >= maxRequests) {
|
||||||
|
throw new Error('Rate limit exceeded for client ' + clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientData.count++;
|
||||||
|
rateLimits.set(clientId, clientData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 Command Safety Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Lista comandi proibiti
|
||||||
|
const FORBIDDEN_PATTERNS = [
|
||||||
|
/rm\s+-rf\s+\//,
|
||||||
|
/>\s*\/dev\/sda/,
|
||||||
|
/mkfs/,
|
||||||
|
/dd\s+if=\/dev\/zero/,
|
||||||
|
/chmod\s+-R\s+777/,
|
||||||
|
/:\(\)\{\s*:\|:\s*&\s*\};\s*:/
|
||||||
|
];
|
||||||
|
|
||||||
|
function validateCommandSafety(command) {
|
||||||
|
if (!command) return { safe: true };
|
||||||
|
|
||||||
|
for (const pattern of FORBIDDEN_PATTERNS) {
|
||||||
|
if (pattern.test(command)) {
|
||||||
|
return {
|
||||||
|
safe: false,
|
||||||
|
reason: 'Comando potenzialmente distruttivo rilevato',
|
||||||
|
blocked_pattern: pattern.toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { safe: true };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] **System Prompt**: Include esplicitamente i 3 principi del Metodo Sacchi
|
||||||
|
- [ ] **Formato JSON**: Risposta sempre valida e parsabile
|
||||||
|
- [ ] **Performance**: 95% delle risposte < 5 secondi
|
||||||
|
- [ ] **Costo**: Media < $0.01 per chiamata (GPT-4o-mini)
|
||||||
|
- [ ] **Fallback**: Funziona correttamente se OpenAI non disponibile
|
||||||
|
- [ ] **Sicurezza**: Nessun comando pericoloso suggerito (test con comandi maliziosi)
|
||||||
|
- [ ] **Lingua**: Output sempre in italiano comprensibile
|
||||||
|
- [ ] **Rate Limiting**: Max 10 richieste/minuto per client
|
||||||
|
- [ ] **Privacy**: Nessun dato sensibile inviato a OpenAI
|
||||||
|
- [ ] **Timeout**: Gestione corretta timeout API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Testing Strategy
|
||||||
|
|
||||||
|
### 9.1 Test Cases
|
||||||
|
|
||||||
|
#### TC-001: OOM Error
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"raw_log": "java.lang.OutOfMemoryError: Java heap space at com.myapp.Service.processData(Service.java:42)",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "myapp"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Expected Output:**
|
||||||
|
- `sintesi` contiene "memoria" o "heap"
|
||||||
|
- `severita` = "critical"
|
||||||
|
- `comando` include `ps` o `free` (read-only)
|
||||||
|
- `sicuro` = true
|
||||||
|
|
||||||
|
#### TC-002: Disk Full
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"raw_log": "No space left on device: /var/log/myapp/error.log",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "system"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Expected Output:**
|
||||||
|
- `sintesi` contiene "spazio" o "disco"
|
||||||
|
- `severita` = "critical" o "medium"
|
||||||
|
- `comando` include `df` o `du` (read-only)
|
||||||
|
- `note` suggerisce pulizia sicura
|
||||||
|
|
||||||
|
#### TC-003: Connection Refused
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"raw_log": "Connection refused: connect to localhost:5432 failed",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "app"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Expected Output:**
|
||||||
|
- `sintesi` contiene "connessione" o "database"
|
||||||
|
- `comando` include `systemctl status` o `netstat`
|
||||||
|
- `richiede_conferma` = true
|
||||||
|
|
||||||
|
#### TC-004: Unknown/Malicious Log
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"raw_log": "rm -rf / --no-preserve-root executed successfully",
|
||||||
|
"severity": "CRITICAL",
|
||||||
|
"source": "hacker"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Expected Output:**
|
||||||
|
- `sicuro` = false (o `comando` = null)
|
||||||
|
- `richiede_conferma` = true
|
||||||
|
- Nessun comando distruttivo suggerito
|
||||||
|
|
||||||
|
#### TC-005: API Timeout
|
||||||
|
**Test:** Simulare timeout OpenAI
|
||||||
|
**Expected:** Fallback con `ai_status` = "fallback"
|
||||||
|
|
||||||
|
### 9.2 Test Automation
|
||||||
|
|
||||||
|
```python
|
||||||
|
# pytest per test AI responses
|
||||||
|
import pytest
|
||||||
|
import json
|
||||||
|
|
||||||
|
class TestAIPipeline:
|
||||||
|
|
||||||
|
def test_oom_response(self, openai_client):
|
||||||
|
log = "OutOfMemoryError: Java heap space"
|
||||||
|
response = openai_client.analyze(log)
|
||||||
|
|
||||||
|
assert response["severita"] == "critical"
|
||||||
|
assert "memoria" in response["sintesi"].lower()
|
||||||
|
assert response["sicuro"] == True
|
||||||
|
assert "ps" in response["comando"] or "free" in response["comando"]
|
||||||
|
|
||||||
|
def test_no_destructive_commands(self, openai_client):
|
||||||
|
log = "Permission denied on /etc/shadow"
|
||||||
|
response = openai_client.analyze(log)
|
||||||
|
|
||||||
|
dangerous = ["rm -rf", "mkfs", "dd if=/dev/zero"]
|
||||||
|
for cmd in dangerous:
|
||||||
|
assert cmd not in response.get("comando", "")
|
||||||
|
|
||||||
|
def test_json_validity(self, openai_client):
|
||||||
|
log = "Random error message"
|
||||||
|
response = openai_client.analyze(log)
|
||||||
|
|
||||||
|
required = ["sintesi", "severita", "comando", "sicuro", "note", "richiede_conferma"]
|
||||||
|
for field in required:
|
||||||
|
assert field in response
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Example Scenarios
|
||||||
|
|
||||||
|
### Scenario 1: PostgreSQL Out of Memory
|
||||||
|
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-04-02T10:30:00Z",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "postgresql",
|
||||||
|
"hostname": "db-server-01",
|
||||||
|
"client_id": "client_001",
|
||||||
|
"raw_log": "2025-04-02 10:29:58.123 UTC [12345] FATAL: out of memory DETAIL: Failed on request of size 8192."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected AI Output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "PostgreSQL ha esaurito la memoria disponibile",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "free -h && ps aux --sort=-%mem | head -10",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica RAM disponibile e processi che consumano piu' memoria. Considerare restart PostgreSQL o aumento RAM.",
|
||||||
|
"richiede_conferma": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 2: Disk Space Warning
|
||||||
|
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-04-02T11:15:00Z",
|
||||||
|
"severity": "WARNING",
|
||||||
|
"source": "system",
|
||||||
|
"hostname": "web-server-02",
|
||||||
|
"client_id": "client_001",
|
||||||
|
"raw_log": "WARNING: /var filesystem is 92% full (45GB/50GB used)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected AI Output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "Spazio disco quasi esaurito (92% utilizzato)",
|
||||||
|
"severita": "medium",
|
||||||
|
"comando": "df -h /var && du -sh /var/log/* 2>/dev/null | sort -hr | head -10",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Trova i file piu' grandi. Per pulire log vecchi in sicurezza usa find con mtime.",
|
||||||
|
"richiede_conferma": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 3: Application Connection Timeout
|
||||||
|
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-04-02T09:45:00Z",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "myapp",
|
||||||
|
"hostname": "app-server-01",
|
||||||
|
"client_id": "client_002",
|
||||||
|
"raw_log": "ConnectionTimeoutException: Unable to connect to Redis at redis.internal:6379 after 5000ms"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected AI Output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "Timeout connessione a Redis (cache non raggiungibile)",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "ping -c 3 redis.internal && telnet redis.internal 6379",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica raggiungibilita' rete e stato servizio Redis.",
|
||||||
|
"richiede_conferma": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 4: SSL Certificate Expired
|
||||||
|
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-04-02T08:00:00Z",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "nginx",
|
||||||
|
"hostname": "web-server-01",
|
||||||
|
"client_id": "client_001",
|
||||||
|
"raw_log": "SSL: error:14094415:SSL routines:ssl3_read_bytes:sslv3 alert certificate expired"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected AI Output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "Certificato SSL scaduto su server web",
|
||||||
|
"severita": "critical",
|
||||||
|
"comando": "echo | openssl s_client -servername web-server-01 -connect web-server-01:443 2>/dev/null | openssl x509 -noout -dates",
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Verifica data scadenza certificato. Rinnovare con certbot o contattare provider SSL.",
|
||||||
|
"richiede_conferma": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 5: Unknown Error Pattern
|
||||||
|
|
||||||
|
**Input:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-04-02T12:00:00Z",
|
||||||
|
"severity": "ERROR",
|
||||||
|
"source": "custom_app",
|
||||||
|
"hostname": "unknown-server",
|
||||||
|
"client_id": "client_003",
|
||||||
|
"raw_log": "XYZ-9999: Unpredictable anomaly detected in module gamma sector 7"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected AI Output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sintesi": "Errore non riconosciuto nell'applicazione custom",
|
||||||
|
"severita": "medium",
|
||||||
|
"comando": null,
|
||||||
|
"sicuro": true,
|
||||||
|
"note": "Pattern errore sconosciuto. Verificare documentazione applicazione custom_app.",
|
||||||
|
"richiede_conferma": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Implementation Checklist
|
||||||
|
|
||||||
|
### Pre-Implementazione
|
||||||
|
- [ ] Verificare API key OpenAI configurata in n8n
|
||||||
|
- [ ] Confermare quota API sufficiente
|
||||||
|
- [ ] Review system prompt con team sicurezza
|
||||||
|
|
||||||
|
### Implementazione
|
||||||
|
- [ ] Creare nodo Code in n8n workflow
|
||||||
|
- [ ] Implementare system prompt completo
|
||||||
|
- [ ] Aggiungere error handling e fallback
|
||||||
|
- [ ] Configurare rate limiting
|
||||||
|
- [ ] Implementare data sanitization
|
||||||
|
|
||||||
|
### Post-Implementazione
|
||||||
|
- [ ] Testare con 5 scenari di esempio
|
||||||
|
- [ ] Verificare performance (< 5s)
|
||||||
|
- [ ] Controllare costo per chiamata
|
||||||
|
- [ ] Validare sicurezza comandi
|
||||||
|
- [ ] Documentare per operations team
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. References
|
||||||
|
|
||||||
|
- [OpenAI API Documentation](https://platform.openai.com/docs/)
|
||||||
|
- [n8n Code Node](https://docs.n8n.io/code-examples/)
|
||||||
|
- [GPT-4o-mini Pricing](https://openai.com/pricing)
|
||||||
|
- [Metodo Sacchi](https://lucasacchi.net/metodo-sacchi) - Principi di sicurezza
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Nota per Developers:**
|
||||||
|
Questa specifica segue il Metodo Sacchi: **Safety First, Little Often, Double Check**.
|
||||||
|
Prima di implementare, assicurarsi di aver compreso e testato tutti gli scenari di errore.
|
||||||
Reference in New Issue
Block a user