Serverless Operations, inc

>_cd /blog/id_a5cd7it0dja

title

Amazon Bedrock AgentCore (1) Bedrockとの違いの整理と AgentCore Runtime ~はじめてのAgentの起動~

Amazon Bedrock AgentCore は、AIエージェントを「安全に」「大規模に」構築・運用するための、AWSによるモジュール型プラットフォームとして2025年7月のNewYork AWS Summitで発表されました。様々なフレームワークやモデルとの連携が可能で、開発環境を本番環境へ移行させるために必要なスケーラビリティ、信頼性、セキュリティなどの機能が提供されます。

Amazon Bedrock と AgentCore の違い

Bedrockはさまざまなモデルを提供する基盤サービスが中核となっています。さらに生成AIモデルを活用したアプリケーション開発を支えるツールとして、RAG用(Knowledge Bases)、エージェント構築支援(Bedrock Agents)、ガードレールなどのマネージド機能が提供されています。

一方AgentCoreはBedrockを含む各種モデルやツールを組み合わせて「本番運用できるAIエージェント基盤」を提供するプラットフォームとして設計されています。LangGraph / Strands / CrewAI など、Bedrock外のフレームワークとも統合可能であり、その利用にBedrockそのものは必ずしも必須ではない、という特徴があります。

さらに、エージェントの 認証・監査・監視 まで含めたAWSその他サービスとの連携がシームレスに行われるようになっており「運用フェーズ」もカバーしています。

Agent :エージェントとは

「エージェント」という言葉は生成AIを活用したアプリケーション開発において多用される言葉です。

生成AIを活用したアプリケーションにおける エージェントとは、
「大規模言語モデル(LLM)の推論能力を活かし、外部のツールやデータにアクセスしながら、ユーザーの目的達成に向けて自律的に判断・実行する仕組み」 を指します。

LLM単体ではできない検索・計算・API呼び出し・Web操作などを補い、ユーザーの曖昧な指示を複数ステップに分解して実行し、記憶や状態管理を活用しながらタスクを継続し、外部ツールの利用順序を自律的にオーケストレーションする仕組みを提供します。

AWS Bedrock AgentCore Starter Toolkit

Amazon Bedrock AgentCore Starter Toolkit は、Python で開発されたオープンソース(Apache 2.0 ライセンス)の CLI ツールキットで、AgentCoreの様々な機能が統合されており、ゼロインフラ管理で最小限の設定だけで AI エージェントをデプロイできる仕組みが提供されます。

Bedrock Agent Core の機能群

Bedrock Agent Coreはさまざまな機能を提供しておりこれらは独立して動作します。その簡単なサマリーは以下となります。

  • Runtime:サーバレスでセキュアな実行環境を提供し、セッションの隔離や長時間稼働に対応
  • Memory:会話コンテキストや長期記憶の管理を簡略化する機能
  • Gateway:API や Lambda を Agent 用ツール化し、OAuth やセキュリティ統制を統合
  • Code Interpreter:サンドボックス内でコードを安全に実行
  • Browser:クラウド上のブラウザで Web 操作を自動化
  • Observability:ダッシュボードやトレースなど、エージェントの可視化をサポート
  • Identity:エージェントの認証・認可基盤を簡単に整備

AgentCore Starterkit はこれらの機能を1個づつ試すことができるサンプルを提供してくれます。

さっそくやってみる

この第一回の記事では一番基本のRuntime を試します。リージョンはus-west-2が自動で指定されます。

1.Bedrockのモデルアクセス

Bedrockマネージメントコンソール、左ペインの下部にある モデルアクセス をクリックします。

Claude Sonnet 4 のアクセス権を付与します。

2. IAM ユーザーの作成

AWS CLI に設定するためのIAMユーザーを作成します。

