はじめに
こんにちは。高橋 (ポインコ兄) です。
AIエージェントに「全部任せる」のは、まだ怖い。でも、AIを一切使わないのももったいない——そんなジレンマを解決する新しい統合が登場しました。
AWS Step Functions × Amazon Bedrock AgentCore の統合です (2026年6月、プレビュー) 。以降、Amazon Bedrock AgentCore を「AgentCore」と略します。
この組み合わせにより、ワークフローの中に「AIが考えるステップ」を埋め込み、さらに重要な判断の前だけ人間が承認する、という構成がシンプルに実現できます。
本記事では、経費申請の AI 審査 + 人間承認フロー をハンズオンで構築しながら、この統合の使い方と実ユースケースを解説します。
こんな人におすすめ
- ✅ Step Functions を使ったことがある (基本操作は理解済み)
- ✅ AIエージェントをワークフローに組み込む方法を知りたい
- ✅ 「AI全自動」ではなく「AI + 人間の協調」設計に興味がある

- はじめに
- Step Functions × AgentCore とは
- この統合がなかった場合の実装
- ハンズオン:経費申請 AI 審査ワークフロー
- 実ユースケース考察
- 注意点・ハマりどころ
- まとめ
- 参考リンク
- あとがき
Step Functions × AgentCore とは
AgentCore Harness (マネージドハーネス)
Amazon Bedrock AgentCore の マネージドハーネス は、AIエージェントの実行基盤です。モデル・システムプロンプト・ツールを設定として宣言するだけで、オーケストレーションコードなしに動作します。
従来のエージェント実装:
自分でループを書く → モデル呼び出し → ツール実行 → 再度モデル呼び出し → ...
ハーネスを使うと:
モデル・ツール・プロンプトを設定 → InvokeHarness を呼ぶだけ
Step Functions との統合
Step Functions から Harness を呼び出すには、以下の Task 定義を使います:
{ "Type": "Task", "Resource": "arn:aws:states:::bedrockagentcore:invokeHarness", "Arguments": { "HarnessArn": "arn:aws:bedrock-agentcore:us-east-1:123456789012:harness/my-harness", "RuntimeSessionId": "{% $uuid() %}", "Messages": [ { "Content": [{ "Text": "{% $states.input.userMessage %}" }], "Role": "user" } ], "SystemPrompt": [{ "Text": "You are a helpful assistant." }], "Model": { "BedrockModelConfig": { "ModelId": "global.anthropic.claude-sonnet-4-6" } } }, "End": true }
主な制約 (重要)
| 項目 | 内容 |
|---|---|
| 統合パターン | Request Response のみ (.sync・コールバックは非対応) |
| 最大実行時間 | 15分 |
| レスポンス | 最終ターンのテキストのみ (途中のツール使用・推論ブロックは含まれない) |
| セッション継続 | RuntimeSessionId を同一にすることで複数呼び出し間でコンテキスト継続可能 |
| 利用可能リージョン | us-east-1, us-west-2, eu-central-1, ap-southeast-2 (プレビュー) |
この統合がなかった場合の実装
従来の構成
AgentCore 統合がない場合、Step Functions から AI エージェントを使うには Lambda を中継する必要がありました。
[Step Functions]
→ Task: Lambda 呼び出し
→ Lambda 内で agent loop を自前実装
→ Bedrock Converse API 呼び出し
→ ツール使用があれば実行して結果をフィードバック
→ end_turn になるまでループ
→ 最終応答を返す
→ 次の State へ
Lambda のコードはおよそこうなります:
import boto3, json bedrock = boto3.client('bedrock-runtime', region_name='us-east-1') def handler(event, context): messages = [{"role": "user", "content": [{"text": event["userMessage"]}]}] system = [{"text": "あなたは経費審査担当者です..."}] for _ in range(10): # 無限ループ防止 response = bedrock.converse( modelId="us.anthropic.claude-sonnet-4-6", # クロスリージョン推論プロファイル messages=messages, system=system, ) stop_reason = response["stopReason"] assistant_msg = response["output"]["message"] messages.append(assistant_msg) if stop_reason == "end_turn": for block in assistant_msg["content"]: if "text" in block: return {"result": block["text"]} elif stop_reason == "tool_use": tool_results = [] for block in assistant_msg["content"]: if "toolUse" in block: result = execute_tool(block["toolUse"]) # ツール実行 tool_results.append({ "toolResult": { "toolUseId": block["toolUse"]["toolUseId"], "content": [{"text": json.dumps(result)}] } }) messages.append({"role": "user", "content": tool_results}) raise Exception("Max iterations reached") def execute_tool(tool_use): # ツール実行ロジックを自前実装... pass
そして Step Functions 側は Lambda を呼ぶだけ:
{ "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:ACCOUNT_ID:function:expense-review-agent", "Parameters": { "userMessage.$": "States.Format('申請者: {}\n金額: {}円\n目的: {}', $.applicant, $.amount, $.purpose)" }, "Next": "判定分岐" }
何が変わったか
| 項目 | 従来 (Lambda 中継) | AgentCore 統合 |
|---|---|---|
| agent loop の実装 | 自前で書く (数十〜数百行) | ハーネスが管理 (不要) |
| ツール実行のフィードバック | 自前実装 | ハーネスが処理 |
| セッション継続 (多ターン) | DynamoDB 等でメッセージ履歴を自前保存 | RuntimeSessionId を指定するだけ |
| モデル・プロンプトの変更 | Lambda コードを修正してデプロイ | 設定変更のみ (コード変更なし) |
| 可観測性 (ターン詳細) | CloudWatch への書き込みを自前実装 | 自動で CloudWatch に記録 |
| 実行時間の上限 | Lambda の最大 15 分 | ハーネス側の設定に依存 (最大 15 分) |
AgentCore 統合の本質は「Lambda で書いていた agent loop のボイラープレートをなくす」ことです。Step Functions 側からは、複雑なエージェントも「ひとつの Task ステート」として扱えるようになります。
Lambda 版のデプロイ手順
実際に動かしてみましょう。コードは hands-on/lambda/index.py、State Machine 定義は hands-on/statemachine-lambda.json です。
1. Lambda をパッケージ化 & デプロイ
cd hands-on/lambda zip function.zip index.py # IAM ロール作成 aws iam create-role \ --role-name expense-review-lambda-role \ --assume-role-policy-document file://../iam-lambda-trust.json \ --region us-east-1 aws iam put-role-policy \ --role-name expense-review-lambda-role \ --policy-name bedrock-converse \ --policy-document file://../iam-lambda-policy.json # ※ iam-lambda-policy.json の Resource には foundation-model/* に加えて # inference-profile/* も必要 (クロスリージョン推論プロファイル使用のため) # Lambda 関数作成 (ACCOUNT_ID を自分のIDに差し替え) aws lambda create-function \ --function-name expense-review-agent \ --runtime python3.12 \ --role arn:aws:iam::ACCOUNT_ID:role/expense-review-lambda-role \ --handler index.handler \ --zip-file fileb://function.zip \ --timeout 120 \ --region us-east-1
2. State Machine 作成
# lambda/ から一段上に戻る cd .. # statemachine-lambda.json の ACCOUNT_ID を差し替えてから実行 aws stepfunctions create-state-machine \ --name expense-review-lambda \ --definition file://statemachine-lambda.json \ --role-arn arn:aws:iam::ACCOUNT_ID:role/StepFunctionsRole \ --region us-east-1
3. テスト実行
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:us-east-1:ACCOUNT_ID:stateMachine:expense-review-lambda \
--input '{"applicant":"山田太郎","amount":30000,"purpose":"書籍購入費 (AWS認定試験対策) "}' \
--region us-east-1

