Claude Code × Amazon Bedrock: guía práctica para usar Claude en producción con AWS
Producción con Claude Code y Amazon Bedrock: IAM, Converse API, logs, Guardrails y control de costos.
Llamar directamente a la API de Anthropic es cómodo para un prototipo con Claude. En producción sobre AWS la conversación cambia: gestión de claves, límites de IAM, logs de auditoría, atribución de costos, retención de datos, reintentos y cuotas. Ahí es donde Amazon Bedrock como runtime para Claude se vuelve una opción práctica.
Amazon Bedrock es el servicio administrado de modelos fundacionales de AWS. En términos simples, permite invocar Claude usando identidad, permisos, logs, facturación y controles operativos de AWS. Claude Code sigue siendo útil, pero no conviene pedirle solo un demo. Hay que pedir código revisable: IAM, invocación del modelo, Guardrails, logging, manejo de errores y límites de costo.
La lección de Masa en proyectos reales fue clara: un demo de Bedrock puede funcionar rápido, pero la revisión de producción pregunta cosas más duras. ¿Qué rol invoca el modelo? ¿Qué modelos están permitidos? ¿Qué pasa ante throttling? ¿Se guardan prompts? ¿Cómo se atribuye el costo por equipo? Esta guía convierte esas preguntas en implementación.
El contenido se basa en documentación oficial de AWS consultada el 3 de junio de 2026. Los IDs de modelo, Regions, cuotas y precios de Bedrock pueden cambiar; verifica siempre antes de desplegar.
- 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
Arquitectura de producción
No empieces pidiendo a Claude Code “añade un chat con Bedrock”. Primero define quién llama, dónde corre, qué modelo usa, qué Guardrails aplican, dónde quedan los logs y cómo se atribuye el costo.
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 es la interfaz conversacional unificada de Bedrock. Guardrails evalúa entradas y respuestas según políticas de seguridad configuradas. model invocation logging puede enviar datos de invocación a CloudWatch Logs o S3. IAM principal attribution ayuda a ver costos por usuario o rol de IAM.
Casos de uso
El primer caso es un asistente de preguntas sobre documentación interna. Guardas runbooks, especificaciones y procedimientos en S3 y Knowledge Bases, recuperas fragmentos relevantes y Claude genera una respuesta con citas. Eso es RAG: retrieval augmented generation, o generación apoyada en información recuperada.
El segundo caso es borrador de respuestas de soporte. Claude puede combinar una pregunta del cliente, el plan contratado y plantillas aprobadas para crear una respuesta que un operador revisa. Antes de automatizar al 100%, conserva aprobación humana, Guardrails, tratamiento de datos personales y logs de auditoría.
El tercer caso es un asistente de operaciones para ingeniería. Puede resumir logs de CloudWatch, preparar checklists de despliegue, redactar notas de incidente o convertir runbooks en tareas. Claude Code aporta valor porque puede modificar API, Lambda, IAM, pruebas y documentación en un mismo cambio revisable.
El cuarto caso es operación de contenido para un sitio como ClaudeCodeLab. QA de artículos, longitud de description, enlaces internos y revisión de bloques de código pueden ejecutarse bajo IAM y facturación de AWS. Si la calidad del contenido afecta ingresos, combina este flujo con la guía de costos de Claude Code y el workflow de verificación.
Configuración inicial
Necesitas Node.js 20 o superior, credenciales de AWS CLI, una cuenta con Bedrock y un modelo Anthropic disponible en la Region elegida. Según la cuenta, Anthropic puede requerir datos de primer uso, permisos de AWS Marketplace y un método de pago válido.
No fijes un modelo copiado de un artículo. Lista lo disponible en tu cuenta y usa una variable de entorno.
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}"
Crea un proyecto mínimo en 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
Añade scripts a package.json.
{
"type": "module",
"scripts": {
"chat": "tsx src/chat.ts",
"stream": "tsx src/stream.ts",
"typecheck": "tsc --noEmit"
}
}
IAM mínimo
Aunque uses Converse API, IAM necesita permisos de invocación. Las llamadas no streaming usan bedrock:InvokeModel; las streaming usan bedrock:InvokeModelWithResponseStream. Separa permisos de logs de aplicación de la configuración de model invocation logging de 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"
}
]
}
Reemplaza cuenta, Regions, guardrail y patrón de modelo. Si usas cross-Region inference, incluye también el inference profile y los ARNs de destino. Para revisar mínimo privilegio, continúa con la guía de AWS IAM con Claude Code.
Invocar Claude
El detalle importante es requestMetadata. AWS lo documenta como útil para filtrar logs de invocación, así que agrega request ID, feature y categoría de usuario.
// 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);
Ejecuta:
export AWS_REGION=us-east-1
export BEDROCK_MODEL_ID="anthropic.claude-sonnet-4-20250514-v1:0"
npm run chat -- "Explica Amazon Bedrock en tres líneas"
npm run stream -- "Crea un checklist de producción para Bedrock"
npm run typecheck
Patrón con Lambda
En Lambda, inicializa el cliente fuera del handler, valida entrada y limita maxTokens en servidor.
// 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" }),
};
}
};
No reintentes ValidationException a ciegas: suele ser una entrada inválida. ThrottlingException y ServiceUnavailableException sí pueden usar backoff exponencial y jitter. Para el flujo serverless completo, revisa Claude Code × AWS Lambda.
RAG con Knowledge Bases
Para chat con documentos internos, Bedrock Knowledge Bases suele ser mejor punto de partida que montar tu propio vector store. Devuelve respuestas con citas, lo que facilita revisar la fuente. Recuerda que Guardrails se aplica a entrada y salida generada, no a las referencias recuperadas; limita datos sensibles con S3, KMS, IAM y clasificación de datos.
// 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 };
}
Logs y costos
Usa dos capas de logs. La aplicación debe registrar requestId, feature, tipo de usuario, modelo, usage, latencia y stop reason. Evita guardar prompts completos salvo que tu política de privacidad y retención lo permita.
El model invocation logging de Bedrock puede escribir en CloudWatch Logs o S3. Es útil para auditoría, pero también puede guardar entradas y salidas sensibles. Define retención, cifrado, ciclo de vida de S3 y permisos antes de activarlo de forma amplia.
Para costos, limita maxTokens en la API, elige modelos por complejidad, registra usage y usa IAM principal attribution para agrupar costos por rol. Prompt caching ayuda con contextos largos y repetidos, pero no con prompts cortos o totalmente dinámicos.
Prompt para Claude Code
claude -p "
Añade invocación de Claude mediante Amazon Bedrock a este repositorio.
Requisitos:
- Usar AWS SDK v3 Converse API
- Leer el modelo desde BEDROCK_MODEL_ID
- Usar us-east-1 si AWS_REGION no está definido
- Limitar maxTokens a 1200 en servidor
- Añadir requestMetadata con requestId, feature y userId
- Añadir guardrailConfig solo si BEDROCK_GUARDRAIL_ID existe
- Registrar usage, latencyMs y stopReason como JSON
- No reintentar ValidationException
- Tratar ThrottlingException y ServiceUnavailableException como retryable
- Documentar una política IAM de mínimo privilegio en README
- Mockear el cliente Bedrock en tests; no llamar la API real
Muestra el plan antes de editar y reporta typecheck y tests al final.
"
Errores comunes
El primero es no verificar acceso al modelo. Prueba list-foundation-models y una llamada Converse pequeña antes de integrar.
El segundo es congelar un model ID copiado de un artículo. Usa configuración y valida al iniciar.
El tercero es creer que Guardrails garantiza exactitud. Sirve para políticas de seguridad, no para reemplazar revisión humana, validación de dominio o autorización.
El cuarto es guardar demasiados logs. Los prompts completos ayudan a depurar, pero pueden filtrar datos personales o internos.
El quinto es reintentar errores incorrectos. Validation requiere corregir entrada o código; throttling y fallos temporales sí pueden reintentarse.
El sexto es controlar costos solo desde frontend. Los límites reales deben vivir en la API.
Ruta de monetización
El valor de Bedrock no está en el snippet de SDK, sino en pasar del demo a producción: IAM, logs, costos, Guardrails, revisiones y adopción de equipo. Para plantillas reutilizables, empieza en productos ClaudeCodeLab. Para un rollout adaptado a tu repositorio con CLAUDE.md, IAM, verification receipts y CI gates, usa formación y consultoría Claude Code.
Lecturas relacionadas: Claude Code AWS Lambda, Claude Code AWS IAM, costos de Claude Code y workflow de verificación.
Resultado práctico
Al probar este patrón, la mayor mejora no vino de añadir más capas de código, sino de hacer explícitas las condiciones en el prompt para Claude Code: modelo por variable de entorno, log de usage, no reintentar ValidationException, Guardrails opcional por entorno y política IAM en README. El diff fue más pequeño y la revisión más clara. El éxito con Bedrock depende menos de memorizar el SDK y más de definir las restricciones operativas antes de generar código.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.