0Pricing
AI Engineering Academy · Lezione

Il loop di esecuzione del codice

Progetti il loop scrittura-esecuzione-osservazione, in cui l'agente genera codice, un esecutore in sandbox lo esegue, stdout e stderr vengono acquisiti e restituiti come osservazioni e l'agente corregge gli errori.

Il loop di esecuzione del codice è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.

Agenti che scrivono ed eseguono codice

Un agente per l'esecuzione del codice è un tipo speciale di agente IA che risolve i problemi scrivendo codice, eseguendolo, osservando l'output e ripetendo il processo finché l'attività non è completata. A differenza degli agenti che utilizzano solo strumenti predefiniti, gli agenti di codice creano al volo nuovi strumenti computazionali. Questo li rende straordinariamente flessibili: un agente di codice può tentare qualsiasi attività programmabile.

Il ciclo Scrittura-Esecuzione-Osservazione

Il nucleo di un agente per l'esecuzione del codice è un ciclo composto da tre passaggi: Scrittura — l'LLM genera codice Python per risolvere il passaggio corrente dell'attività. Esecuzione — il codice viene eseguito in un ambiente isolato e stdout/stderr vengono acquisiti. Osservazione — l'output dell'esecuzione viene restituito all'LLM come nuova osservazione, che utilizza queste informazioni per decidere cosa scrivere successivamente. Il ciclo continua finché l'attività non viene completata o non viene raggiunto il limite di iterazioni.

def code_execution_loop(task: str, max_iterations=10):
    messages = [
        {'role': 'system', 'content': 'You are a Python coding agent. Write code to solve tasks step by step.'},
        {'role': 'user', 'content': task}
    ]
    
    for i in range(max_iterations):
        # WRITE: LLM generates code
        response = llm.complete(messages)
        code = extract_code_block(response)
        
        if not code:
            return response  # LLM gave a final answer without code
        
        # EXECUTE: run the code
        output, error = execute_safely(code)
        
        # OBSERVE: feed output back
        observation = f'Output:\n{output}' if not error else f'Error:\n{error}'
        messages.append({'role': 'assistant', 'content': response})
        messages.append({'role': 'user', 'content': observation})
    
    return 'Max iterations reached'

Estrazione dei blocchi di codice dall'output dell'LLM

In genere gli LLM racchiudono il codice generato in blocchi di codice delimitati in Markdown: ```python ... ```. Il ciclo di esecuzione deve estrarre il codice da questi blocchi in modo affidabile prima di eseguirlo. Utilizzi un'espressione regolare o un semplice parser per individuare i blocchi delimitati e gestire casi limite come backtick annidati, più blocchi di codice nella stessa risposta oppure codice senza specificatore del linguaggio.

import re

def extract_code_block(response: str) -> str | None:
    # Match ```python ... ``` or ``` ... ```
    pattern = r'```(?:python)?\n(.*?)```'
    matches = re.findall(pattern, response, re.DOTALL)
    if not matches:
        return None
    # Return the LAST code block (often the most complete version)
    return matches[-1].strip()

# Test
sample_response = '''I will calculate the sum:\n\n```python\nnumbers = [1, 2, 3, 4, 5]\nprint(sum(numbers))\n```'''
code = extract_code_block(sample_response)
print(repr(code))  # 'numbers = [1, 2, 3, 4, 5]\nprint(sum(numbers))'

Acquisizione di stdout e stderr

Quando esegue codice generato dall'agente, deve acquisire sia stdout (output normale) sia stderr (messaggi di errore e traceback) per restituirli all'LLM. L'utilizzo del modulo Python subprocess è l'approccio più semplice: esegua il codice come processo separato e acquisisca entrambi i flussi. Imposti un timeout per impedire che il codice bloccato impedisca il funzionamento dell'agente.

import subprocess
import tempfile
import os

def execute_code(code: str, timeout_seconds=30) -> tuple[str, str]:
    # Write code to a temp file
    with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
        f.write(code)
        tmp_path = f.name
    
    try:
        result = subprocess.run(
            ['python', tmp_path],
            capture_output=True,
            text=True,
            timeout=timeout_seconds
        )
        stdout = result.stdout[:5000]  # cap output length
        stderr = result.stderr[:2000]  # cap error length
        return stdout, stderr
    except subprocess.TimeoutExpired:
        return '', f'TimeoutError: code exceeded {timeout_seconds}s limit'
    finally:
        os.unlink(tmp_path)  # always clean up temp file

