Claude Code × Amazon Bedrock实战指南:在AWS上安全运行Claude
用Claude Code和Amazon Bedrock构建生产级Claude功能,覆盖IAM、Converse API、日志、Guardrails和成本控制。
直接调用Anthropic API很适合快速原型,但把Claude放进AWS生产环境时,问题会变成API key怎么管、IAM权限怎么收敛、日志保存在哪里、费用算到哪个团队、失败后能不能安全重试。此时,通过Amazon Bedrock调用Claude会更适合企业和团队项目。
Amazon Bedrock是AWS的托管基础模型服务。通俗地说,它让你的应用通过AWS的身份认证、权限、日志、账单和运维体系来调用Claude,而不是把模型当成完全独立的外部API。Claude Code依然很有价值,但不要只让它写“能跑的demo”。要让它同时产出可审查的IAM、模型调用、Guardrails、日志、重试和成本限制。
Masa在实际项目里踩过的坑是:样例一小时就能跑通,但安全评审会问“哪个role可以调用哪个模型?节流时怎么处理?prompt会不会进日志?费用如何按团队追踪?”本文把这些问题落到代码和Claude Code提示词里。
本文基于2026年6月3日时点的AWS官方文档。Bedrock的模型ID、Region、quota和价格都会变化,发布前请以官方文档为准。
- Amazon Bedrock User Guide
- Inference using Converse API
- Request access to models
- Get information about foundation models
- How Amazon Bedrock Guardrails works
- Model invocation logging
- Troubleshooting Amazon Bedrock API error codes
- Prompt caching
- IAM principal attribution
生产架构先行
不要一开始就对Claude Code说“加一个Bedrock聊天接口”。先固定调用方、运行环境、模型、Guardrails、日志和费用归属。
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提供的统一对话接口。Guardrails是对用户输入和模型输出进行安全策略评估的护栏。model invocation logging可以把模型调用日志发送到CloudWatch Logs或S3。IAM principal attribution用于按IAM用户或role追踪Bedrock推理费用。
适合的用例
第一个用例是内部文档问答。把runbook、产品规格、支持流程放入S3和Knowledge Bases,通过检索相关片段后让Claude生成带引用的答案。这就是RAG,也就是先检索自己的数据,再让模型回答。
第二个用例是客服回复草稿。Claude可以根据客户问题、套餐信息和历史模板生成初稿,再交给人工确认。在完全自动回复之前,建议保留人工审核、Guardrails、个人信息处理和审计日志。
第三个用例是工程运维助手。它可以总结CloudWatch日志、生成部署检查清单、整理事故记录、把runbook变成任务列表。Claude Code的优势是能同时修改API、Lambda、IAM、测试和文档。
第四个用例是ClaudeCodeLab这类内容站运营。文章质量检查、description长度、内部链接建议、代码块审查都可以放在AWS权限和账单体系下运行。若内容直接影响收入,建议同时阅读Claude Code API成本优化和验证收据工作流。
初始设置
需要Node.js 20以上、AWS CLI凭证、启用Bedrock的AWS账号,以及目标Region可用的Anthropic模型。部分账号首次使用Anthropic模型时,需要提交用途信息、具备AWS Marketplace权限并配置有效支付方式。
不要从文章里复制一个模型ID就写死。先列出当前账号能用的模型,再放到环境变量。
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}"
创建一个最小TypeScript项目。
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最小权限
使用Converse API时,IAM层仍然需要模型调用权限。非流式调用需要bedrock:InvokeModel,流式调用需要bedrock:InvokeModelWithResponseStream。应用日志权限和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"
}
]
}
请替换账号ID、Region、Guardrail ID和模型范围。使用跨Region推理时,还要把推理profile和目标Region模型ARN纳入权限设计。更细的最小权限审查可以参考Claude Code AWS IAM指南。
调用Claude
生产实现里最容易遗漏的是requestMetadata。AWS文档说明它可用于筛选模型调用日志,所以建议放入请求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`);
}
}
}
// 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"
npm run stream -- "生成Bedrock生产上线检查清单"
npm run typecheck
Lambda实现模式
放到Lambda时,要在handler外初始化客户端,在服务器端验证输入并限制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通常是输入或参数错误,重试没有意义。ThrottlingException和ServiceUnavailableException才适合指数退避和随机抖动。Serverless整体设计可继续读Claude Code AWS Lambda指南。
用Knowledge Bases做RAG
内部文档聊天优先从Bedrock Knowledge Bases开始,而不是一上来自己搭向量数据库。它可以返回带引用的答案,便于运营人员检查依据。但要注意,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 };
}
日志、成本和Prompt caching
日志分两层。应用日志记录requestId、功能名、调用者类别、模型ID、usage、延迟和停止原因。除非隐私和保存策略明确允许,不要长期保存原始prompt。
Bedrock模型调用日志可以发到CloudWatch Logs或S3,适合审计和问题排查,但也可能包含敏感输入输出。启用前要决定保留周期、加密、S3生命周期和访问权限。
成本控制从API层限制maxTokens开始,再按任务复杂度选择模型,把usage写进日志,并通过IAM principal attribution在Cost Explorer或CUR里按role追踪成本。长而重复的系统prompt可以考虑Prompt caching,但只有稳定前缀足够长且频繁复用时才有明显效果。
给Claude Code的提示词
claude -p "
请在这个repository中通过Amazon Bedrock添加Claude调用。
要求:
- 使用AWS SDK v3 Converse API
- 模型ID从BEDROCK_MODEL_ID读取
- AWS_REGION未设置时默认us-east-1
- 服务器端把maxTokens限制到1200以内
- requestMetadata包含requestId, feature, userId
- 只有设置BEDROCK_GUARDRAIL_ID时才添加guardrailConfig
- 以JSON日志输出usage, latencyMs, stopReason
- 不要重试ValidationException
- 将ThrottlingException和ServiceUnavailableException视为可重试
- 在README写最小权限IAM策略
- 测试中mock Bedrock客户端,不调用真实API
修改前先给计划,完成后报告typecheck和测试结果。
"
常见坑
第一,没确认模型访问就开始写代码。先用list-foundation-models和一次小的Converse调用确认账号、Region和权限。
第二,把博客里的模型ID固定进代码。Bedrock模型ID和Region支持会变,应该用环境变量并在启动时验证。
第三,把Guardrails当成正确性保证。它适合安全策略,不替代人工审核、领域校验和授权。
第四,日志保存过多。完整prompt便于排查,但也可能把个人信息和内部数据放进长期日志。
第五,错误重试策略混乱。Validation类错误需要修输入或代码,节流和临时服务错误才适合重试。
第六,只在前端限制费用。token上限、功能配额和用户配额必须在API层执行。
商业化路径
Bedrock文章的价值不在SDK片段,而在从demo走向生产:IAM、日志、成本、Guardrails、review和团队导入。个人可从ClaudeCodeLab产品获取模板;团队要把CLAUDE.md、IAM审查、验证收据和CI gate落到真实repository,可使用Claude Code培训与咨询。
延伸阅读:Claude Code AWS Lambda、Claude Code AWS IAM、Claude Code API成本优化和验证收据工作流。
实测后的结论
实际尝试后,最大收益不是多写几层封装,而是在给Claude Code的提示词里明确“模型ID来自环境变量”“必须记录usage”“ValidationException不重试”“Guardrails按环境变量启用”“README保留IAM策略”。这样生成的diff更小,review点更清楚。Bedrock导入成败,往往取决于写代码前能否先说清生产约束。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。