Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Amazon Bedrock AgentCore で実現する「Code excecution with MCP」

この記事は AI Agent on AWS - Qiita Advent Calendar 2025 - Qiita 16日目の記事です。

1. はじめに

こんにちは。データ分析エンジニアの木介です。

Anthropic が公開した「Code execution with MCP: building more efficient AI agents」では、MCP(Model Context Protocol)とコード実行環境を組み合わせて、コンテキスト消費を抑えながらツールを使うための新しい設計パターンとして Code execution with MCP が紹介されました。

今回は、この Code execution with MCPAmazon Bedrock AgentCoreStrands Agents で実装し、直接MCPを実行するAgent と比較して、実行時間・消費トークンの観点で評価していきます。

www.anthropic.com

aws.amazon.com

2. 概要

1.1 Code execution with MCP の概要

直接MCPを実行するAgentでは、LLM が MCP ツールを直接呼び出し、その結果をコンテキストに積み上げながら推論を進めます。
この方式は実装がシンプルな一方で、

  • ツール定義(スキーマや説明文)
  • ツール実行結果(大量のレコードや本文)
  • 途中経過のやり取り

がコンテキストを圧迫しやすく、結果として トークン消費(コスト)やレイテンシーに影響が出やすい、という課題があります。

これに対して Code execution with MCP は上記図のように、

  1. LLM が「ツール呼び出しの計画」を直接進めるのではなく、まず コードを生成
  2. 生成したコードを コード実行環境(例:AgentCore Code Interpreter)で実行
  3. コードの中から MCP ツール(= 外部 API)を必要な分だけ呼び出し
  4. 結果をコード側でフィルタリング・集約して、LLM へ返す情報量を最小化

という流れで、コンテキストの肥大化を抑えます。
これにより無駄なコンテキストによる消費トークン量の増大や実行時間を抑えることが出来ます。

2.1 Code execution with MCP の特徴

2.1.1 構成要素

本記事で作成するCode execution with MCPの構成は、次のコンポーネントで成り立ちます。

2.1.2 利点

Code execution with MCP を採用することで、次の利点が期待できます。

観点 期待できる利点 内容
トークン効率 コンテキスト削減 大量データをそのままモデルに渡さず、コード側で集約して「必要な結果だけ」を返せる
データ加工 柔軟な前処理 pandas 等でフィルタ・集計・統計処理を実行し、回答に必要な情報だけ抽出できる
運用性 再利用性の向上 よく使う分析処理をスキル化し、プロンプトから切り離して再利用しやすい
セキュリティ 中間結果の制御 コード実行環境に中間データを保持し、モデルへ渡す情報量を明示的に制御できる

3. 検証内容

本記事では、以下の Amazon Review データセットを用いて「レビューに基づく質問応答」を行う Agent を作成し、比較検証します。

amazon-reviews-2023.github.io

比較する構成は次の 2 種類です。

  1. 直接MCPを実行するAgent

    • LLM が MCP ツールを直接呼び出して結果をコンテキストに積む
  2. Code execution with MCPを使ったAgent

    • LLM がコードを生成し、コード実行環境から MCP ツールを呼び出して集約結果のみを LLM に返す

検証観点は以下の 2点です。

  1. 実行時間
  2. 消費トークン(コンテキスト)

3.1 直接MCPを実行するAgentの構成

S3 に配置した Amazon Review の JSONL を Lambda 経由で取得し、AgentCore Gateway から MCP ツールとして利用します。

AgentCoreを使ったMCPを利用するAgentの作成については以下の記事で説明しているため、こちらも参照頂ければと思います。

acro-engineer.hatenablog.com

以下の形でMCPを使ったAgentの実行を行います。

  1. ユーザが Agent に質問
  2. Agent が必要に応じて AgentCore Gateway 経由で MCP ツール(Lambda)を実行
  3. 取得結果をコンテキストに積みながら推論し、回答を返す

