immagine copertina del post su - come costruire un sistema di auto-riparazione del codice python


Il software moderno è affetto da una patologia cronica: la comodità. I framework tentano di indovinare le intenzioni dello sviluppatore, introducendo dipendenze non richieste che rompono il determinismo. Il problema di Cursor, Continue e dei loro simili non è che non funzionano, ma che introducono rumore elettrico tra lo sviluppatore e il ferro. La loro complessità è inutile e pericolosa. Questi tool indicizzano migliaia di file creando database vettoriali pesanti solo per tentare di indovinare cosa vuoi scrivere. Risultato: lag, consumo di RAM e risposte spesso vaghe.

La via "Diretta" Il tuo attuatore.py legge solo ciò che serve, quando serve. È deterministico, non statistico, non costa nulla e non ti costringe a pagare due volte un abbonamento che gia possiedi con Gemini AI o Vertex di Google Cloud.

2. Interfacce "Soft" per Problemi "Hard"

Cursor propone tasti come "Apply", "Chat" e "Composer" che nascondono i comandi reali. Ti rendono dipendente dalla loro UI. Se Cursor chiude i battenti domani, sei paralizzato. Questo approccio crea una barriera all'apprendimento di strumenti fondamentali e flussi di lavoro sottostanti. Immagina di dover tornare a un editor di testo standard o a un IDE senza queste scorciatoie proprietarie: la tua produttività crollerebbe. È come imparare a guidare solo un'auto con cambio automatico super assistito e poi ritrovarsi di fronte a un'auto con cambio manuale senza sapere come usare la frizione. Invece di automatizzare e astrarre, dovremmo concentrarci sull'apprendimento dei comandi di base e sulla comprensione dei processi, garantendo la nostra indipendenza e trasferibilità delle competenze.

> La via più DIRETTA: Usare Sublime Text o un editor qualsiasi e uno script Python di 50 righe significa possedere lo strumento e non essere un ospite di VS Code: sei il padrone della tua toolchain in Python.

L'Architettura della toolchain

Ho implementato un ecosistema diviso in quattro file python dove ogni componente ha uno scopo specifico e chiaro. Niente astrazioni su paradigmi come accade con "Cursor" e "Conitnue.dev" ma semplici algoritmi.

1. L'Attuatore (attuatore.py)

