El bucle de ejecución de código
Diseñe el bucle escribir-ejecutar-observar, en el que el agente genera código, un ejecutor aislado lo ejecuta, captura stdout y stderr y los devuelve como observaciones, y el agente corrige los errores.
El bucle de ejecución de código es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.
Partes de esta lección aún no han sido traducidas y se muestran en inglés.
Code Agents: Writing and Running Code
A code execution agent is a special type of AI agent that solves problems by writing code, running it, observing the output, and iterating until the task is complete. Unlike agents that only use pre-defined tools, code agents create new computational tools on the fly. This makes them extraordinarily flexible: any task that can be programmed can be attempted by a code agent.
The Write-Execute-Observe Loop
The core of a code execution agent is a loop with three steps: Write — the LLM generates Python code to solve the current step of the task. Execute — the code is run in a sandboxed environment and stdout/stderr are captured. Observe — the execution output is fed back to the LLM as a new observation, which it uses to decide what to write next. This loop continues until the task is complete or an iteration limit is reached.
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'Extracting Code Blocks from LLM Output
LLMs typically wrap generated code in markdown fenced code blocks: ```python ... ```. Your execution loop must reliably extract the code from these blocks before running it. Use a regex or a simple parser to find fenced blocks and handle edge cases like nested backticks, multiple code blocks in one response, or code without a language specifier.
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))'Capturing stdout and stderr
When executing agent-generated code, you must capture both stdout (normal output) and stderr (error messages and tracebacks) to feed back to the LLM. Using Python's subprocess module is the simplest approach: run the code as a separate process and capture both streams. Set a timeout to prevent code that hangs from blocking the agent.
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 fileHandling Errors Intelligently
When code produces an error, the error message (traceback, exception type, line number) is fed back to the LLM as an observation. The LLM then analyzes the error and writes corrected code. To help the LLM debug effectively, include the exact error text in the observation, include the line of code that caused the error, and ask the LLM explicitly to fix it before trying again.
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.'Detecting Task Completion
The code execution loop must know when to stop. Common completion signals include: the LLM returns a response with no code block (only a natural language answer), the LLM writes a special marker like # TASK_COMPLETE, the code produces an output file that the agent was asked to create, or a validation function confirms the output meets the success criteria. Always implement an iteration limit as a 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')Passing Data Between Iterations
One challenge in code execution loops is state persistence between iterations. Each code block runs in a fresh Python process, so variables defined in one iteration are not available in the next. Solutions include: writing intermediate data to files, using a persistent process with code injected via exec(), or explicitly instructing the LLM to redefine necessary variables at the start of each code block.
# 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())
'''Structuring the System Prompt
The system prompt for a code execution agent must teach the LLM how to use the loop effectively. Key elements to include: how to write executable Python code blocks, how to check intermediate outputs before proceeding, how to signal task completion, what libraries are available in the sandbox, and what file paths the agent is allowed to read from and write to.
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.
'''Output Truncation and Token Management
Agent-generated code can produce enormous outputs: DataFrame printouts, long lists, verbose logs. Feeding the full output back to the LLM wastes tokens and can overflow the context window. Always truncate execution output before adding it to the conversation. A good default is 2000-5000 characters, plus a note that the output was truncated so the LLM knows it may have more to explore.
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)'The Code Loop in Practice
A real code execution agent for data analysis might run 5-15 iterations to: load a CSV, explore its structure, clean the data, compute statistics, generate a chart, and produce a summary report. Each iteration builds on the previous, with the LLM adapting its approach based on actual data values and shapes discovered at runtime. This dynamic adaptation is what makes code agents so powerful for exploratory tasks.
Security Considerations in Code Loops
Running LLM-generated code on your infrastructure is a significant security risk. Never run agent code without sandboxing. The LLM might inadvertently (or due to prompt injection) generate code that: deletes files, exfiltrates data, makes network requests to external services, or exhausts system resources. Always execute in an isolated environment with strict resource limits, network restrictions, and filesystem boundaries.
Quick Check
Test your understanding of the code execution loop from this lesson.
Lesson Recap
In this lesson you learned: the write-execute-observe loop is the core pattern of code execution agents, stdout and stderr from execution are fed back as observations that guide the LLM's next code generation, and output truncation and iteration limits are essential safety mechanisms. Next up we explore sandboxing code execution with Docker and RestrictedPython.
Preguntas frecuentes
¿La lección «El bucle de ejecución de código» es gratis?
Sí — el texto completo de «El bucle de ejecución de código» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.
¿Qué aprenderé en «El bucle de ejecución de código»?
Diseñe el bucle escribir-ejecutar-observar, en el que el agente genera código, un ejecutor aislado lo ejecuta, captura stdout y stderr y los devuelve como observaciones, y el agente corrige los error… Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Engineering Academy?
No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «El bucle de ejecución de código»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?
Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- El bucle de ejecución de código
- Aislamiento con Docker y RestrictedPython
- Gestión del estado entre pasos de ejecución
- Creación de un agente de análisis de datos