ENGINEER BLOG ENGINEER BLOG
  • 公開日
  • 最終更新日

【Bedrock】Bedrock AgentからAWS利用料金を教えてもらう

この記事を共有する

目次

はじめに

皆さんこんにちは!パーソル&サーバーワークスの小泉です。
最近AIの進化は目まぐるしく、驚かされます。 徐々に勉強していかないと、と思ったので今日は勉強したことを備忘録としてまとめました!

やりたいこと

以下のようなやりとりができるかどうか、検証してみました。

私:「〇月から〇月のAWS利用料金を教えて」
Amazon Bedrock Agents:「○○円だよ~」
(以降 Bedrock Agentと記載します)

完成したもの

利用料金

やったこと

以下のステップで検証を行いました。

  1. Bedrock Agentの作成
  2. プロンプト確認
  3. CostExplorerとの比較

Bedrock Agentの作成

Bedrockのエージェントの画面から「エージェントを作成」をクリックします。

エージェント作成

エージェントビルダーの設定を以下画面の通りに設定します。

エージェントビルダー

アクショングループを追加して以下画面の通りに設定します。

actiongroup

アクショングループ関数に以下を設定します。

{
  "name": "lambda-test",
  "description": "lambda-test",
  "parameters": {
    "start_month": {
      "description": "開始月 (1-12)",
      "required": "False",
      "type": "string"
    },
    "end_month": {
      "description": "終了月 (1-12)",
      "required": "False",
      "type": "string"
    }
  },
  "requireConfirmation": "ENABLED"
}

※Lambdaは既に作成済みのものを使用しています。
作成したLambdaのコードは以下になります。

import logging
import json
import boto3
from typing import Dict, Any
from http import HTTPStatus
from datetime import datetime, timedelta
# Configure logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Initialize Cost Explorer client
ce_client = boto3.client('ce')
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    """
    AWS Lambda handler for processing Bedrock agent requests to fetch AWS cost data.
    Args:
        event (Dict[str, Any]): The Lambda event containing action details
        context (Any): The Lambda context object
    Returns:
        Dict[str, Any]: Response containing the cost data results
    """
    try:
        logger.info('Event: %s', json.dumps(event))
        action_group = event['actionGroup']
        function = event['function']
        message_version = event.get('messageVersion', '1.0')
        # Extract parameters from the event
        parameters = {}
        for param in event.get('parameters', []):
            if param.get('name') and param.get('value'):
                parameters[param['name']] = param['value']
        # Get start and end date from parameters
        start_month = parameters.get('start_month')
        end_month = parameters.get('end_month')
        logger.info(f"Received parameters: start_month={start_month}, end_month={end_month}")
        # Validate and process date parameters
        current_year = datetime.now().year
        if not start_month or not start_month.isdigit():
            start_month = str(datetime.now().month - 1)  # Default to last month
        if not end_month or not end_month.isdigit():
            end_month = str(datetime.now().month)  # Default to current month
        # Convert month numbers to dates in format YYYY-MM-DD
        start_date = f"{current_year}-{start_month.zfill(2)}-01"
        # Calculate end date (last day of end_month)
        end_month_int = int(end_month)
        end_year = current_year
        if end_month_int == 12:
            next_month = 1
            next_year = end_year + 1
        else:
            next_month = end_month_int + 1
            next_year = end_year
        end_date_obj = datetime(next_year, next_month, 1) - timedelta(days=1)
        end_date = end_date_obj.strftime("%Y-%m-%d")
        logger.info(f"Querying Cost Explorer from {start_date} to {end_date}")
        # Get cost data from AWS Cost Explorer
        cost_data = get_cost_data(start_date, end_date)
        # Prepare response
        response_text = format_cost_response(cost_data, start_date, end_date)
        response_body = {
            'TEXT': {
                'body': response_text
            }
        }
        action_response = {
            'actionGroup': action_group,
            'function': function,
            'functionResponse': {
                'responseBody': response_body
            }
        }
        response = {
            'response': action_response,
            'messageVersion': message_version
        }
        logger.info('Response: %s', json.dumps(response))
        return response
    except KeyError as e:
        logger.error('Missing required field: %s', str(e))
        return {
            'statusCode': HTTPStatus.BAD_REQUEST,
            'body': f'Error: {str(e)}'
        }
    except Exception as e:
        logger.error('Unexpected error: %s', str(e), exc_info=True)
        return {
            'statusCode': HTTPStatus.INTERNAL_SERVER_ERROR,
            'body': f'Internal server error: {str(e)}'
        }
