LLMs like Anthropic’s Claude are good at writing code, but can’t execute code. Fortunately, Claude understands this and allows you to supplement its functionality with external “tools”.

In this guide we’ll hook up the Riza Code Interpreter API as a tool for Claude to use when it wants to execute code. Using Riza as the code execution environment keeps your local machine safe in case the code written by Claude does something unexpected.

We’ll run Python, but Riza can execute JavaScript, PHP, and Ruby too.

Example code

Get the full code for this example in our GitHub.

Getting started

To generate Python with Anthropic’s models, you’ll need an API key from the Anthropic Console.

To execute Python using Riza’s Code Interpreter API, you’ll need an API key from the Riza Dashboard.

Make these API keys available in your shell environment:

export ANTHROPIC_API_KEY="your_anthropic_api_key"
export RIZA_API_KEY="your_riza_api_key"

In this guide we’ll use the Anthropic and Riza Python API client libraries.

Python environment setup

Create a virtualenv and activate it:

python3 -m venv venv
source venv/bin/activate

Install the anthropic and rizaio packages with pip:

pip install anthropic rizaio

Scenario

We’ll create a message for Claude that requires code execution to answer. Here, we ask Claude to base32 encode the message “purple monkey dishwasher.”

messages = [
    {
        "role": "user",
        "content": "Please base32 encode this message: purple monkey dishwasher"
    },
]

Sending this message to Claude without offering any additional tools results in unexpected hallucinated output. Here are two consecutive responses to the above prompt.

Response 1
The base32 encoded version of the message "purple monkey dishwasher" is:

PRUPVY6ZCRMFZXGNLUMVXG64TFOQ
Response 2
Here is the base32 encoded version of the message "purple monkey dishwasher":

PRYXGEYRCUDQVS4

The results are inconsistent, and neither is correct (the correct result is OB2XE4DMMUQG233ONNSXSIDENFZWQ53BONUGK4Q=).

For a correct result, we can offer Claude a tool to execute Python.

Step 1: Import required libraries

First we import and initialize required libraries from Anthropic and Riza.

import anthropic
import rizaio

client = anthropic.Anthropic()
riza = rizaio.Riza()

Step 2: Define a code execution tool

Before sending the message to Claude, we’ll describe the Riza Code Interpreter as a tool using a tool definition that Claude understands.

In this example, we will use Riza’s Execute Function API to run the code. So we define a tool that takes in the required inputs of the Execute Function API:

tools = [{
    "name": "execute_python_function",
    "description":
        """
        Execute a Python function that takes in one parameter, `input`.
        `input` is a Python object that can have any fields.
        The Python runtime includes the entire standard library.
        Write output by returning a Python object with any relevant fields.
        """,
    "input_schema": {
        "type": "object",
        "properties": {
            "code": {
                "type": "string",
                "description":
                    """
                    The Python function to execute.
                    The function signature must be: `def execute(input)`.
                    """,
            },
            "input": {
                "type": "object",
            }
        },
        "required": ["code", "input"],
    }
}]

In our script, we’ll also define an execute_function() helper, which we’ll use to handle any tool use calls. This function uses Riza to safely execute code.

def execute_function(language, code, function_input):
    resp = riza.command.exec_func(
        language=language,
        code=code,
        input=function_input
    )
    if int(resp.execution.exit_code) > 0:
        raise ValueError(f"Riza execution resulted in a non-zero exit code: {resp.execution.exit_code}")
    return resp

Step 3: Call Claude and handle tool use

Now we can send the message and tool definition to Claude.

response = client.messages.create(
    model="claude-3-7-sonnet-latest",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

If Claude wants to use the execute_python_function tool to execute Python code, the response will include a tool_use block with the execute_python_function name.

This block will have a set of input parameters corresponding to the input properties we specified in our tool definition. In this case, the parameters we care about are code and input.

We take the code that Claude wants to run and execute it using Riza:

for block in response.content:
    if block.type == 'tool_use' and block.name == 'execute_python_function':
        tool_used = True
        riza_response = execute_function("python", block.input['code'], block.input['input'])

        messages.append({
            "role": "assistant",
            "content": response.content,
        })
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": str(riza_response.output),
                }
            ],
        })

If the execution is successful, we add the output as a tool_result message to send back. This step is optional, as the code execution output might be all you need.

Step 4: (optional) Call Claude with the tool use result for a final answer

We send the updated list of messages back to Claude to get a final answer incorporating the code execution output:

response = client.messages.create(
    model="claude-3-7-sonnet-latest",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)
print(response)

With the extra tool in its belt, Claude comes up with the correct response.

Response
The base32 encoded version of the message "purple monkey dishwasher" is:

OB2XE4DMMUQG233ONNSXSIDENFZWQ53BONUGK4Q=

See the full example on GitHub.