Use Cases (更新: 2026/6/3)

Claude Code × Amazon Bedrock実践ガイド:AWSでClaudeを安全に本番運用する

Claude CodeとAmazon BedrockでClaudeを本番運用する実装手順。IAM、Converse API、ログ、費用管理まで。

Claude Code × Amazon Bedrock実践ガイド:AWSでClaudeを安全に本番運用する

Claude CodeでAI機能を作るとき、最初はAnthropic APIを直接呼ぶ方が簡単です。ただ、会社のAWSアカウントで本番運用する段階になると、APIキー管理、監査ログ、請求の分離、VPCやIAMの説明責任が一気に重くなります。そこで現実的な選択肢になるのがAmazon Bedrock経由でClaudeを呼ぶ構成です。

Bedrockは、Claudeなどの基盤モデルをAWSの認証・権限・ログ・請求の仕組みに乗せて使うためのマネージドサービスです。初心者向けに言うと、モデルそのものを自分で運用するのではなく、AWSの入口から安全に呼び出せるAI実行基盤です。Claude Codeは、そのBedrock実装を作るための相棒として使います。丸投げではなく、IAM、リトライ、ログ、費用上限、ガードレールを明文化したうえでコードを書かせるのがポイントです。

Masaが業務で一番痛かったのは「動くサンプル」はすぐ作れたのに、レビューで「このLambdaはどのモデルに、誰の権限で、いくらまで、どのログを残して呼ぶのか」と聞かれた瞬間に説明が止まったことです。この記事では、その反省を前提に、Claude Codeへ渡せるレベルまで具体化したBedrock本番構成を作ります。

この記事は2026年6月3日時点のAWS公式ドキュメントを基準にしています。BedrockのモデルID、リージョン、料金、クォータは変わるため、実装時は必ず次の公式情報を確認してください。

本番で使う全体像

まず構成を固定します。Claude Codeに「BedrockでチャットAPIを作って」とだけ頼むと、動くけれど監査できないコードになりがちです。最初に、誰が呼ぶのか、どのモデルを使うのか、どこで止めるのか、どこへログを出すのかを図にします。

flowchart LR
  U["User / Admin UI"] --> A["API Gateway or ALB"]
  A --> R["Lambda or ECS task"]
  R --> G["Input validation and budget guard"]
  G --> B["Amazon Bedrock Runtime<br/>Converse / ConverseStream"]
  B --> C["Claude model"]
  R --> L["App logs<br/>CloudWatch Logs"]
  B --> M["Model invocation logs<br/>CloudWatch Logs or S3"]
  R --> K["Knowledge Bases<br/>optional RAG"]
  R --> Q["Cost Explorer / CUR<br/>IAM principal attribution"]

この構成で扱う用語も先に整理します。Converse APIは、Bedrock上の複数モデルに対して会話形式で呼び出す統一APIです。Guardrailsは、入力とモデル応答に対して禁止トピック、個人情報、危険な内容などを評価する安全柵です。model invocation loggingは、Bedrock呼び出しの入力・出力・メタデータをCloudWatch LogsやS3に保存する機能です。IAM principal attributionは、Bedrockの費用をIAMユーザーやロール単位で見えるようにする費用管理の仕組みです。

向いているユースケース

1つ目は、社内ドキュメント検索チャットです。S3やナレッジベースに手順書、障害対応メモ、仕様書を置き、Bedrock Knowledge Basesで検索してClaudeに回答させます。RAGはRetrieval-Augmented Generationの略で、社内情報を検索してから回答を作る方式です。一般知識だけで答えさせるより、根拠を確認しやすくなります。

2つ目は、問い合わせ一次回答の下書きです。顧客からの問い合わせ、契約プラン、過去の回答テンプレートをClaudeに渡し、オペレーターが確認する前提の返信案を作ります。この場合はGuardrails、禁止トピック、個人情報マスク、監査ログが必須です。完全自動返信にする前に、人間承認を残す方が事故を減らせます。