def get_cost_data(start_date: str, end_date: str) -> Dict:
    """
    Gets cost data from AWS Cost Explorer for the specified date range.
    Args:
        start_date (str): Start date in format YYYY-MM-DD
        end_date (str): End date in format YYYY-MM-DD
    Returns:
        Dict: Cost Explorer response
    """
    try:
        response = ce_client.get_cost_and_usage(
            TimePeriod={
                'Start': start_date,
                'End': end_date
            },
            Granularity='MONTHLY',
            Metrics=['BlendedCost', 'UnblendedCost'],
            GroupBy=[
                {
                    'Type': 'DIMENSION',
                    'Key': 'SERVICE'
                }
            ]
        )
        return response
    except Exception as e:
        logger.error(f"Error getting cost data: {str(e)}")
        raise
def format_cost_response(cost_data: Dict, start_date: str, end_date: str) -> str:
    """
    Formats the Cost Explorer response into a readable summary.
    Args:
        cost_data (Dict): Cost Explorer response
        start_date (str): Start date used in the query
        end_date (str): End date used in the query
    Returns:
        str: Formatted response text
    """
    try:
        results = cost_data.get('ResultsByTime', [])
        if not results:
            return f"No cost data available for the period from {start_date} to {end_date}."
        response_lines = [f"AWS コスト情報 ({start_date} から {end_date}まで):"]
        total_cost = 0.0
        service_costs = {}
        # Process results
        for time_period in results:
            period_start = time_period.get('TimePeriod', {}).get('Start')
            period_end = time_period.get('TimePeriod', {}).get('End')
            response_lines.append(f"\n期間: {period_start} から {period_end}")
            groups = time_period.get('Groups', [])
            if not groups:
                amount = time_period.get('Total', {}).get('BlendedCost', {}).get('Amount', '0')
                unit = time_period.get('Total', {}).get('BlendedCost', {}).get('Unit', 'USD')
                total_period_cost = float(amount)
                total_cost += total_period_cost
                response_lines.append(f"総コスト: {total_period_cost:.2f} {unit}")
            else:
                period_total = 0.0
                for group in groups:
                    service_name = group.get('Keys', ['Unknown'])[0]
                    amount = group.get('Metrics', {}).get('BlendedCost', {}).get('Amount', '0')
                    unit = group.get('Metrics', {}).get('BlendedCost', {}).get('Unit', 'USD')
                    cost = float(amount)
                    period_total += cost
                    if service_name in service_costs:
                        service_costs[service_name] += cost
                    else:
                        service_costs[service_name] = cost
                total_cost += period_total
        # Add service breakdown
        response_lines.append("\nサービス別内訳:")
        sorted_services = sorted(service_costs.items(), key=lambda x: x[1], reverse=True)
        for service, cost in sorted_services:
            if cost > 0.01:  # Only show services with significant cost
                response_lines.append(f"- {service}: {cost:.2f} USD ({(cost/total_cost*100):.1f}%)")
        response_lines.append(f"\n総コスト: {total_cost:.2f} USD")
        return "\n".join(response_lines)
    except Exception as e:
        logger.error(f"Error formatting cost data: {str(e)}")
        return f"コストデータの処理中にエラーが発生しました: {str(e)}"

プロンプトの確認

以下のプロンプトを投げてみました。

1月から3月のAWS利用料金を教えて
実際にどのような挙動をしているか右側の画面で教えてくれます!

トレース

結果は465.39USDでした!結構使ってますね!(料金は会社負担です)

CostExplorerとの比較

最後に、マネコン上に表示されるCostExplorerと実際にどれくらいの差額があるのか確認してみました。

costexplorer

Bedrockで確認すると465.39USD
マネジメントコンソールからだと468.59USD
微妙に差がありますね...(これを追及すると沼りそうなので今回は実施しません。)

感想

次は、各月の為替レートを考慮して、AWSの利用料金をその月のレートで日本円換算して表示させてみたいです。

この記事は私が書きました

小泉 和貴

記事一覧

全国を旅行することを目標に、仕事を頑張っています。

小泉 和貴

この記事を共有する

クラウドのご相談

CONTACT

クラウド導入や運用でお悩みの方は、お気軽にご相談ください。
専門家がサポートします。

サービス資料ダウンロード

DOWNLOAD

ビジネスをクラウドで加速させる準備はできていますか?
今すぐサービス資料をダウンロードして、詳細をご確認ください。