メインコンテンツに移動

builders.flash

Amazon Bedrock AgentCore harness と AWS Step Functions を組み合わせて安全に AI エージェントを構築してみよう !

2026-08-03 | Author : 伊勢田 氷琴, 菅原 太樹

Missing alt text value

はじめに

みなさん、こんにちは ! AI エージェントを使う時、確実に行なって欲しい処理が行われていなかったことはありませんか ?

とはいえ、綿密にワークフローを組むと AI エージェントの柔軟性が失われてしまいますよね。今回は ソリューションアーキテクトの伊勢田と菅原が、AI の柔軟性とエンタープライズに求められる決定論的処理を組み合わせたソリューションをご紹介します。

なお本記事では、同じ入力に対して常に同じ結果が返る性質を「決定論的」、同じ入力でも結果が一通りに定まらない性質を「非決定的」と呼んでいます。AI が確率的に応答を組み立てる以上、その振る舞いは非決定的である、というのがこの記事の出発点です。

 

X ポスト » | Facebook シェア » | はてブ »

builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。

今すぐ登録 »

AI エージェントの非決定性のコントロール

AWS の最新情報を取得し、毎週私にメールを送付してくれる AI エージェントを作ると考えましょう。プロンプトは下記の通りで、簡単に作成できますね。

AWS Knowledge MCP を利用し、今週の AWS What's New をサマリーしてください。
その結果を、e-Mail MCP を使用して私に送付してください。

ところがある週メールが送付されませんでした。AI エージェントの実行基盤にはエラーも例外も出ていない。エージェントの応答だけを見れば、成功したようにしか見えない。

こういったことが、皆様にもあるのではないでしょうか?最新情報のサマリーなどは、非決定的な外的要因を柔軟に受け止めるため AI が最適ですが、メールサーバーへの接続などの決定論的処理は従来通り機械的に行い、エラーが起きた時は明示的に把握したいですよね。

本記事では、非決定的な AI による処理と、決定論的な処理の良いとこどりをするために、AWS Step FunctionsAmazon Bedrock AgentCore harness を組み合わせて実現するところまでを、段階的に解説していきます。

Missing alt text value

Step 1 : AgentCore harness によるお手軽エージェント構築

冒頭で紹介した簡単なエージェントは、今年 6 月に GA した Amazon Bedrock AgentCore harness で簡単に構築することができます。AgentCore harness は、エージェントが何をするかを宣言することによって、コードの作成をせずに AgentCore 上に AI エージェントを構築することができる機能となっています。まずは、決定論的や非決定的など難しいことは気にせず、最新情報取得エージェントを作ってみましょう。

今後の拡張性のために、Step Functions から AgentCore harness を呼び出してみます。のちの章でこのエージェントを制御していきますが、PoC の段階でももStep Functions から構築することで、デバッグのしやすさと拡張性が高くなります。

Step Functions の Workflow Studio を開いたら、左側のステート検索欄から InvokeHarness をドラックアンドドロップしてみましょう。

Missing alt text value

ワンクリックでエージェントを作成し、設定する

ステートを設置したら、右側のパネルから定義をしていきます。

Quick Create Harness をクリックして 1 分ほど待つと、裏側で自動で何も設定されていない AgentCore harness が作成されます。実はこのままでもデフォルトモデルとデフォルトのシステムプロンプトが設定されており、基本的な動作なら実行することが可能です。今回はエージェントに対する指示と、ツールの設定のみ行いましょう。

AgentCore に対する指示を Message 欄に入力

Messages の欄に、下記の JSON をペーストします。この JSON は、AgentCore に対する指示が入っています。今回は毎回同じ文が送信されますが、前回までのステートから変数を用いて入力することも可能です。

json
[
  {
    "Content": [
      {
        "Text": "今日の日付を確認して、AWS Knowledge MCP を利用し、今週の AWS What's New をサマリーしてください。"
      }
    ],
    "Role": "user"
  }
]

ツールの設定

ツールの設定では、AWS Knowledge Server をセットしてみましょう。 Tools から Add tool ボタンをクリックし、プルダウンボックスか Remote MCP Server をクリックします。Endpoint URL に AWS Knowledge MCP Server の URL である https://knowledge-mcp.global.api.aws を入力します。Name には aws-knowledge-mcp を入力します。

余談ですがツールには、Gateway やブラウザツール、Code Interpreter などの AgentCore の機能を利用することもできます。

Missing alt text value

ワークフローを実行し、エージェントの動きを見る

それでは、作ったエージェントを動かしてみましょう ! 右上の保存ボタンをクリックしたら、実行ボタンを押して実行を開始します。エージェントがリサーチするのに 2 分ほど待機すると、実行ステータスが緑色に変わって実行が成功したのがわかるはずです。

