- 公開日
- 最終更新日
【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の利用料金をその月のレートで日本円換算して表示させてみたいです。
この記事は私が書きました
小泉 和貴
記事一覧全国を旅行することを目標に、仕事を頑張っています。