3.2 Code execution with MCPを使ったAgentの構成

MCP ツール呼び出しを「コード側」に寄せ、コードで加工した結果だけを LLM に渡します。

以下の形でAgentからコードベースでのツール実行を行います。

  1. ユーザが Agent に質問
  2. Agent が必要に応じてコードを生成
  3. AgentCore Code Interpreter がコードを実行し、必要なデータ取得(MCP)と集計を完結
  4. 集計結果(必要最小限)を Agent に返し、回答を生成

4. 直接MCPを実行するAgentの実装

この構成では、Amazon Review を取得する Lambda を MCP サーバーとして公開し、AgentCore Gateway 経由で呼び出します。

MCP ツールは次の 2 つです。

  1. データセットのカラム情報を取得するツール
  2. レビューのスコアと日付を元にレコードを取得するツール

4.1 MCP用のLambdaの実装

import json
import logging
from functools import cache
from typing import Any

import boto3
import numpy as np
import pandas as pd

# ===== ロガー設定(AWS Lambda 用)=====
logger = logging.getLogger()
logger.setLevel(logging.INFO)

BUCKET = 'XXXXX'
JSON_RECORD_KEY = 'YYYY'

s3_client = boto3.client('s3')


@cache
def _get_record() -> pd.DataFrame:
    """
    S3 から JSONL ファイルを取得して DataFrame にして返す。
    """
    local_path = '/tmp/record.jsonl'

    # S3 からダウンロード
    s3_client.download_file(BUCKET, JSON_RECORD_KEY, local_path)

    # JSON Lines 形式を DataFrame として読み込み
    df = pd.read_json(local_path, lines=True)

    return df


def get_amazon_review(
    rating: float,
    start_timestamp: int,
    end_timestamp: int,
) -> str:
    """
    S3 上の JSONL を DataFrame で読み込み、
    rating と timestamp の範囲でフィルタした結果を JSON 文字列で返す。
    """
    df = _get_record()

    ts_col = df['timestamp']
    logger.info('timestamp dtype=%s', ts_col.dtype)

    # timestamp の型に応じて比較値を揃える
    if np.issubdtype(ts_col.dtype, np.datetime64):
        start = pd.to_datetime(start_timestamp, unit='ms')
        end = pd.to_datetime(end_timestamp, unit='ms')
        logger.info('Converted bounds to datetime. start=%s, end=%s', start, end)
    elif np.issubdtype(ts_col.dtype, np.number):
        # 数値ならそのまま
        start = start_timestamp
        end = end_timestamp
    else:
        logger.info('timestamp is non-numeric non-datetime; converting to datetime')
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        ts_col = df['timestamp']
        start = pd.to_datetime(start_timestamp, unit='ms')
        end = pd.to_datetime(end_timestamp, unit='ms')

    # rating 完全一致 & timestamp 範囲指定
    filtered = df[(df['rating'] == rating) & (ts_col >= start) & (ts_col <= end)]

    json_str = filtered.to_json(orient='records', force_ascii=False)

    return json_str


def get_columns() -> list[str]:
    """Get the list of columns in the dataset."""
    keys = [
        'rating',
        'title',
        'text',
        'images',
        'asin',
        'parent_asin',
        'user_id',
        'timestamp',
        'helpful_vote',
        'verified_purchase',
    ]
    logger.info('get_columns called. columns=%s', keys)
    return keys