出力は、実行の入力と出力タブを参照します。成功していれば、今週の AWS What's New がマークダウン形式で確認できるはずです。この画面ではマークダウンが JSON に組み込まれており見にくいですが、心の目でパースしましょう。実行が成功しているかどうかはわかるはずです。

AWS Step Functions の Workflow Studio から画面遷移することなく、たった数度のステップでエージェントを設定することができました。非常に簡単に実証できることがお分かりいただけましたか ? エージェントが正常に動作することがわかったので、次は決定論的な処理を用いてエージェントを本番の構成にしていきます。

Missing alt text value

Step 2 : 決定論的に処理させたい部分を分離するパターン

AgentCore harness を使い、調査エージェントをワークフローの中で呼び出せることを確認できました。

次は、調査結果をメールで私に送付させることを考えます。最もプリミティブな方法として、AgentCore harness にメール送信ツールを持たせる実装が考えられます。

この時、AI エージェントが実行するタスクを分解してみると、「リクエストの解釈」→「調査の実行」→「調査結果のまとめ」→「メール送信」という4つの段階に分けることができます。

「リクエストの解釈」→「調査の実行」→「調査結果のまとめ」までは、AI の推論能力をフル活用する領域です。一方で、「メール送信」は、推論というよりは「決定論的に」実行してほしいタスクです。

基盤モデルによって駆動される AI エージェントは、その動作に常に不確定性を含みます。従って、推論の結果によっては「調査が完了しなかったからメールを送信せず終了しよう」「MCP サーバーにアクセスできなかったから調査を終了しよう」といった形でメールを送信せずワークフローが終了する可能性があります。

AWS What's New をサマリーしてメール送信する程度のワークフローであれば、メールが送信されなくても大きな問題にはならないでしょう。しかし、これが決済処理の通知だったら、あるいは重要操作の人間による承認だったらどうなるでしょうか?

そこで、AI エージェントに任せるべきでない「決定論的に」実行してほしい処理をツールではなくワークフローの中に組み込むことで決定論的な方法で取り扱うことを考えます。

Missing alt text value

決定論的に処理出せたい部分を分離する

それでは、決定論的な処理を Step Functions を用いて作成していきましょう。まず、先ほどのシンプルなワークフローを、InvokeHarness による「リクエストの解釈」→「調査の実行」→「調査結果のまとめ」部分に、「メール送信」を Amazon SES の SendEmail アクションに分離することを考えます。

Missing alt text value

ワークフロー定義にステートを追加

先ほどのワークフロー定義に、AgentCore harness の出力を受け取って SES で送信するステートを下記のように追加します。

json
{{
  "Comment": "...",
  "QueryLanguage": "JSONata",
  "StartAt": "InvokeHarness",
  "States": {
    "InvokeHarness": {
      "Type": "Task",
      "Resource": "arn:aws:states:::bedrockagentcore:invokeHarness",
      "Arguments": {
        "HarnessArn": "arn:aws:bedrock-agentcore:region:accountId:harness/harnessId",
        "RuntimeSessionId": "{% $uuid() & $uuid() %}",
        "Messages": [
          {
            "Role": "user",
            "Content": [
              { "Text": "{% $states.input.userMessage %}" }
            ]
          }
        ]
      },
      "Assign": {
        "agentText": "{% $states.result.Output.Message.Content[0].Text %}",
        "userMessage": "{% $states.input.userMessage %}"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "BedrockAgentCore.ThrottlingException",
            "BedrockAgentCore.InternalServerException"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "SendEmail"
    },
    "SendEmail": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:sesv2:sendEmail",
      "Arguments": {
        "FromEmailAddress": "your-email-address",
        "Destination": {
          "ToAddresses": ["your-email-address"]
        },
        "Content": {
          "Simple": {
            "Subject": { "Data": "{% 'AgentCore harness output for: ' & $userMessage %}" },
            "Body": { "Text": { "Data": "{% $agentText %}" } }
          }
        }
      },
      "End": true
    }
  }
}
}

harness 出力は Converse 形状なので、最終テキスト Output.Message.Content[0].Text を変数 agentText に退避し、件名用に userMessage も変数化して次ステートへ引き回しています。

これでも動きます。

Human in the Loop パターンを実現するには ?

今回は必要ありませんが、コールバックタスクを用いて処理を待機させる Step Functions の機能を活かして、下記のように人間の承認をリクエストすることもできます。これによって Human in the Loop パターンも実現することができます。少し実装が必要ですので、Human in the Loop パターンについては、以下のドキュメントも参照してください。

