- 公開日
- 最終更新日
【Bedrock】Bedrock AgentからAWS利用料金を教えてもらう
この記事を共有する

目次
はじめに
皆さんこんにちは!パーソル&サーバーワークスの小泉です。
最近AIの進化は目まぐるしく、驚かされます。
徐々に勉強していかないと、と思ったので今日は勉強したことを備忘録としてまとめました!
やりたいこと
以下のようなやりとりができるかどうか、検証してみました。
私:「〇月から〇月のAWS利用料金を教えて」
Amazon Bedrock Agents:「○○円だよ~」
(以降 Bedrock Agentと記載します)
完成したもの
やったこと
以下のステップで検証を行いました。
- Bedrock Agentの作成
- プロンプト確認
- CostExplorerとの比較
Bedrock Agentの作成
Bedrockのエージェントの画面から「エージェントを作成」をクリックします。
エージェントビルダーの設定を以下画面の通りに設定します。
アクショングループを追加して以下画面の通りに設定します。
アクショングループ関数に以下を設定します。
{ "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と実際にどれくらいの差額があるのか確認してみました。
Bedrockで確認すると465.39USD
マネジメントコンソールからだと468.59USD
微妙に差がありますね...(これを追及すると沼りそうなので今回は実施しません。)
感想
次は、各月の為替レートを考慮して、AWSの利用料金をその月のレートで日本円換算して表示させてみたいです。
この記事は私が書きました
小泉 和貴
記事一覧全国を旅行することを目標に、仕事を頑張っています。