def lambda_handler(event: dict[str, Any], context: Any) -> dict[str, Any]:
    """
    Lambda エントリポイント。

    Bedrock AgentCore から呼ばれる前提で、
    context.client_context.custom['bedrockAgentCoreToolName'] から tool_name を取得して処理を切り替える。
    """

    try:
        # Bedrock AgentCore 固有のコンテキストから tool 名を取得
        tool_name = context.client_context.custom['bedrockAgentCoreToolName'].split('__', 1)[-1]
        logger.info('Detected tool_name=%s', tool_name)

    except Exception:
        logger.exception('Failed to get tool_name from context.client_context.custom')
        return {
            'statusCode': 500,
            'body': {
                'error': "Failed to parse tool name from context. Check client_context.custom['bedrockAgentCoreToolName'].",
            },
        }

    try:
        if tool_name == '_get_amazon_review':
            rating = float(event['rating'])
            start_timestamp = int(event['start_timestamp'])
            end_timestamp = int(event['end_timestamp'])

            json_str = get_amazon_review(
                rating=rating,
                start_timestamp=start_timestamp,
                end_timestamp=end_timestamp,
            )

            return {
                'statusCode': 200,
                'body': json_str,
            }

        elif tool_name == '_get_columns':
            cols = get_columns()

            return {
                'statusCode': 200,
                'body': json.dumps(
                    {
                        'columns': cols,
                    },
                    ensure_ascii=False,
                ),
            }

        else:
            return {
                'statusCode': 400,
                'body': {
                    'error': f'Unknown tool_name: {tool_name}',
                },
            }

    except Exception as e:
        logger.exception('Error occurred in lambda_handler.')
        return {
            'statusCode': 500,
            'body': {
                'error': f'Error occurred: {str(e)}',
            },
        }

4.2 Agent(直接MCPを実行するAgent)

モデルは Claude Haiku 4.5 を利用し、ツールとして上記 Lambda を AgentCore Gateway 経由で MCP として利用します。

import os
from textwrap import dedent
from typing import Any

import boto3
from bedrock_agentcore.identity.auth import requires_access_token
from dotenv import load_dotenv
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.agent.agent_result import AgentResult
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient

load_dotenv()


MODEL_ID = 'global.anthropic.claude-haiku-4-5-20251001-v1:0'
# AgentCore Gateway URL
GATEWAY_URL = os.environ['AGENTCORE_GATEWAY_URL']
# AgentCore Gatewayにアクセスするために作成したIdentityの名前
OAUTH_PROVIDER = os.environ['AGENTCORE_GATEWAY_OAUTH_PROVIDER']
# Cognitoリソースに紐づくカスタムスコープ
OAUTH_SCOPE = os.environ['AGENTCORE_GATEWAY_OAUTH_SCOPES'].split(',')

SYSTEM_PROMPT = dedent(
    """
    Amazon Reviewデータセットに関連する質問に回答してください。
    **ユーザの確認を待たず、柔軟な分析を行い、回答をしてください**
    必要に応じて `run_mcp_analysis` ツールを使用してください
    """
).strip()


# アクセストークンを取得する
@requires_access_token(provider_name=OAUTH_PROVIDER, scopes=OAUTH_SCOPE, auth_flow='M2M')
def get_access_token(*, access_token) -> str:
    """Fetch access token using Bedrock AgentCore identity service."""
    return access_token


def run_agent(prompt: str, **kwargs: Any) -> AgentResult:
    access_token = get_access_token()  # pylint: disable=E1125
    mcp_client = MCPClient(
        lambda: streamablehttp_client(
            GATEWAY_URL,
            headers={'Authorization': f'Bearer {access_token}'},
            timeout=300,
        )
    )
    with mcp_client:
        tools = mcp_client.list_tools_sync()
        agent = Agent(
            model=BedrockModel(
                model_id=MODEL_ID,
                additional_request_fields={
                    'thinking': {'type': 'enabled', 'budget_tokens': 4000},
                    'anthropic_beta': ['interleaved-thinking-2025-05-14'],
                },
            ),
            tools=tools,
            system_prompt=SYSTEM_PROMPT,
        )
        return agent(prompt.strip(), **kwargs)

5. Code execution with MCPを使ったAgentの実装

この構成では、MCP ツールを「直接」呼ばず、AgentCore Code Interpreter 上で実行されるコードから MCP を呼び出します。

