6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon Bedrock AgentCore Code Interpreterの環境をお好みで拡張する

6
Last updated at Posted at 2025-11-01

はじめに

Amazon Bedrock AgentCore Code Interpreterは、AIエージェントが実行するのに最適化されたクローズドな実行環境だ。コンテナにより起動・破棄を簡単に行うことができ、安全なコード実行を行うことができる。

たとえば、AIエージェントに対して以下の処理を実行させるようなユースケースが考えられる。

  • 複雑な計算式を実行した結果を取得したい
  • 集計した情報をグラフ化する(グラフ描画ライブラリを使う)
  • 時間のかかる集計処理を Amazon Bedrock AgentCore から切り離して非同期で実行する

以下、公式のDeveloper Guideより抜粋のイメージ図。

image.png

このCode Interpreterの実行環境は、以下のページに書かれているライブラリがプリインストールされている。

デフォルトでも充分な種類のライブラリ入っているが、本記事では、この中にあるものでは足りない場合の対応方法について紹介をする。

Amazon Bedrock AgentCore Code Interpreterを定義する

10/17に、TerraformのAWS ProviderがAmazon Bedrock AgentCoreのresourcesに対応したので、まずはこれでCode Interpreterを定義する。

resource "aws_bedrockagentcore_code_interpreter" "example" {
  name        = local.bac_codeinterpreter_name
  description = "Example Code Interpreter"

  network_configuration {
    network_mode = "PUBLIC"
  }
}

とても簡単!

なお、Code Interpreterを実行するには以下の権限が必要になるため、実行するprincipal(たとえばAmazon Bedrock AgentCore Runtimeから実行する場合はbedrock-agentcore.amazonaws.comなど)に対して権限を追加しておこう。

