お知らせ・ブログ

オノコムからの最新情報や、生成AI、AWSクラウド、ノーコードアプリケーションに関する有益な情報をお届けします。

WEBシステム開発(インフラエンジニア)がBedrock AgentCoreでTypeScript+Vercel AI SDK+Honoをデプロイしてみた

2026年01月08日
斧山 洋一
ブログ

こんにちわ。昨年末はラスベガス(AWS re:Invent)に行ったり、事務所移転があったり、決算があったりで、バタバタしっぱなしだった斧山です。

AWSでは今 Bedrock AgentCore が熱いということで、ハンズオンしないといけないなと思いつつも、全然着手できていませんでした。

とにかくやらねばならぬ、ということで正月休みにハンズオンをしてみました(といっても Claude Code が、ですが)。


自己紹介と今回のテーマ

まず、私の経歴です。

もともとはデータセンターでオンプレミスのサーバー構築・運用をしていました。主に WEB 系、ゲーム系です。そこから WEB 開発(Perl、PHP)を経て、AWS クラウドが黒船のようにやって来て、気がつけばオンプレミスをクラウドへマイグレーションしつつ、Node.js、React.js などでシステム開発をしているという、いわゆる古い人間です。

そんな自分から見ると、Bedrock AgentCore が「すごい」と言われても、

  • Lambda と何が違うのか
  • EC2、ECS、Fargate、App Runner とどう違うのか

が正直よく分からない。

ということで、「とにかくハンズオンしてみた記録」をまとめます。
ハンズオンと言いながら、手順というより 抽象化理解の備忘録 です。

この記事では以下を整理します

  • AgentCore Runtime の正体
  • Lambda / ECS との違い
  • Webサーバーは必要か
  • どんなケースで使うべきか

また、以下の方を対象としています。

  • Bedrock / Lambda / ECS の基礎理解がある方向けです

古いインフラエンジニアがまず知りたいこと

最初に浮かんだ疑問はこれです。

多分コンテナを動かしてくれるのだろうけど、Web サーバーは要るのか?

業務では、React + Vite を CloudFront にアップロードする構成が多く、他にも WordPress や Laravel を EC2 で配信するケースがあります。その場合は Apache や Nginx をインストールして起動します。

では AI エージェントの場合はどうなのか。

  • そもそも Web サーバーは必要なのか
  • フロントエンドとどうやって通信するのか
  • AgentCore はどこまで面倒を見てくれるのか

このあたりが全く分かりませんでした。

現在弊社では、AI エージェントは Vercel AI SDK を使用し、Lambda 上に配置して AppSync 経由で React から呼び出す構成が多いです。

それと比べて AgentCore は何が違うのか、整理してみました。


Bedrock AgentCore のサービス構成

AgentCore は一言で言っても、いくつかのサービスに分かれています。

サービス 役割 主な機能
Runtime エージェントのデプロイ・実行基盤 サーバーレスホスティング、自動スケーリング、セッション管理
Memory 永続的なコンテキスト記憶 イベントメモリ、セマンティックメモリ、会話履歴保持
Gateway API → MCP ツール変換 既存 API のツール化、認証統合、セマンティック検索
Identity 認証・認可管理 OAuth / OIDC 統合、ユーザー代理認証、トークン管理
Built-in Tools マネージドツール Code Interpreter、Browser
Observability 監視・トレース OpenTelemetry 統合、分散トレース、メトリクス
Policy セキュリティガバナンス Cedar ポリシー評価、アクセス制御、監査ログ

これらはそれぞれ独立して利用可能で、必ず全部を組み合わせる必要はありません。
例えば、Lambda から AgentCore Memory だけを使う、といった構成も可能です。

※ Identity、Gateway はRuntimeと連携して使用するため、独立して使用することはできません。 ※ Policy は Gateway と連携してツール呼び出しを制御するため、独立して使用することはできません。

本記事では Runtime に焦点を当てて解説します。


Bedrock AgentCore Runtime は Strands SDK 前提?

AgentCore Runtime は、AWS が提供する Strands SDKAgentCore SDK を使うことで、ほぼノーコードで AI エージェントをデプロイできます。

ただし、これらは基本的に Python ベース です。

  • Strands SDK は TypeScript 対応が発表されたが、未実装機能が多い
  • 現時点では TypeScript 版と AgentCore SDK を組み合わせて使えない

AI の世界は、機械学習の流れもあり Python 中心です。
Jupyter Notebook 上で開発する文化ですね。

ただ、

抽象化されすぎていて、何が起きているのか分からない

というのが正直な感想でした。

そこで今回は、Strands SDK / AgentCore SDK を使わず、

  • Node.js
  • TypeScript
  • Hono.js
  • Vercel AI SDK
  • AWS SDK
  • AWS CDK

でデプロイしてみることにしました(Claude Code が)。


Node.js / TypeScript / Hono で動かしてみた

イメージコードは以下のような感じです。