Gestione intelligente degli errori

Quando il codice produce un errore, il messaggio di errore (traceback, tipo di eccezione, numero di riga) viene restituito all'LLM come osservazione. L'LLM analizza quindi l'errore e scrive il codice corretto. Per aiutare l'LLM a eseguire il debug in modo efficace, includa nell'osservazione il testo esatto dell'errore, includa la riga di codice che ha causato l'errore e chieda esplicitamente all'LLM di correggerlo prima di riprovare.

def format_error_observation(code: str, error: str) -> str:
    lines = code.split('\n')
    
    # Extract line number from traceback if available
    line_match = re.search(r'line (\d+)', error)
    error_context = ''
    if line_match:
        line_num = int(line_match.group(1))
        if 1 <= line_num <= len(lines):
            error_context = f'\nFailing line: {lines[line_num - 1].strip()}'
    
    return f'''Execution failed with error:{error_context}\n\n{error}\n\nPlease analyze the error and write corrected code.'''

# Usage in loop
output, err = execute_code(code)
if err:
    observation = format_error_observation(code, err)
else:
    observation = f'Output:\n{output}\n\nContinue with the next step or provide the final answer.'

Rilevamento del completamento dell'attività

Il ciclo di esecuzione del codice deve sapere quando fermarsi. Tra i segnali comuni di completamento vi sono: l'LLM restituisce una risposta senza blocchi di codice (solo una risposta in linguaggio naturale), l'LLM scrive un marcatore speciale come # TASK_COMPLETE, il codice produce un file di output che l'agente doveva creare oppure una funzione di convalida conferma che l'output soddisfa i criteri di successo. Implementi sempre un limite di iterazioni come fallback.

COMPLETION_MARKERS = ['TASK COMPLETE', 'FINAL ANSWER:', 'DONE:']

def is_task_complete(response: str, code: str | None, output: str, success_fn=None) -> bool:
    # Check for explicit completion markers
    for marker in COMPLETION_MARKERS:
        if marker in response.upper():
            return True
    
    # No code generated - LLM is done
    if code is None:
        return True
    
    # Custom success function (e.g., check output file exists)
    if success_fn and success_fn(output):
        return True
    
    return False

# Example success function
def report_was_generated(output: str) -> bool:
    import os
    return os.path.exists('analysis_report.pdf')

Passaggio dei dati tra le iterazioni

Una difficoltà dei cicli di esecuzione del codice è la persistenza dello stato tra le iterazioni. Ogni blocco di codice viene eseguito in un processo Python nuovo, quindi le variabili definite in un'iterazione non sono disponibili in quella successiva. Le soluzioni includono: scrivere i dati intermedi in file, utilizzare un processo persistente con codice inserito tramite exec() oppure istruire esplicitamente l'LLM a ridefinire le variabili necessarie all'inizio di ogni blocco di codice.

# Strategy 1: Save to files between iterations
# Iteration 1: LLM writes
'''
import pandas as pd
df = pd.read_csv('data.csv')
df_cleaned = df.dropna()
df_cleaned.to_parquet('cleaned.parquet')  # save for next iteration
print('Cleaned rows:', len(df_cleaned))
'''

# Iteration 2: LLM reads previous output
'''
import pandas as pd
df = pd.read_parquet('cleaned.parquet')  # reload from file
result = df.groupby('category').sum()
result.to_csv('result.csv')
print(result.head())
'''

Strutturazione del prompt di sistema

Il prompt di sistema di un agente per l'esecuzione del codice deve insegnare all'LLM a utilizzare il ciclo in modo efficace. Gli elementi principali da includere sono: come scrivere blocchi di codice Python eseguibili, come controllare gli output intermedi prima di procedere, come segnalare il completamento dell'attività, quali librerie sono disponibili nella sandbox e quali percorsi di file l'agente è autorizzato a leggere e scrivere.

