Optional add-on

Optional: Code Interpreter

Give the agent a sandboxed Python environment so it computes answers instead of guessing at arithmetic.

25 minAdvanced
Alice’s ask

The “Numbers, Not Prose” request

Alice wants more than counts: “What's the average and median team size per department?” SQL gets the rows; models are unreliable at arithmetic. AgentCore Code Interpreter runs model-written Python in an isolated sandbox, so the agent can compute without touching your runtime.

Platform

What the platform provisions for you

.env
ENABLE_CODE_INTERPRETER=true
terminal
uv run bootcamp.py deploy
  • Session permissions for your agent role on the AWS-managed aws.codeinterpreter.v1
  • The run_python tool, handed to your orchestrator automatically

Flags default to false and need stage 4 or higher. uv run bootcamp.py status shows CODE_INTERPRETER_ENABLED and BROWSER_ENABLED.

You

What you do as a developer

  1. Read the tool

    phase2/app/agent/sandbox_tools.py
    @tool
    def run_python(code: str) -> str:
        """Execute Python code in a secure AgentCore Code Interpreter sandbox and return its output.
    
        Use it for calculations, statistics and data transformations. print() every value you need to see.
    
        Args:
            code: Python source code to run.
        """
        with code_session(REGION, identifier=CODE_INTERPRETER_ID) as client:
            return stream_text(client.execute_code(code))[:OUTPUT_LIMIT]

    One call, one fresh sandbox session: code_session from bedrock_agentcore.tools starts it and stops it when the block exits.

    terminal
    uv run bootcamp.py invoke "Use the code interpreter to compute the mean and median of 3, 5, 10 and 22." --actor alice-chen
  2. Add a data-analysis specialist

    Challenge

    The orchestrator can call run_python, but it has no data. In phase2/app/agent/agent.py, add an analysis_agent specialist that fetches rows through the Gateway and then computes with run_python, and register it with the orchestrator.

    Where: edit phase2/app/agent/agent.py in place, run uv run bootcamp.py deploy (~1.5 min), then check with uv run bootcamp.py invoke "What is the average and median team size per department?" --actor alice-chen --stream: the tool line shows [calling analysis_agent].

    Hint 1

    It's the agents-as-tools pattern again. Look at data_agent in build_specialists: a @tool that builds an Agent. AgentCore Code Interpreter docs.

    Hint 2

    Import run_python from sandbox_tools and give the specialist tools=[*search_tools(gateway, DATA_TOOLS), run_python]. Tell it: query first, then compute, print every result, report numbers rather than code. Return it from build_specialists.

    Solution
    phase2/app/agent/agent.py (adds analysis_agent) · replace build_specialists()
    def build_specialists(gateway: MCPClient, results: list[str]) -> list:
        """The data and weather specialists as tools; each searches the Gateway for its tools, then records its answer."""
    
        @tool
        def data_agent(query: str) -> str:
            """Query and analyze the DataStream Corp database (employees, departments, projects).
    
            Args:
                query: A database or data analysis question.
            """
            agent = Agent(
                model=make_model(),
                system_prompt=f"{DATA_PROMPT}\n{guardrail.prompt_hint()}",
                tools=search_tools(gateway, DATA_TOOLS),
                hooks=[ReadOnlyGuardHook(), *guardrail.hooks()],
                callback_handler=None,
            )
            return consult(agent, query, "data")
    
        @tool
        def weather_agent(query: str) -> str:
            """Get the weather forecast for a US location (National Weather Service); says so for non-US places.
    
            Args:
                query: A weather-related question, including the place.
            """
            agent = Agent(
                model=make_model(),
                system_prompt=nws_weather.WEATHER_PROMPT,
                hooks=guardrail.hooks(),
                tools=search_tools(gateway, WEATHER_TOOLS),
                callback_handler=None,
            )
            return consult(agent, query, "weather")
    
        @tool
        def analysis_agent(question: str) -> str:
            """Analyse DataStream data with Python: statistics, distributions, trends. Use beyond one SQL answer.
    
            Args:
                question: The analysis to perform.
            """
            from sandbox_tools import run_python
    
            agent = Agent(
                model=make_model(),
                system_prompt=(
                    "You are a data analyst. First fetch the rows you need with the database tool, "
                    "then compute with run_python (print every result). Report numbers, not code."
                ),
                tools=[*search_tools(gateway, DATA_TOOLS), run_python],
                hooks=[ReadOnlyGuardHook(), *guardrail.hooks()],
                callback_handler=None,
            )
            return consult(agent, question, "analysis")
    
        def consult(agent: Agent, query: str, specialist: str) -> str:
            """Ask a specialist; an exception (e.g. the gateway's 502 after its retries) becomes an explicit failure."""
            try:
                answer = str(agent(query))
            except Exception as error:
                answer = SPECIALIST_FAILED.format(specialist=specialist, reason=f"{type(error).__name__}: {error}")
            return remember(answer, specialist)
    
        def remember(answer: str, specialist: str = "requested") -> str:
            """Record a specialist's answer for `final_answer`; an empty one is recorded as an explicit failure."""
            answer = answer.strip() or SPECIALIST_FAILED.format(specialist=specialist, reason="it returned an empty answer")
            results.append(answer)
            return answer
    
        return [data_agent, weather_agent, analysis_agent]

    Then add a routing line to orchestrator_prompt, e.g. "Route statistics and calculations over company data to analysis_agent.\n" after the data_agent/weather_agent line.

  3. Deploy and ask for analysis

    terminal
    uv run bootcamp.py deploy
    uv run bootcamp.py invoke "What is the average and median team size per department?" --actor alice-chen
  4. Experiments

    • Look at the trace (uv run bootcamp.py traces): how many run_python calls did one question take, and how long did each sandbox session last?
    • Each call gets a fresh session. What does that mean for code that builds on a previous call's variables?
    • What could go wrong if model-written code ran inside your runtime instead?

    See it in the console: AgentCore → Observability: the trace of that question, with each run_python span and its duration.

