AI EngineeringZero to ProductionHome·About·Contact
Anthropic Skills · Chapter K7

Claude on Vertex / GCP

The third front door to Claude — Vertex AI on Google Cloud: ADC auth, the SDK, streaming/tool use, mapping across API/Bedrock/Vertex, and abstracting the provider behind one client.

⏱️ ~2.5 hours🧪 6 labs🎯 Beginner→Tech-lead

Learning objectives

  • Explain the three front doors to Claude (API, Bedrock, Vertex).
  • Authenticate to Vertex with Google Cloud ADC.
  • Call Claude on Vertex incl. streaming and tool use.
  • Choose a front door and standardize it for a team.
▶ Runnable companionCode saved under code/ak7-claude-vertex-gcp/. Python runs offline; configs are ready to use.

1 · Three front doors, one Claude essential

You've reached Claude via the Anthropic API and Bedrock. Vertex AI is the third — Claude on Google Cloud, billed through GCP with Google's auth, IAM, and data residency. Same model, same Messages concepts; different plumbing.

Anthropic API api key Bedrock (AWS) IAM role Vertex (GCP) gcloud ADC
🗺️ How to read this diagram

This picture makes one reassuring point: there are three ways in to the very same Claude models, and picking one is mostly about whose bill and login you use, not about learning a new AI. Read the three boxes left to right.

  • Anthropic API (left) — the direct door. You sign in with an api key (a secret string from Anthropic). Simplest to start with.
  • Bedrock (AWS) (middle) — Claude served through Amazon's cloud. Instead of an API key you use an IAM role (AWS's permission system), so the bill and access control live in AWS.
  • Vertex (GCP) (right) — Claude served through Google Cloud. You sign in with gcloud ADC (Google's Application Default Credentials). This lesson is about this door.
  • The arrows just show these are three entrances; behind all of them sits the same Claude. What changes between doors is only the client object you create and the auth (key vs IAM vs ADC) — not how you write prompts.

In short: Same brain, three lobbies. Enterprises already living in AWS or GCP pick the matching door so AI spend, logins, and data stay in the cloud they already trust.

2 · Authenticate with ADC essential

Vertex uses Google Application Default Credentials — no API key. Locally you authenticate once with gcloud; on GCP compute you use the attached service account. Same "no keys in code" discipline as boto3 (W1).

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
shell · one-time setup
setup.shpip install "anthropic[vertex]"
gcloud auth application-default login
gcloud config set project my-gcp-project
gcloud services enable aiplatform.googleapis.com
▶ How this works

Before any code can reach Claude on Vertex, you do a one-time setup in your terminal: install the right library and log in to Google Cloud. These are shell commands (you run them in a terminal, not in Python).

  1. pip install "anthropic[vertex]" installs Anthropic's Python library plus the extra Vertex pieces (the [vertex] part). This is what gives you the AnthropicVertex client used later.
  2. gcloud auth application-default login opens your browser to sign in to Google. It saves credentials on your machine so your code can authenticate without any key in the code — that saved login is what "ADC" (Application Default Credentials) means.
  3. gcloud config set project my-gcp-project tells Google which project (billing + resources bucket) to use. gcloud services enable aiplatform.googleapis.com switches on the Vertex AI service for that project — like flipping the breaker before you use the appliance.

What the output means: Nothing prints a result you keep — these commands just prepare your machine and project. After they succeed, your Python can talk to Vertex.

Try this: Run gcloud config list afterward to confirm your project is set. If a later call fails with a permissions error, it's almost always one of these four steps that was skipped.

3 · Call Claude on Vertex intermediate

The AnthropicVertex client mirrors the base SDK — set region + project, then use the same messages.create from C2. Model IDs use Vertex naming (an @ version suffix).

Python · a first call (needs GCP creds to run)
vertex_call.pyfrom anthropic import AnthropicVertex
client = AnthropicVertex(region="us-east5", project_id="my-gcp-project")

resp = client.messages.create(
    model="claude-3-5-sonnet-v2@20241022",
    max_tokens=300,
    system="You are a concise SRE assistant.",
    messages=[{"role": "user", "content": "In one sentence, what is Vertex AI?"}])
print(resp.content[0].text)
▶ How this works

This is the whole shape of a Claude call on Vertex — and it's deliberately almost identical to the direct-API call from earlier lessons. Only the first two lines (the client) and the model id are Vertex-specific.

  1. from anthropic import AnthropicVertex imports the Vertex flavor of the client. client = AnthropicVertex(region="us-east5", project_id="my-gcp-project") builds it — you pass a Google Cloud region and your project instead of an API key, because the login you did in setup is picked up automatically.
  2. client.messages.create(...) is the exact same request method as the direct API. max_tokens caps the reply length; system sets the assistant's role; messages is the conversation (here one user question).
  3. model="claude-3-5-sonnet-v2@20241022" uses Vertex's naming style — note the @ and date version suffix. That naming is the main thing that differs from the direct API's model ids.
  4. resp.content[0].text pulls the text out of the reply. The reply's content is a list of blocks, so [0] grabs the first block and .text its words.

What the output means: Prints Claude's one-sentence answer describing Vertex AI. (This block is marked "needs GCP creds" because a real network call to Google requires the login from step 2.)

Try this: Compare this to the first-call code in the Anthropic-API lesson: swap Anthropic() for AnthropicVertex(region=..., project_id=...) and change the model id — everything else is the same. That's the whole point of this chapter.

The runnable blocks below need no cloudReal Vertex calls need GCP credentials, so that call is marked. The mapping/normalization logic below runs offline so you can execute and understand the portability without an account.

4 · Map concepts across all three doors intermediate

Everything transfers — only the client construction and model id change. Model the mapping so switching front doors is a config change, not a rewrite.

Python · one config, three front doors (runs offline)
front_doors.pydef client_config(door):
    return {
        "api":     {"client": "Anthropic()",       "auth": "ANTHROPIC_API_KEY",
                    "model": "claude-opus-4-8"},
        "bedrock": {"client": "boto3 bedrock-runtime","auth": "IAM role",
                    "model": "anthropic.claude-3-5-sonnet-20241022-v2:0"},
        "vertex":  {"client": "AnthropicVertex()",  "auth": "gcloud ADC",
                    "model": "claude-3-5-sonnet-v2@20241022"},
    }[door]

for door in ["api", "bedrock", "vertex"]:
    c = client_config(door)
    print(f"{door:8} {c['client']:26} auth={c['auth']}")
print("messages shape is IDENTICAL across all three")
api      Anthropic()                auth=ANTHROPIC_API_KEY
bedrock  boto3 bedrock-runtime       auth=IAM role
vertex   AnthropicVertex()           auth=gcloud ADC
messages shape is IDENTICAL across all three
▶ How this works

This tiny program (which runs offline — no cloud needed) proves the chapter's big idea: the three doors differ in only three things — the client you build, the auth you use, and the model id. Everything else about a request is identical.

  1. client_config(door) is a function returning a small dictionary of facts for one door. The { ... }[door] pattern builds a lookup table of all three doors and then immediately picks the one named by door.
  2. Each entry lists that door's client (Anthropic(), boto3, or AnthropicVertex()), its auth method (API key / IAM role / gcloud ADC), and its model id string. Notice the model ids differ in format but name the same models.
  3. for door in ["api", "bedrock", "vertex"]: loops over all three and prints a lined-up row for each, using f-string alignment (:8, :26 pad to fixed widths so the columns line up).
  4. The last print states the punchline in words: the messages shape — how you phrase the actual request — is identical across all three.

What the output means: Three aligned rows (one per door) showing client / auth / model, then the line messages shape is IDENTICAL across all three — exactly the console output shown just below the code.

Try this: Add a fourth fictional door to the dictionary and to the loop list, then re-run. The table grows automatically — because the loop, not repeated code, does the printing.

5 · Advanced — streaming & tool use on Vertex advanced

Streaming uses the same with client.messages.stream(...) context manager; tool use uses the same tools=[...] schema and loop from C2. Only client + model id differ.

Python · streaming on Vertex (needs GCP creds)
vertex_stream.pyfrom anthropic import AnthropicVertex
client = AnthropicVertex(region="us-east5", project_id="my-gcp-project")
with client.messages.stream(
    model="claude-3-5-sonnet-v2@20241022", max_tokens=200,
    messages=[{"role": "user", "content": "Count to five slowly."}]) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
print()
▶ How this works

Streaming shows Claude's reply as it's typed, chunk by chunk, instead of waiting for the whole answer. The key lesson here: streaming on Vertex uses the exact same code as the direct API — only the client and model id are Vertex-specific.

  1. The first two lines build the same AnthropicVertex client as before (region + project, no key).
  2. with client.messages.stream(...) as stream: opens a live streaming connection. The with block guarantees it closes cleanly when finished. This is the identical method name used on the direct API.
  3. for text in stream.text_stream: receives small text chunks as Claude produces them. print(text, end="", flush=True) prints each chunk immediately with no line break, so words appear to flow in real time. The final bare print() just adds a newline at the end.

What the output means: You'd watch "Count to five slowly." get answered progressively, the digits appearing one after another rather than all at once. (Marked "needs GCP creds" — a real stream requires the Vertex login.)

Try this: Put this side by side with the streaming code from the Anthropic-API lesson. Only the client = ... line and the model id changed — the streaming loop is the same, which is exactly why moving providers is easy.

6 · Professional — choosing a front door professional

Enterprises pick Bedrock or Vertex to keep AI spend and data in the cloud they already use — one bill, one IAM model, one data-residency story. Model the decision.

Python · pick the front door (runs)
choose_door.pydef choose_door(on_aws, on_gcp, wants_simplest, data_residency):
    if data_residency == "aws" or (on_aws and not on_gcp): return "bedrock"
    if data_residency == "gcp" or (on_gcp and not on_aws): return "vertex"
    if wants_simplest: return "api (direct)"
    return "api (direct)"

print(choose_door(on_aws=True, on_gcp=False, wants_simplest=False, data_residency=None))
print(choose_door(on_aws=False, on_gcp=True, wants_simplest=False, data_residency=None))
print(choose_door(on_aws=False, on_gcp=False, wants_simplest=True, data_residency=None))
bedrock
vertex
api (direct)
▶ How this works

This little decision function (it runs offline) encodes how a team actually picks a front door. It reads top to bottom and returns the first rule that matches — so order matters.

  1. The parameters describe your situation: are you already on_aws / on_gcp, do you just want the wants_simplest path, and is there a data_residency rule (data must stay in a specific cloud).
  2. Rule 1: if data must stay in AWS, or you're on AWS and not GCP, choose Bedrock. Rule 2: the mirror image for Google → Vertex. A return stops the function immediately, so a matched earlier rule wins.
  3. If no cloud constraint applies, if wants_simplest: return falls back to the direct api — and so does the final line, making the direct API the default.
  4. The three print(choose_door(...)) calls at the bottom test three situations: an AWS shop, a GCP shop, and someone who just wants the simplest option.

What the output means: Prints bedrock, then vertex, then api (direct) — matching the console output shown below the code, one line per test case.

Try this: Add a call with both on_aws=True and on_gcp=True and no residency rule — which door wins? Trace the rules top to bottom to predict it, then run to confirm. That's how you reason about any decision function.

7 · Tech-lead — abstract the provider tech-lead

A lead wraps the front door behind one internal client so the app code never hard-codes a provider. Switching API→Bedrock→Vertex (for cost, compliance, or a new region) becomes a config change — the DF4 "one client" lesson applied to model providers.

Provider-agnostic = future-proofWhen app code calls llm.complete(...) and one adapter picks API/Bedrock/Vertex from config, you can move providers for cost or compliance without touching business logic. A lead who builds that abstraction saves the team a painful migration later.

Exercise AK7.1 — Port a call to Vertex + abstract it

Context: Porting a working call to Vertex and then hiding the provider behind an abstraction is the portability payoff in practice: same Messages code, one config value to switch clouds.

Your task: Port a C2 or W2 call to run on Vertex (ADC auth, AnthropicVertex, the Vertex model id), then write a tiny provider abstraction so the same app code runs against any of the three doors by changing one config value.

Requirements:

  • The ported call authenticates via ADC (no Anthropic API key in code)
  • It uses AnthropicVertex with the bare (unprefixed) Vertex model id
  • The provider abstraction follows the shape of front_doors.py
  • Switching provider is a single config-value change — the app code is unchanged
  • The abstraction handles the model-id prefix difference (Bedrock's anthropic.) internally

💡 Hint: Get the raw Vertex call working first, then wrap only the two things that differ — the client constructor and the id prefix — behind the abstraction.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Three front doors, one ClaudeBeginner

Context: The same Claude model is reachable through the first-party API, Amazon Bedrock, and Google Vertex; the Messages surface is identical through every door, so portability comes down to the client class and the model-ID convention.

Your task: Map each front door — first-party, Vertex, Bedrock — to its client class and model-ID convention.

Requirements:

  • First-party uses Anthropic() with the bare model id
  • Vertex uses AnthropicVertex(...) with the bare id (no prefix)
  • Bedrock uses its Bedrock client with an anthropic.-prefixed id
  • Note the Messages API surface is the same through every door
  • Show all three mapped side by side

💡 Hint: The only real differences are the client you construct and whether the model id carries a prefix.

Show solution

Same model, three access paths that differ mainly in client + ID prefix:

def front_door(provider):
    return {
        "anthropic": ("Anthropic()",              "bare id: claude-opus-4-8"),
        "vertex":    ("AnthropicVertex(...)",     "bare id: claude-opus-4-8 (no prefix)"),
        "bedrock":   ("AnthropicBedrockMantle(...)", "prefixed: anthropic.claude-opus-4-8"),
    }[provider]

for p in ("anthropic", "vertex", "bedrock"):
    print(p, "->", front_door(p))

The Messages API surface is the same through every door — you swap the client class and mind the model-ID convention: Vertex uses the bare id, Bedrock adds an anthropic. prefix. That's what makes provider-portability cheap.

Exercise 2 · Authenticate with ADCIntermediate

Context: On Vertex you don't hold an Anthropic key — the SDK signs requests with Google Application Default Credentials resolved from the environment — which is why there is no API key in the code.

Your task: Show the Vertex authentication setup with ADC and explain why there is no API key in code. Needs the gcloud CLI + a GCP project to run.

Requirements:

  • Do the one-time local auth with gcloud auth application-default login
  • Construct AnthropicVertex naming only the project and region — no key
  • Explain there is no ANTHROPIC_API_KEY: auth comes from ADC
  • Note the production equivalent (service account / workload identity)
  • Verify current constructor args in the docs

💡 Hint: The client names project and region; the credentials come from the environment (ADC), not a string in your code.

Show solution

ADC supplies the credentials; the client just names project + region:

# One-time local auth — populates Application Default Credentials:
#   gcloud auth application-default login

from anthropic import AnthropicVertex
client = AnthropicVertex(project_id="my-gcp-project", region="us-east5")
# No ANTHROPIC_API_KEY — auth comes from ADC (the gcloud login above,
# or a service account / workload identity in production).

On Vertex you don't hold an Anthropic key: the SDK signs requests with Google credentials resolved from the environment (ADC). Locally that's gcloud auth application-default login; in prod it's a service account or workload identity. Verify current constructor args in the docs.

Exercise 3 · Call Claude on VertexAdvanced

Context: Once the Vertex client is built, a Messages call is identical to first-party — only the client construction and the unprefixed model id differ — though region matters on Vertex for data residency.

Your task: Make a basic Messages call through AnthropicVertex using the bare model ID. Needs GCP auth to run.

Requirements:

  • Build an AnthropicVertex client with project and region
  • Call messages.create with the bare model id — no anthropic. prefix
  • Set max_tokens and a user message; extract the text from the response
  • The messages.create surface is unchanged from first-party
  • Note region matters (global recommended, or a specific region for data residency); verify supported regions in the docs

💡 Hint: Reuse your first-party call verbatim — swap only the client and drop the model-id prefix.

Show solution

Once the client is built, the call is identical to first-party:

from anthropic import AnthropicVertex
client = AnthropicVertex(project_id="my-gcp-project", region="us-east5")

resp = client.messages.create(
    model="claude-opus-4-8",          # bare id on Vertex — NO anthropic. prefix
    max_tokens=512,
    messages=[{"role": "user", "content": "Explain ADC in one sentence."}],
)
print(next(b.text for b in resp.content if b.type == "text"))

The messages.create surface is unchanged — only the client construction and the (unprefixed) model ID differ from the first-party API. Region matters on Vertex: global is recommended, or a specific region for data residency. Verify supported regions in the docs.

Exercise 4 · Map concepts across all three doorsExpert

Context: Core Messages, streaming, tools, and prompt caching work on Vertex, but several server tools and endpoints don't — so a capability map is what stops you promising a feature that isn't there.

Your task: Write a small capability map that decides whether a requested feature is available on Vertex and what changes versus first-party.

Requirements:

  • Encode the availability facts: messages/streaming/tools and prompt caching are available
  • Mark automatic prompt caching as first-party-only
  • Mark web fetch, code execution, Files API, and Batches as unavailable on Vertex
  • Mark web search as the basic variant only
  • A lookup returns availability for a given feature, defaulting to "verify in the docs"
  • State the takeaway: check the availability table before promising a feature on Vertex

💡 Hint: Build a small table of what's on and off on Vertex; the first-party-only and unavailable entries are the ones that bite.

Show solution

Encode the availability differences that actually bite on Vertex:

VERTEX = {                      # from the platform-availability table
    "messages/streaming/tools": True,
    "prompt_caching":           True,
    "automatic_prompt_caching": False,   # 1P only
    "web_search":               "basic only (web_search_20250305)",
    "web_fetch":                False,
    "code_execution":           False,
    "files_api":                False,
    "batches":                  False,
}
def on_vertex(feature):
    return VERTEX.get(feature, "verify in platform-availability docs")

print(on_vertex("prompt_caching"))            # True
print(on_vertex("web_fetch"))                 # False
print(on_vertex("web_search"))                # basic only (web_search_20250305)

Core Messages/streaming/tools and prompt caching work on Vertex, but several server tools and endpoints don't: web fetch, code execution, Files API, and Batches are unavailable, web search is the basic variant only, and automatic prompt caching is first-party-only. Check the availability table before promising a feature on Vertex.

Exercise 5 · Choose a front doorProfessional

Context: Choosing a provider is a routing decision on the team's existing cloud, auth model, and needed features — but a first-party-only feature (Batches, Files, code execution) overrides everything and points back to the first-party API.

Your task: Write a selector that recommends first-party / Vertex / Bedrock from a team's real constraints.

Requirements:

  • The selector takes existing cloud, whether a first-party-only feature is needed, and the auth/billing preference
  • A needed first-party-only feature overrides and returns first-party
  • GCP-native teams → Vertex (ADC auth, GCP billing, data in their project)
  • AWS or wanting IAM/Marketplace billing → Bedrock
  • Otherwise default to the first-party API
  • Demonstrate each branch with an example

💡 Hint: Check the first-party-only override first; only after that does existing cloud pick Vertex vs Bedrock.

Show solution

Route by where they already run and which features they need:

def choose_door(existing_cloud, needs_1p_only_feature, wants_iam_billing):
    if needs_1p_only_feature:
        return "first-party API — Batches / Files / code exec are 1P-only"
    if existing_cloud == "gcp":
        return "Vertex — ADC auth, GCP billing, data stays in your GCP project"
    if existing_cloud == "aws" or wants_iam_billing:
        return "Bedrock — AWS IAM auth + Marketplace billing"
    return "first-party API — simplest, full feature surface"

print(choose_door("gcp", False, False))   # Vertex
print(choose_door("aws", False, True))    # Bedrock
print(choose_door("none", True, False))   # first-party (needs 1P-only feature)

Pick the door that matches the team's existing cloud (Vertex for GCP-native auth/billing, Bedrock for AWS IAM/Marketplace) — but if they need a first-party-only feature (Batches, Files API, code execution), that overrides and points back to the first-party API.

Exercise 6 · Abstract the provider (tech-lead)Industry scenario

Context: Because every door exposes the same messages.create, the only provider-specific parts are the client constructor and the model-ID prefix — isolate both behind a factory and the rest of the codebase is portable.

Your task: As tech lead, write a small factory that returns the right client and normalizes the model ID so the codebase is insulated from the provider choice.

Requirements:

  • A factory selects the client by provider (first-party / Vertex / Bedrock), reading config/env
  • It returns both the client and the correct model-id prefix (empty except Bedrock's anthropic.)
  • A helper normalizes the model id by applying the prefix
  • Call sites use the same messages.create regardless of provider
  • Keep provider-specific feature gates behind the same abstraction
  • Verify client names/args in the docs

💡 Hint: Push the only two differences — constructor and id prefix — into the factory so everything downstream is provider-agnostic.

Show solution

One factory, provider-agnostic call sites — the difference lives in construction:

import os

def make_client(provider):
    if provider == "vertex":
        from anthropic import AnthropicVertex
        return AnthropicVertex(project_id=os.environ["GCP_PROJECT"], region="us-east5"), ""
    if provider == "bedrock":
        from anthropic import AnthropicBedrockMantle
        return AnthropicBedrockMantle(aws_region=os.environ["AWS_REGION"]), "anthropic."
    from anthropic import Anthropic
    return Anthropic(), ""          # first-party

def model_id(base, prefix):         # normalize per provider
    return prefix + base            # bedrock -> "anthropic.claude-opus-4-8"

client, prefix = make_client(os.environ.get("LLM_PROVIDER", "anthropic"))
resp = client.messages.create(model=model_id("claude-opus-4-8", prefix),
                              max_tokens=256,
                              messages=[{"role":"user","content":"ping"}])

Because every door exposes the same messages.create, the only provider-specific parts are the client constructor and the model-ID prefix — isolate both behind a factory and the rest of the codebase is portable. Keep provider-specific feature gates (see the availability map) behind the same abstraction. Verify client names/args in the docs.

✓ Checkpoint — you can move on when you can…

  • Explain the three front doors and why enterprises pick a cloud one.
  • Authenticate to Vertex with ADC.
  • Call Vertex; map client/auth/model across doors.
  • Abstract the provider behind one internal client.

Knowledge check check yourself

✓ Knowledge check

When calling Claude on Vertex, what actually changes versus the direct Anthropic API, and what stays the same?

Show answer
Only the client construction (AnthropicVertex(region=..., project_id=...) instead of Anthropic()) and the model-ID format (e.g. claude-3-5-sonnet-v2@20241022) change. The messages.create() request shape, streaming, and tool use are identical because it's the same underlying model.
✓ Knowledge check

If you skip gcloud services enable aiplatform.googleapis.com, what happens, and why do teams still choose Vertex or Bedrock over the direct API?

Show answer
You get permission errors that look like auth failures but are really the Vertex AI service not being activated on the project. Teams still choose a cloud front door mainly for data residency, IAM-based access control, and consolidated billing, i.e. governance reasons, not because the model is any more capable there.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in