ハンズオン:経費申請 AI 審査ワークフロー
ワークフロー概要
[申請入力]
↓
[AgentCore: 経費審査] ← AI が申請を審査し APPROVE / REJECT / REVIEW を判定
↓
[Choice: 判定分岐]
├─ APPROVE → [承認処理 (完了) ]
├─ REJECT → [却下通知 (完了) ]
└─ REVIEW → [SNS 通知 + 人間承認待ち]
↓ (承認 / 却下コールバック)
[AgentCore: 最終回答生成]
↓
[完了]
ポイント:AgentCore は Request Response のみなので、人間承認は SNS + .waitForTaskToken の別 State で実装します。
Step 1:AgentCore Harness の作成

- AWS コンソール → Amazon Bedrock → AgentCore → ハーネス
- 「ハーネスを作成 (高度なハーネス作成)」をクリック
- 以下を設定:
| 項目 | 設定値 |
|---|---|
| ハーネス名 | expense-review-harness |
| モデル | Claude Sonnet 4 (claude-sonnet-4-6) |
| システムプロンプト | 後述 |
システムプロンプト (経費審査エージェント) :
あなたは経費申請の審査担当者です。以下のルールに従って申請を審査してください。 審査ルール: - 5万円以下かつ明確な業務目的がある → APPROVE - 金額に関わらず不審な内容・ルール違反がある → REJECT - 5万円超または判断が難しい場合 → REVIEW レスポンス形式 (必ずこの形式で返答) : DECISION: [APPROVE|REJECT|REVIEW] REASON: [判定理由を1〜2文で]
Step 2:SNS トピックの作成 (人間承認用)

