Learn/Claude Academy: Build with the APIguide

Claude with Amazon Bedrock

What changes when you call Claude through Bedrock with boto3, Converse and Titan embeddings.

Series Claude Academy: Build with the API Part 5 of 6
  1. Building with the Claude API, part 1: Requests, evals and prompting
  2. Building with the Claude API, part 2: Tool use and RAG
  3. Building with the Claude API, part 3: Claude features and MCP
  4. Building with the Claude API, part 4: Claude Code, computer use and agents
  5. Claude with Amazon Bedrock
  6. Claude on Google Cloud's Vertex AI

A walkthrough of the Claude Academy course Claude with Amazon Bedrock (Build with the API track). Each lesson is summarised with my own tables and examples; take the course for the full lessons. All the courses are listed in the Claude Academy course index.

Note: This page focuses on what differs on Bedrock. Shared concepts such as prompt evals, prompt engineering, RAG theory, MCP and agent patterns are covered in my Building with the Claude API series. A few details were added from docs.claude.com and AWS docs, including IAM actions, the AnthropicBedrock client and some Converse field names; verify those against the lessons.

Prerequisites:

  • Python and JSON.
  • An AWS account with Bedrock access, and local AWS credentials (aws configure or a profile). Check them with aws sts get-caller-identity.
  • The boto3 package.
  • Docker, for the computer-use demo.
  • An Anthropic API key for the MCP project, which calls the Claude API directly.

Course Outline (and how it maps to the main course)

Module Bedrock-specific content Shared with the main course
Intro Model family overview (Opus / Sonnet / Haiku) —
Working with the API boto3 Converse API, regions, inference profiles, inferenceConfig Request lifecycle, multi-turn, system prompts, temperature, prefill and stop sequences
Prompt evaluation & engineering Same pipeline built on the Converse chat() helper Main notes §2–3
Tool use (13 lessons) toolSpec / toolUse / toolResult format, batch tool, structured data with tools, flexible to_json extraction, text editor Main notes §4
RAG (10 lessons) Titan embeddings, reranking, contextual retrieval Chunking, BM25 and RRF in main notes §5
Features Thinking via additionalModelRequestFields, image and document parts in bytes, cachePoint Main notes §6
MCP (12 lessons) Adds an “MCP review” lesson Main notes §7
Agents Claude Code, MCP enhancements, parallel Claude Code with worktrees, automated debugging, computer use, qualities of agents Main notes §8–9
  • The Bedrock course has no web search, Files API or code execution lessons, which reflects what Bedrock supported.

1. Models, Regions and Access

  • Opus is the most intelligent. Sonnet balances speed and intelligence and is strong at coding. Haiku is the fastest.
    • The course uses Haiku 4.5. It supports extended thinking with a manual budget, but not adaptive thinking.
  • Enable model access in the Bedrock console under Model access. Embedding models such as Titan may need access requested separately.
  • Regions matter. Not every model is in every region. Using the wrong region gives a “model doesn’t exist” or invalid identifier error.
  • Newer models need a cross-region inference profile rather than on-demand throughput. The inference profile ID is the base model ID with a geographic prefix:
Kind Example
Base model ID anthropic.claude-haiku-4-5-20251001-v1:0
Inference profile (US) us.anthropic.claude-haiku-4-5-20251001-v1:0
Other prefixes global., eu., jp., apac. (availability varies by model)
  • You find inference profiles in the Bedrock console under Cross-region inference. Calling a base ID directly can fail with “on-demand throughput isn’t supported”.
  • IAM: the caller needs bedrock:InvokeModel, and bedrock:InvokeModelWithResponseStream for streaming. This applies to Converse and ConverseStream too. Automated debugging also needs CloudWatch Logs read access. (Taken from AWS docs, not the lessons.)
  • Auth uses standard AWS credentials (environment variables, profile or role). There is no Anthropic API key, except for the MCP project.

2. Client Setup and the Converse API