AmazonBedrockFullAccess , BedrockAgentCoreFullAccess という2つのビルトインIAMポリシーを設定します。さらに追加で以下のインラインポリシーを追加しておきます。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "IAMRoleManagement",
			"Effect": "Allow",
			"Action": [
				"iam:CreateRole",
				"iam:DeleteRole",
				"iam:GetRole",
				"iam:PutRolePolicy",
				"iam:DeleteRolePolicy",
				"iam:AttachRolePolicy",
				"iam:DetachRolePolicy",
				"iam:TagRole",
				"iam:ListRolePolicies",
				"iam:ListAttachedRolePolicies"
			],
			"Resource": [
				"arn:aws:iam::*:role/*BedrockAgentCore*",
				"arn:aws:iam::*:role/service-role/*BedrockAgentCore*"
			]
		},
		{
			"Sid": "CodeBuildProjectAccess",
			"Effect": "Allow",
			"Action": [
				"codebuild:StartBuild",
				"codebuild:BatchGetBuilds",
				"codebuild:ListBuildsForProject",
				"codebuild:CreateProject",
				"codebuild:UpdateProject",
				"codebuild:BatchGetProjects"
			],
			"Resource": [
				"arn:aws:codebuild:*:*:project/bedrock-agentcore-*",
				"arn:aws:codebuild:*:*:build/bedrock-agentcore-*"
			]
		},
		{
			"Sid": "CodeBuildListAccess",
			"Effect": "Allow",
			"Action": [
				"codebuild:ListProjects"
			],
			"Resource": "*"
		},
		{
			"Sid": "IAMPassRoleAccess",
			"Effect": "Allow",
			"Action": [
				"iam:PassRole"
			],
			"Resource": [
				"arn:aws:iam::*:role/AmazonBedrockAgentCore*",
				"arn:aws:iam::*:role/service-role/AmazonBedrockAgentCore*"
			]
		},
		{
			"Sid": "CloudWatchLogsAccess",
			"Effect": "Allow",
			"Action": [
				"logs:GetLogEvents",
				"logs:DescribeLogGroups",
				"logs:DescribeLogStreams"
			],
			"Resource": [
				"arn:aws:logs:*:*:log-group:/aws/bedrock-agentcore/*",
				"arn:aws:logs:*:*:log-group:/aws/codebuild/*"
			]
		},
		{
			"Sid": "S3Access",
			"Effect": "Allow",
			"Action": [
				"s3:GetObject",
				"s3:PutObject",
				"s3:ListBucket",
				"s3:CreateBucket",
				"s3:PutLifecycleConfiguration"
			],
			"Resource": [
				"arn:aws:s3:::bedrock-agentcore-*",
				"arn:aws:s3:::bedrock-agentcore-*/*"
			]
		},
		{
			"Sid": "ECRRepositoryAccess",
			"Effect": "Allow",
			"Action": [
				"ecr:CreateRepository",
				"ecr:DescribeRepositories",
				"ecr:GetRepositoryPolicy",
				"ecr:InitiateLayerUpload",
				"ecr:CompleteLayerUpload",
				"ecr:PutImage",
				"ecr:UploadLayerPart",
				"ecr:BatchCheckLayerAvailability",
				"ecr:GetDownloadUrlForLayer",
				"ecr:BatchGetImage",
				"ecr:ListImages",
				"ecr:TagResource"
			],
			"Resource": [
				"arn:aws:ecr:*:*:repository/bedrock-agentcore-*"
			]
		},
		{
			"Sid": "ECRAuthorizationAccess",
			"Effect": "Allow",
			"Action": [
				"ecr:GetAuthorizationToken"
			],
			"Resource": "*"
		}
	]
}

このポリシーには以下の通りAgentをAWS上にデプロイし運用管理を行うためのAWSサービスへの操作権限が含まれています。

作成されたAgentCoreはコンテナ形式でECRへ保存されCodeBuildを使ってデプロイされるため、必要な権限が付与されています。

3. Agent の作成

では準備ができましたのでAgentを作成していきます。

まず必要なライブラリをインストールします。

pip install bedrock-agentcore strands-agents bedrock-agentcore-starter-toolkit

次にAgent本体となる agent.py を作成します。

from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent

app = BedrockAgentCoreApp()
agent = Agent()

@app.entrypoint
def invoke(payload):
    """Your AI agent function"""
    user_message = payload.get("prompt", "Hello! How can I help you today?")
    result = agent(user_message)
    return {"result": result.message}

if __name__ == "__main__":
    app.run()

動作に必要な requirements.txt をあわせて作成します。

cat > requirements.txt << EOF
bedrock-agentcore
strands-agents
EOF

4. ローカルでの起動

AWSへのデプロイ前にまずはローカルでテストを行います。

python agent.py
INFO:     Started server process [1364]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

Agentが起動します。別のシェルでアクセスを行いテストコマンドを実行します。

curl -X POST http://localhost:8080/invocations   -H "Content-Type: application/json"   -d '{"prompt": "Hello!"}'
{"result": {"role": "assistant", "content": [{"text": "Hello again! How can I assist you today?"}]}}

5. AWS へのデプロイ と起動

ではAgentをAWS上にデプロイを行い起動します。コンテナイメージの作成は自動で行われるため意識する必要はありません。

agentcore configure -e agent.py
agentcore launch

いくつか入力が求められますが、すべて[Enter]でデフォルトのまま進めます。

起動が完了したらテストをおこないます。

agentcore invoke '{"prompt": "横浜はどういうところですか?"}'
Payload:
{
  "prompt": "横浜はどういうところですか?"
}
Invoking BedrockAgentCore agent 'agent' via cloud endpoint
Session ID: 4163c454-86a8-4a6b-aba7-305a16461e66
Response:
{
  "ResponseMetadata": {
    "RequestId": "7ea424b4-d823-4842-9a8c-77d1bd7b6d56",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Sat, 16 Aug 2025 07:47:44 GMT",
      "content-type": "application/json",
      "transfer-encoding": "chunked",
      "connection": "keep-alive",
      "x-amzn-requestid": "7ea424b4-d823-4842-9a8c-77d1bd7b6d56",
      "baggage": "Self=1-68a03799-2fd827b26c1591242d734ef9,session.id=4163c454-86a8-4a6b-aba7-305a16461e66",
      "x-amzn-bedrock-agentcore-runtime-session-id": "4163c454-86a8-4a6b-aba7-305a16461e66",
      "x-amzn-trace-id": "Root=1-68a03799-4b5ae84878c7a91865fef259;Self=1-68a03799-2fd827b26c1591242d734ef9"
    },
    "RetryAttempts": 0
  },
  "runtimeSessionId": "4163c454-86a8-4a6b-aba7-305a16461e66",
  "traceId": "Root=1-68a03799-4b5ae84878c7a91865fef259;Self=1-68a03799-2fd827b26c1591242d734ef9",
  "baggage": "Self=1-68a03799-2fd827b26c1591242d734ef9,session.id=4163c454-86a8-4a6b-aba7-305a16461e66",
  "contentType": "application/json",
  "statusCode": 200,
  "response": [
    "b'{\"result\": {\"role\": \"assistant\", \"content\": [{\"text\":
\"\\xe6\\xa8\\xaa\\xe6\\xb5\\x9c\\xe3\\x81\\xaf\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe3\\x81\\xae\\xe7\\xa5\\x9e\\xe5\\xa5\\x
88\\xe5\\xb7\\x9d\\xe7\\x9c\\x8c\\xe3\\x81\\xab\\xe3\\x81\\x82\\xe3\\x82\\x8b\\xe6\\xb8\\xaf\\xe6\\xb9\\xbe\\xe9\\x83\\x
bd\\xe5\\xb8\\x82\\xe3\\x81\\xa7\\xe3\\x80\\x81\\xe4\\xba\\xba\\xe5\\x8f\\xa3\\xe7\\xb4\\x84370\\xe4\\xb8\\x87\\xe4\\xba
\\xba\\xe3\\x82\\x92\\xe6\\x93\\x81\\xe3\\x81\\x99\\xe3\\x82\\x8b\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe7\\xac\\xac\\xe4\\xba
\\x8c\\xe3\\x81\\xae\\xe9\\x83\\xbd\\xe5\\xb8\\x82\\xe3\\x81\\xa7\\xe3\\x81\\x99\\xe3\\x80\\x82\\\\n\\\\n**\\xe4\\xb8\\x
bb\\xe3\\x81\\xaa\\xe7\\x89\\xb9\\xe5\\xbe\\xb4\\xef\\xbc\\x9a**\\\\n\\\\n\\xf0\\x9f\\x8f\\x99\\xef\\xb8\\x8f
**\\xe6\\xad\\xb4\\xe5\\x8f\\xb2** -
1859\\xe5\\xb9\\xb4\\xe3\\x81\\xab\\xe9\\x96\\x8b\\xe6\\xb8\\xaf\\xe3\\x81\\x97\\xe3\\x81\\x9f\\xe5\\x9b\\xbd\\xe9\\x9a\
\x9b\\xe8\\xb2\\xbf\\xe6\\x98\\x93\\xe6\\xb8\\xaf\\xe3\\x81\\xa8\\xe3\\x81\\x97\\xe3\\x81\\xa6\\xe7\\x99\\xba\\xe5\\xb1\
\x95\\xe3\\x81\\x97\\xe3\\x80\\x81\\xe8\\xa5\\xbf\\xe6\\xb4\\x8b\\xe6\\x96\\x87\\xe5\\x8c\\x96\\xe3\\x81\\xae\\xe7\\x8e\
\x84\\xe9\\x96\\xa2\\xe5\\x8f\\xa3\\xe3\\x81\\xa8\\xe3\\x81\\xaa\\xe3\\x82\\x8a\\xe3\\x81\\xbe\\xe3\\x81\\x97\\xe3\\x81\
\x9f\\\\n\\\\n\\xf0\\x9f\\x8c\\x8a **\\xe8\\xa6\\xb3\\xe5\\x85\\x89\\xe5\\x9c\\xb0**\\\\n-
\\xe3\\x81\\xbf\\xe3\\x81\\xaa\\xe3\\x81\\xa8\\xe3\\x81\\xbf\\xe3\\x82\\x89\\xe3\\x81\\x8421\\xef\\xbc\\x88\\xe9\\xab\\x
98\\xe5\\xb1\\xa4\\xe3\\x83\\x93\\xe3\\x83\\xab\\xe7\\xbe\\xa4\\xe3\\x81\\xa8\\xe8\\xa6\\xb3\\xe8\\xa6\\xa7\\xe8\\xbb\\x
8a\\xef\\xbc\\x89\\\\n- \\xe8\\xb5\\xa4\\xe3\\x83\\xac\\xe3\\x83\\xb3\\xe3\\x82\\xac\\xe5\\x80\\x89\\xe5\\xba\\xab\\\\n-
\\xe4\\xb8\\xad\\xe8\\x8f\\xaf\\xe8\\xa1\\x97\\xef\\xbc\\x88\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe6\\x9c\\x80\\xe5\\xa4\\xa7
\\xe8\\xa6\\x8f\\xe6\\xa8\\xa1\\xef\\xbc\\x89\\\\n- \\xe5\\xb1\\xb1\\xe4\\xb8\\x8b\\xe5\\x85\\xac\\xe5\\x9c\\x92\\\\n-
\\xe3\\x82\\xb3\\xe3\\x82\\xb9\\xe3\\x83\\xa2\\xe3\\x83\\xaf\\xe3\\x83\\xbc\\xe3\\x83\\xab\\xe3\\x83\\x89\\\\n\\\\n\\xf0
\\x9f\\x8d\\x9c **\\xe3\\x82\\xb0\\xe3\\x83\\xab\\xe3\\x83\\xa1** -
\\xe3\\x82\\xb5\\xe3\\x83\\xb3\\xe3\\x83\\x9e\\xe3\\x83\\xbc\\xe3\\x83\\xa1\\xe3\\x83\\xb3\\xe3\\x80\\x81\\xe3\\x82\\xb7
\\xe3\\x82\\xa6\\xe3\\x83\\x9e\\xe3\\x82\\xa4\\xe3\\x80\\x81\\xe3\\x83\\x8a\\xe3\\x83\\x9d\\xe3\\x83\\xaa\\xe3\\x82\\xbf
\\xe3\\x83\\xb3\\xe3\\x81\\xaa\\xe3\\x81\\xa9\\xe6\\xa8\\xaa\\xe6\\xb5\\x9c\\xe7\\x99\\xba\\xe7\\xa5\\xa5\\xe3\\x81\\xae
\\xe6\\x96\\x99\\xe7\\x90\\x86\\xe3\\x81\\x8c\\xe6\\x9c\\x89\\xe5\\x90\\x8d\\\\n\\\\n\\xf0\\x9f\\x9a\\x83
**\\xe3\\x82\\xa2\\xe3\\x82\\xaf\\xe3\\x82\\xbb\\xe3\\x82\\xb9** -
\\xe6\\x9d\\xb1\\xe4\\xba\\xac\\xe3\\x81\\x8b\\xe3\\x82\\x89\\xe9\\x9b\\xbb\\xe8\\xbb\\x8a\\xe3\\x81\\xa7\\xe7\\xb4\\x84
30\\xe5\\x88\\x86\\xe3\\x81\\xa8\\xe9\\x9d\\x9e\\xe5\\xb8\\xb8\\xe3\\x81\\xab\\xe4\\xbe\\xbf\\xe5\\x88\\xa9\\\\n\\\\n\\x
f0\\x9f\\x8f\\x9b\\xef\\xb8\\x8f **\\xe6\\x96\\x87\\xe5\\x8c\\x96** -
\\xe7\\x95\\xb0\\xe5\\x9b\\xbd\\xe6\\x83\\x85\\xe7\\xb7\\x92\\xe3\\x81\\x82\\xe3\\x81\\xb5\\xe3\\x82\\x8c\\xe3\\x82\\x8b
\\xe8\\xa1\\x97\\xe4\\xb8\\xa6\\xe3\\x81\\xbf\\xe3\\x81\\xa8\\xe3\\x80\\x81\\xe3\\x83\\xa2\\xe3\\x83\\x80\\xe3\\x83\\xb3
\\xe3\\x81\\xaa\\xe9\\x83\\xbd\\xe5\\xb8\\x82\\xe6\\x99\\xaf\\xe8\\xa6\\xb3\\xe3\\x81\\x8c\\xe8\\xaa\\xbf\\xe5\\x92\\x8c
\\xe3\\x81\\x97\\xe3\\x81\\x9f\\xe7\\x8b\\xac\\xe7\\x89\\xb9\\xe3\\x81\\xae\\xe9\\x9b\\xb0\\xe5\\x9b\\xb2\\xe6\\xb0\\x97
\\\\n\\\\n\\xe6\\x9d\\xb1\\xe4\\xba\\xac\\xe3\\x81\\xab\\xe9\\x9a\\xa3\\xe6\\x8e\\xa5\\xe3\\x81\\x97\\xe3\\x81\\xaa\\xe3
\\x81\\x8c\\xe3\\x82\\x89\\xe3\\x82\\x82\\xe7\\x8b\\xac\\xe8\\x87\\xaa\\xe3\\x81\\xae\\xe9\\xad\\x85\\xe5\\x8a\\x9b\\xe3
\\x82\\x92\\xe6\\x8c\\x81\\xe3\\x81\\xa4\\xe3\\x80\\x81\\xe5\\x9b\\xbd\\xe9\\x9a\\x9b\\xe8\\x89\\xb2\\xe8\\xb1\\x8a\\xe3
\\x81\\x8b\\xe3\\x81\\xa7\\xe4\\xbd\\x8f\\xe3\\x81\\xbf\\xe3\\x82\\x84\\xe3\\x81\\x99\\xe3\\x81\\x84\\xe9\\x83\\xbd\\xe5
\\xb8\\x82\\xe3\\x81\\xa8\\xe3\\x81\\x97\\xe3\\x81\\xa6\\xe7\\x9f\\xa5\\xe3\\x82\\x89\\xe3\\x82\\x8c\\xe3\\x81\\xa6\\xe3
\\x81\\x84\\xe3\\x81\\xbe\\xe3\\x81\\x99\\xe3\\x80\\x82\"}]}}'"
  ]
}

