
Amazon Bedrock AgentCore 入門 - AI エージェントを自作しながら 「ハーネス」 の仕組みを学んでみた
はじめに
クラスメソッドオペレーションズのShimizuです。
日常業務に Claude Code をはじめとした AI エージェントツールをよく利用しますが、ある時ふと「これ、中身はどういう仕組みで動いているんだろう」と気になりました。
最近「ハーネスエンジニアリング」という言葉をよく聞きますが、「ハーネス」と「エージェント」はどういう関係なのか、正直ピンときていませんでした。
この仕組みを理解するには実際に触ってみるのが早いと考え、今回 Amazon Bedrock AgentCore で簡単な AI エージェントを構築する 30 分程度のハンズオンをやってみました。以下に、その内容をご紹介します。
初心者の私がハマったポイントなど、実際に手を動かすことで AI エージェントの仕組みがわかる入門記事を目指していますので、ぜひご覧いただけると幸いです。
Amazon Bedrock AgentCore とは
Amazon Bedrock AgentCore は AWS 基盤上で AI エージェントを構築できるマネージドサービスです。
用語解説
Bedrock AgentCore は主に以下の要素で構成されます。
| 用語 | ひとこと説明 |
|---|---|
| ハーネス | エージェントの本体。これにモデルやツールを組み合わせて使う |
| ランタイム | エージェントを動かすためのインフラ(実行環境) |
| モデル | エージェントの頭脳。Claude や GPT などから選べる |
| ツール | エージェントが使用する道具。Lambda 関数や API 等を使える |
| ゲートウェイ | ハーネスから各ツールを使用するための入り口 |
| メモリー | エージェントがセッションを記憶するための領域(今回は使用しない) |
パソコンに例えると、ハーネスは基盤、モデルはCPU、ツールはUSB等で接続した機器、という筆者なりの理解です。
コストについて
Bedrock AgentCore は基本的に Lambda などと同様、実行した分のみ課金される従量制です。詳細は以下をご覧ください。
今回のように小規模なエージェントをテストで数回実行する程度であれば、比較的少ないコストで済みます。筆者が行ったハンズオンでは $1 未満で済みました。
※ ただしメモリやストレージなどのセッション保存に関する追加リソースを作成すると、固定料金がかかる場合があります。そのため今回のハンズオンでは、これらの使用を避けています。
やってみた
今回はエージェントの実用性よりも、仕組みを理解することに重点を置いているため、以下のような最小限の構成でハンズオンを実施しました。
| 手順 | 所要時間 |
|---|---|
| 1. CloudWatch アラームをチェックする Lambda 関数のデプロイ | 約 2 分 |
| 2. AgentCore ゲートウェイを作成して Lambda をツール化する | 約 3 分 |
| 3. AgentCore ハーネスを作成する | 約 3 分 |
| 4. 意図的にアラーム状態にした CloudWatch アラームをデプロイする | 約 2 分 |
| 5. AgentCore ハーネスの動作確認をする | 約 10 分 |
| 6. 作成したリソースのクリーンアップ | 約 10 分 |
前提条件
- AWSアカウントで一連のサービス(AgentCore、Lambda、CloudFormation 等)の操作権限を有している
- リージョン:東京(ap-northeast-1)※ リージョンによっては機能に制限あり
- Bedrock のモデルアクセスが有効になっていること
上記前提のもとで、以下にハンズオン手順をご紹介します。
手順1:CloudWatch アラームをチェックする Lambda 関数のデプロイ
まずはエージェントが使用するツールとなる Lambda 関数を作成します。ここは時間をかけずに CloudFormation でサクっとデプロイします。
下記のテンプレート内容をコピーして、ローカルに .yaml 形式のファイルで保存します。ファイル名は任意ですが、ここでは cfn-templates/agentcore-handson-1-tool.yaml とします。
テンプレートはこちら
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Bedrock AgentCore hands-on, part 1 of 2.
Creates the tool Lambda function, its read-only execution role, and the IAM
service role that AgentCore Gateway assumes. Create this stack before creating
the gateway in the AgentCore console, and specify GatewayServiceRoleArn as the
gateway service role instead of letting the console generate one.
Parameters:
NamePrefix:
Type: String
Default: agentcore-handson
Description: Prefix for the names of the resources created by this stack
GatewayNamePrefix:
Type: String
Default: agentcore-handson-gateway
Description: >
Name you will give the gateway in the AgentCore console. The gateway service
role trust policy is scoped to gateway ARNs starting with this value, so the
gateway name must begin with exactly this string.
Resources:
# ---------------------------------------------------------------
# Lambda execution role (read-only)
# ---------------------------------------------------------------
ToolFunctionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${NamePrefix}-lambda-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: describe-alarms-readonly
PolicyDocument:
Version: '2012-10-17'
Statement:
# Read-only on alarms. No write or delete actions are granted.
- Sid: CloudWatchAlarmsReadOnly
Effect: Allow
Action:
- cloudwatch:DescribeAlarms
Resource: '*'
- Sid: LambdaBasicExecution
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${NamePrefix}-*'
# ---------------------------------------------------------------
# The tool itself (Lambda function invoked through AgentCore Gateway)
# ---------------------------------------------------------------
ToolFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub '${NamePrefix}-check-alarms'
Description: Returns CloudWatch alarms currently in ALARM state (AgentCore tool)
Runtime: python3.13
# The handler name must match the function name defined in the code below.
Handler: index.lambda_handler
Role: !GetAtt ToolFunctionRole.Arn
Timeout: 30
MemorySize: 256
Code:
ZipFile: |
import boto3
cloudwatch = boto3.client("cloudwatch")
def lambda_handler(event, context):
"""Return alarms currently in ALARM state (read-only)."""
resp = cloudwatch.describe_alarms(StateValue="ALARM")
alarms = []
# Metric alarms
for a in resp.get("MetricAlarms", []):
alarms.append({
"AlarmName": a["AlarmName"],
"AlarmType": "Metric",
"StateReason": a.get("StateReason"),
})
# Composite alarms are included as well
for a in resp.get("CompositeAlarms", []):
alarms.append({
"AlarmName": a["AlarmName"],
"AlarmType": "Composite",
"StateReason": a.get("StateReason"),
})
return {
"alarm_count": len(alarms),
"alarms": alarms,
}
# ---------------------------------------------------------------
# IAM service role assumed by AgentCore Gateway.
#
# The console can generate this role automatically, but creating it up front
# avoids the IAM propagation race that makes target creation fail with
# "Gateway service is not authorized to perform AssumeRole on Gateway role".
# ---------------------------------------------------------------
GatewayServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${NamePrefix}-gateway-service-role'
Description: Service role assumed by AgentCore Gateway to invoke the tool Lambda
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AgentCoreGatewayAssumeRole
Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Condition:
# Confused deputy protection: restrict to this account and to
# gateways whose name starts with GatewayNamePrefix.
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:gateway/${GatewayNamePrefix}-*'
Policies:
- PolicyName: gateway-base
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: GetGateway
Effect: Allow
Action:
- bedrock-agentcore:GetGateway
Resource: !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:gateway/${GatewayNamePrefix}-*'
- Sid: GetConfigurationBundleVersion
Effect: Allow
Action:
- bedrock-agentcore:GetConfigurationBundleVersion
Resource: !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:configuration-bundle/*'
Condition:
StringEquals:
aws:ResourceAccount: !Ref AWS::AccountId
aws:RequestedRegion: !Ref AWS::Region
- PolicyName: gateway-invoke-lambda
PolicyDocument:
Version: '2012-10-17'
Statement:
# Scoped to the single tool function created by this stack.
- Sid: InvokeToolFunction
Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt ToolFunction.Arn
Outputs:
ToolFunctionArn:
Description: Lambda function ARN to specify as the gateway target
Value: !GetAtt ToolFunction.Arn
ToolFunctionName:
Description: Lambda function name
Value: !Ref ToolFunction
GatewayServiceRoleArn:
Description: Specify this ARN as the gateway service role in the AgentCore console
Value: !GetAtt GatewayServiceRole.Arn
RequiredGatewayNamePrefix:
Description: The gateway name must start with this string to satisfy the trust policy
Value: !Ref GatewayNamePrefix
CloudFormation のコンソール画面からスタックの作成に進み、保存した .yaml ファイルを選択して進めます。

スタック名は任意ですが、ここではファイル名に合わせて agentcore-handson-1-tool とします。パラメータは変更せずに、スタックの作成へ進みます。

作成されるリソースは Lambda 関数、Lambda にアタッチする IAM ロール、AgentCore ゲートウェイが使用するサービスロールの 3 つです。これらの出力値はあとからゲートウェイの作成時に入力するため、控えておきましょう。

次は用意した Lambda 関数を、エージェントが使えるようにツール化します。
手順2:AgentCore ゲートウェイを作成して Lambda をツール化する
Bedrock AgentCore のコンソール画面から「ゲートウェイ > ゲートウェイを作成」をクリックします。

次にゲートウェイの詳細画面へ進みます。
ゲートウェイ名は任意ですが、ここでは CloudFormation の出力にある agentcore-handson-gateway に設定します。(注1)
さらに IAM アクセス許可は「別のロールを使用」を選択して、先ほど手順1で作成したサービスロール role/agentcore-handson-gateway-service-role を指定します。(注1)
(注1)ここで「デフォルトロールを作成」を選択すると、必要な権限を持つサービスロールが自動作成されますが、筆者が検証した時点ではこの方法を取ると、後工程のターゲット登録がされない不具合を確認しました。それを回避するため、あえて今回はサービスロールを事前作成し、ゲートウェイ名もあらかじめ固定にする方法をとっています。

次の画面ではアクセス許可の方法を指定します。デフォルトでは「JSON Web Tokens (JWT) を使用」になっていますが、これを選択すると Cognito のリソースが作成されるなど設定が複雑になるため、今回は「IAM 許可を使用」を選択します。

次の画面では、ターゲットとなるツール(Lambda 関数)を設定していきます。今回は以下のように設定します。
Target Protocol:
MCP Target
ターゲット名:
任意ですが、何のためのツールか分かりやすいように lambda-cwalarm-target とします。
ターゲットタイプ:
Lambda ARN
Lambda ARN:
CloudFormation で事前に作成した Lambda 関数の ARN を入力します。
ターゲットスキーマ:
「インラインスキーマを定義」にします。
その他の追加設定等は変えずに次へ進みます。

入力した内容を確認して「ゲートウェイを作成」をクリックします。

ゲートウェイの作成は 2 分程度で完了します。
ステータスが "Ready" になり、設定したターゲット(Lambda 関数)が登録されていれば OK です。

これでエージェントの手足となるゲートウェイは作成できたので、次はエージェントの本体であるハーネスを作成します。
手順3:AgentCore ハーネスを作成する
AgentCore コンソールの「ハーネス > 高度なハーネス作成」をクリックします。

ハーネスの設定画面では、以下のように入力します。
ハーネス名:
任意ですが、ここでは agentcore_handson_harness とします。
※ なぜかハーネス名にはハイフン(-)が使えないので、アンダースコアを含めた名前にします。
モデルソース:
Bedrock
API ソース:
Bedrock
モデル:
今回は JP Antropic Claude Halku 4.5 にします。
システムプロンプト:
ここはあとで変更しますが、とりあえずデフォルトのままにします。
メモリ:
これを有効にすることでエージェントの記憶領域ができ、前回のセッションを記憶するようなエージェントを作成できます。ただし前述したように課金を避けるため、今回は無効にします。
ゲートウェイ:
先ほど作成したゲートウェイ(本手順に沿っていれば agentcore-handson-gateway)を選択します。
アウトバウンド認証設定:
「IAM ロール」にします。
※ 他にもブラウザツールやコードインタープリターなどが用意されていますが、今回は追加しません。
スキル:
他の AI エージェントで作成済みのスキルや、AWS の Github リポジトリに用意されたスキルなどを追加できますが、今回は追加しません。
高度な設定:
今回は設定しませんが、どのような設定が可能かをざっと書いておきます。
| 項目 | 説明 |
|---|---|
| ファイルシステムの設定 | S3 や EFS をマウントして、ファイルの保存領域を持たせられる |
| ネットワーク | デフォルトではエージェントはパブリックだが、VPC 内に作成することも可能。 |
| カスタム環境 | ECR に保存したコンテナイメージ URI を指定すると、追加のランタイムやリソースを参照できる |
| 環境変数 | key-value 形式でハーネスに渡す環境変数を設定可能 |
| ライフサイクルの設定 | アイドルセッションタイムアウトや最大有効期間を指定可能 |
| 切り詰め | 会話履歴が制限を超えた場合に、どのように切り詰めるかを選択します。(この設定により、エージェントがどの程度まで以前のコンテキストを把握しているか、を調整できる)スライディングウィンドウにデフォルト設定されています。 |
| 許可されたツール | 呼び出し中にハーネスが呼び出すことを許可されるツールを制限します。デフォルトでは組み込みのファイルシステムおよびシェル操作を含む、すべての設定済みツールにアクセスできます。 |
| 呼び出しの制限 | 最大イテレーション、タイムアウト時間、最大トークンを設定可能 |
インバウンド認証:
今回はゲートウェイと合わせて「IAM 許可を使用」にします。
Permission:
「デフォルトロールを作成」にして、新しいロールが自動作成されるようにします。

一通り入力したら「ハーネスを作成」をクリックします。内容に問題がなければ、3 分ほどでハーネスの作成が完了してステータスが「準備完了」になります。

実はこれだけで、ハーネス(エージェント本体)の作成は完了です!
ここで動作テストをしてもよいのですが、アラーム状態になっている CloudWatch アラームをきちんと検出できるかテストするため、次の手順でテスト用 CloudWatch アラームを作成します。
(不要な場合は手順4をスキップして、手順5の動作確認をご覧ください)
手順4:意図的にアラーム状態にした CloudWatch アラームをデプロイする
ここは時間をかけずに CloudFormation でサクっとデプロイします。
下記のテンプレート内容をコピーして、ローカルに .yaml 形式のファイルで保存します。ファイル名は任意ですが、ここでは agentcore-handson-2-alarm.yaml とします。
テンプレートはこちら
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Bedrock AgentCore hands-on, part 2 of 2.
Creates a CloudWatch alarm that is intentionally kept in ALARM state, so the
agent has something to detect. Create this stack after the gateway and the
harness are ready, right before testing in the playground.
Parameters:
NamePrefix:
Type: String
Default: agentcore-handson
Description: Prefix for the names of the resources created by this stack
Resources:
# ---------------------------------------------------------------
# Alarm used to verify the agent behavior.
#
# It watches a custom metric that never receives any data point, and missing
# data is treated as breaching. As a result the alarm settles into ALARM state
# without publishing any metric.
# ---------------------------------------------------------------
AlwaysAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub '${NamePrefix}-always-alarm'
AlarmDescription: Kept in ALARM state on purpose for the AgentCore hands-on
Namespace: !Sub '${NamePrefix}/Dummy'
MetricName: DummyMetric
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 0
ComparisonOperator: LessThanOrEqualToThreshold
# No data is ever published, so the alarm falls to the ALARM side.
TreatMissingData: breaching
Outputs:
AlarmName:
Description: Name of the alarm the agent should detect
Value: !Ref AlwaysAlarm
手順1と同様に CloudFormation のコンソールから .yaml ファイルを指定し、スタックを新規作成します。
スタック名は任意ですが、テンプレート名と合わせて agentcore-handson-2-alarm とします。パラメータはそのままで OK です。

作成されるリソースは、シンプルに ClaudWatch アラーム 1 つのみです。
作成完了したら対象アラームの画面より、意図的に常時アラーム状態となっていることを確認します。

これで検出対象の CloudWatch アラームを準備できました。
次はいよいよ、作成したハーネス(エージェント本体)の動作確認を行っていきます!
手順5:AgentCore ハーネスの動作確認をする
AgentCore コンソールに「プレイグラウンド」が用意されているので、ここでハーネスの動作確認ができます。
※ 本番利用時にはハーネスをプログラム等から呼び出して利用するのですが、複雑になるので今回は触れません。
コンソールの左側メニューより「ハーネスプレイグラウンド」を選択し、先ほど作成したハーネス名を選択して「ハーネスをテスト」をクリックします。

チャット画面からプロンプトを入力することで、ハーネスの応答を確認できます。
まずは何ができるかを確認したいので「あなたはどんなエージェントですか?どんなツールを使えますか?」と聞いてみます。
すると、ファイル操作やプログラムの実行ができること、先ほど追加したツール lambda-cwalarm-target も使えることを教えてくれました。

早速「CloudWatch アラームのチェックをしてください」と指示してみます。
期待する動作は、追加したツール lambda-cwalarm-target を使用して、CloudWatch アラームをチェックしてくれることです。
ですが「IAM の権限不足によりアクセスできない」と回答されてしまいました。どうやら、そのままでは追加したツールを自動的に使ってくれないようです・・

そこで今度は、右側のシステムプロンプトに「CloudWatch アラームのチェックを指示したらツール lambda-cwalarm-target を使用してください」と明示的に入れてみました。
その状態で再度「CloudWatch アラームをチェックしてください」と指示すると、今度は期待した通りにツールを使用して、アラームチェックを実行してくれました!

このように、ただハーネスにツールを接続しているだけでは使ってくれず、システムプロンプトでエージェントの基本的な振る舞い(例えばどのような時にツールを使用するか、等)を設定することが必要であると分かりました。
他にもいろいろ試してみたいのですが、記事が長くなってしまうので、最後に後片付けの手順を記載して締めくくります。
手順6:作成したリソースのクリーンアップ
先述のように AgentCore は基本的に使用した分だけの従量制です(メモリやストレージ等の追加リソースを作成した場合は例外)
そのため今回作成したリソース一式を検証用に残しておいても、実行しなければ大きな課金にはなりませんが、念のためクリーンアップの手順を記載しておきます。(この順番通りにやらなければ、リソース間の依存関係が邪魔して削除できないケースがあるためです)
まずハーネスから削除します。
AgentCore コンソール画面で対象のハーネス名にチェックを入れて「削除」をクリックします。

次にゲートウェイのターゲット(ツール)を削除します。
ゲートウェイの詳細画面でターゲット名にチェックを入れて「削除」をクリックします。

次はゲートウェイを削除します。
対象のゲートウェイ名にチェックを入れて「削除」をクリックします。

次に CloudFormation スタックを削除します。
本記事の手順通りに進めていれば agentcore-handson-1-tool と agentcore-handson-2-alarm の 2 つのスタックがあるはずなので、1 つずつ選択して「スタックを削除」をクリックします。

最後に AgentCore サービスによって自動作成された IAM ロールを削除します。
IAM ロールのコンソールで AgentCore と検索すると、ハーネス用とランタイム用に自動作成された IAM ロールがあるはずなので、選択して「削除」をクリックします。

これで、今回作成したリソース一式のクリーンアップは完了です。お疲れ様でした!
さいごに
いかがでしたでしょうか。
これまで筆者の中で「ハーネス」と「エージェント」の概念が曖昧でしたが、実際に手を動かして作ってみることで具体的に理解できました。
ハーネスはエージェントの構成要素(インフラ、モデル、プロンプト、ツール、アクセス許可など)をまとめるための箱であり、エージェントの使い勝手や安全性はモデルの性能だけでなく、ハーネスの設計により左右されるのだと理解しました。
巷でよく聞く「ハーネスエンジニアリング」という言葉についても、理解の解像度がぐっと上がった実感があります。
今回はアラームを確認するだけの簡単なエージェントでしたが、今後はもっと実用的なものにも挑戦して、また機会があれば記事にしたいと思います。
本記事を読んで、筆者のように「エージェントの自作は思ったより簡単そうなので、チャレンジしてみよう」と思う方がいらっしゃれば嬉しいです。
参考資料
- AgentCore ハーネス
- AgentCore ハーネスとランタイム
- AWS マネジメントコンソールを使用して AgentCore ゲートウェイを作成する
- サポートされている AWS リージョン - Amazon Bedrock AgentCore
- Amazon Bedrock AgentCore の料金
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。
※2026年1月 アノテーション㈱から社名変更しました










