Come ho costruito il mio assistente AI locale per lo sviluppo di plugin WordPress (guida pratica)

20 minuti di lettura

📋 Indice

  1. Introduzione
  2. Perché un’assistente AI locale?
  3. L’architettura del sistema
  4. Step 1: Installazione e configurazione di LM Studio
  5. Step 2: Integrazione con VS Code (Continue)
  6. Step 3: Il mio form PHP personalizzato con Laragon
  7. Step 4: Specializzazione WordPress
  8. Step 5: Test e risultati
  9. Considerazioni sui costi e privacy
  10. Conclusioni

1️⃣ Introduzione

Come sviluppatore WordPress, mi sono sempre trovato di fronte a un dilemma: da un lato gli assistenti AI come ChatGPT sono incredibilmente utili, dall’altro non mi sentivo a mio agio a condividere codice proprietario di plugin e clienti su server di terze parti.

La soluzione? Costruire un assistente AI completamente locale, che girasse sul mio PC, specializzato in WordPress e perfettamente integrato nel mio flusso di lavoro.

In questo post ti mostro passo-passo come ho fatto, con tanto di screenshot e codice che puoi copiare e adattare.

*Figura 1: Il risultato finale – tre modalità di interazione con la stessa AI locale*

Il risultato finale - tre modalità di interazione con lo stesso assistente AI
Il form in localhost

2️⃣Perché un’assistente AI locale? 

Prima di entrare nel tecnico, vale la pena chiedersi: perché tutto questo sforzo quando esistono decine di servizi cloud?

CaratteristicaCloud (ChatGPT, Claude)Locale (LM Studio)
Privacy❌ I tuoi dati vanno ai server✅ Tutto rimane sul tuo PC
Costo💰 Canone mensile o a consumo✅ Una tantum (hardware)
Offline❌ Necessaria connessione✅ Funziona ovunque
PersonalizzazioneLimitata✅ Totale (system prompt)
Vendor lock-in❌ Dipendi da terzi✅ Sei tu il padrone

*Figura 2: Architettura cloud vs architettura locale – i dati non lasciano mai il tuo computer

Locale vs cloud

3️⃣ L’architettura del sistema