ポイントは、Agent に「何でもできる」と思わせるのではなく、使わせたい操作をSKILLS.mdとしてドキュメント化し、必要時だけ参照させることです。
今回は以下のAnthropicが公式に公開しているSKILLS.mdを参考にして、実行できる内容を記載しています。

github.com

これにより、コンテキストの肥大化を防ぎつつ、実行ロジックを再利用できます。

5.1 Agent(Code execution with MCPを使ったAgent)

from textwrap import dedent
from typing import Any

import boto3
from strands import Agent
from strands.agent.agent_result import AgentResult
from strands.models import BedrockModel
from strands_tools import current_time, file_read
from strands_tools.code_interpreter import AgentCoreCodeInterpreter

MODEL_ID = 'global.anthropic.claude-haiku-4-5-20251001-v1:0'


SYSTEM_PROMPT = dedent(
    """
    Amazon Reviewデータセットに関連する質問に回答してください。

    以下のツールを必ず利用してください
       1. file_read: ファイルを読み込みます
       2. current_time: 現在の日時を取得します
       3. code_interpreter: Pythonコードを実行してデータ分析を行います
    """
).strip()

SKILLS_PROMPT = dedent(
    """
    以下のSKILLSを利用できます。
    ---
    name: "amazon-review-analytics"
    description: "Amazon レビュー JSONL を pandas DataFrame に変換し、質問に応じた統計情報を返すSKILLS.md。ファイル読込時は file_read、集計処理は code_interpreter を必ず使用する。"
    ---
    """
).strip()


def run_agent(prompt: str, **kwargs: Any) -> AgentResult:
    code_interpreter = AgentCoreCodeInterpreter(region='us-east-1', identifier='interpreter-XXX')
    agent = Agent(
        model=BedrockModel(
            model_id=MODEL_ID,
            additional_request_fields={
                'thinking': {'type': 'enabled', 'budget_tokens': 4000},
                'anthropic_beta': ['interleaved-thinking-2025-05-14'],
            },
        ),
        tools=[current_time, code_interpreter.code_interpreter, file_read],
        system_prompt=SYSTEM_PROMPT + SKILLS_PROMPT,
    )
    return agent(prompt.strip(), **kwargs)

5.2 SKILLS.md

以下のSKILL.mdをAgentが任意で読み込むことで、適切なコードを使ったツールの利用が可能となります。

長いので折りたたむ

# Amazon レビュー統計分析スキル(実装コード組み込み版)

このスキルは、S3 上に保存された Amazon レビュー JSONL を  
**`scripts/s3_loader.py` と `scripts/create_dataframe.py` に含まれる実装**  
を用いて DataFrame 化し、ユーザーの質問に応じた統計情報を返すものです。

以下にスキル内部で利用する **正式な実装コード** を全て組み込み、  
SKILL 定義からスクリプトが参照できるようにしました。

---

# 1. 組み込み実装コード(SKILL 内蔵)

## 1.1 `scripts/s3_loader.py`(このスキルに内包される実装)

```python
# scripts/s3_loader.py
import os
from functools import cache
import boto3
import pandas as pd

DEFAULT_BUCKET = "XXXX"
DEFAULT_KEY = "YYYY"
DEFAULT_LOCAL_PATH = "/tmp/record.jsonl"

s3_client = boto3.client("s3")


@cache
def load_reviews_dataframe_from_s3() -> pd.DataFrame:
    bucket = os.getenv("AMAZON_REVIEW_BUCKET", DEFAULT_BUCKET)
    key = os.getenv("AMAZON_REVIEW_JSONL_KEY", DEFAULT_KEY)
    local_path = os.getenv("AMAZON_REVIEW_LOCAL_PATH", DEFAULT_LOCAL_PATH)

    # S3 → ローカルへダウンロード
    s3_client.download_file(bucket, key, local_path)

    # JSONL → DataFrame
    df = pd.read_json(local_path, lines=True)
    return df
`````

---