3つ目は、開発チーム向けの運用アシスタントです。CloudWatch Logsの要約、Runbookの提示、デプロイ手順の確認、PR説明文の下書きなどに使えます。Claude CodeでAPI、Lambda、IAM、テスト、運用ドキュメントを同時に更新できるため、チーム標準に落とし込みやすい領域です。

4つ目は、ClaudeCodeLabのようなコンテンツサイト運用です。記事の品質チェック、内部リンク候補、説明文の長さ、コードブロックの検査をBedrock上のClaudeに任せると、AWS請求・IAM・ログに寄せたままAIレビューを回せます。収益導線を含む記事運用なら、Claude CodeのAPIコスト削減ガイド検証レシートワークフローと組み合わせると実務に近づきます。

セットアップ

前提はNode.js 20以上、AWS CLI設定済み、Bedrockを使うAWSアカウント、対象リージョンのClaudeモデル利用権限です。Anthropicモデルでは初回利用時に用途情報の提出が必要になる場合があります。2026年時点のAWSドキュメントでは、モデルアクセスは自動化が進んでいますが、Marketplace権限、支払い方法、AnthropicのFirst Time Use要件が足りないとAccessDeniedExceptionになります。

モデルIDは記事に固定しません。次のコマンドで自分のアカウントとリージョンで使えるClaudeモデルを確認し、環境変数に入れてください。

export AWS_REGION=us-east-1

aws bedrock list-foundation-models \
  --region "$AWS_REGION" \
  --query "modelSummaries[?providerName=='Anthropic'].[modelId,modelName]" \
  --output table

export BEDROCK_MODEL_ID="anthropic.claude-sonnet-4-20250514-v1:0"
aws bedrock get-foundation-model \
  --region "$AWS_REGION" \
  --model-identifier "$BEDROCK_MODEL_ID" \
  --query "modelDetails.{input:inputModalities,output:outputModalities,streaming:responseStreamingSupported}"

ローカル検証用の最小プロジェクトを作ります。

mkdir bedrock-claude-lab
cd bedrock-claude-lab
npm init -y
npm install @aws-sdk/client-bedrock @aws-sdk/client-bedrock-runtime @aws-sdk/client-bedrock-agent-runtime
npm install --save-dev typescript tsx @types/node
npx tsc --init --module NodeNext --moduleResolution NodeNext --target ES2022
mkdir -p src/lambda

package.jsonには実行スクリプトを追加します。

{
  "type": "module",
  "scripts": {
    "chat": "tsx src/chat.ts",
    "stream": "tsx src/stream.ts",
    "typecheck": "tsc --noEmit"
  }
}

IAMは最小権限から始める

BedrockのConverse APIを使う場合でも、IAMアクションとしてはモデル呼び出しの権限を許可します。非ストリーミングはbedrock:InvokeModel、ストリーミングはbedrock:InvokeModelWithResponseStreamが必要です。ログ出力はアプリ側のCloudWatch Logs権限と、Bedrockのモデル呼び出しログ設定を分けて考えます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBedrockModelsForStartupCheck",
      "Effect": "Allow",
      "Action": ["bedrock:ListFoundationModels", "bedrock:GetFoundationModel"],
      "Resource": "*"
    },
    {
      "Sid": "InvokeOnlyApprovedClaudeModels",
      "Effect": "Allow",
      "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
      "Resource": [
        "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-*",
        "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-*",
        "arn:aws:bedrock:us-east-1:123456789012:inference-profile/*"
      ]
    },
    {
      "Sid": "ApplyApprovedGuardrail",
      "Effect": "Allow",
      "Action": ["bedrock:ApplyGuardrail"],
      "Resource": "arn:aws:bedrock:us-east-1:123456789012:guardrail/your-guardrail-id"
    }
  ]
}

本番では123456789012、リージョン、モデル、ガードレールIDを自分の環境に置き換えます。Cross-Region inferenceを使う場合は、推論プロファイルと宛先リージョン側のモデルARNも権限設計に入れます。このあたりはClaude Code × AWS IAMガイドの最小権限レビューと同じ考え方です。

Converse APIを呼ぶ