data "aws_iam_policy_document" "custom" {
  # (中略)
  statement {
    effect = "Allow"

    actions = [
      "bedrock-agentcore:StartCodeInterpreterSession",
      "bedrock-agentcore:InvokeCodeInterpreter",
      "bedrock-agentcore:StopCodeInterpreterSession",
      "bedrock-agentcore:GetCodeInterpreterSession", # 今回は使用しない
    ]

    resources = [
      aws_bedrockagentcore_code_interpreter.example.code_interpreter_arn,
    ]
  # (中略)
}

環境を拡張してみる

まずは、お試しのPythonスクリプトでやってみよう。
今回は、実験として、pytestを実行可能にしてみる。

Code Interpreter IDの取得

コマンドラインで以下のように実行して、Code Interpreter IDを控えておこう。

$ terraform state show aws_bedrockagentcore_code_interpreter.example

サンプルスクリプト

次に、任意のコマンドをCode Interpreter上で実行する以下のスクリプトを作成する。

test.py
import boto3
import json
import sys

REGION = sys.argv[1]
CODE_INTERPRETER_ID = sys.argv[2]
COMMANDS = sys.argv[3:]

bedrock_agentcore_client = boto3.client("bedrock-agentcore", region_name=REGION)

session_response = bedrock_agentcore_client.start_code_interpreter_session(
    codeInterpreterIdentifier=CODE_INTERPRETER_ID,
    sessionTimeoutSeconds=180,
)
session_id = session_response["sessionId"]
print(f"Created session ID: {session_id}\n")

for COMMAND in COMMANDS:
    print(f"Execute command: {COMMAND}")
    response = bedrock_agentcore_client.invoke_code_interpreter(
        codeInterpreterIdentifier=CODE_INTERPRETER_ID,
        sessionId=session_id,
        name="executeCommand",
        arguments={"command": COMMAND},
    )
    for event in response["stream"]:
        print(json.dumps(event["result"], default=str, indent=2))
    print("\n")

print(f"Stopping the code interpreter session")
stop_response = bedrock_agentcore_client.stop_code_interpreter_session(
    codeInterpreterIdentifier=CODE_INTERPRETER_ID,
    sessionId=session_id,
)

これを、まずは素の状態でpytestを実行してみよう。

$ python test.py ap-northeast-1 <取得したCode Interpreter ID> pytest

すると、当然のことながらエラーになる。

Created session ID: XXXXXXXXXXXXXXXXXXXXXXXXXX

Execute command: pytest
{
  "content": [
    {
      "type": "text",
      "text": "/bin/sh: line 1: pytest: command not found\r\n"
    }
  ],
  "structuredContent": {
    "stdout": "",
    "stderr": "/bin/sh: line 1: pytest: command not found\r\n",
    "exitCode": 127,
    "executionTime": 0.00620579719543457
  },
  "isError": true
}


Stopping the code interpreter session

続いて、以下のように事前にインストールをしてみる。

python test.py ap-northeast-1 <取得したCode Interpreter ID> \
  'pip install --upgrade pip --progress-bar off' \
  'pip install pytest --progress-bar off' \
  'pytest --color=no'

すると、以下のような結果が得られる。
pytestの結果が"isError": trueだが、これは、pytestに何もテスト情報を渡していないためだ。
出力内容が"command not found"ではなくなっているので、インストール自体は正常に終了していることが分かる。

Created session ID: XXXXXXXXXXXXXXXXXXXXXXXXXX

Execute command: pip install --upgrade pip --progress-bar off
{
  "content": [
    {
      "type": "text",
      "text": "Requirement already satisfied: pip in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (25.2)\r\nCollecting pip\r\n  Downloading pip-25.3-py3-none-any.whl.metadata (4.7 kB)\r\nDownloading pip-25.3-py3-none-any.whl (1.8 MB)\r\nInstalling collected packages: pip\r\n  Attempting uninstall: pip\r\n    Found existing installation: pip 25.2\r\n    Uninstalling pip-25.2:\r\n      Successfully uninstalled pip-25.2\r\nSuccessfully installed pip-25.3\r\n"
    }
  ],
  "structuredContent": {
    "stdout": "Requirement already satisfied: pip in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (25.2)\r\nCollecting pip\r\n  Downloading pip-25.3-py3-none-any.whl.metadata (4.7 kB)\r\nDownloading pip-25.3-py3-none-any.whl (1.8 MB)\r\nInstalling collected packages: pip\r\n  Attempting uninstall: pip\r\n    Found existing installation: pip 25.2\r\n    Uninstalling pip-25.2:\r\n      Successfully uninstalled pip-25.2\r\nSuccessfully installed pip-25.3\r\n",
    "stderr": "",
    "exitCode": 0,
    "executionTime": 2.39536714553833
  },
  "isError": false
}


Execute command: pip install pytest --progress-bar off
{
  "content": [
    {
      "type": "text",
      "text": "Collecting pytest\r\n  Downloading pytest-8.4.2-py3-none-any.whl.metadata (7.7 kB)\r\nCollecting iniconfig>=1 (from pytest)\r\n  Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB)\r\nRequirement already satisfied: packaging>=20 in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (from pytest) (24.0)\r\nCollecting pluggy<2,>=1.5 (from pytest)\r\n  Downloading pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB)\r\nRequirement already satisfied: pygments>=2.7.2 in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (from pytest) (2.18.0)\r\nDownloading pytest-8.4.2-py3-none-any.whl (365 kB)\r\nDownloading pluggy-1.6.0-py3-none-any.whl (20 kB)\r\nDownloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB)\r\nInstalling collected packages: pluggy, iniconfig, pytest\r\nSuccessfully installed iniconfig-2.3.0 pluggy-1.6.0 pytest-8.4.2\r\n"
    }
  ],
  "structuredContent": {
    "stdout": "Collecting pytest\r\n  Downloading pytest-8.4.2-py3-none-any.whl.metadata (7.7 kB)\r\nCollecting iniconfig>=1 (from pytest)\r\n  Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB)\r\nRequirement already satisfied: packaging>=20 in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (from pytest) (24.0)\r\nCollecting pluggy<2,>=1.5 (from pytest)\r\n  Downloading pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB)\r\nRequirement already satisfied: pygments>=2.7.2 in /opt/amazon/genesis1p-tools/venv/lib64/python3.12/site-packages (from pytest) (2.18.0)\r\nDownloading pytest-8.4.2-py3-none-any.whl (365 kB)\r\nDownloading pluggy-1.6.0-py3-none-any.whl (20 kB)\r\nDownloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB)\r\nInstalling collected packages: pluggy, iniconfig, pytest\r\nSuccessfully installed iniconfig-2.3.0 pluggy-1.6.0 pytest-8.4.2\r\n",
    "stderr": "",
    "exitCode": 0,
    "executionTime": 1.016230583190918
  },
  "isError": false
}

Execute command: pytest --color=no
{
  "content": [
    {
      "type": "text",
      "text": "============================= test session starts ==============================\r\nplatform linux -- Python 3.12.11, pytest-8.4.2, pluggy-1.6.0\r\nrootdir: /opt/amazon/genesis1p-tools/var\r\nplugins: Faker-19.13.0, anyio-4.11.0\r\ncollecting ... \rcollected 0 items                                                              \r\n\r\n============================ no tests ran in 0.01s =============================\r\n"
    }
  ],
  "structuredContent": {
    "stdout": "",
    "stderr": "============================= test session starts ==============================\r\nplatform linux -- Python 3.12.11, pytest-8.4.2, pluggy-1.6.0\r\nrootdir: /opt/amazon/genesis1p-tools/var\r\nplugins: Faker-19.13.0, anyio-4.11.0\r\ncollecting ... \rcollected 0 items                                                              \r\n\r\n============================ no tests ran in 0.01s =============================\r\n",
    "exitCode": 5,
    "executionTime": 0.8704497814178467
  },
  "isError": true
}

Stopping the code interpreter session

再度以下のように実行すると、当然ながら"command not found"になるため、セッションを作る都度、環境が作り直されていることが分かる。

$ python test.py ap-northeast-1 <取得したCode Interpreter ID> pytest

AIエージェントに組み込む

では、今度はこのコードをAIエージェントに組み込んでみよう。
Strands Agents on Amazon Bedrock AgentCoreを使って、以下のように定義する。

さきほどのサンプルコードとの違いとして、writeFilesを使って、プロダクトコードとテストコードを適切なパスに配置することで、pytestを成功させる。

また、肝になるのは以下の部分。

    code_client = CodeInterpreter(os.environ.get("AWS_REGION"))
    code_session_id = code_client.start(
        identifier=os.environ.get("AWS_BEDROCK_AGENTCORE_CODEINTERPRETER_ID"),
        session_timeout_seconds=120,
    )

以下のDeveloper Guideでは、AgentCoreCodeInterpreter()という関数を使っているが、これは、AWSがデフォルトで用意しているSANDBOXでセッションを作ってしまい、変更することができない。GitHubのコードを読み込まないと上記の作りに辿り着けないので、ドキュメントが改訂されるまでは本記事を参考にしていただければと思う。

from bedrock_agentcore.tools.code_interpreter_client import CodeInterpreter
from strands import Agent, tool

@tool
def execute_pytest(
    code_path: str, code: str, test_code_path: str, test_code: str
) -> str:
    """Execute Python code"""
    logger.info("tool execute_pytest used.")

    code_client = CodeInterpreter(os.environ.get("AWS_REGION"))
    code_session_id = code_client.start(
        identifier=os.environ.get("AWS_BEDROCK_AGENTCORE_CODEINTERPRETER_ID"),
        session_timeout_seconds=120,
    )

    logger.info(f"code_session_id: {code_session_id}")

    response = code_client.invoke(
        "executeCommand", {"command": "pip install pytest", "clearContext": False}
    )

    for event in response["stream"]:
        logger.info(event["result"])

    response = code_client.invoke(
        "writeFiles",
        {
            "content": [{"path": code_path, "text": code}],
            "clearContext": False,
        },
    )

    for event in response["stream"]:
        logger.info(event["result"])

    response = code_client.invoke(
        "writeFiles",
        {
            "content": [{"path": test_code_path, "text": test_code}],
            "clearContext": False,
        },
    )

    for event in response["stream"]:
        logger.info(event["result"])

    response = code_client.invoke(
        "executeCommand",
        {"command": f"pytest {test_code_path} -v --color=no", "clearContext": False},
    )

    events = list(response["stream"])

    for event in events:
        logger.info(event["result"])

    for event in events:
        return json.dumps(event["result"])

これを、以下のように呼び出し元のエージェントに設定する。

    codetest_agent = Agent(
        model=bedrock_model,
        system_prompt="""# Description
You are a helpful AI assistant that validates code through test code execution.

TOOL AVAILABLE:
- execute_pytest: Run Pytest and see output. You should provide the code path, code, test code path, and test code.

RESPONSE FORMAT: The execute_pytest tool returns a JSON response with:
- isError: Boolean indicating if there was an error
- content: Array of content objects with type and text/data
- structuredContent: For code execution, includes stdout, stderr, exitCode, executionTime

For successful code execution, the output will be in content[0].text and also in structuredContent.stdout.
Check the isError field to see if there was an error.
""",
        tools=[execute_pytest],
    )

これを実行すると、

{
  "content": [
    {
      "type": "text",
      "text": "============================= test session starts ==============================\r\nplatform linux -- Python 3.12.11, pytest-8.4.2, pluggy-1.6.0 -- /opt/amazon/genesis1p-tools/venv/bin/python\r\ncachedir: .pytest_cache\r\nrootdir: /opt/amazon/genesis1p-tools/var\r\nplugins: Faker-19.13.0, anyio-4.11.0\r\ncollecting ... \rcollected 21 items\r\n\r\ntest_fizzbuzz.py::TestFizzBuzz::test_fizz_multiple_of_3 PASSED           [  4%]\r\ntest_fizzbuzz.py::TestFizzBuzz::test_buzz_multiple_of_5 PASSED           [  9%]\r\n(中略)test_fizzbuzz.py::TestPrintFizzBuzz::test_print_fizzbuzz_custom_range PASSED [100%]\r\n\r\n============================== 21 passed in 0.10s ==============================\r\n"
    }
  ],
  "structuredContent": {
    "stdout": "============================= test session starts ==============================\r\nplatform linux -- Python 3.12.11, pytest-8.4.2, pluggy-1.6.0 -- /opt/amazon/genesis1p-tools/venv/bin/python\r\ncachedir: .pytest_cache\r\nrootdir: /opt/amazon/genesis1p-tools/var\r\nplugins: Faker-19.13.0, anyio-4.11.0\r\ncollecting ... \rcollected 21 items\r\n\r\ntest_fizzbuzz.py::TestFizzBuzz::test_fizz_multiple_of_3 PASSED           [  4%]\r\ntest_fizzbuzz.py::TestFizzBuzz::test_buzz_multiple_of_5 PASSED           [  9%]\r\n(中略)test_fizzbuzz.py::TestPrintFizzBuzz::test_print_fizzbuzz_custom_range PASSED [100%]\r\n\r\n============================== 21 passed in 0.10s ==============================\r\n",
    "stderr": "",
    "exitCode": 0,
    "executionTime": 0.9144566059112549
  },
  "isError": False
}

という出力になり、'isError': Falseの結果が得られる。

これで、自分だけの自由なCode Interpreterを作れるようになった!

余談

気になって、Code Interpreterの環境を調べてみた。

サンプルで作ったスクリプトで、

$ python test.py ap-northeast-1 <取得したCode Interpreter ID> 'uname -a'

と実行すると、以下のように出力され、Amazon Linux2023のARM版が起動しているのが分かる。

Linux localhost 6.1.148-11.267.amzn2023.aarch64 #1 SMP Mon Aug 25 20:57:50 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux\r\n
6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?