Il sistema che ho realizzato è composto da 4 elementi chiave che comunicano tra loro:

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   VS CODE (Continue)          FORM PERSONALIZZATA           │
│   (Assistente nell'editor)     (Interfaccia web PHP)        │
│          │                              │                   │
│          └──────────┬───────────────┘   │                   │
│                     ↓                    │                  │
│           LM STUDIO (DeepSeek)            │                 │
│           (Il "cervello" sulla 1234)      │                 │
│                     ↑                      │                │
│                    │                      │                 │
│              LARAGON (PHP Server) ─────────┘                │
│              (Solo per la form web)                         | 
└─────────────────────────────────────────────────────────────┘

Il bello è che il modello è uno solo (DeepSeek-Coder-V2-Lite-Instruct), ma può essere chiamato da diverse interfacce a seconda delle esigenze del momento.

4️⃣ Step 1: Installazione e configurazione di LM Studio

4.2 Scelta del modello

Ho scelto DeepSeek-Coder-V2-Lite-Instruct per diversi motivi:

  • Specializzato in codice (perfetto per plugin WordPress)
  • Leggero abbastanza per girare su PC normale (~4-5GB di RAM)
  • Supporto contesto 4K token (sufficiente per la maggior parte delle richieste)
  • Gratuito e open source

4.3 Avvio del server

La parte fondamentale: una volta caricato il modello, ho attivato il server locale dalla scheda Developer:

L’endpoint da ricordare è: http://localhost:1234/v1/chat/completions (API compatibile OpenAI).

4.4 Test rapido dal browser

Per verificare che tutto funzioni, ho aperto:

http://localhost:1234/api/v1/models

5️⃣ Step 2: Integrazione con VS Code (Continue)

5.1 Installazione dell’estensione

In VS Code ho cercato e installato l’estensione Continue.

*Figura 7: L’estensione Continue – il ponte tra VS Code e LM Studio*

L'estensione continue in Visual Studio Code

5.2 Configurazione del file config.yaml

La magia avviene nel file di configurazione. Ecco il mio config.yaml:

name: Local Config
version: 1.0.0
schema: v1

models:
  - name: DeepSeek WP Expert
    provider: lmstudio
    model: deepseek-coder-v2-lite-instruct
    apiBase: http://localhost:1234/v1
    systemMessage: |
      Sei un esperto sviluppatore WordPress con 10+ anni di esperienza. 
      Quando scrivi codice PHP per WordPress:
      - Usa sempre if (!defined('ABSPATH')) come prima riga
      - Implementa nonce per form security
      - Segui le WordPress Coding Standards
      - Preferisci funzioni WordPress a PHP nudo
      - Usa escaping appropriato (esc_html, esc_attr)

Figura 8: La configurazione che specializza DeepSeek per WordPress

La configurazione che specializza DeepSeek per WordPress

5.3 Prime interazioni

Una volta configurato, ho selezionato il modello “DeepSeek WP Expert” dal menu a tendina di Continue e ho iniziato a testarlo.

Il test chiave: ho chiesto “Crea un plugin WordPress base” e ho subito visto il codice iniziare con if ( ! defined( 'ABSPATH' ) )Segno che il system prompt funziona! 👍

6️⃣ Step 3: Il mio form PHP personalizzato con Laragon

Questa è la parte che preferisco: un’interfaccia web costruita su misura per le mie esigenze, ospitata localmente con Laragon.

6.1 Perché Laragon?

Uso Laragon per lo sviluppo WordPress locale, quindi ho pensato: “Perché non usarlo anche per questo?”. L’idea è semplice:

  • Laragon serve pagine PHP sulla porta 80 (http://localhost)
  • Il mio script PHP chiama LM Studio sulla porta 1234
  • Nessun problema CORS (le chiamate sono server-to-server)

6.2 Lo script completo

Ecco il codice che ho inserito in C:\laragon\www\lm-chat\chat-ai.php:

<?php
// chat-ai.php - Il mio client personalizzato per LM Studio

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit();
}

// Configurazione
$lmStudioUrl = 'http://127.0.0.1:1234/v1/chat/completions';
$modelName = 'deepseek-coder-v2-lite-instruct';

// System prompt per WordPress
$wordpressPrompt = "Sei un esperto sviluppatore WordPress...";

// System prompt per PHP puro
$phpPrompt = "Sei un esperto sviluppatore PHP (puro, senza framework)...";

function callLMStudio($message, $mode) {
    global $lmStudioUrl, $modelName, $wordpressPrompt, $phpPrompt;
    
    $systemPrompt = ($mode === 'wordpress') ? $wordpressPrompt : $phpPrompt;
    
    $data = [
        'model' => $modelName,
        'messages' => [
            ['role' => 'system', 'content' => $systemPrompt],
            ['role' => 'user', 'content' => $message]
        ],
        'temperature' => 0.5,
        'max_tokens' => 2000
    ];
    
    $ch = curl_init($lmStudioUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        $result = json_decode($response, true);
        return $result['choices'][0]['message']['content'] ?? 'Errore';
    } else {
        return "Errore HTTP $httpCode";
    }
}

// Gestione richiesta AJAX
if (isset($_GET['ajax']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $userMessage = $_POST['message'] ?? '';
    $mode = $_POST['mode'] ?? 'wordpress';
    $aiResponse = callLMStudio($userMessage, $mode);
    
    // Pulisce i backtick markdown
    $aiResponse = preg_replace('/```(php|html|javascript)?\s*/', '', $aiResponse);
    $aiResponse = str_replace('```', '', $aiResponse);
    
    echo json_encode(['response' => trim($aiResponse)]);
    exit();
}

// Gestione richiesta normale
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_GET['ajax'])) {
    $userMessage = $_POST['message'] ?? '';
    $mode = $_POST['mode'] ?? 'wordpress';
    $aiResponse = callLMStudio($userMessage, $mode);
    
    $aiResponse = preg_replace('/```(php|html|javascript)?\s*/', '', $aiResponse);
    $aiResponse = str_replace('```', '', $aiResponse);
}
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Locale per WordPress</title>
    <style>
        body { font-family: 'Segoe UI', sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; background: #f9f9f9; }
        h1 { color: #23282d; border-bottom: 3px solid #0073aa; padding-bottom: 10px; }
        textarea { width: 100%; height: 120px; padding: 15px; border: 1px solid #ddd; border-radius: 4px; font-family: monospace; }
        select, button { padding: 10px 15px; margin: 10px 0; border-radius: 4px; }
        button { background: #0073aa; color: white; border: none; cursor: pointer; }
        button:hover { background: #005a87; }
        .response { margin-top: 20px; background: white; border-left: 4px solid #46b450; padding: 15px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .response pre { background: #f1f1f1; padding: 15px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; font-family: 'Consolas', monospace; }
        .mode-selector { margin: 15px 0; background: #fff; padding: 15px; border-radius: 4px; border: 1px solid #e5e5e5; }
        .loading { display: none; margin-left: 10px; color: #666; }
    </style>
</head>
<body>
    <h1>🤖 Assistente WordPress Locale</h1>
    
    <div class="mode-selector">
        <strong>Modalità:</strong>
        <select name="mode" id="mode">
            <option value="wordpress" selected>WordPress Expert</option>
            <option value="pure-php">PHP Puro</option>
        </select>
        <span style="margin-left: 20px; color: #666;">(Il modello gira localmente - zero dati in cloud!)</span>
    </div>
    
    <form method="POST" id="chatForm">
        <textarea name="message" placeholder="Chiedimi qualcosa su WordPress o PHP..."><?php echo htmlspecialchars($_POST['message'] ?? '') ?></textarea>
        <button type="submit">Invia</button>
        <span class="loading" id="loading">Elaborazione in corso...</span>
    </form>
    
    <?php if (isset($aiResponse)): ?>
    <div class="response">
        <strong>Risposta:</strong>
        <pre><?php echo htmlspecialchars($aiResponse) ?></pre>
    </div>
    <?php endif; ?>
    
    <script>
    document.getElementById('chatForm').addEventListener('submit', async (e) => {
        e.preventDefault();
        
        const formData = new FormData(e.target);
        formData.append('mode', document.getElementById('mode').value);
        
        const loading = document.getElementById('loading');
        loading.style.display = 'inline';
        
        try {
            const response = await fetch('?ajax=1', {
                method: 'POST',
                body: formData
            });
            
            const data = await response.json();
            
            let responseDiv = document.querySelector('.response');
            if (!responseDiv) {
                responseDiv = document.createElement('div');
                responseDiv.className = 'response';
                document.body.appendChild(responseDiv);
            }
            
            responseDiv.innerHTML = `<strong>Risposta:</strong><pre>${data.response}</pre>`;
            
        } catch (error) {
            alert('Errore: ' + error.message);
        } finally {
            loading.style.display = 'none';
        }
    });
    </script>
</body>
</html>

Figura 10: La mia interfaccia personalizzata – WordPress o PHP puro con un click

 La mia interfaccia personalizzata - WordPress o PHP puro con un click

6.3 La magia della pulizia delle risposte

Un dettaglio che ho scoperto: i modelli tendono a rispondere con blocchi di codice delimitati da “`, che nel tag <pre> vengono visualizzati letteralmente. Ho risolto con due semplici righe:

$aiResponse = preg_replace('/```(php|html|javascript)?\s*/', '', $aiResponse);
$aiResponse = str_replace('```', '', $aiResponse);

7️⃣ Step 4: Specializzazione WordPress

7.1 Il system prompt che fa la differenza

Il vero segreto per avere un assistente specializzato in WordPress è il system prompt. Ecco quello che uso:

Sei un esperto sviluppatore WordPress con 10+ anni di esperienza. 
Hai conoscenza approfondita di:
- WordPress Coding Standards (WPCS)
- Plugin development best practices
- Hooks (action e filter)
- REST API di WordPress
- Security best practices (nonce, capability checks)

Quando scrivi codice PHP per WordPress:
- Usa sempre if (!defined('ABSPATH')) come prima riga
- Implementa nonce per form security
- Segui le convenzioni di nomenclatura WordPress
- Preferisci funzioni WordPress a PHP nudo quando possibile
- Includi commenti PHPDoc
- Usa escaping appropriato (esc_html, esc_attr, wp_kses)

7.2 I risultati

Con questo prompt, il modello inizia automaticamente ogni plugin con:

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Impedisce accesso diretto
}

8️⃣ Step 5: Test e risultati

8.1 Velocità di risposta

Ho fatto dei test comparativi:

RichiestaTempo LM Studio (chat)Tempo VS Code (Continue)
Funzione PHP semplice3-4 secondi5-6 secondi
Plugin WordPress completo8-10 secondi12-15 secondi

8.2 Qualità del codice

La qualità del codice generato è sorprendentemente alta:

  • ✅ Rispetta gli standard WordPress
  • ✅ Include controlli di sicurezza
  • ✅ Commenti PHPDoc completi
  • ✅ Codice pronto all’uso

8.3 Scenari

ScenarioStrumentoPerché
Debug rapido nel codiceContinue (Ctrl+L)Contestuale al file aperto
Generare plugin interiForm PHP personalizzataInterfaccia pulita, cronologia
Test velociLM Studio ChatMassima velocità

9️⃣ Considerazioni sui costi e privacy

9.1 Costi hardware

L’investimento iniziale:

Costo totale: 0€ (oltre all’hardware già posseduto)

9.2 Confronto con soluzioni cloud

VoceChatGPT PlusGitHub CopilotLocale (questo)
Canone mensile20€/mese10€/mese0€
Privacy dati❌ Su server OpenAI❌ Su server Microsoft✅ Sul tuo PC
Limiti richieste✅ 40 messaggi/3h✅ Illimitato?✅ Illimitato
Offline❌ No❌ No✅ Sì

9.3 Il vantaggio principale

I miei plugin proprietari, il codice dei clienti, le logiche di business – tutto rimane sul mio PC. Nessuna NDA violata, nessun dubbio su chi vede cosa.

🔟 Conclusioni

10.1 Cosa ho imparato

Costruire questo sistema mi ha insegnato che:

  1. L’AI locale è matura – modelli come DeepSeek sono competitivi con i servizi cloud
  2. La personalizzazione è tutto – con un buon system prompt, l’AI diventa uno specialista del tuo dominio
  3. L’integrazione è semplice – API standard (OpenAI-compatible) rendono tutto facile

10.2 Prossimi passi

Ora che ho questa base, voglio:

10.3 Consigli per chi vuole provare

Se vuoi replicare questo setup:

  1. Inizia con LM Studio e un modello piccolo
  2. Prova Continue in VS Code (è immediato)
  3. Costruisci la tua interfaccia solo se ti serve
  4. Sperimenta con i system prompt – è lì che sta la magia

📚 Risorse utili


💬 Commenti e domande

Hai provato a configurare un assistente AI locale? Hai domande su qualche passaggio? Lascia un commento qui sotto o contattami direttamente!

📖 Letto da: 24

Lascia una risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Contatti Free Service Chi sono Highlight Portfolio Blog & News