index.ts

import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { ping } from './routes/ping.js';
import { invocations } from './routes/invocations.js';

// CloudWatch Logs対応:改行を除去して1行出力
const originalConsole = { ...console };
const formatForCloudWatch = (...args: unknown[]): string => {
  return args
    .map((arg) => {
      if (typeof arg === 'string') return arg.replace(/\n/g, ' ');
      try {
        return JSON.stringify(arg).replace(/\n/g, ' ');
      } catch {
        return String(arg);
      }
    })
    .join(' ');
};
console.log = (...args: unknown[]) => originalConsole.log(formatForCloudWatch(...args));
console.error = (...args: unknown[]) => originalConsole.error(formatForCloudWatch(...args));
console.warn = (...args: unknown[]) => originalConsole.warn(formatForCloudWatch(...args));

const app = new Hono();

// ミドルウェア - /ping はログ出力しない
app.use('*', async (c, next) => {
  if (c.req.path === '/ping') {
    return next();
  }
  return logger()(c, next);
});

// ルート
app.route('/ping', ping);
app.route('/invocations', invocations);

// AgentCore Runtimeはポート8080を使用
const port = 8080;

serve({
  fetch: app.fetch,
  port: port,
});

invocations.ts

import { Hono } from 'hono';
import { invokeOpenAI, type OpenAIRequest } from '../services/openai.js';

const invocations = new Hono();

interface InvocationPayload {
  input: OpenAIRequest;
}

/**
 * AgentCore Runtimeからのリクエストを処理するエンドポイント
 */
invocations.post('/', async (c) => {
  try {
    const payload = await c.req.json<InvocationPayload>();

    console.log('Received invocation:', JSON.stringify(payload, null, 2));

    if (!payload.input) {
      return c.json(
        {
          result: false,
          message: null,
          error: 'Invalid payload: input is required',
        },
        400
      );
    }

    const response = await invokeOpenAI(payload.input);

    return c.json(response);
  } catch (error) {
    console.error('Error processing invocation:', error);

    return c.json(
      {
        result: false,
        message: null,
        error: error instanceof Error ? error.message : 'Unknown error occurred',
      },
      500
    );
  }
});

export { invocations };

上記のようなコードを Docker と AWS CDK を使い、サクッとデプロイできました。


CDK でのデプロイ

CDK のコードです。

import * as cdk from 'aws-cdk-lib';
import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';

export class OpenaiRuntimeStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ローカル Dockerfile からビルド
    const artifact = agentcore.AgentRuntimeArtifact.fromAsset(
      path.join(__dirname, '../app')
    );

    // AgentCore Runtime 作成(IAM認証)
    const runtime = new agentcore.Runtime(this, 'OpenAIRuntime', {
      runtimeName: 'OpenAIRuntime',
      agentRuntimeArtifact: artifact,
      environmentVariables: {
        SECRET_ARN: 'arn:aws:secretsmanager:ap-northeast-1:xxxx:secret:xxxx',
      },
      networkConfiguration: agentcore.RuntimeNetworkConfiguration.usingPublicNetwork(),
      // IAM認証(デフォルト)
    });

    // Secrets Manager 読み取り権限を付与
    runtime.addToRolePolicy(
      new iam.PolicyStatement({
        actions: ['secretsmanager:GetSecretValue'],
        resources: ['arn:aws:secretsmanager:ap-northeast-1:xxxx:secret:xxxx'],
      })
    );

    // 出力
    new cdk.CfnOutput(this, 'RuntimeArn', {
      value: runtime.agentRuntimeArn,
      description: 'AgentCore Runtime ARN',
    });
  }
}

なぜ Express.js ではなく Hono.js なのか。 以前、TypeScript 対応の AI エージェントフレームワーク Mastra を触ったときに Hono が対応していて、いいなと思ったからです。それだけです。

MCP サーバーの SDK なども使うのであれば、Express.js の方が良いかもしれません。


AgentCore Runtime の単発呼び出しは Lambda とほぼ同じ

実際にデプロイして分かったことです(今回は Agent のみで、MCP や認証は割愛)。

  • Runtime 側で HTTP を待ち受ける
  • Runtime 内部ではコンテナが実行される
  • コンテナ内部で HTTP サーバーとしてリクエストを処理する(Runtime は HTTP Proxy として処理)

Lambda URL の場合は、コード内で HTTP サーバーを立てる必要はありません。 一方、AgentCore Runtime では Express や Hono などの Web サーバーが必要 です。

つまり、

  • ECS / Fargate で Web サーバーを立てる構成
  • AgentCore Runtime

は実行モデルとしてはかなり近いです。

待ち受けるエンドポイント仕様は以下の通りです。

エンドポイント ポート 用途
/invocations 8080 エージェントのメインロジック
/ping 8080 ヘルスチェック
/ws 8080 WebSocket(双方向ストリーミング)