import boto3
client = boto3.client("bedrock-runtime", region_name="us-west-2")
model_id = "us.anthropic.claude-haiku-4-5-20251001-v1:0"

messages = [{"role": "user", "content": [{"text": "What is quantum computing? One sentence."}]}]
response = client.converse(modelId=model_id, messages=messages)
print(response["output"]["message"]["content"][0]["text"])

Converse vs the Anthropic Messages API

Concept Anthropic SDK (messages.create) Bedrock Converse (boto3)
Content "content": "text" or [{"type":"text","text":…}] "content": [{"text": …}] (no type key)
Model model="claude-haiku-4-5" modelId="us.anthropic.claude-…-v1:0"
System system="…" system=[{"text": "…"}]
Params max_tokens, temperature, stop_sequences inferenceConfig={"maxTokens", "temperature", "stopSequences"} (camelCase)
Response text msg.content[0].text resp["output"]["message"]["content"][0]["text"]
Stop reason stop_reason == "tool_use" resp["stopReason"] == "tool_use"
Extra fields Top-level parameters (thinking=…) additionalModelRequestFields={"thinking": …}
  • Roles must alternate between user and assistant. Prefill and stop sequences work the same way as in the main course.
def chat(messages, system=None, temperature=1.0, stop_sequences=[], tools=None, tool_choice="auto"):
    params = {"modelId": model_id, "messages": messages,
              "inferenceConfig": {"temperature": temperature, "stopSequences": stop_sequences}}
    if system:
        params["system"] = [{"text": system}]
    if tools:
        params["toolConfig"] = {"tools": tools}
        if tool_choice != "auto":
            params["toolConfig"]["toolChoice"] = {"tool": {"name": tool_choice}}
    resp = client.converse(**params)
    parts = resp["output"]["message"]["content"]
    text = "\n".join(p["text"] for p in parts if "text" in p)
    return {"text": text, "parts": parts, "stop_reason": resp["stopReason"]}

Streaming

resp = client.converse_stream(modelId=model_id, messages=messages)
for event in resp["stream"]:
    if "contentBlockDelta" in event:
        print(event["contentBlockDelta"]["delta"]["text"], end="")
  • Events arrive in this order: messageStart → contentBlockDelta… → contentBlockStop → messageStop → metadata, which carries the usage.

Alternative: the Anthropic SDK on Bedrock (docs, not the course)

# pip install "anthropic[bedrock]"
from anthropic import AnthropicBedrock
client = AnthropicBedrock(aws_region="us-west-2")      # uses AWS credential chain
msg = client.messages.create(model="us.anthropic.claude-haiku-4-5-20251001-v1:0",
                             max_tokens=256, messages=[{"role": "user", "content": "Hi"}])
  • With this client, you can reuse the main-course code almost unchanged.

3. Tool Use on Bedrock

  • Schema: wrap the schema as {"toolSpec": {"name", "description", "inputSchema": {"json": <JSON Schema>}}}, then pass it as toolConfig={"tools": [...]}.
  • Response: the assistant message contains a {"toolUse": {"toolUseId", "name", "input"}} part. Check stopReason == "tool_use".
  • Result: send a user message containing {"toolResult": {"toolUseId": id, "content": [{"text": json.dumps(out)}], "status": "error"}}. Include "status": "error" only when the tool failed.
toolChoice Behaviour
{"auto": {}} Model decides whether to use a tool (default)
{"any": {}} Model must use some tool
{"tool": {"name": "x"}} Model must use tool x
  • Batch tool: define a batch_tool whose input is invocations: [{name, arguments}]. arguments is a JSON string, which run_batch parses with json.loads.
    • Add a batch_tool branch to run_tool that passes tool_input directly, without **.
    • This nudges Claude to run independent calls together. In the course example, computing two dates is done in a single batch call.
  • Structured data with tools: give Claude an extraction schema such as article_details (title, author, topics), force it with toolChoice, and read the toolUse input. No follow-up request is needed.
    • This is more reliable than prefill, but needs more setup.
  • Flexible extraction: use one generic to_json tool that accepts any properties (additionalProperties). Describe the shape you want in the prompt, for example "title": str, "topics": List[str].
    • This gives roughly 90% of the quality for 10% of the setup. Use dedicated schemas for critical or nested data.
  • Text editor: use text_editor_20250728, named str_replace_based_edit_tool, on Claude 4+. Use text_editor_20250124 / str_replace_editor, which also has undo_edit, on earlier models.
    • You implement view, str_replace, create and insert yourself.
    • Current tool IDs for Bedrock are listed in the AWS “Anthropic-defined tools” docs.