Il braccio armato. Si interfaccia direttamente con Gemini 2.0 Flash per riscrivere i file sul disco. Include un filtro "anti-allucinazione" per eliminare i tripli backticks (```) e straberie del Markdown (AI) che corromperebbero l'esecuzione del server Flask.

attuatore.py

   import os, requests, json, sys
   import config
   URL = f"https://generativelanguage.googleapis.com/v1beta/models/{config.MODEL}:generateContent?key={config.API_KEY}"
   def get_context():
   ctx = ""
   for root, _, files in os.walk("."):
   if any(x in root for x in config.EXCLUDE_DIRS): continue
   for f in files:
   if f.endswith(config.EXTENSIONS):
   p = os.path.join(root, f)
   try:
   with open(p, 'r', encoding='utf-8') as s:
   ctx += f"FILE:{p}\n{s.read()}\nEND_FILE\n"
   except: pass
   return ctx
   def run():
   query = sys.argv[1] if len(sys.argv) > 1 else "Analizza il codice e proponi miglioramenti."
   context = get_context()
   prompt = f"""ROLE: IoT/Web Expert. STYLE: RAW, Brutal, Technical.
   TASK: Modify or analyze the code based on the QUEST.
   FORMAT: If modifying, respond ONLY with:
   ---START_WRITE:filepath---
   content
   ---END_WRITE---
   IMPORTANT: DO NOT USE MARKDOWN (```). WRITE RAW CODE DIRECTLY.
   CONTEXT:
   {context}
   QUEST: {query}"""
   try:
   r = requests.post(URL, json={"contents": [{"parts": [{"text": prompt}]}]})
   r.raise_for_status()
   res = r.json()['candidates'][0]['content']['parts'][0]['text']
   if "---START_WRITE:" in res:
   parts = res.split("---START_WRITE:")[1:]
   for p in parts:
   header, body = p.split("---", 1)
   fname = header.strip()
   content = body.split("---END_WRITE---")[0].strip()
   # FILTRO ANTI-PAGLIACCIO: Rimuove allucinazioni Markdown
   clean_content = []
   for line in content.splitlines():
   if not line.strip().startswith("```"):
   clean_content.append(line)
   with open(fname, 'w', encoding='utf-8') as f:
   f.write("\n".join(clean_content))
   print(f"DONE: {fname} aggiornato (clean).")
   else:
   print(res)
   except Exception as e:
   print(f"ERRORE API: {e}")
   if __name__ == "__main__":
   run()
   

2. Il Meccanico (healer.py)

Un supervisore asincrono con logica di Watchdog. Lancia il server, lo monitora per 5 secondi critici e, se rileva un'uscita anomala, invia lo stderr all'Attuatore per il fix immediato.

healer.py

   import subprocess
   import sys
   import os
   import time
   import asyncio
   async def run_and_repair(target_file):
   print(f"--- FASE DI TEST: {target_file} ---")
   if target_file == "server.py":
   # Lancia server.py in background
   cmd = [sys.executable, target_file]
   proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
   # Monitora per 5 secondi in modo asincrono
   try:
   await asyncio.wait_for(asyncio.sleep(5), timeout=5)
   if proc.poll() is None:
   print("ESITO: Successo. Il server è attivo da almeno 5 secondi.")
   print("SERVER_STABILE") # Aggiunto print per segnalare server stabile
   return True
   else:
   print("ESITO: Crash rilevato dopo il timeout!")
   stdout, stderr = proc.communicate()
   print(f"LOG:\n{stderr}")
   error_msg = stderr.replace('"', '\\"')
   repair_quest = f"Il file {target_file} crasha con questo errore: {error_msg}. Riparalo mantenendo la logica RAW."
   print(f"--- ATTIVAZIONE RIPARAZIONE VIA 101.py ---")
   subprocess.run([sys.executable, "101.py", repair_quest])
   return False
   except asyncio.TimeoutError:
   if proc.poll() is None:
   print("ESITO: Successo. Il server è attivo da almeno 5 secondi (Timeout).")
   print("SERVER_STABILE") # Aggiunto print per segnalare server stabile
   return True
   else:
   print("ESITO: Crash rilevato durante il timeout!")
   stdout, stderr = proc.communicate()
   print(f"LOG:\n{stderr}")
   error_msg = stderr.replace('"', '\\"')
   repair_quest = f"Il file {target_file} crasha con questo errore: {error_msg}. Riparalo mantenendo la logica RAW."
   print(f"--- ATTIVAZIONE RIPARAZIONE VIA 101.py ---")
   subprocess.run([sys.executable, "101.py", repair_quest])
   return False
   except Exception as e:
   print(f"ERRORE DURANTE IL MONITORAGGIO: {e}")
   return False
   else:
   # Esegue il file e cattura stderr (per altri file)
   cmd = [sys.executable, target_file]
   proc = subprocess.run(cmd, capture_output=True, text=True)
   if proc.returncode == 0:
   print("ESITO: Successo. Il codice gira senza errori.")
   return True
   print(f"ESITO: Crash rilevato!\nLOG:\n{proc.stderr}")
   # Prepara la QUEST per 101.py
   error_msg = proc.stderr.replace('"', '\\"')
   repair_quest = f"Il file {target_file} crasha con questo errore: {error_msg}. Riparalo mantenendo la logica RAW."
   # Chiama l'attuatore 101.py per il fix
   print(f"--- ATTIVAZIONE RIPARAZIONE VIA 101.py ---")
   subprocess.run([sys.executable, "101.py", repair_quest])
   return False
   if __name__ == "__main__":
   if len(sys.argv) < 2:
   print("Errore: Specifica il file da testare/riparare.")
   else:
   asyncio.run(run_and_repair(sys.argv[1]))
   

3. Il Server (server.py)

Il nucleo operativo. Spogliato di dotenv e altre dipendenze inutili. Implementa una ricerca dinamica delle porte per garantire l'uptime anche in caso di collisioni di rete [cite: 2026-02-10].


   from flask import Flask, request, send_from_directory
   import json
   import os
   import socket
   os.environ["FLASK_SKIP_DOTENV"] = "1"
   app = Flask(__name__, static_folder='.')
   DATA_FILE = "data.json"
   # Rotta per servire i file HTML/CSS/JS dalla root
   @app.route('/')
   @app.route('/
   <path:path>
   ')
   def serve_file(path='index.html'):
   return send_from_directory('.', path)
   @app.route('/submit', methods=['POST'])
   def submit():
   try:
   data = request.form.to_dict()
   records = []
   if os.path.exists(DATA_FILE):
   with open(DATA_FILE, 'r', encoding='utf-8') as f:
   records = json.load(f)
   records.append(data)
   with open(DATA_FILE, 'w', encoding='utf-8') as f:
   json.dump(records, f, indent=4)
   return "DATO SALVATO", 200
   except Exception as e:
   return str(e), 500
   def find_available_port(start_port=5000, max_attempts=100):
   """Trova una porta disponibile a partire da start_port."""
   for port in range(start_port, start_port + max_attempts):
   try:
   with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
   s.bind(('0.0.0.0', port))
   return port
   except OSError:
   # La porta è in uso, prova la successiva
   pass
   raise Exception("Nessuna porta disponibile trovata nell'intervallo specificato.")
   if __name__ == '__main__':
   try:
   port = find_available_port()
   print(f"Server attivo su http://192.168.1.142:{port}")
   app.run(host='0.0.0.0', port=port, debug=False)
   except Exception as e:
   print(f"Errore: {e}")
   

4. Il Config (config.py)

config.py

   API_KEY = "LE_TUE_API_DI_GREMINI"
   MODEL = "gemini-2.0-flash"
   EXTENSIONS = ('.py', '.c', '.h', '.json', '.yaml', '.sh', '.html', '.css')
   EXCLUDE_DIRS = [".git", "__pycache__", ".continue"]
   

Test sul campo: Healing in diretta

Durante il burn-in test, l'AI ha introdotto un errore di sintassi: records.app end. Il risultato?


   (w) lenovo ~/microsito (master)$ python3 healer.py server.py 
   --- FASE DI TEST: server.py ---
   ESITO: Crash rilevato durante il timeout!
   LOG:
   File "/home/sebadima/microsito/server.py", line 20
   data = request.form.to   _dict()
   ^^^^^
   SyntaxError: invalid syntax
   --- ATTIVAZIONE RIPARAZIONE VIA 101.py ---
   DONE: server.py aggiornato (clean).
   
  1. healer.py ha rilevato il SyntaxError istantaneamente.
  2. Il log di errore è stato passato a 101.py.
  3. Il codice è stato corretto, ripulito e salvato.
  4. Il server è tornato online in meno di 10 secondi.

Questo è il vero vantaggio competitivo: mentre la concorrenza debugga manualmente, il mio sistema evolve e si corregge da solo.

Conclusioni

Il punto non è “usare l’AI per scrivere codice”. Il punto è controllare l’AI come si controlla un attuatore industriale: input chiaro, output verificabile, ciclo chiuso di feedback. In questo progetto il modello non è un oracolo creativo, ma un componente della pipeline. Non decide, non astrae, non impone workflow: esegue ed è il contraio esatto della filosofia dei Cursor o Continue.

Il paradigma del Self-Healing che abbiamo costruito non è magia statistica. È ingegneria:

  • Un Attuatore che scrive codice in formato RAW, senza UI intermediarie.
  • Un Meccanico che osserva, testa e reagisce in modo deterministico.
  • Un Server minimale che privilegia uptime e semplicità strutturale.
  • Una configurazione esplicita, leggibile, modificabile, portabile.

Il risultato è un ciclo chiuso:

 Errore → Log → AI → Patch → Test → Stabilità 

Questo è controllo. Questo è possesso della toolchain.

Nel mondo IoT e Industrial, dove un crash non è solo un fastidio ma può significare fermo macchina, perdita dati o downtime di rete, l’idea di un sistema che rileva, analizza e corregge in autonomia diventa un vantaggio competitivo reale. Non è un plugin. È un’architettura.

La vera rivoluzione non è avere un assistente che suggerisce codice mentre scrivi. È avere un sistema in grado di:

  • monitorare continuamente lo stato del software,
  • interpretare gli errori come segnali elettrici,
  • applicare correzioni mirate,
  • verificare la stabilità prima di dichiarare successo.

È la trasposizione del concetto di watchdog hardware nel dominio software potenziato dall’AI. È embedded thinking applicato al cloud.

Oggi è un server Flask che si auto-ripara. Domani può essere un nodo IoT che aggiorna il firmware in modo intelligente, un gateway industriale che corregge configurazioni errate, una pipeline CI/CD che si stabilizza autonomamente prima del deploy su Github.

Questo è solo il primo passo verso un’infrastruttura software che non aspetta l’intervento umano per sopravvivere. ong>Self-Healing non è automazione. È resilienza programmata. E quando la resilienza diventa parte della tua architettura, non stai più semplicemente scrivendo codice: stai costruendo sistemi che evolvono.

Prossimo step: Espansione della telemetria Sentinel Node.

Conclusione: Dal Debug al Determinismo

Il punto non è “usare l’AI per scrivere codice”. Il punto è controllare l’AI come si controlla un attuatore industriale: input chiaro, output verificabile, ciclo chiuso di feedback. In questo progetto il modello non è un oracolo creativo, ma un componente della pipeline. Non decide, non astrae, non impone workflow: esegue.

Il paradigma del Self-Healing che abbiamo costruito non è magia statistica. È ingegneria:

  • Un Attuatore che scrive codice in formato RAW, senza UI intermediarie.
  • Un Meccanico che osserva, testa e reagisce in modo deterministico.
  • Un Server minimale che privilegia uptime e semplicità strutturale.
  • Una configurazione esplicita, leggibile, modificabile, portabile.

Il risultato è un ciclo chiuso:

 Errore → Log → AI → Patch → Test → Stabilità 

Questo è controllo. Questo è possesso della toolchain.

Nel mondo IoT e Industrial, dove un crash non è solo un fastidio ma può significare fermo macchina, perdita dati o downtime di rete, l’idea di un sistema che rileva, analizza e corregge in autonomia diventa un vantaggio competitivo reale. Non è un plugin. È un’architettura.

La vera rivoluzione non è avere un assistente che suggerisce codice mentre scrivi. È avere un sistema che:

  • monitora continuamente lo stato del software,
  • interpreta gli errori come segnali elettrici,
  • applica correzioni mirate,
  • verifica la stabilità prima di dichiarare successo.

È la trasposizione del concetto di watchdog hardware nel dominio software potenziato dall’AI. È embedded thinking applicato al cloud.

In un ecosistema dominato da strumenti pesanti, interfacce opache e dipendenze nascoste, questa architettura dimostra che bastano pochi file Python e una API ben orchestrata per costruire un sistema resiliente, portabile e sotto pieno controllo dello sviluppatore.

Il futuro non appartiene agli ambienti che “indovinano”. Appartiene ai sistemi che misurano, reagiscono e si adattano in modo riproducibile.

La differenza è sottile ma fondamentale: non stiamo delegando il nostro lavoro all’AI. Stiamo integrando l’AI come un componente deterministico di un ciclo ingegneristico. È un cambio di mentalità.

Oggi è un server Flask che si auto-ripara. Domani può essere:

  • un nodo IoT che aggiorna il firmware in modo intelligente,
  • un gateway industriale che corregge configurazioni errate,
  • una pipeline CI/CD che si stabilizza autonomamente prima del deploy.

Questo è solo il primo passo verso un’infrastruttura software che non aspetta l’intervento umano per sopravvivere. Self-Healing non è automazione. È resilienza programmata. E quando la resilienza diventa parte della tua architettura, non stai più semplicemente scrivendo codice: stai costruendo sistemi che evolvono.