代码执行循环
设计“编写-执行-观察”循环:智能体生成代码,沙箱执行器运行代码,捕获标准输出和标准错误并将其作为观察结果反馈,然后由智能体修复错误。
代码执行循环 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
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.
常见问题解答
「代码执行循环」课时是免费的吗?
是的 — 「代码执行循环」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。
「代码执行循环」这节课中我会学到什么?
设计“编写-执行-观察”循环:智能体生成代码,沙箱执行器运行代码,捕获标准输出和标准错误并将其作为观察结果反馈,然后由智能体修复错误。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Engineering Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「代码执行循环」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Engineering Academy 课中编写并运行代码吗?
能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。