4. RAG Differences

  • Embeddings use Amazon Titan, not VoyageAI.
def generate_embedding(text, model_id="amazon.titan-embed-text-v2:0", dimensions=1024, normalize=True):
    body = json.dumps({"inputText": text, "dimensions": dimensions, "normalize": normalize})
    resp = client.invoke_model(modelId=model_id, body=body,
                               accept="application/json", contentType="application/json")
    return json.loads(resp["body"].read())["embedding"]
  • Titan v2 may need access requested first. v1 is fine for learning.
  • Embeddings are produced through invoke_model with a model-specific JSON body, not through Converse.
  • Reranking: after the hybrid (vector + BM25, RRF) search, Claude reorders the chunks. Give each chunk an ID and ask for {"document_ids": [...]} via prefill and stop sequence.
  • Contextual retrieval: Claude writes a short snippet that situates each chunk in the document, and the snippet is prepended before indexing. For large documents, use the first few chunks plus the chunks just before the target as context.

5. Claude Features on Bedrock

Feature Bedrock (Converse) format / limits
Extended thinking additionalModelRequestFields={"thinking": {"type": "enabled", "budget_tokens": 1024}} (minimum 1024) on Haiku/Sonnet/Opus 4.5 and earlier. Newer models use {"type": "adaptive", "display": "summarized"} with effort; budget_tokens is deprecated, or rejected with a 400 on Opus 4.7+. The response has a reasoning part with a signature, or redactedContent.
Images {"image": {"format": "png", "source": {"bytes": raw_bytes}}}. Raw bytes, not base64. The course states 20 images per request, 3.75 MB each and 8000 px max. Tokens ≈ w×h/750.
PDFs / citations {"document": {"format": "pdf", "name": "earth", "source": {"bytes": b}, "citations": {"enabled": True}}}. The response mixes text parts with citation content parts.
Prompt caching {"cachePoint": {"type": "default"}} parts, instead of cache_control. Put them after the content to cache: at the end of the system list, at the end of the tools list or inside messages. TTL is 5 min, the minimum is 1024 tokens, and the prefix must be identical.
system = [{"text": "You are a senior software..."}, {"cachePoint": {"type": "default"}}]
tools  = [{"toolSpec": get_current_datetime_schema},
          {"toolSpec": add_duration_to_datetime_schema},
          {"cachePoint": {"type": "default"}}]

