Add granular developer guide for BaseCodeExecutor covering architecture, execution flow, backend environments (BuiltIn, UnsafeLocal, Container, GKE gVisor, Vertex AI, Agent Engine), delimiter extraction, and error retry loops under docs/guides/code_executors/code_executor/index.md. Co-authored-by: Shangjie Chen <deanchen@google.com> PiperOrigin-RevId: 967423934
9.5 KiB
BaseCodeExecutor
BaseCodeExecutor is the interface ADK uses to execute model-generated code blocks and incorporate execution results into the conversation. It supports model-delegated execution, isolated local processes, Docker containers, Kubernetes/GKE sandboxes, and managed Vertex AI / Agent Engine backends.
Introduction
Language models excel at generating code to perform mathematical calculations, data transformations, and chart rendering, but they cannot execute code directly without an execution runtime. Running untrusted model-generated code on an application server introduces security and reliability risks, including resource exhaustion, privilege escalation, and credential exposure.
The code executor subsystem resolves this by abstracting code execution behind BaseCodeExecutor. An LlmAgent delegates code block extraction, execution isolation, and result formatting to the configured executor. Six implementations ship with ADK covering development, containerized isolation, Kubernetes orchestration, and Google Cloud managed environments.
Get started
The code executor is configured on an LlmAgent through the code_executor parameter. The example below configures UnsafeLocalCodeExecutor to run Python code generated by the agent in a local subprocess:
agent = LlmAgent(
name="calculator_agent",
instruction="When asked a math problem, write Python code to compute the exact result.",
code_executor=UnsafeLocalCodeExecutor(timeout_seconds=30),
)
When the model produces a code block enclosed in Python delimiters, the agent intercepts the block, runs it through execute_code, and feeds the standard output or error back to the model before returning the final answer.
How it works
The execution lifecycle coordinates between LlmAgent, the _code_execution flow processor, CodeExecutionUtils, and the executor:
sequenceDiagram
autonumber
participant Model as LLM / Model Response
participant Flow as _code_execution Processor
participant Utils as CodeExecutionUtils
participant Exec as BaseCodeExecutor
Model->>Flow: Generates response with code block
Flow->>Utils: extract_code_and_truncate_content()
Utils-->>Flow: Returns extracted ExecutableCode Part
Flow->>Exec: execute_code(InvocationContext, CodeExecutionInput)
Exec-->>Flow: Returns CodeExecutionResult (stdout/stderr/files)
Flow->>Utils: build_code_execution_result_part()
Utils-->>Flow: Returns CodeExecutionResult Part
alt Execution Failed (stderr) & retries remaining
Flow->>Model: Re-send error in conversation for correction
else Success or max retries reached
Flow->>Model: Pass execution result for final synthesis
end
- Extraction: During post-processing of a model response,
CodeExecutionUtils.extract_code_and_truncate_contentscans the response text for delimiters configured incode_block_delimiters(e.g.```pythonor```tool_code). The first detected code block is extracted into atypes.Part.from_executable_code. - Execution: The flow wraps the code and any session-attached files into a
CodeExecutionInputand invokescode_executor.execute_code(invocation_context, code_execution_input). - Result Formatting:
CodeExecutionResultcapturesstdout,stderr, and generatedoutput_files.CodeExecutionUtils.build_code_execution_result_partconverts this into atypes.Part.from_code_execution_resultwith statusOUTCOME_OKorOUTCOME_FAILED. - Retry Loop: If execution produces
stderr, the agent increments the invocation error count viaCodeExecutorContext. Iferror_retry_attemptshas not been exceeded, the error is appended to the conversation context and sent back to the model for correction. - Multi-Turn Conversion: On subsequent model calls,
CodeExecutionUtils.convert_code_execution_partsconverts prior executable code and result parts to text representations so standard chat models without native code-execution tool calling can parse previous turns.
Configuration options
The base class defines the following configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
optimize_data_file |
bool |
False |
Extract and process CSV data files from the model request and attach them to the executor. |
stateful |
bool |
False |
Whether state and variables persist across multiple code executions within a session. |
error_retry_attempts |
int |
2 |
Number of consecutive execution error retries before giving up. |
code_block_delimiters |
list[tuple[str, str]] |
[('```tool_code\n', '\n```'), ('```python\n', '\n```')] |
Delimiter pairs used to locate code blocks in model output. |
execution_result_delimiters |
tuple[str, str] |
('```tool_output\n', '\n```') |
Delimiters used when formatting execution output for text-based model turns. |
timeout_seconds |
int | None |
None |
Wall-clock execution timeout in seconds. |
optimize_data_file instructs the flow to inspect user prompt parts for text/csv payloads, parsing them and placing them into CodeExecutionInput.input_files so the executed code can load datasets without manual upload plumbing.
stateful declares whether variables, functions, and imports defined in one turn remain accessible in subsequent code executions. Stateless executors recreate fresh processes for every snippet.
error_retry_attempts limits the self-correction loop when generated code raises an exception or syntax error. Once exhausted, the agent stops re-prompting the model and surfaces the error.
code_block_delimiters and execution_result_delimiters control the syntactic markup for identifying code blocks in model output and presenting results back to models that do not support native executable code parts.
timeout_seconds sets a hard cutoff for code execution. Subclasses implement timeouts via supervisor processes, container alarms, or Kubernetes watch intervals.
Choosing an implementation
All concrete code executor implementations are imported from google.adk.code_executors.
| Implementation | Environment / Use Case | Isolation Level |
|---|---|---|
BuiltInCodeExecutor |
Gemini native server-side execution. | Model provider sandbox |
UnsafeLocalCodeExecutor |
Local dev and unit tests. Runs via multiprocessing.Process. |
None (runs on host machine) |
ContainerCodeExecutor |
Local or self-hosted Docker container with network disabled and dropped capabilities. | Container isolation |
GkeCodeExecutor |
Kubernetes cluster using gVisor-sandboxed Pods or Agent Sandbox client. | Kernel sandbox (gVisor) |
VertexAiCodeExecutor |
Google Cloud Vertex AI Code Interpreter Extension. | Managed cloud sandbox |
AgentEngineSandboxCodeExecutor |
Vertex AI Reasoning Engine / Agent Engine sandbox environments. | Managed cloud sandbox |
Advanced applications
Hardened container execution
For self-hosted production workloads, ContainerCodeExecutor provides isolated Docker execution with non-root security defaults:
from google.adk.agents import LlmAgent
from google.adk.code_executors import ContainerCodeExecutor
agent = LlmAgent(
name="data_analyst",
instruction="Analyze data using Python scripts.",
code_executor=ContainerCodeExecutor(
image="python:3.11-slim",
network_enabled=False,
timeout_seconds=60,
),
)
Kubernetes gVisor sandboxing
In Kubernetes environments, GkeCodeExecutor provisions ephemeral Jobs with gVisor (runsc) runtime isolation:
from google.adk.agents import LlmAgent
from google.adk.code_executors import GkeCodeExecutor
agent = LlmAgent(
name="k8s_code_agent",
code_executor=GkeCodeExecutor(
namespace="agent-sandboxes",
image="python:3.11-slim",
cpu_limit="1000m",
mem_limit="1Gi",
timeout_seconds=120,
),
)
Limitations
- Sequential Execution of All Code Blocks: ADK automatically extracts and executes every code block matching the configured delimiters in sequence. It does not support selective or conditional execution of individual code blocks within a single response turn.
- Host Security with
UnsafeLocalCodeExecutor:UnsafeLocalCodeExecutorexecutes code on the local host. It must never be used with untrusted user input in production. - Statefulness Backend Constraints:
UnsafeLocalCodeExecutorandContainerCodeExecutordo not supportstateful=Trueoroptimize_data_file=True. Setting either parameter raisesValueError. - Optional Dependencies:
VertexAiCodeExecutor,ContainerCodeExecutor,GkeCodeExecutor, andAgentEngineSandboxCodeExecutorrequire extra packages installed viapip install "google-adk[extensions]".
Related samples
- Built-in Code Execution — Data science agent using
BuiltInCodeExecutor. - GKE Sandbox Code Execution — Secure Python execution in GKE with
GkeCodeExecutor. - Custom Code Execution — Stateful custom execution extending
VertexAiCodeExecutor. - Agent Engine Sandbox — Managed code execution using
AgentEngineSandboxCodeExecutor. - Vertex AI Code Execution — Using Vertex Code Interpreter with session state.