日本語出力はそのままでは化けてしまいます。出力を例えば test.json に保存すれば以下のコマンドで正しい日本語にデコードできます。

python3 fix.py < test.json
#!/usr/bin/env python3
# fix_agentcore.py
# agentcore invoke の出力から b'...' 断片を抽出→連結→UTF-8デコード→内側JSON→テキスト抽出

import sys, re, ast, json

def strip_ansi(s: str) -> str:
    return re.sub(r'\x1B\[[0-?]*[ -/]*[@-~]', '', s)

def find_bytes_literals(s: str):
    """ログ全体から b'...'(および b"...") を順番に総当りで抜き出す。
       途中で入る実際の改行は除去し、JSON由来の '\\' は '\' に戻す。"""
    out = []
    i = 0
    n = len(s)

    def grab(start_quote: str, end_quote: str, pos: int):
        esc = False
        j = pos
        while j < n:
            ch = s[j]
            if ch == end_quote and not esc:
                return j
            if ch == '\\':
                esc = not esc
            else:
                esc = False
            j += 1
        return -1

    while True:
        # b' または b"
        idx1 = s.find("b'", i)
        idx2 = s.find('b"', i)
        idx = min([x for x in [idx1, idx2] if x != -1], default=-1)
        if idx == -1:
            break

        quote = s[idx+1]  # ' or "
        end = grab(quote, quote, idx+2)
        if end == -1:
            # 閉じが見つからない場合はスキップして次へ
            i = idx + 2
            continue

        inner = s[idx+2:end]

        # 実際の改行・復帰は除去(CLIの折返しで壊れるため)
        inner = inner.replace("\r", "").replace("\n", "")
        # JSONエスケープ由来の \\ を \ に戻す(reprとして正しい形に近づける)
        inner = inner.replace("\\\\", "\\")
        # b'...' として評価(ダメなら b"..." も試す)
        lit = f"b'{inner}'"
        try:
            b = ast.literal_eval(lit)
        except Exception:
            lit = f'b"{inner.replace("\"","\\\"")}"'
            try:
                b = ast.literal_eval(lit)
            except Exception:
                b = None
        if b is not None:
            out.append(b)

        i = end + 1

    return out