## 1.2 `scripts/create_dataframe.py`(このスキルに内包される実装)

```python
# scripts/create_dataframe.py
import pandas as pd
from typing import list, Optional
from datetime import datetime
from scripts.s3_loader import load_reviews_dataframe_from_s3

COLUMNS = [
    "rating", "title", "text", "images", "asin", "parent_asin",
    "user_id", "timestamp", "helpful_vote", "verified_purchase"
]


def get_columns() -> list[str]:
    return COLUMNS


def get_reviews_dataframe(
    use_s3: bool = True,
    local_path: Optional[str] = None
) -> pd.DataFrame:

    if use_s3:
        df = load_reviews_dataframe_from_s3()
    else:
        if local_path is None:
            raise ValueError("local_path must be provided when use_s3=False")
        df = pd.read_json(local_path, lines=True)

    # timestamp → datetime 変換
    if "timestamp" in df:
        df["timestamp_dt"] = pd.to_datetime(df["timestamp"], unit="ms")

    return df
```

---

# 2. このスキルの **利用ルール**

## 2.1 ファイルを読むときは必ず `file_read` を使用する

もしユーザーが
「scripts のコードを見せて」
「JSONL の中身を確認して」
と要求した場合、
**assistant が自前で open() を使ってファイルを読むことは禁止。**

必ず:

```json
{"tool": "file_read", "path": "scripts/create_dataframe.py"}
```

のように行う。

---

## 2.2 pandas での集計・フィルタ・統計処理はすべて `code_interpreter` で実行する

例:

```json
{
  "tool": "code_interpreter",
  "code": "from scripts.create_dataframe import get_reviews_dataframe\n df = get_reviews_dataframe(); df.rating.mean()"
}
```

assistant は Python を直接実行したふりをしてはいけない。
**すべて code_interpreter の中で処理する。**

---

# 3. スキルの処理フロー(正式仕様)

## 3.1 Step 1:ユーザーの質問内容と必要な統計を推定する

抽出する項目:

* 知りたい情報(平均評価、分布、件数、top helpful など)
* フィルタ条件

  * ASIN / parent_asin
  * 期間
  * verified purchase
  * rating のしきい値
  * helpful_vote 最低値

---

## 3.2 Step 2:DataFrame の取得(scripts 実装を利用)

```python
from scripts.create_dataframe import get_reviews_dataframe
df = get_reviews_dataframe(use_s3=True)
```

※ code_interpreter 内で実行する

---

## 3.3 Step 3:フィルタリング(pandas)

例:ASIN で絞り込み

```python
df = df[df["asin"] == target_asin]
```

期間フィルタ:

```python
df = df[(df["timestamp_dt"] >= start_dt) & (df["timestamp_dt"] <= end_dt)]
```

verified purchase:

```python
df = df[df["verified_purchase"] == True]
```

---

## 3.4 Step 4:必要な統計量を計算

* 件数
* 平均評価
* 中央値
* 星の分布
* helpful_vote 上位 N 件
* ASIN ごとの統計(groupby)

---

## 3.5 Step 5:結果を Markdown で要約して返す

例:

```md
**ASIN B01N0TQ0OH のレビュー傾向(128 件)**

| 指標 | 値 |
|------|-----|
| 平均評価 | 4.62 |
| 中央値 | 5.0 |
| 星 5 割合 | 65 % |
| verified purchase | 92 % |
```

---

# 4. 回答例テンプレート(このスキルが返す文章)

```md
### 結果の概要
ASIN **B01N0TQ0OH** のレビュー **128 件** を対象に統計を算出しました。  
平均評価は **4.62**、星 5 が **65%** を占めています。

### 主要統計
| 指標 | 値 |
|------|-----|
| 件数 | 128 |
| 平均評価 | 4.62 |
| 中央値 | 5.0 |
| 星 5 割合 | 65 % |
| verified purchase 割合 | 92 % |

### 集計条件
- 期間:全期間  
- verified purchase:制限なし  
- ASIN:B01N0TQ0OH

### 補足
レビュー本文は必要最小限のみ引用し、統計傾向中心に回答します。
```