CODE_AGENT_SYSTEM_PROMPT = '''
You are a Python code execution agent. Solve tasks by writing and running code.

Rules:
1. Write ALL code inside ```python ... ``` blocks.
2. Write small, testable code blocks - do not try to do everything in one block.
3. When you see execution output, analyze it and decide whether to continue or fix errors.
4. Available libraries: pandas, numpy, matplotlib, requests, json, os, pathlib.
5. You can read/write files only in /workspace/ directory.
6. When the task is fully complete, say TASK COMPLETE and summarize the result.
7. If you are stuck after 3 attempts at the same error, say ESCALATE and explain the problem.
'''

Troncamento dell'output e gestione dei token

Il codice generato dagli agenti può produrre output enormi: stampe di DataFrame, elenchi molto lunghi, log dettagliati. Restituire l'intero output all'LLM spreca token e può superare la finestra di contesto. Tronchi sempre l'output dell'esecuzione prima di aggiungerlo alla conversazione. Un valore predefinito efficace è compreso tra 2000 e 5000 caratteri, accompagnato da una nota che indichi che l'output è stato troncato, così l'LLM sa che potrebbe esserci altro da esplorare.

MAX_OUTPUT_CHARS = 3000

def format_observation(stdout: str, stderr: str) -> str:
    if stderr:
        # Errors take priority - show full error
        return f'Error:\n{stderr[:MAX_OUTPUT_CHARS]}'
    
    if len(stdout) > MAX_OUTPUT_CHARS:
        truncated = stdout[:MAX_OUTPUT_CHARS]
        remaining = len(stdout) - MAX_OUTPUT_CHARS
        return f'{truncated}\n... [output truncated, {remaining} more characters]'
    
    return f'Output:\n{stdout}' if stdout else 'Code executed successfully (no output)'

Il ciclo del codice nella pratica

Un vero agente per l'esecuzione del codice destinato all'analisi dei dati potrebbe eseguire da 5 a 15 iterazioni per: caricare un CSV, esplorarne la struttura, pulire i dati, calcolare statistiche, generare un grafico e produrre un report riepilogativo. Ogni iterazione si basa sulla precedente e l'LLM adatta il proprio approccio in base ai valori e alle forme effettivi dei dati scoperti durante l'esecuzione. È questo adattamento dinamico a rendere gli agenti di codice così potenti per le attività esplorative.

Considerazioni sulla sicurezza nei cicli del codice

Eseguire codice generato da un LLM sulla propria infrastruttura rappresenta un rischio significativo per la sicurezza. Non esegua mai il codice dell'agente senza una sandbox. L'LLM potrebbe generare inavvertitamente (oppure a causa di una prompt injection) codice che: elimina file, esfiltra dati, invia richieste di rete a servizi esterni oppure esaurisce le risorse di sistema. Esegua sempre il codice in un ambiente isolato con limiti rigorosi sulle risorse, restrizioni di rete e confini del filesystem.

Verifica rapida

Verifichi la propria comprensione del ciclo di esecuzione del codice trattato in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che: il ciclo scrittura-esecuzione-osservazione è il modello fondamentale degli agenti per l'esecuzione del codice; stdout e stderr dell'esecuzione vengono restituiti come osservazioni che guidano la generazione del codice successivo da parte dell'LLM; e il troncamento dell'output e i limiti di iterazione sono meccanismi di sicurezza essenziali. Nella prossima lezione analizzeremo l'isolamento dell'esecuzione del codice con Docker e RestrictedPython.

Domande Frequenti

La lezione «Il loop di esecuzione del codice» è gratuita?

Sì — il testo completo di «Il loop di esecuzione del codice» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.

Cosa imparerò in «Il loop di esecuzione del codice»?

Progetti il loop scrittura-esecuzione-osservazione, in cui l'agente genera codice, un esecutore in sandbox lo esegue, stdout e stderr vengono acquisiti e restituiti come osservazioni e l'agente corre… Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Engineering Academy?

Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Il loop di esecuzione del codice»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?

Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Il loop di esecuzione del codice
  2. Sandboxing con Docker e RestrictedPython
  3. Gestire lo stato tra i passaggi di esecuzione
  4. Creare un agente per l'analisi dei dati
← Torna a AI Engineering Academy