次はAWS SDK v3でClaudeを呼びます。リクエストにはrequestMetadataを入れます。AWS公式ドキュメントでは、Converse APIのリクエストメタデータはモデル呼び出しログのフィルタリングに使えるため、機能名、ユーザー種別、リクエストIDを入れておくと調査が楽になります。

// src/bedrock-client.ts
import { randomUUID } from "node:crypto";
import {
  BedrockRuntimeClient,
  ConverseCommand,
  ConverseStreamCommand,
  type ConverseCommandInput,
} from "@aws-sdk/client-bedrock-runtime";

export const AWS_REGION = process.env.AWS_REGION ?? "us-east-1";
export const BEDROCK_MODEL_ID =
  process.env.BEDROCK_MODEL_ID ?? "anthropic.claude-sonnet-4-20250514-v1:0";

export const bedrock = new BedrockRuntimeClient({
  region: AWS_REGION,
  maxAttempts: Number(process.env.BEDROCK_MAX_ATTEMPTS ?? "3"),
});

type AskClaudeInput = {
  prompt: string;
  system?: string;
  maxTokens?: number;
  temperature?: number;
  userId?: string;
  feature?: string;
};

function optionalGuardrail(): ConverseCommandInput["guardrailConfig"] | undefined {
  const guardrailIdentifier = process.env.BEDROCK_GUARDRAIL_ID;
  if (!guardrailIdentifier) return undefined;

  return {
    guardrailIdentifier,
    guardrailVersion: process.env.BEDROCK_GUARDRAIL_VERSION ?? "DRAFT",
    trace: "enabled",
  };
}

export async function askClaude(input: AskClaudeInput) {
  const requestId = randomUUID();
  const startedAt = Date.now();

  const response = await bedrock.send(
    new ConverseCommand({
      modelId: BEDROCK_MODEL_ID,
      system: input.system ? [{ text: input.system }] : undefined,
      messages: [{ role: "user", content: [{ text: input.prompt }] }],
      inferenceConfig: {
        maxTokens: input.maxTokens ?? 800,
        temperature: input.temperature ?? 0.2,
      },
      guardrailConfig: optionalGuardrail(),
      requestMetadata: {
        requestId,
        feature: input.feature ?? "local-cli",
        userId: input.userId ?? "anonymous",
      },
    })
  );

  const text =
    response.output?.message?.content
      ?.map((block: { text?: string }) => block.text ?? "")
      .join("") ?? "";

  console.log(
    JSON.stringify({
      level: "info",
      event: "bedrock_converse",
      requestId,
      modelId: BEDROCK_MODEL_ID,
      latencyMs: Date.now() - startedAt,
      stopReason: response.stopReason,
      usage: response.usage,
      metrics: response.metrics,
    })
  );

  return { text, usage: response.usage, stopReason: response.stopReason, requestId };
}

export async function streamClaude(prompt: string) {
  const response = await bedrock.send(
    new ConverseStreamCommand({
      modelId: BEDROCK_MODEL_ID,
      messages: [{ role: "user", content: [{ text: prompt }] }],
      inferenceConfig: { maxTokens: 1200, temperature: 0.2 },
      guardrailConfig: optionalGuardrail(),
      requestMetadata: { feature: "stream-cli", requestId: randomUUID() },
    })
  );

  if (!response.stream) throw new Error("Bedrock did not return a stream.");

  for await (const event of response.stream) {
    const text = event.contentBlockDelta?.delta?.text;
    if (text) process.stdout.write(text);

    if (event.metadata?.usage) {
      process.stderr.write(`\nusage=${JSON.stringify(event.metadata.usage)}\n`);
    }
  }
}

CLIから呼ぶファイルです。

// src/chat.ts
import { askClaude } from "./bedrock-client.js";

const prompt = process.argv.slice(2).join(" ").trim();
if (!prompt) {
  console.error('Usage: npm run chat -- "Summarize Amazon Bedrock in three bullets"');
  process.exit(1);
}

const result = await askClaude({
  prompt,
  system: "You are a concise AWS assistant. If you are unsure, say what to verify.",
  maxTokens: 600,
  feature: "developer-chat",
});

