giovedì 27 novembre 2025

Corso di Prompt Engineering: Prompt per il codice e l’automazione


Prompt per il codice e l’automazione

1) Prompting in ambienti di sviluppo (Python, JS, Bash, SQL)

1.1 Pattern di prompt “comando completo”

Usa questo scheletro, adattandolo al linguaggio:

  • Contesto: (cosa devo fare e perché)
  • Vincoli: (librerie, versioni, complessità, performance)
  • Output atteso: (firma funzione, formato, test inclusi?)
  • Stile: (commenti, docstring, tipizzazione, lint)

Template breve

Contesto: voglio <obiettivo>.
Linguaggio: <Python/JS/Bash/SQL>.
Vincoli: <librerie/versioni/limiti>.
Output: <file singolo/funzione + test + istruzioni>.
Stile: <docstring/tipi/commenti chiari>.

1.2 Esempi concreti

Python – Funzione + test

Prompt efficace

Scrivi in Python 3.11 una funzione media(lista: list[float]) -> float che ritorna la media arrotondata a 2 decimali. Aggiungi test con pytest, docstring e gestione di lista vuota con ValueError.

Soluzione

def media(lista: list[float]) -> float:
    """
    Calcola la media di una lista di numeri con arrotondamento a 2 decimali.
    :raises ValueError: se la lista è vuota
    """
    if not lista:
        raise ValueError("Lista vuota")
    return round(sum(lista) / len(lista), 2)

Test pytest

import pytest
from soluzione import media

def test_media_normale():
    assert media([1, 2, 3]) == 2.00

def test_media_decimali():
    assert media([10.1, 20.2]) == 15.15

def test_lista_vuota():
    with pytest.raises(ValueError):
        media([])

Perché funziona: firma chiara, eccezione esplicita, test significativi.

JavaScript – Map/Filter/Reduce

Prompt

Scrivi una funzione nomiMaggiorenni(persone) che riceve array di {nome, eta} e ritorna i nomi con eta >= 18. Aggiungi JSDoc e test rapidi con esempi.

Soluzione

/**
 * @param {{nome: string, eta: number}[]} persone
 * @returns {string[]}
 */
function nomiMaggiorenni(persone) {
  return persone.filter(p => p.eta >= 18).map(p => p.nome);
}

// esempi "test-like"
console.assert(JSON.stringify(nomiMaggiorenni([{nome:"A",eta:18},{nome:"B",eta:17}])) === JSON.stringify(["A"]));
console.assert(nomiMaggiorenni([]).length === 0);

Bash – Script sicuro/idempotente

Prompt

Crea uno script Bash POSIX che crei backup/ (se non esiste) e copi tutti i .txt correnti dentro backup/, con messaggi e set -euo pipefail.

Soluzione

#!/usr/bin/env bash
set -euo pipefail