---

# 5. 注意事項(スキルとしてのルール)

* **この SKILL に内蔵されている scripts の公式実装を必ず利用する**
* DataFrame の取得は **code_interpreter** のみで実行する
* ファイル内容の参照は **file_read** のみで行う
* 外部アクセスや独自の S3 アクセスコードを新規に書いてはいけない
* 統計値はできるだけ 2 桁程度に丸めて返す
* 文章は簡潔に、必要な統計を優先する

6. 検証結果

直接MCPを実行するAgent と Code execution with MCPを使ったAgent の結果を比較します。

6.1 質問と回答

質問

最も高く評価された製品カテゴリは何ですか?

直接MCPを実行するAgentによる回答

データが取得できました。これらは2020年1月〜2月に寄せられた5つ星 のレビューです。このデータを分析して、製品タイトルから最も頻繁に評価されている製品カテゴリを調べます。

分析結果から、**Amazon Reviewデータセットの最も高く評価された製品カテゴリ**は以下の通りです:## 📊 分析結果

### **最も高く評価されたカテゴリ(ランキング)**

1. **コーヒーフィルター関連** ★★★(最も高く評価)
   - 「コーヒーフィルター」「K-Cupフィルター」「再利用可能なコーヒーフィルター」
   - 多くの5つ星レビュー(価値、使いやすさ、環境への配慮)

2. **冷蔵庫部品・交換部品** ★★☆
   - 「冷蔵庫フィルター」「冷蔵庫ドアビン」「冷蔵庫ガスケット」
   - 多数のレビュー(性能、簡単な取付)

3. **洗濯機・乾燥機部品** ★★☆
   - 「乾燥機ローラー」「洗濯機フィルター」「乾燥機ベルト」
   - 高い満足度(修理費用の削減)

4. **調理台・ストーブ関連** ★☆☆
   - 「バーナーカバー」「ストーブギャップカバー」「バーナーヘッド」

5. **その他家電部品**
   - 「アイスメーカー」「食洗機バスケット」「オーブンヒーター」

### 🔍 主な 特徴

- **コーヒーフィルター製品**が最も一貫して高評価を受けている
- レビュアーは **コスト効率性**、**環境への配慮**、**簡単な使用性**を高く評価
- 代替品(ブランド品ではない製品)で**同等かそれ以上の性能**を提供する製品が好評

データセット内の最も高く評価された製 品は、**環境的に持続可能で経済的な家庭用消耗品**です。

Code execution with MCPを使ったAgentによる回答

完璧です!これで分析が完了 しました。結果をまとめます。

---

## 📊 Amazon Reviewデータセット分析結果

### **最も高く評価された製品カテゴリ**

Amazon Reviewデータセット(20,000件のレビュー)に基づいて、**最も高く評価された製品カテゴリは ASIN `B0B3DB5HTC`** です。

### 🏆 **トップ3 最高 評価カテゴリ(実用的なレビュー数を含む)**

| 順位 | ASIN | 平均評価 | レビュー数 | 5つ星割合 | 検証済み購入率 |
|------|------|---------|----------|---------|-------------|
| 1 | **B0B3DB5HTC** | **4.86/5.0** | **130件** | 90.8% | 100% |
| 2 | B01KJ2FVFW | 4.87/5.0 | 60件 | 98.3% | 100% |
| 3 | B08YBGCNHP | 4.72/5.0 | 82件 | 85.4% | 99% |

### 📈 **詳細統計(最高評価カテゴリ B0B3DB5HTC)**

- **平均評価:4.86/5.0**
- **総レビュー数:130件**
- **5つ星レビュー:118件(90.8%)**
- **4つ星レビュー:10件**
- **1つ星レビュー:2件(1.5%)**
- **検証済み購入率:100%**
- **平均ヘルプフル投票数:0.18**