6. MCP, Claude Code and Agents (Bedrock flavour)

  • MCP: the content is the same as main notes §7, plus a review lesson on tools (model), resources (app) and prompts (user).
  • Parallelizing Claude Code: run several instances in separate git worktrees to avoid file conflicts.
    • Automate the setup with custom commands in .claude/commands/*.md, for example /project:create_worktree feature_a using $ARGUMENTS.
    • Let Claude merge the branches back and resolve conflicts.
  • Automated debugging: a daily GitHub Action runs Claude to:
    1. Pull the last 24 hours of CloudWatch logs with the AWS CLI.
    2. Deduplicate the errors.
    3. Fix the code.
    4. Open a PR.
    • The course example is a mistyped Bedrock model ID that only appeared in production on Amplify.
    • The workflow needs CloudWatch IAM read access and repository write permission.
  • Computer use: run the Anthropic quickstarts reference Docker image. It needs Docker plus a local AWS profile (usually default), because it calls Claude through Bedrock.
    • It is ordinary tool use: a small schema is expanded into mouse, keyboard and screenshot actions, which run in an isolated container.
  • Qualities of agents:
    • Focused tool sets running in loops.
    • Context is everything; agents gather it with tools and observe before acting.
    • High-value, low-error-cost tasks.
    • Continuous evaluation.

Quick-Review Flashcards

  1. Q: Which boto3 client and method does the course use to call Claude? → A: boto3.client("bedrock-runtime", region_name=…) and client.converse(modelId=…, messages=…).
  2. Q: How does a Converse text content block differ from a Messages API block? → A: It is {"text": "…"}, with no "type" key, and content is always a list.
  3. Q: Where do temperature and stop sequences go in Converse? → A: In inferenceConfig={"temperature": …, "stopSequences": […]}, in camelCase.
  4. Q: Why might you get “model doesn’t exist” or “on-demand throughput isn’t supported”? → A: You are in the wrong region, or you need a cross-region inference profile ID such as us.anthropic.claude-haiku-4-5-20251001-v1:0.
  5. Q: Which streaming events carry text in ConverseStream? → A: contentBlockDelta → delta.text. The metadata event carries usage at the end.
  6. Q: What is the Bedrock tool schema wrapper? → A: {"toolSpec": {"name", "description", "inputSchema": {"json": …}}} inside toolConfig.tools.
  7. Q: How do you force a specific tool in Converse? → A: toolConfig["toolChoice"] = {"tool": {"name": "article_details"}}.
  8. Q: What does the batch tool solve? → A: Claude often calls independent tools one at a time. A batch_tool with a list of invocations groups them into one turn.
  9. Q: What is the trade-off with the flexible to_json extraction tool? → A: About 90% of the quality for 10% of the setup. The structure is described in the prompt instead of a schema.
  10. Q: Which embedding model does the Bedrock course use, and how is it called? → A: Amazon Titan Text Embeddings v2 (amazon.titan-embed-text-v2:0), via invoke_model with inputText, dimensions and normalize.
  11. Q: How do you pass extended-thinking settings in Converse? → A: additionalModelRequestFields={"thinking": {...}}.
  12. Q: How are images sent on Bedrock, and what limits does the course state? → A: As {"image": {"format": "png", "source": {"bytes": …}}} with raw bytes. Up to 20 images, 3.75 MB each and 8000 px max.
  13. Q: What is the Bedrock equivalent of cache_control? → A: A {"cachePoint": {"type": "default"}} part placed after the content to cache. The minimum is 1024 tokens and the TTL is 5 minutes.
  14. Q: How do you avoid file conflicts with parallel Claude Code instances? → A: Give each instance its own git worktree and branch, then merge.
  15. Q: What does the automated-debugging GitHub Action do? → A: It fetches 24 hours of CloudWatch logs, deduplicates the errors, has Claude fix them and opens a PR.
  16. Q: Which Anthropic SDK client targets Bedrock? → A: AnthropicBedrock(aws_region=…) from anthropic[bedrock]. It uses Messages API syntax with Bedrock model IDs.

Practice Ideas

  • Port the main course’s chat() helper to Converse: system list, inferenceConfig, toolConfig and a returned stopReason. Then rebuild the reminder tool loop with toolUse and toolResult.
  • Call the same model with a base ID and with a us. inference profile, and note the errors and the region behaviour. List the available models with aws bedrock list-foundation-models --by-provider anthropic.
  • Write a least-privilege IAM policy for an app that only invokes and streams one Claude inference profile.
  • Build a Titan-embedding RAG pipeline with BM25, RRF, Claude reranking and contextual retrieval. Compare the results on an ID-heavy query.
  • Add cachePoint to the system prompt and tool list, and compare the cache read and write counts in the usage of repeated calls.
  • Implement the batch tool and the flexible to_json extractor, then compare the extraction quality against a dedicated schema on 10 articles.
  • Re-implement one notebook with AnthropicBedrock instead of boto3 and list the code differences.