mkdir -p backup
shopt -s nullglob
txts=(*.txt)
if (( ${#txts[@]} == 0 )); then
  echo "Nessun file .txt da copiare."
  exit 0
fi
cp -- *.txt backup/
echo "Backup completato ($(date))."

SQL – Aggregazioni

Prompt

In SQL standard, conta gli ordini per cliente nella tabella ordini(cliente_id, data, totale), ordina decrescente, includi alias chiari.

Soluzione

SELECT cliente_id, COUNT(*) AS numero_ordini
FROM ordini
GROUP BY cliente_id
ORDER BY numero_ordini DESC;

1.3 Esercizi (con soluzioni)

Esercizio A
Scrivi due prompt per calcolare la somma dei numeri pari 1..100:

  • in Python usa for;
  • in JavaScript usa reduce.

Soluzione (prompt + codice)

  • Prompt Python

    Python 3.11. Scrivi funzione somma_pari_1_100() che usa un ciclo for e ritorna la somma dei pari tra 1 e 100. Aggiungi un test pytest.

    def somma_pari_1_100() -> int:
        s = 0
        for n in range(2, 101, 2):
            s += n
        return s
    
  • Prompt JavaScript

    JavaScript. Crea costante sommaPari calcolata con Array.from e reduce, considerando i pari da 1 a 100. Aggiungi un console.assert.

    const sommaPari = Array.from({length: 100}, (_, i) => i + 1)
      .filter(n => n % 2 === 0)
      .reduce((acc, n) => acc + n, 0);
    
    console.assert(sommaPari === 2550);
    

2) Refactoring, testing e debug assistiti

2.1 Refactoring guidato

Prompt

Esegui refactoring di questo codice per leggibilità e performance, senza cambiare il comportamento. Aggiungi commenti su cosa hai migliorato e perché. Applica PEP8, tipizzazione, early return.

Esempio (prima → dopo) Prima

def f(nums):
    s=0
    for i in range(len(nums)):
        if nums[i]%2==0:
            s=s+nums[i]
    return s

Dopo

from typing import Iterable

def somma_pari(nums: Iterable[int]) -> int:
    """Somma i numeri pari."""
    return sum(n for n in nums if n % 2 == 0)

Perché meglio: meno codice, più espressivo, tipizzato, complessità invariata.

2.2 Testing: unit, property-based, golden files

  • Unit test (pytest/unittest)
  • Property-based (hypothesis): genera casi automaticamente.
  • Golden test: confronta output con “snapshot” noto.

Prompt esempio (pytest + hypothesis)

Scrivi test pytest e usa hypothesis per validare che somma_pari sia uguale alla somma dei n tali che n % 2 == 0.

import pytest
from hypothesis import given, strategies as st
from soluzione import somma_pari

@given(st.lists(st.integers(min_value=-1000, max_value=1000), max_size=200))
def test_somma_pari_property(nums):
    assert somma_pari(nums) == sum(n for n in nums if n % 2 == 0)

2.3 Debug assistito (tecnica a checklist)

  1. Riproduci l’errore con un test minimo
  2. Leggi il traceback/log
  3. Isola la riga sospetta
  4. Aggiungi asserzioni e log
  5. Correggi, poi test di non regressione

Esempio Bug: “division by zero”

def rapporto(a: float, b: float) -> float:
    return a / b

Fix

def rapporto(a: float, b: float) -> float:
    if b == 0:
        raise ZeroDivisionError("b non può essere zero")
    return a / b

3) Istruzioni multi-turno per funzioni complesse

3.1 Strategia “progressive enhancement”

  • Turno 1 – MVP: funzionante, senza fronzoli
  • Turno 2 – Robustezza: error handling, logging
  • Turno 3 – UX/Performance: progress bar, streaming, caching
  • Turno 4 – Test & Docs: unit test + README

Caso studio: downloader di file

  • T1 (MVP)

    Python. Funzione download(url: str, path: str) che scarica un file via requests e lo salva. Gestisci HTTP base.

  • T2 (Robustezza)

    Aggiungi gestione timeout, retry con backoff, dimensione massima, eccezioni specifiche.

  • T3 (UX/Perf)

    Aggiungi barra di progresso tqdm con streaming chunked e resume se possibile.

  • T4 (Test & Docs)

    Crea test con responses o pytest-httpserver e README con esempi.

3.2 Esercizio (con soluzione)

Obiettivo: fattoriale in tre turni

  • T1 – Base

    def fattoriale(n: int) -> int:
        res = 1
        for k in range(2, n+1):
            res *= k
        return res
    
  • T2 – Input negativi

    def fattoriale(n: int) -> int:
        if n < 0:
            raise ValueError("n deve essere >= 0")
        res = 1
        for k in range(2, n+1):
            res *= k
        return res
    
  • T3 – Memoizzazione

    from functools import lru_cache
    
    @lru_cache(maxsize=None)
    def fattoriale(n: int) -> int:
        if n < 0:
            raise ValueError("n deve essere >= 0")
        if n in (0, 1):
            return 1
        return n * fattoriale(n-1)
    

4) Tool interattivi no-code/low-code (HTML+JS)

4.1 Mini-tool: Generatore di password (accessibile e “copiabile”)

Prompt