console.log(result.text);
// src/stream.ts
import { streamClaude } from "./bedrock-client.js";

const prompt = process.argv.slice(2).join(" ").trim();
if (!prompt) {
  console.error('Usage: npm run stream -- "Write a deployment checklist"');
  process.exit(1);
}

await streamClaude(prompt);

実行します。

export AWS_REGION=us-east-1
export BEDROCK_MODEL_ID="anthropic.claude-sonnet-4-20250514-v1:0"

npm run chat -- "Amazon Bedrockを初学者向けに3行で説明して"
npm run stream -- "Bedrockを本番導入するチェックリストを作って"
npm run typecheck

Lambda化するときの実装

Lambdaに載せる場合は、Bedrockクライアントをハンドラー外で初期化します。コンテナ再利用時に接続や設定を使い回せるためです。API Gatewayの前段で認証し、Lambdaでは入力サイズ、maxTokens、機能名を必ず制限します。

// src/lambda/assistant-handler.ts
import { askClaude } from "../bedrock-client.js";

type ApiEvent = {
  body?: string | null;
  requestContext?: { requestId?: string };
};

const headers = { "content-type": "application/json; charset=utf-8" };

export const handler = async (event: ApiEvent) => {
  try {
    const body = JSON.parse(event.body ?? "{}") as {
      prompt?: string;
      maxTokens?: number;
      userId?: string;
    };

    if (!body.prompt || body.prompt.length > 8000) {
      return {
        statusCode: 400,
        headers,
        body: JSON.stringify({ error: "prompt is required and must be <= 8000 chars" }),
      };
    }

    const result = await askClaude({
      prompt: body.prompt,
      maxTokens: Math.min(body.maxTokens ?? 800, 1200),
      userId: body.userId ?? "anonymous",
      feature: "support-assistant",
    });

    return {
      statusCode: 200,
      headers,
      body: JSON.stringify({
        text: result.text,
        usage: result.usage,
        stopReason: result.stopReason,
        requestId: result.requestId,
      }),
    };
  } catch (error) {
    const name =
      typeof error === "object" && error && "name" in error ? String(error.name) : "UnknownError";
    const retryable = ["ThrottlingException", "ServiceUnavailableException", "InternalServerException"].includes(name);

    console.error(JSON.stringify({ level: "error", event: "assistant_failed", name, retryable }));

    return {
      statusCode: retryable ? 503 : 500,
      headers,
      body: JSON.stringify({ error: retryable ? "Please retry later" : "Generation failed" }),
    };
  }
};

ValidationExceptionは入力やパラメータの問題なので、そのまま再試行しても直りません。ThrottlingExceptionServiceUnavailableException、一時的な内部エラーは指数バックオフとジッターで再試行します。AWS SDKにもリトライ機構がありますが、キューやAPIの上位層でも「何回まで」「どのHTTPステータスで返すか」を決めます。Lambda全体の実装はClaude Code × AWS Lambda完全ガイドと合わせて読むとつながります。

RAGはKnowledge Basesから始める

社内文書チャットを作るなら、最初から独自ベクトルDBを組むよりBedrock Knowledge Basesを使う方が検証が速いです。回答に引用を含められるため、運用者が「どの文書を根拠にしたか」を確認できます。ただしAWS公式ドキュメントの注意どおり、Guardrailsは取得された参照そのものには適用されず、入力と生成応答に適用されます。機密文書の投入範囲はS3、KMS、IAM、データ分類で先に制限します。

// src/rag.ts
import {
  BedrockAgentRuntimeClient,
  RetrieveAndGenerateCommand,
} from "@aws-sdk/client-bedrock-agent-runtime";

const agentRuntime = new BedrockAgentRuntimeClient({
  region: process.env.AWS_REGION ?? "us-east-1",
});