json
"SendApprovalEmail": {
  "Type": "Task",
  "Resource": "arn:aws:states:::aws-sdk:sesv2:sendEmail.waitForTaskToken",
  "Arguments": {
    "FromEmailAddress": "your-email-address",
    "Destination": {
      "ToAddresses": ["your-email-address"]
    },
    "Content": {
      "Simple": {
        "Subject": { "Data": "{% '【承認依頼】AgentCore harness output for: ' & $userMessage %}" },
        "Body": {
          "Text": {
            "Data": "{% $agentText & '\n\n---\n✅ 承認する: https://your-approval-api/approve?token=' & $states.context.Task.Token & '\n❌ 却下する: https://your-approval-api/reject?token=' & $states.context.Task.Token %}"
//省略
}

Step 3 : Workflow as Tool パターン

決定論的な処理を分離する際に、もう一つの方法論として Workflow as Tools パターンも考えられます。Workflow as Tools パターンとは、先ほどとは逆に、決定論的なワークフローの実行をAI エージェントのツールとして実装する設計パターンです。これによって、ワークフローの決定性と AI エージェントによる柔軟な判断を分離して設計することができます。

Missing alt text value

Workflow を AI エージェントから呼び出す

先ほどの AWS What's New をサマリーし、メール送信する例に戻ります。このシンプルなワークフローに、AWS What's New で観測されたアップデートが、自社で運用するサービスに関連するものであった場合、関連チームに向けてより詳細な調査結果と影響度分析レポートを作成し、送ることを考えてみます。例えば次のようなワークフローを設計できるでしょう。

Missing alt text value

プロンプトの修正

まず、AgentCore harness の定義は下記のようにします。

json
あなたは AWS の最新アップデート(AWS What's New)を監視し、自社が運用するサービスへの影響を分析する調査アシスタントです。

【自社が運用するサービスの前提】
- Amazon ECS 上で稼働するコンテナ化された Web API 群
- Amazon Aurora PostgreSQL をデータストアとして使用
- Amazon S3 + CloudFront による静的配信
- Amazon Bedrock を用いた生成 AI 機能

【あなたのタスク】
ユーザーから渡された AWS のアップデート情報について、必要に応じて aws_knowledge ツール(search_documentation / read_documentation)で一次情報を確認し、以下を判断・作成してください。
1. そのアップデートが上記の自社運用サービスに関連するかどうかを判断する。
2. 関連する場合は、詳細調査結果と影響度分析(何が変わるか、どのサービスに、どの程度の影響か、推奨アクション)をまとめる。

【出力形式】
必ず次の JSON オブジェクトだけを出力してください。前後に説明文やマークダウンのコードブロックを付けてはいけません。

{
  "relevant": true または false,
  "subject": "メール件名(関連ありの場合のみ。日本語)",
  "impact_level": "high | medium | low(関連ありの場合のみ)",
  "affected_services": ["影響を受ける自社サービス名の配列"],
  "report": "関連チーム向けの詳細調査結果と影響度分析レポート本文(日本語のプレーンテキスト。関連ありの場合のみ)",
  "reason": "関連なしと判断した場合の理由(関連なしの場合のみ)"
}

事実と推論を区別し、確認できた一次情報に基づいて記述してください。断定できない点は推測であることを明示してください。

AgentCore Gateway の作成

AgentCore harness とワークフローを接続するために AgentCore Gateway を利用します。ターゲットスキーマは下記のように設定します。

json
{
  "mcp": {
    "lambda": {
      "lambdaArn": "arn:aws:lambda:region:accountid:function:waat-email-tool",
      "toolSchema": {
        "inlinePayload": [
          {
            "name": "send_email",
            "description": "Send an email. Use this tool whenever the user asks to send an email. Returns a confirmation message in Japanese.",
            "inputSchema": {
              "type": "object",
              "properties": {
                "to": { "type": "string", "description": "recipient email address" },
                "subject": { "type": "string", "description": "email subject" },
                "body": { "type": "string", "description": "email body" }
              },
              "required": ["to"]
            },
            "outputSchema": {
              "type": "object",
              "properties": { "result": { "type": "string", "description": "confirmation message" } }
            }
          }
        ]
      }
    }
  }
}

Lambda 関数をセット

AgentCore Gateway の target としてステートマシンを呼び出す Lambda 関数をセットします。Lambda 関数は、詳細レポートを AgentCore harness 上の AI エージェントに作成させ、結果を受け取って Amazon SES を使ってメールを送信するワークフローを呼び出すようにします。

python
import json
import os
import boto3

sfn = boto3.client("stepfunctions")
STATE_MACHINE_ARN = os.environ["STATE_MACHINE_ARN"]
DELIMITER = "___"

def lambda_handler(event, context):
    # Resolve the invoked tool name (format: "<target>___<tool>")
    tool_name = None
    try:
        original = context.client_context.custom["bedrockAgentCoreToolName"]
        tool_name = original.split(DELIMITER, 1)[-1]
    except Exception:
        tool_name = "send_email"

    # Start a synchronous execution of State Machine A.
    resp = sfn.start_sync_execution(
        stateMachineArn=STATE_MACHINE_ARN,
        input=json.dumps(event or {}),
    )

    status = resp.get("status")
    if status != "SUCCEEDED":
        return {
            "statusCode": 500,
            "error": f"State Machine A did not succeed: {status}",
            "cause": resp.get("cause"),
        }

    output = json.loads(resp.get("output") or "{}")
    # output == {"message": "メールを送信しました"}
    return {
        "tool": tool_name,
        "result": output.get("message", output),
        "stateMachineExecutionArn": resp.get("executionArn"),
    }

ワークフローの構築

最後に、ツールとなる詳細調査+レポート作成+メール送信ワークフローは次のような定義とします。

json
{
  "Comment": "...",
  "StartAt": "Investigate",
  "States": {
    "Investigate": {
      "Type": "Task",
      "Resource": "arn:aws:states:::bedrockagentcore:invokeHarness",
      "Parameters": {
        "HarnessArn": "arn:aws:bedrock-agentcore:region:accountId:harness/harnessId",
        "RuntimeSessionId.$": "States.Format('{}{}', $$.Execution.Name, $$.Execution.Name)",
        "Messages": [
          {
            "Role": "user",
            "Content": [
              { "Text.$": "States.Format('AWS の最新アップデート情報で次のものを観測しました: {}。このアップデートについて調査し、自社サービスへの影響度分析レポートを作成してください。', $.whatsNew)" }
            ]
          }
        ]
      },
      "Next": "SendReport"
    },
    "SendReport": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:sesv2:sendEmail",
      "Parameters": {
        "FromEmailAddress": "your-email-address",
        "Destination": {
          "ToAddresses": ["your-email-address"]
        },
        "Content": {
          "Simple": {
            "Subject": {
              "Data": "AWS アップデート影響分析レポート",
              "Charset": "UTF-8"
            },
            "Body": {
              "Text": {
                "Data.$": "$.Output.Message.Content[0].Text",
                "Charset": "UTF-8"
              }
            }
          }
        }
      },
      "End": true
    }
  }
}

完成 !

AWS What's New をサマリーする部分は AI エージェントの仕事です。そして、その内容を踏まえて関連度・影響度を推定するためにも AI エージェントの柔軟性が必要となります。一方で、自社に影響があるアップデートが存在するとわかった時、詳細レポートを作ってメールを配信する部分は確実に実行されるべきフローです。AI エージェントの判断でこのフローを起動することにより、AI エージェントの柔軟性とワークフローの決定性のいいとこ取りをすることができました。

まとめ

AI エージェントの柔軟性というメリットと非決定性という特性を、Step Functions による決定論的コントロールで制御することができました。また、冒頭にあげたように、 AgentCore harness は Step Functions に簡単に組み込むことが可能になります。

ぜひ皆様も、このハンズオンのステップで簡単に AI エージェントを検証し、本番活用を行ってみてください!

今回のブログ記事は、AWS Summit Japan 2026 で発表された「ワークフローオーケストレーターにおける複雑性と非決定性のコントロール」というセッションの内容の一部をさらに深ぼったものです。ぜひ 動画資料 もご覧ください!

筆者プロフィール

Missing alt text value

伊勢田 氷琴

アマゾンウェブサービスジャパン合同会社
広域事業統括本部 テクニカルソリューション部
ソリューションアーキテクト

ソリューションアーキテクトとして、普段は幅広い業種・業界のお客様の技術支援に取り組んでいる。
AI/ML を専門領域として活動している。
Bedrock に無限の愛を抱いており、1時間に一回新しいモデルが登場していないかコンソールを覗きに行く。

LinkedIn

Missing alt text value

菅原 太樹 (Taiki Sugawara)

アマゾンウェブサービスジャパン合同会社
金融ソリューション本部
ソリューションアーキテクト

主に保険業界のお客様を担当しているソリューションアーキテクト。サーバーレスのエキスパートとして業界を跨いで活動している。
Step Functions に無限の愛を抱いており、毎週新しいステートがないかコンソールを覗きに行く。

X: @taikis_techLinkedIn