この仕様さえ満たせば、中身は何でも動きます。

  • Python に縛られない
  • Node.js 以外でも OK
  • Go / Rust も可能

Apache や Nginx、Tomcat を使ってPerlやJavaも理論上は可能ですが、AI 向け SDK の少なさやパフォーマンス面を考えると現実的ではなさそうです。

ちなみに AgentCore Runtime の呼び出しは認証があるため、curlなどではなく基本的には AWS SDK 経由で Lambda Invoke のように呼び出します。Cognito などで認証・認可さえ得ていれば、React などのフロントエンドからも ゼロトラスト で呼び出し可能です。


Lambda と比較して

単発呼び出しの場合、

  • Lambda:最大 15 分
  • AgentCore Runtime:最大 15 分

どちらもコールドスタートがあります。

そのため、単発実行だけを見ると大きな優劣はありません

すでに Lambda 上で AI エージェントを動かしていて困っていないのであれば、無理に移行する必要はないでしょう。

どちらも、

「プログラム実行環境を AWS が用意してくれる」

という点では同じです。


では何が違うのか

Lambda や ECS で十分では?と思いますが、違いは主に以下です。

  • SDK による AI エージェントのノーコードデプロイ
  • セッション ID ごとに VM が起動(最長 8 時間)
  • ストリーミング処理が強い
  • MCP / A2A プロトコル対応
  • インフラ・セキュリティ設定が不要(AI エージェント特化)

Lambda でも実装可能ですが、多くのものを 自前で作らなくてよい のがポイントです。

特に、スレッド・ユーザー単位で VM とメモリが管理される点は、会話型 AI エージェントでは大きなメリットです。

セッション ID を指定すると、

  • 最大 8 時間、同じ VM が応答
  • デプロイ後でも起動中の VM は同一コードで動作
  • セッション単位でセキュリティ分離

といった挙動になります。

ストリーミングも、UI/UX 的には必須ではありませんが、

  • ツール実行
  • 長いコンテキスト

が絡む場合は、体感速度の差は大きいです。


よくある誤解

「AgentCore を使ったから AI エージェント」「Lambda だから違う」というのは誤解です。

どちらも 実行環境 にすぎません。

AI エージェントの本質は以下です。

  1. 推論(Reasoning)
  2. ツール実行(Action)
  3. ループ(Iteration)
  4. 記憶(Memory)

Vercel AI SDK と既存 AWS リソースでこれを実装しても AI エージェントです。

Strands、Mastra、Bedrock Agent などは、これを まとめて提供してくれる ものとなります。


メモリについて

AgentCore Memory は、

  • 短期記憶
  • 長期記憶

を提供します。

これは Runtime 専用ではなく、Lambda などから AWS SDK 経由で利用可能です。

短期記憶はデフォルトで 90 日保持されます(7 日〜 365 日で設定可能)。

ただし、

DynamoDB に会話履歴を保存して LLM に渡すのと何が違うのか?

という疑問も出てきます。

例えば、以下のように会話履歴を組み立てて AI SDK に渡すのも同じ発想です。

const messages = [
  {
    role: 'user',
    content: [
      {
        type: 'text',
        text: text_value,
      },
    ],
  },
  {
    role: role,
    content: [
      {
        type: 'text',
        text: text_value,
      },
    ],
  },
];

const result = await generateText({
  model: openai(AI_MODEL_ID),
  messages: messages,
});

短期記憶に関しては、できることはほぼ同じ です。保存期間を超えてユーザーが履歴を参照したい場合は、別途 DB に保存する必要があります。

AgentCore は、

AI エージェントの動作に関わるバックエンド

を提供するもので、フロントエンドの要件まですべて満たすものではありません。

なお、Strands + AgentCore Runtime のみで完結させた場合でも、

  • Web UI は付いてこない
  • Streamlit や React で別途構築が必要

です。


結論:優劣ではなく適材適所

既存資産があるなら、無理に移行する必要はありません。 ただし、新規案件であれば AgentCore を選んでおくのは合理的です。

  • ストリーミングが必要か
  • セッション管理が必要か

といった ユースケースベース で判断すべきです。

AI エージェントの開発効率は、Runtime よりも フレームワーク側の影響が大きい のも事実です。

WEB システム・インフラの実績があるチームとしては、

  • Vercel AI SDK
  • TypeScript
  • Hono
  • Node / Bun
  • 既存 AWS スタック

を活かし続ける選択も重要です。

AgentCore は発展途上ですが進化は速い。 要件に合えば積極的に採用し、そうでなければ従来構成を選ぶ。

古い技術を過大評価せず、新しい技術も過信しない。 そのバランスを忘れないようにしたいと思います。

そして何より、Claude Code をはじめとする AI コーディングの進化 も無視できません。

フレームワークの枠に収まらない場合は、AI によるフルコード開発の方が有利な場面も増えていきそうです。

もし、記事に間違いがあればXまで教えていただけましたら幸いです。 https://x.com/onoyamayoichi