aws sns create-topic --name expense-approval-topic --region us-east-1
承認リンクを受け取るメールアドレスをサブスクリプション登録します。
Step 3:State Machine の作成
Workflow Studio で以下の ASL を定義します。

{ "Comment": "経費申請 AI 審査 + 人間承認ワークフロー", "StartAt": "AI経費審査", "States": { "AI経費審査": { "Type": "Task", "Resource": "arn:aws:states:::bedrockagentcore:invokeHarness", "Arguments": { "HarnessArn": "arn:aws:bedrock-agentcore:us-east-1:ACCOUNT_ID:harness/expense-review-harness", "RuntimeSessionId": "{% $uuid() %}", "Messages": [ { "Content": [{ "Text": "{% '申請者: ' & $states.input.applicant & '\n金額: ' & $string($states.input.amount) & '円\n目的: ' & $states.input.purpose %}" }], "Role": "user" } ] }, "Next": "判定分岐", "ResultPath": "$.agentResult" }, "判定分岐": { "Type": "Choice", "Choices": [ { "StringMatches": "DECISION: APPROVE*", "Variable": "$.agentResult.Output.Message.Content[0].Text", "Next": "承認処理" }, { "StringMatches": "DECISION: REJECT*", "Variable": "$.agentResult.Output.Message.Content[0].Text", "Next": "却下通知" } ], "Default": "人間承認待ち" }, "承認処理": { "Type": "Pass", "Result": { "status": "APPROVED" }, "End": true }, "却下通知": { "Type": "Pass", "Result": { "status": "REJECTED" }, "End": true }, "人間承認待ち": { "Type": "Task", "Resource": "arn:aws:states:::sns:publish.waitForTaskToken", "Arguments": { "TopicArn": "arn:aws:sns:us-east-1:ACCOUNT_ID:expense-approval-topic", "Message": { "taskToken": "{% $states.context.Task.Token %}", "agentJudgement": "{% $states.input.agentResult.Output.Message.Content[0].Text %}", "application": "{% $states.input %}" } }, "Next": "最終回答生成", "TimeoutSeconds": 86400 }, "最終回答生成": { "Type": "Task", "Resource": "arn:aws:states:::bedrockagentcore:invokeHarness", "Arguments": { "HarnessArn": "arn:aws:bedrock-agentcore:us-east-1:ACCOUNT_ID:harness/expense-review-harness", "RuntimeSessionId": "{% $uuid() %}", "Messages": [ { "Content": [{ "Text": "{% '人間の最終判断: ' & $states.input.humanDecision & '\n申請内容: ' & $string($states.input.application) & '\n申請者への最終通知メッセージを丁寧に作成してください。' %}" }], "Role": "user" } ] }, "End": true } } }
Step 4:実行してみる
3パターンの入力でテストします:
# APPROVE ケース (3万円・業務目的明確)
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:us-east-1:ACCOUNT_ID:stateMachine:expense-review-agentcore \
--input '{"applicant":"山田太郎","amount":30000,"purpose":"書籍購入費 (AWS認定試験対策) "}' \
--region us-east-1
# REJECT ケース (業務外)
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:us-east-1:ACCOUNT_ID:stateMachine:expense-review-agentcore \
--input '{"applicant":"山田太郎","amount":3000,"purpose":"個人的な飲み会の代金"}' \
--region us-east-1
# REVIEW ケース (5万円超)
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:us-east-1:ACCOUNT_ID:stateMachine:expense-review-agentcore \
--input '{"applicant":"山田太郎","amount":80000,"purpose":"クラウド系カンファレンス参加費 (re:Invent 2026) "}' \
--region us-east-1

実行結果 (実際の出力) :
APPROVE:
{ "status": "APPROVED", "reason": "DECISION: APPROVE\nREASON: 申請金額30,000円は上限の5万円以下であり、AWS認定試験対策の書籍購入費として業務上の目的が明確です。不審な点も見当たらないため、承認といたします。" }
REJECT:
{ "status": "REJECTED", "reason": "DECISION: REJECT\nREASON: 申請目的が「個人的な飲み会」であり、業務との関連性が認められないため否認です。経費申請は業務上必要な支出に限られます。" }
REVIEW → 人間が承認した場合:
{ "status": "HUMAN_DECIDED", "humanDecision": { "humanDecision": "APPROVED" }, "agentJudgement": "DECISION: REVIEW\nREASON: 申請金額80,000円が審査基準の上限 (50,000円) を超過しているため、上長または経理部門による追加承認が必要です。" }
Lambda 版との出力比較
実際に動かして気づいた面白い点があります。同じシステムプロンプトを渡しているにもかかわらず、AgentCore 版はより豊かな回答を生成しました。
REVIEW ケースの agentJudgement (抜粋) :
📋 **審査サマリー** | 項目 | 内容 | 判定 | |------|------|------| | 申請者 | 山田太郎 | ✅ | | 金額 | 80,000円 | ⚠️ 上限超過 | | 業務目的 | カンファレンス参加費 | ✅ 明確 | | 不審点 | なし | ✅ | > **次のアクション**: 上長承認 or 経理部門へエスカレーション後、再審査をお願いします。
Lambda 版では「DECISION: REVIEW\nREASON: ...」の1〜2文で終わっていましたが、AgentCore 版はエージェントが自発的に Markdown テーブルの審査サマリーと次のアクションまで生成しました。同じプロンプトでも ハーネスの実行コンテキストがエージェントの推論を豊かにする 可能性を示唆しています。
Step 5:CloudWatch で AI の推論を確認
実行履歴から AgentCore ステップをクリックすると、CloudWatch へのリンクが表示されます。ここでエージェントのターンごとの推論・ツール使用が確認できます。