def main():
    data = sys.stdin.read()
    if not data:
        print("usage: agentcore invoke '{\"prompt\":\"...\"}' 2>&1 | python3 fix_agentcore.py", file=sys.stderr)
        sys.exit(2)

    data = strip_ansi(data)
    pieces = find_bytes_literals(data)

    if not pieces:
        print("ERROR: no b'...'/b\"...\" bytes-literals found in input.", file=sys.stderr)
        sys.exit(1)

    blob = b"".join(pieces)

    # UTF-8 → 内側JSON
    try:
        inner_text = blob.decode("utf-8")
    except UnicodeDecodeError as e:
        print(f"ERROR: UTF-8 decode failed: {e}", file=sys.stderr)
        sys.exit(1)

    # JSONパースして content[].text を抽出
    try:
        inner = json.loads(inner_text)
    except json.JSONDecodeError:
        # 失敗したらそのまま本文を出す
        print(inner_text)
        sys.exit(0)

    content = inner.get("result", {}).get("content", [])
    texts = [c.get("text") for c in content if isinstance(c, dict) and "text" in c]
    if texts:
        print("\n\n".join(t.strip() for t in texts if t))
    else:
        # 見つからなければ内側JSONを丸ごと
        print(inner_text)

if __name__ == "__main__":
    main()

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-