Crea una pagina HTML+JS con input lunghezza (min 4, max 64), checkbox per maiuscole/minuscole/numeri/simboli, pulsante “Genera” e “Copia”. Nessuna libreria esterna, stile CSS leggero, validazione e ARIA.

Soluzione (snippet completo, pronto-uso)

<!doctype html>
<html lang="it">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Generatore Password</title>
<style>
  body{font-family:system-ui,Arial,sans-serif;max-width:720px;margin:2rem auto;padding:0 1rem}
  .card{border:1px solid #ddd;border-radius:12px;padding:1rem;box-shadow:0 2px 8px #0001}
  label{display:block;margin:.5rem 0}
  button{padding:.6rem 1rem;border-radius:10px;border:1px solid #ccc;cursor:pointer}
  .row{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}
  input[type="text"]{width:100%;padding:.6rem;border:1px solid #ccc;border-radius:8px}
</style>
<div class="card" role="region" aria-labelledby="tit">
  <h1 id="tit">Generatore di Password</h1>
  <label> Lunghezza:
    <input type="number" id="len" min="4" max="64" value="12" aria-describedby="lenHelp">
  </label>
  <small id="lenHelp">Valori tra 4 e 64</small>
  <div class="row" role="group" aria-label="Opzioni caratteri">
    <label><input type="checkbox" id="lower" checked> minuscole</label>
    <label><input type="checkbox" id="upper" checked> maiuscole</label>
    <label><input type="checkbox" id="nums"  checked> numeri</label>
    <label><input type="checkbox" id="syms"> simboli</label>
  </div>
  <div class="row" style="margin-top:1rem">
    <button id="gen">Genera</button>
    <button id="copy" aria-live="polite">Copia</button>
  </div>
  <label style="margin-top:1rem">Risultato:
    <input id="out" type="text" readonly aria-readonly="true">
  </label>
</div>
<script>
(function(){
  const $ = id => document.getElementById(id);
  const pools = {
    lower: "abcdefghijklmnopqrstuvwxyz",
    upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    nums:  "0123456789",
    syms:  "!@#$%^&*()-_=+[]{};:,.?/|~"
  };
  function generate(len, opts){
    let pool = "";
    Object.entries(opts).forEach(([k, v]) => { if(v) pool += pools[k]; });
    if(!pool) throw new Error("Seleziona almeno un set di caratteri");
    // garantisci almeno un carattere da ogni set selezionato
    const selectedSets = Object.entries(opts).filter(([,v])=>v).map(([k])=>pools[k]);
    const result = [];
    selectedSets.forEach(set => result.push(set[Math.floor(Math.random()*set.length)]));
    for(let i=result.length; i<len; i++){
      result.push(pool[Math.floor(Math.random()*pool.length)]);
    }
    // shuffle Fisher–Yates
    for (let i=result.length-1; i>0; i--){
      const j = Math.floor(Math.random()*(i+1));
      [result[i], result[j]] = [result[j], result[i]];
    }
    return result.join("");
  }
  $("gen").addEventListener("click", ()=>{
    const len = +$("len").value;
    if (len < 4 || len > 64){ alert("Lunghezza non valida"); return; }
    try {
      $("out").value = generate(len, {
        lower: $("lower").checked,
        upper: $("upper").checked,
        nums:  $("nums").checked,
        syms:  $("syms").checked
      });
    } catch(e){ alert(e.message); }
  });
  $("copy").addEventListener("click", async ()=>{
    const v = $("out").value;
    if(!v){ alert("Niente da copiare"); return; }
    await navigator.clipboard.writeText(v);
    $("copy").textContent = "Copiato!";
    setTimeout(()=> $("copy").textContent = "Copia", 1200);
  });
})();
</script>
</html>

Project Work – Mini-app “Task Tracker” (AI + codice assistito)

Requisiti

  • Aggiungi attività (titolo, priorità)
  • Completa/riapri, elimina
  • Contatore completati / totali
  • Persistenza con localStorage
  • UI responsive, zero dipendenze

Prompt iniziale

Crea una pagina HTML+CSS+JS, un unico file, con lista to-do con campi “titolo” (obbligatorio) e “priorità” (bassa/media/alta). Salva su localStorage. Filtri per “tutte/attive/completate” e contatori live. Aggiungi funzioni pure testabili per le operazioni sulla lista.

Soluzione (file unico, pronto-uso)

Incolla il seguente codice in un file .html e aprilo nel browser.

<!doctype html>
<html lang="it">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Task Tracker</title>
<style>
  :root { --gap:.6rem; --radius:14px }
  body{font-family:system-ui,Arial,sans-serif;margin:2rem auto;max-width:800px;padding:0 1rem}
  .card{border:1px solid #ddd;border-radius:var(--radius);padding:1rem;box-shadow:0 2px 8px #0001}
  .row{display:flex;gap:var(--gap);flex-wrap:wrap}
  input[type="text"], select{padding:.6rem;border:1px solid #ccc;border-radius:10px;flex:1}
  button{padding:.6rem 1rem;border:1px solid #ccc;border-radius:10px;cursor:pointer}
  ul{list-style:none;padding:0;margin:.5rem 0}
  li{display:flex;align-items:center;gap:.5rem;justify-content:space-between;padding:.5rem;border-bottom:1px dashed #eee}
  .title.done{text-decoration:line-through;opacity:.6}
  .pill{border:1px solid #ccc;border-radius:999px;padding:.1rem .5rem;font-size:.8rem}
  .pill.high{border-color:#f55}
  .pill.medium{border-color:#fa3}
  .pill.low{border-color:#6a6}
  .toolbar{display:flex;justify-content:space-between;align-items:center;gap:.6rem;flex-wrap:wrap;margin:.5rem 0}
  .filters button[aria-pressed="true"]{outline:2px solid #444}
</style>
<div class="card">
  <h1>Task Tracker</h1>
  <div class="row">
    <input id="taskTitle" type="text" placeholder="Nuova attività..." aria-label="Titolo attività">
    <select id="priority" aria-label="Priorità">
      <option value="low">Bassa</option>
      <option value="medium">Media</option>
      <option value="high">Alta</option>
    </select>
    <button id="add">Aggiungi</button>
  </div>

  <div class="toolbar">
    <div id="counters" aria-live="polite">0 completate su 0</div>
    <div class="filters" role="group" aria-label="Filtri">
      <button data-filter="all" aria-pressed="true">Tutte</button>
      <button data-filter="active" aria-pressed="false">Attive</button>
      <button data-filter="done" aria-pressed="false">Completate</button>
    </div>
  </div>

  <ul id="list" aria-live="polite"></ul>
</div>

<script>
// ---- Modello e funzioni pure (testabili) ----
function createTask(title, priority="low"){
  if(!title || !title.trim()) throw new Error("Titolo obbligatorio");
  return { id: crypto.randomUUID(), title: title.trim(), priority, done: false, createdAt: Date.now() };
}
function toggleTask(tasks, id){ return tasks.map(t => t.id===id ? {...t, done: !t.done} : t); }
function removeTask(tasks, id){ return tasks.filter(t => t.id!==id); }
function addTask(tasks, task){ return [task, ...tasks]; }
function filterTasks(tasks, mode){
  if(mode==="active") return tasks.filter(t => !t.done);
  if(mode==="done")   return tasks.filter(t =>  t.done);
  return tasks;
}
function countDone(tasks){ return tasks.filter(t => t.done).length; }

// ---- Persistenza ----
const STORAGE_KEY = "task_tracker_v1";
const load = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
const save = (tasks) => localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));

// ---- UI ----
let state = { tasks: load(), filter: "all" };
const $ = sel => document.querySelector(sel);
const listEl = $("#list"), countersEl = $("#counters");

function render(){
  const shown = filterTasks(state.tasks, state.filter);
  listEl.innerHTML = "";
  shown.forEach(t => {
    const li = document.createElement("li");
    const left = document.createElement("div");
    const right = document.createElement("div"); right.className = "row";

    const chk = document.createElement("input"); chk.type = "checkbox"; chk.checked = t.done;
    chk.addEventListener("change", () => updateState({ tasks: toggleTask(state.tasks, t.id) }));

    const title = document.createElement("span"); title.textContent = t.title; title.className = "title" + (t.done ? " done" : "");

    const pill = document.createElement("span"); pill.className = "pill " + (t.priority==="high"?"high":t.priority==="medium"?"medium":"low");
    pill.textContent = t.priority === "high" ? "Alta" : t.priority === "medium" ? "Media" : "Bassa";

    left.className = "row"; left.append(chk, title, pill);

    const del = document.createElement("button"); del.textContent = "Elimina";
    del.addEventListener("click", () => updateState({ tasks: removeTask(state.tasks, t.id) }));

    right.append(del);
    li.append(left, right);
    listEl.append(li);
  });
  const done = countDone(state.tasks);
  countersEl.textContent = `${done} completate su ${state.tasks.length}`;
  save(state.tasks);
}

function updateState(patch){
  state = { ...state, ...patch };
  render();
}

document.getElementById("add").addEventListener("click", () => {
  const title = document.getElementById("taskTitle").value;
  const priority = document.getElementById("priority").value;
  try {
    const task = createTask(title, priority);
    document.getElementById("taskTitle").value = "";
    updateState({ tasks: addTask(state.tasks, task) });
  } catch(e){ alert(e.message); }
});

document.querySelectorAll(".filters button").forEach(btn => {
  btn.addEventListener("click", () => {
    document.querySelectorAll(".filters button").forEach(b => b.setAttribute("aria-pressed","false"));
    btn.setAttribute("aria-pressed","true");
    updateState({ filter: btn.dataset.filter });
  });
});

render();
</script>
</html>

Test manuali rapidi

  • Aggiungi 3 task (alta/media/bassa), completa/riapri, filtra per “Attive” e “Completate”.
  • Ricarica la pagina: i dati persistono.
  • Prova ad aggiungere titolo vuoto: appare l’errore.

Verifica finale (con risposte)

  1. Prompt per CSV → media colonna → salva risultato
    Prompt consigliato

Python 3.11. Leggi un CSV dati.csv con colonna valore, calcola la media e scrivi media.txt con 2 decimali. Usa csv o pandas (specifica entrambe le versioni). Gestisci file mancante e colonna assente. Aggiungi un test con file temporaneo.

Soluzione minima con pandas

import pandas as pd, sys

def media_colonna_csv(path: str, col: str="valore") -> float:
    df = pd.read_csv(path)
    if col not in df.columns:
        raise KeyError(f"Colonna {col} assente")
    m = round(float(df[col].mean()), 2)
    with open("media.txt","w",encoding="utf-8") as f:
        f.write(f"{m}\n")
    return m

if __name__ == "__main__":
    try:
        media_colonna_csv(sys.argv[1], sys.argv[2] if len(sys.argv)>2 else "valore")
        print("OK")
    except Exception as e:
        print(f"Errore: {e}", file=sys.stderr); sys.exit(1)
  1. Due modi per ottimizzare molti if annidati
  • Guard clauses (early return) e dizionari/lookup tables al posto di if/elif multipli.
  • Strategy pattern / funzioni di dispatch (mappa chiave → funzione), oppure pattern matching (Python 3.10+ match, TS switch + oggetti).
  1. Differenza tra debug e testing
  • Testing: attività proattiva per verificare che il comportamento sia corretto (prima e durante lo sviluppo).
  • Debug: attività reattiva per trovare e correggere la causa di un errore osservato.

Cheat-sheet (rapido)

  • Specificare sempre: linguaggio + versione, librerie, formato output, vincoli, test richiesti.
  • Iterare: MVP → robustezza → performance → test/docs.
  • Chiedere spiegazioni: “Commenta le modifiche, indica complessità, cita alternative”.
  • Automazione sicura: log, validazioni, idempotenza (Bash), transazioni/rollback (SQL), test.

Nessun commento:

Posta un commento

Corso Fondamenti di Informatica e Reti: 6 Reti di computer e Internet

Reti di computer e Internet Introduzione Prova a pensare alla vita quotidiana senza reti informatiche: niente messaggi WhatsApp, niente m...