Amazon Bedrock AgentCore 入門 - AI エージェントを自作しながら 「ハーネス」 の仕組みを学んでみた

Amazon Bedrock AgentCore 入門 - AI エージェントを自作しながら 「ハーネス」 の仕組みを学んでみた

Bedrock AgentCore のハンズオンを通して AI エージェントの仕組み(ハーネス・ランタイム・ツール等)を理解するための入門記事です。
2026.08.14

はじめに

クラスメソッドオペレーションズの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 などと同様、実行した分のみ課金される従量制です。詳細は以下をご覧ください。

https://aws.amazon.com/jp/bedrock/agentcore/pricing/

今回のように小規模なエージェントをテストで数回実行する程度であれば、比較的少ないコストで済みます。筆者が行ったハンズオンでは $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 ファイルを選択して進めます。

1-1

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

1-2

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

1-3

次は用意した Lambda 関数を、エージェントが使えるようにツール化します。

手順2:AgentCore ゲートウェイを作成して Lambda をツール化する

Bedrock AgentCore のコンソール画面から「ゲートウェイ > ゲートウェイを作成」をクリックします。

2-1

次にゲートウェイの詳細画面へ進みます。

ゲートウェイ名は任意ですが、ここでは CloudFormation の出力にある agentcore-handson-gateway に設定します。(注1)

さらに IAM アクセス許可は「別のロールを使用」を選択して、先ほど手順1で作成したサービスロール role/agentcore-handson-gateway-service-role を指定します。(注1)

(注1)ここで「デフォルトロールを作成」を選択すると、必要な権限を持つサービスロールが自動作成されますが、筆者が検証した時点ではこの方法を取ると、後工程のターゲット登録がされない不具合を確認しました。それを回避するため、あえて今回はサービスロールを事前作成し、ゲートウェイ名もあらかじめ固定にする方法をとっています。

2-2

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

2-3

次の画面では、ターゲットとなるツール(Lambda 関数)を設定していきます。今回は以下のように設定します。

Target Protocol:
MCP Target

ターゲット名:
任意ですが、何のためのツールか分かりやすいように lambda-cwalarm-target とします。

ターゲットタイプ:
Lambda ARN

Lambda ARN:
CloudFormation で事前に作成した Lambda 関数の ARN を入力します。

ターゲットスキーマ:
「インラインスキーマを定義」にします。

その他の追加設定等は変えずに次へ進みます。

2-4

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

2-5

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

2-6

これでエージェントの手足となるゲートウェイは作成できたので、次はエージェントの本体であるハーネスを作成します。

手順3:AgentCore ハーネスを作成する

AgentCore コンソールの「ハーネス > 高度なハーネス作成」をクリックします。

3-1

ハーネスの設定画面では、以下のように入力します。

ハーネス名:
任意ですが、ここでは 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-2

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

3-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 です。

4-1

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

4-2

これで検出対象の CloudWatch アラームを準備できました。

次はいよいよ、作成したハーネス(エージェント本体)の動作確認を行っていきます!

手順5:AgentCore ハーネスの動作確認をする

AgentCore コンソールに「プレイグラウンド」が用意されているので、ここでハーネスの動作確認ができます。
※ 本番利用時にはハーネスをプログラム等から呼び出して利用するのですが、複雑になるので今回は触れません。

コンソールの左側メニューより「ハーネスプレイグラウンド」を選択し、先ほど作成したハーネス名を選択して「ハーネスをテスト」をクリックします。

5-1

チャット画面からプロンプトを入力することで、ハーネスの応答を確認できます。

まずは何ができるかを確認したいので「あなたはどんなエージェントですか?どんなツールを使えますか?」と聞いてみます。

すると、ファイル操作やプログラムの実行ができること、先ほど追加したツール lambda-cwalarm-target も使えることを教えてくれました。

5-2

早速「CloudWatch アラームのチェックをしてください」と指示してみます。
期待する動作は、追加したツール lambda-cwalarm-target を使用して、CloudWatch アラームをチェックしてくれることです。

ですが「IAM の権限不足によりアクセスできない」と回答されてしまいました。どうやら、そのままでは追加したツールを自動的に使ってくれないようです・・

5-3

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

その状態で再度「CloudWatch アラームをチェックしてください」と指示すると、今度は期待した通りにツールを使用して、アラームチェックを実行してくれました!

5-4

このように、ただハーネスにツールを接続しているだけでは使ってくれず、システムプロンプトでエージェントの基本的な振る舞い(例えばどのような時にツールを使用するか、等)を設定することが必要であると分かりました。

他にもいろいろ試してみたいのですが、記事が長くなってしまうので、最後に後片付けの手順を記載して締めくくります。

手順6:作成したリソースのクリーンアップ

先述のように AgentCore は基本的に使用した分だけの従量制です(メモリやストレージ等の追加リソースを作成した場合は例外)

そのため今回作成したリソース一式を検証用に残しておいても、実行しなければ大きな課金にはなりませんが、念のためクリーンアップの手順を記載しておきます。(この順番通りにやらなければ、リソース間の依存関係が邪魔して削除できないケースがあるためです)

まずハーネスから削除します。
AgentCore コンソール画面で対象のハーネス名にチェックを入れて「削除」をクリックします。

6-1

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

6-2

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

6-3

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

6-4

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

6-5

これで、今回作成したリソース一式のクリーンアップは完了です。お疲れ様でした!

さいごに

いかがでしたでしょうか。

これまで筆者の中で「ハーネス」と「エージェント」の概念が曖昧でしたが、実際に手を動かして作ってみることで具体的に理解できました。

ハーネスはエージェントの構成要素(インフラ、モデル、プロンプト、ツール、アクセス許可など)をまとめるための箱であり、エージェントの使い勝手や安全性はモデルの性能だけでなく、ハーネスの設計により左右されるのだと理解しました。
巷でよく聞く「ハーネスエンジニアリング」という言葉についても、理解の解像度がぐっと上がった実感があります。

今回はアラームを確認するだけの簡単なエージェントでしたが、今後はもっと実用的なものにも挑戦して、また機会があれば記事にしたいと思います。

本記事を読んで、筆者のように「エージェントの自作は思ったより簡単そうなので、チャレンジしてみよう」と思う方がいらっしゃれば嬉しいです。

参考資料

クラスメソッドオペレーションズ株式会社について

クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。
※2026年1月 アノテーション㈱から社名変更しました

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事