実ユースケース考察
1. 社内ワークフローの AI 化 (コンプライアンス対応)
購買申請・稟議・契約レビューなど、ルールが明確な審査業務は AI が一次審査を担い、例外ケースのみ人間にエスカレーションする構成が最適です。
効果:審査工数を削減しながら、重要判断の責任は人間が持つ
2. カスタマーサポートの一次対応
問い合わせ受信 → AgentCore: カテゴリ判定・回答案生成 → 軽微な質問 → 自動回答 → クレーム・複雑な案件 → 担当者へエスカレーション
3. データ ETL パイプラインへの AI 組み込み
非構造化データ (PDF・メール・自由記述フォーム) を構造化する処理を Step Functions のパイプラインに組み込めます。既存の ETL ワークフローに InvokeHarness ステップを追加するだけで AI 推論を追加できます。
4. マルチエージェント並列処理 (Map State)
複数ドキュメント → [Map State] → 各ドキュメントで並列 AgentCore 実行 → 結果集約
Step Functions の Map State と組み合わせることで、大量ドキュメントの並列 AI 処理が実現できます。
注意点・ハマりどころ
Request Response のみ
AgentCore の Task では .sync と .waitForTaskToken が使えません。人間承認などの非同期処理が必要な場合は、SNS・SQS などの別サービスで実装する必要があります。
15分タイムアウト
TimeoutSeconds を 15分超に設定しても、実際は 15分でタイムアウトします。長時間の推論が必要なタスクには向きません。
レスポンスは最終ターンのみ
途中の推論過程やツール使用結果は Output.Message.Content に含まれません。詳細なデバッグは CloudWatch Transaction Search で確認します。
リージョン制限 (プレビュー期間中)
2026年6月時点では、us-east-1・us-west-2・eu-central-1・ap-southeast-2 の 4 リージョンのみ。アジアパシフィック (東京) リージョン (ap-northeast-1) は対応していません。
まとめ
| 項目 | 従来のAIワークフロー | Step Functions × AgentCore |
|---|---|---|
| エージェントループの実装 | 自分で書く | ハーネスに任せる |
| 人間との協調 | 別途設計が必要 | Choice + waitForTaskToken で自然に実装 |
| 可観測性 | 自前で実装 | CloudWatch 連携が組み込み |
| 既存ワークフローとの統合 | 難しい | Task ステートを追加するだけ |
Step Functions × AgentCore の組み合わせは、既存のワークフローに AI の判断力を後付けする のに最適なアプローチです。特に「AI が判断し、人間が最終確認する」というヒューマン・イン・ザ・ループ設計は、AI への信頼を段階的に高めていく実践的な方法として、多くの現場で活用できると感じました。
参考リンク
- What's New: AWS Step Functions adds AgentCore-powered agentic reasoning step
- Invoke Amazon Bedrock AgentCore harness with Step Functions (公式ドキュメント)
- Amazon Bedrock AgentCore Developer Guide
あとがき
本記事では、AWS Step Functions × Amazon Bedrock AgentCore を使って、AI が一次判断し、人間が最終承認するワークフローを構築しました。
AgentCore のハーネスのおかげでエージェントのループ実装が不要になり、Step Functions 側は「AI に渡す → 結果で分岐する」という宣言的な記述だけで済むのが気に入っています。プレビュー段階ですが、これが GA になったとき、どれだけ多くの業務ワークフローが変わるか楽しみです。
兄いわく「ハーネスのシステムプロンプトと Step Functions のワークフロー設計は、Claude Code に相談しながら作ると爆速ですよ〜。このブログを読ませて『こういうフローを作りたい』って言えば大体やってくれます」とのことです。 確かに、ハンズオンをしながら詰まったところをその場で Claude に聞く、というスタイルが一番捗りました。
それではまた、ごきげんよう。
高橋 悠佑 (ポインコ兄) (執筆記事一覧)
健康志向です