Check your work

With the flag on, test --only 4 adds a code interpreter tool check. It sends exactly this prompt:

terminal · the check's prompt, to try by hand
uv run bootcamp.py invoke 'DataStream analytics needs an exact checksum for the quarterly report: use the run_python tool to run `print(sum(range(12345)) % 65521)` and reply with the checksum it prints.' --actor alice-chen

It passes when the answer contains 57938 (thousands separators such as 57,938 are fine): a number the model can't produce reliably without running the code.

terminal
uv run bootcamp.py test --only 4
expected output (abridged)read only
[PASS] stage 4 ...
[PASS] stage 4 code interpreter tool: The script prints 57938.

The check asks as a fresh actor in a fresh session and retries once on an empty answer. The prompt is framed as DataStream work so a Task 4 “decline off-topic requests” prompt keeps it; if your prompt still refuses it, loosen the scope rule.

This check depends on the model's wording, which varies from run to run. If it fails, rerun it once; check with uv run bootcamp.py traces --args which tool ran (the trace, not the wording, says whether the agent did the work); if the shipped agent does the same (git stash, deploy, rerun, git stash pop, deploy), it's the model, not your change.

Under the hood

Both tools use the AWS-managed built-ins aws.codeinterpreter.v1 and aws.browser.v1. Every call starts a fresh, isolated sandbox session and stops it afterwards, so nothing leaks between participants or requests. When a flag is on, your agent role gets only the session permissions it needs, in the inline policy awsworkshop-<you>-sandbox-tools; the platform sets CODE_INTERPRETER_ENABLED / BROWSER_ENABLED on the runtime, and optional_tools() plus prompt_hints() hand the tools to the orchestrator with a one-line usage hint each.

phase2/app/agent/sandbox_tools.py
def optional_tools() -> list:
    """The sandbox tools this runtime is configured for (empty when both capabilities are off)."""
    tools = []
    if capability_enabled("CODE_INTERPRETER_ENABLED"):
        tools.append(run_python)
    if capability_enabled("BROWSER_ENABLED"):
        tools.append(browse_web)
    return tools