export async function askKnowledgeBase(question: string) {
  const knowledgeBaseId = process.env.BEDROCK_KNOWLEDGE_BASE_ID;
  const modelArn = process.env.BEDROCK_GENERATION_MODEL_ARN;

  if (!knowledgeBaseId || !modelArn) {
    throw new Error("Set BEDROCK_KNOWLEDGE_BASE_ID and BEDROCK_GENERATION_MODEL_ARN");
  }

  const response = await agentRuntime.send(
    new RetrieveAndGenerateCommand({
      input: { text: question },
      retrieveAndGenerateConfiguration: {
        type: "KNOWLEDGE_BASE",
        knowledgeBaseConfiguration: {
          knowledgeBaseId,
          modelArn,
          retrievalConfiguration: {
            vectorSearchConfiguration: { numberOfResults: 5 },
          },
        },
      },
    })
  );

  const sources =
    response.citations
      ?.flatMap((citation) => citation.retrievedReferences ?? [])
      .map((reference) => reference.location?.s3Location?.uri)
      .filter(Boolean) ?? [];

  return { answer: response.output?.text ?? "", sources };
}

ログと費用管理

ログは2層で考えます。1つ目はアプリログです。requestId、機能名、ユーザー種別、モデルID、usagelatencyMsstopReasonをCloudWatch LogsへJSONで出します。プロンプト本文は、個人情報や機密が含まれるなら保存しない方針を先に決めます。

2つ目はBedrockのモデル呼び出しログです。CloudWatch LogsまたはS3へ配信できますが、入力や出力も保存対象になり得ます。監査には便利ですが、個人情報を含むチャットをそのまま長期保存すると別のリスクになります。ログ保持期間、S3ライフサイクル、暗号化、アクセス権限をセットで決めてください。

費用管理では、まずmaxTokensをAPI側で固定し、用途ごとにモデルを分けます。分類、抽出、短い整形は軽いモデル、長文生成や設計レビューは高性能モデル、という分け方です。単価はモデルとリージョンで変わるため、記事内の古い価格表を信用せずAWS公式の料金表を確認します。Converse APIの応答にはusageが含まれるので、入力トークン、出力トークン、キャッシュ関連トークンをログに残します。さらにIAM principal attributionを有効にすると、どのロールやチームが費用を使っているかをCost ExplorerやCURで追いやすくなります。

長いシステムプロンプトや固定の社内ルールを何度も送る場合は、Prompt cachingも候補です。ただし短いプロンプトや毎回変わる文脈では効果が出ません。キャッシュ対象は静的な前半に寄せ、動的なユーザー入力は後ろに置く、というプロンプト設計が必要です。

本番投入前の判断基準

Bedrock連携を本番に入れる前に、私は小さなリリース判定表を作ります。まず、モデルIDを環境変数で差し替えられること。次に、IAMポリシーが対象モデル、推論プロファイル、Guardrailだけに絞られていること。さらに、usagerequestIdが必ずログに出て、障害時に1件のリクエストを追跡できることを確認します。ここまでできていない状態でUIだけ先に公開すると、問題が起きたときに原因を追えません。

この表は小さいほど運用で使われます。

もう1つ大事なのは、失敗時のユーザー体験です。Claudeの応答が遅い、ThrottlingExceptionが出た、Guardrailでブロックされた、Knowledge Baseに根拠がなかった、という4つは別の状態です。すべてを「AI生成に失敗しました」で返すと、運用者もユーザーも次の行動を判断できません。Claude Codeには、成功パスだけでなく、この4種類の失敗をログとレスポンスで分けるよう依頼します。

Claude Codeに渡すプロンプト

Claude Codeには、機能だけでなく運用条件を書きます。次のように依頼すると、実装、テスト、README、IAM差分をレビューしやすくなります。

claude -p "
このリポジトリにAmazon Bedrock経由のClaude呼び出しを追加してください。

条件:
- AWS SDK v3のConverse APIを使う
- モデルIDはBEDROCK_MODEL_ID環境変数から読む
- AWS_REGION未指定時はus-east-1
- maxTokensはAPI側で1200以下に丸める
- requestMetadataにrequestId, feature, userIdを入れる
- BEDROCK_GUARDRAIL_IDがある場合だけguardrailConfigを付ける
- usage, latencyMs, stopReasonをJSONログに出す
- ValidationExceptionは再試行しない
- ThrottlingExceptionとServiceUnavailableExceptionは上位で再試行できる形にする
- IAMポリシー例は最小権限でREADMEに書く
- テストではBedrockクライアントをモックし、実APIを呼ばない