このカテゴリは、**Keurig用フィルター製品(コーヒーマシン関連)**と推定され、顧客満足度が極めて高いことが示されています。

### 📊 **全体的な傾向**

- **データセット合計:20,000件のレビュー**
- **カテゴリ数:8,581個**
- **全体平均評価:4.38/5.0**
- **中央値:5.0**

100件以上のレビューを持つカテゴリは限定的(2カテゴリのみ)であり、大多数の製品カテゴリは少数のレビューのみ保有しています。

また、情報の取得のために生成されたコードとしては以下になります。
指定通りに統計を取った値を取得するコードが生成出来ています。

Code execution with MCP利用時に生成されるコード例

import os
from collections import Counter

import boto3
import pandas as pd


# ==============================
# Constants / Environment
# ==============================
DEFAULT_BUCKET = "XXXXX"
DEFAULT_KEY = "YYYYY"
DEFAULT_LOCAL_PATH = "record.jsonl"

bucket = os.getenv("AMAZON_REVIEW_BUCKET", DEFAULT_BUCKET)
key = os.getenv("AMAZON_REVIEW_JSONL_KEY", DEFAULT_KEY)
local_path = os.getenv("AMAZON_REVIEW_LOCAL_PATH", DEFAULT_LOCAL_PATH)

s3_client = boto3.client("s3")


# ==============================
# Load data from S3
# ==============================
print("Downloading data from S3...")
s3_client.download_file(bucket, key, local_path)

print("Loading DataFrame...")
df = pd.read_json(local_path, lines=True)


# ==============================
# Basic dataset inspection
# ==============================
print("Dataset shape:", df.shape)
print("Columns:", list(df.columns))

print("\nData types:")
print(df.dtypes)

print("\nFirst row:")
print(df.iloc[0])


# ==============================
# Category (parent_asin) statistics
# ==============================
if "parent_asin" in df.columns:
    print("\nTop 10 categories (parent_asin) by review count:")

    category_stats = (
        df.groupby("parent_asin")["rating"]
        .agg(["count", "mean"])
        .reset_index()
    )

    category_stats.columns = [
        "parent_asin",
        "review_count",
        "avg_rating",
    ]

    category_stats = category_stats.sort_values(
        "avg_rating",
        ascending=False,
    )

    print(category_stats.head(10))

回答の方向性としては両者と妥当そうです。

6.2 実行時間と消費トークンの比較

実行時間と消費トークンは AgentCore Runtime のトレースから確認しました。

直接MCPを実行するAgentの実行時間と消費トークン量

Code execution with MCPを使ったAgentの実行時間と消費トークン量

観点 実行時間 消費トークン量
直接MCPを実行するAgent 13.0 s 202,114 tokens
Code execution with MCPを使ったAgent 16.2s 32,552 tokens

消費トークン量は、Code execution with MCPを使ったAgent が大幅に少なく、直接MCPを実行するAgentの約 1/6 まで削減できました。

一方で実行時間は、今回は直接MCPを実行するAgentの方が短い結果でした。主な要因として、Code execution with MCPを使ったAgentではコード実行のリトライが発生しており、その分のオーバーヘッドが乗ったことが考えられます。 この点はSKILLS.mdの修正や、より性能の高いモデル(例:Claude Sonnet 4.5)への変更によって改善余地があります。

7. まとめ

本記事では、Anthropic が提案した Code execution with MCP を、Amazon Bedrock AgentCoreStrands Agents を用いて実装し、直接MCPを実行するAgent と比較しました。

結果として、Code execution with MCP を使うことで ツール実行結果をそのままコンテキストに積まない設計が可能になり、消費トークンを大幅に削減できることを確認しました。

今後、扱うツール数が増える(= ツール定義が増える)エージェントや、大量データの集計が必要なエージェントでは、Code execution with MCP が前提になるケースが増えていきそうです。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com