変更前に計画を出し、実装後に型チェックとテスト結果を報告してください。
"

これでClaude Codeの出力が「便利なサンプル」から「レビューできる本番候補」に変わります。特にBEDROCK_MODEL_IDrequestMetadatausageGuardrails、エラー分類を明記するのが効きます。

よくある落とし穴

1つ目は、モデルアクセスを確認せずに実装を始めることです。初回利用、Marketplace権限、支払い方法、組織のSCPが原因でAccessDeniedExceptionになることがあります。Claude Codeにコードを書かせる前に、AWS CLIでlist-foundation-modelsと小さなConverse呼び出しを確認します。

2つ目は、モデルIDを記事からコピペして固定することです。BedrockのモデルIDは更新されます。リージョンごとに利用可否も違います。環境変数で差し替え、起動時にGetFoundationModelで存在確認する方が安全です。

3つ目は、Guardrailsを万能だと思うことです。Guardrailsは強力ですが、業務上の正解性、契約判断、医療・法務判断を保証するものではありません。禁止トピックや個人情報の安全柵として使い、人間レビュー、入力制限、ログ監査と合わせます。

4つ目は、ログを保存しすぎることです。障害調査のためにプロンプト全文を残したくなりますが、個人情報や社内情報を含む場合はリスクになります。本文は保存しない、必要な場合だけ短期保存、S3は暗号化とライフサイクルを設定する、という方針を決めます。

5つ目は、再試行してはいけないエラーを再試行することです。ValidationExceptionや入力サイズ超過はコードや入力の問題です。一方でThrottlingExceptionServiceUnavailableExceptionは一時的に戻る可能性があります。エラー名で分岐し、むやみに同じリクエストを連打しないようにします。

6つ目は、費用上限をUIだけに任せることです。フロントエンドでmaxTokensを制限しても、APIへ直接投げられたら破れます。LambdaやECS側で上限を丸め、機能単位の利用量をログに残し、AWS BudgetsやCost Explorerで監視します。

収益化まで考えるなら

Bedrockの記事は、単なるSDKチュートリアルで終わると差別化しにくいです。読者が本当に困るのは、IAM、ログ、費用、Guardrails、レビュー、チーム導入です。ClaudeCodeLabで自分のAWS構成に合わせた導入手順、CLAUDE.md、IAMレビュー、検証レシート、CIチェックを整えたい場合は、商品一覧のテンプレートやClaude Code研修・導入相談が次の入口になります。

内部リンクとしては、Lambda実装を詰めるならClaude Code × AWS Lambda完全ガイド、権限レビューはClaude Code × AWS IAMガイド、費用最適化はClaude CodeのAPIコスト削減ガイド、作業証跡の残し方は検証レシートワークフローを合わせて読むのが実務的です。

まとめ

Claude CodeとAmazon Bedrockの組み合わせは、「AI機能を速く作る」だけでなく「AWSの権限、監査、請求、運用に乗せる」ために使うと価値が出ます。最初にユースケースを固定し、モデルIDをCLIで確認し、IAMを最小権限にし、Converse APIで呼び出し、Guardrailsとログを設定し、usageを費用管理へつなげます。この順番なら、個人の実験からチームの本番運用へ移すときに説明できます。

この記事で紹介した内容を実際に試した結果、一番効果があったのはコード量を増やすことではなく、Claude Codeへの依頼文に「モデルIDは環境変数」「usageを必ずログ」「ValidationExceptionは再試行しない」「Guardrailsは任意設定」「IAM例もREADMEに残す」と書いたことでした。生成された実装の差分が小さくなり、レビューで確認すべき点も明確になりました。Bedrock導入は、SDKの呼び方よりも、運用条件を先に言語化できるかで成功率が変わります。

#claude-code #aws #bedrock #anthropic #typescript #generative-ai
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。