The most useful file in Anthropic's commerce blueprint is 119 lines long, and it is not a prompt.
It is commerce-common/commerce_common/prompt_assembly.py, and its module docstring gives away the entire commerce agent architecture in three sentences: where the cache breakpoints go, where the per-request context goes, and why moving a single byte between those two places changes your bill. I read the published guide first — A guide to the anatomy of effective commerce agents — nodded along, thought I understood it, then installed the reference repo and found that half of what I "understood" was the marketing-shaped version of a much sharper engineering claim.
So here is the short answer before the long one. A commerce agent — an agent that helps people buy, or helps a business sell — should be one model in a standard agent loop, equipped with skills, typed presentation tools, and a safety layer that lives in your server code rather than in your prompt. No intent router in front. No fleet of domain subagents behind. Anthropic reports that across several enterprise deployments, the single-agent-with-skills design beat both the one-giant-prompt design and the subagent design on quality, frequently at lower cost and latency per task.
I did not ship a production storefront agent on this stack. What I did was clone the repo, read the modules that carry the load, and hold every architectural claim up against the agent systems I do run every day. That's the honest frame for everything below.
Why commerce agent architecture rejects the subagent instinct
My first instinct when I read "shopping agent" was to reach for the pattern I use constantly: an orchestrator that fans work out to specialists. Search agent. Cart agent. Returns agent. It maps beautifully onto an org chart, which should have been the warning.
The guide's argument against it is not aesthetic. A commerce conversation is one tightly coupled session with shared state — the cart, the stated preferences, the order history, the three products already on screen. Every subagent handoff is state-lossy. You pay several times the tokens to reconstruct context the parent already had, and you add seconds of latency to a surface where seconds show up in cart size. Worse, the domains refuse to separate cleanly: a returns question needs order history and cart and catalog, so your clean boundary turns into three chatty round trips.
That matches what I've watched happen in my own multi-agent builds. The moment two agents need the same mutable state, the handoff protocol quietly becomes the product, and you spend your week debugging the seam instead of the behavior. I wrote about where that fan-out does pay off in my breakdown of building agentic AI products — and the honest summary there is the same as here: parallelism earns its place when the work is genuinely independent.
The repo is disciplined about the exception. commerce-common/commerce_common/delegation.py is 59 lines and defines a delegate as "an isolated model call behind a tool" that "receives a task brief and the session handles, never the conversation or the executor, and returns one schema-validated result; it cannot write, present, or invoke other delegates."
Read that list of prohibitions again. That's the whole distinction:
- Delegation — a narrow, self-contained task that needs its own context window and hands back a compact answer. The merchant agent's analysis delegate is the canonical case: give it a brief and read-only tools, get back one validated result, and it cannot widen the set of IDs the session is allowed to write to.
- Hand-off — transferring ownership of the conversation to a domain that runs its own purpose-built agent. Pharmacy. Financial services. The user is now talking to a different system, and everyone knows it.
If your "subagent" is neither of those, it's a function call wearing a costume.
Skills, not subagents — and the one-third rule that decides

So where does domain behavior live? In skills. commerce-common/commerce_common/skills.py keeps it boring on purpose: a skill is a directory holding a SKILL.md with YAML frontmatter (name, description) and a markdown body. The static prompt carries only the index; load_skill returns the body on demand.
The reference agents ship five each:
| Shopping agent | Merchant agent |
|---|---|
search-discovery |
performance-insights |
purchase-research |
catalog-listings |
planning-goals |
inventory-operations |
customer-care |
pricing-promotions |
memory-personalization |
marketing-campaigns |
The system prompt keeps what every turn needs: grounding, cart and checkout semantics, presentation rules, product search.
Now the part I want engineering leads to take away, because it is a rule you can apply this week and it has nothing to do with commerce. Deciding between the system prompt and a skill is a frequency question, not a tidiness question. Loading a skill costs a model turn. The guide's rule of thumb: anything relevant to a third or more of your traffic belongs in the system prompt. Anything rarer becomes a skill — and when a signal makes the skill predictable (the arrival page, the campaign dashboard the operator came from), your harness injects it up front instead of making the model discover it.
Three things are never skills: safety constraints, legal and brand constraints, and key user facts. An allergy does not get lazy-loaded.
The skill bodies themselves are worth stealing as a writing sample. Here's a fragment of shopping-agent/skills/search-discovery/SKILL.md:
Show three to six options in
present_productswith the one you recommend first. Each pick'sreasonis one clause naming the customer's own constraint it meets… Before saying that several options fit under a figure, add up their prices. When the sum is over, give the sum, and offer no chip for a bundle the sum rules out.
Not one sentence of role-play. Not one "you are a helpful shopping assistant." It reads like a spec handed to a new hire on the floor, which is exactly the register that survives model upgrades — the same conclusion I landed on when Opus 5 forced me to rewrite my own instructions in the prompting shift post.
What the tool list tells you about the design
shopping-agent/core/shopping_agent/tools/registry.py registers twenty tools. Grouped, they tell the story better than any diagram:
- Read:
search_products,get_product_details,get_cart,get_preferences,get_orders,get_order_status,search_policies,get_fulfillment_options - Write:
add_to_cart,update_cart_item,remove_from_cart,save_memory,checkout - Recall:
recall_memories - Render:
present_products,present_comparison,present_plan,present_guide,present_order_status,present_suggestions
Six of twenty tools exist purely to put pixels on a screen. Hold that thought.
Two tooling rules carry most of the weight. First, build agent tools on top of your core systems, don't rebuild the systems inside the tools. search_products calls your existing search and ranking stack and returns ranked results; the model decides which to show and in what order, and never attempts to re-rank from scratch. The anti-pattern the guide names is the availability check that internally calls catalog, then inventory per store, then fulfillment cutoffs, then substitution rules — four hops stitched together inside a tool because the backend was missing an endpoint. That is not a prompt problem. The fix is one backend endpoint.
Second, tool results are context, so return only fields the model reasons with. An image URL on every row of a fifty-result response is pure tax — thousands of tokens the model cannot use to make a decision, paid on every turn that history stays in the window. Errors get the same treatment in reverse: reshape them into instructions. "Include a product ID when querying availability" moves the agent forward. A raw 403 makes it guess.
If you've worked through Anthropic's guidance on writing effective tools for agents, this is that discipline applied to a domain where the token bill scales with catalog size.
UI components as tools: the trade-off nobody warns you about
Those six present_* tools are the part of the commerce agent architecture I'd import into non-commerce products tomorrow.
The pattern: the model calls a presentation tool with typed arguments. The server validates against the schema, enriches every row from server records, emits an event, and the client renders a native component. docs/safety.md spells out the enforcement — IDs without provenance are dropped and reported, a component with nothing left after the drop is refused, suggestion chips are sanitized and capped at four.
The alternative — custom tags the model emits and the client parses — works in a demo and degrades as the surface grows. The model is trained on tool calls, not on your markup, so reliability drops as components nest. Your prompt bloats with format instructions. And your conversation history is now stored in a non-native format you'll be migrating for years.
There's a second, quieter benefit that took me a re-read to appreciate. Because the presentation call is in the conversation, the agent has a record of what's on screen. When the customer says "book the first hotel," the referent is resolvable. Which means you should structure presentation arguments the way the UI is actually structured — ordered rows, carousels — because that ordering is what the phrase "the first one" indexes into.
Now the trade-off. Tool arguments normally buffer per top-level value before they're handed over, so a card with a products array won't start rendering until that entire array has streamed. The repo's answer is in prompt_assembly.py:
def with_eager_input(tools, names):
"""Ask the API to stream these tools' input as it is generated instead of one
top-level value at a time, so a card's first item can render at its first key."""
return [t | {"eager_input_streaming": True} if t.get("name") in names else t for t in tools]
Set eager_input_streaming: true and you get token-level streaming — the first product card paints while the third is still being written. You also give up the server-side schema guarantee, because the input now arrives as written, valid JSON or not. The repo wraps that case rather than hoping: a call whose input never parses returns an error result and the handler never runs. Two lines of comment in the source do more to explain the cost than the entire blog section did.
Where the latency actually goes
The framing I keep coming back to: quality of outcome moved retention, engagement, and cart size more than marginal latency gains did. A fast wrong answer is still a wrong answer. Attack latency on two fronts anyway — end-to-end and perceived — because the second one is cheap.
Task completion latency is a sum across turns of time-to-last-token plus tool processing. Three levers pull on that sum:
1. Fewer turns. Load likely context up front — the product page they arrived from, the campaign dashboard the operator opened. Then the counterintuitive one: if production shows more than roughly five turns per task, the faster model is frequently the smarter one, because it plans in one turn what the cheaper model discovers over four. And issue parallel tool calls so they return as a single array of results instead of a chain.
2. Faster tools. Optimize the backend rather than stitching around it. Then dispatch tools eagerly as their arguments finish streaming, instead of waiting for the full block — multi-second gaps collapse to a few hundred milliseconds. The Claude Agent SDK does this by default. There's a prompt-level trick too: tell the model to emit its slowest call first, so the long one starts earliest.
3. Faster tokens. Model and effort level, chosen by sweep rather than vibes. More on that below.
For perceived latency, the number that reframed it for me: a rendered commerce response is typically 500–700 output tokens. Without streaming, that's five-plus seconds of spinner on every single turn. Stream the presentation tool parameters and render progressively. Then show the work — short plain-language progress lines built from tool arguments or a dedicated user_facing_message parameter. "Finding hotels near the water" is not decoration; it is the difference between a user who waits and a user who taps back.
Prompt caching is the cost model, not an optimization

This is the section where reading the source beat reading the guide.
The economics first, because they're stark: cached input reads cost a tenth of fresh ones, cache writes run roughly 1.25x, so the write pays for itself on the second use. The best deployments Anthropic describes run 90–99% cache hit rates on the default five-minute cache, and cached reads land roughly 1.5–2x faster at around 100k tokens. This is latency and cost from the same change.
Caching is prefix-based, which means one mutated byte near the front invalidates everything behind it. The blueprint splits the request into three segments:
| Segment | Contents | Changes |
|---|---|---|
| Global | System prompt + tool definitions | Never, per deploy — byte-identical, breakpoint at the end |
| Session | Per-user context, history, memory facts | Per session |
| Volatile | Current time, current page | Every turn — and it goes at the very end |
The most common mistake named in the guide is putting a timestamp or the current page at the top of the system prompt. Do that and you buy a 0% cache hit rate, every turn, forever. I have shipped that bug. It is invisible in dev, where you never hit the cache anyway, and it shows up as a bill.
Here's how build_system_blocks enforces it in 8 lines:
return [
{"type": "text", "text": static_text, "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": context},
]
Two system blocks. The static one carries the breakpoint; everything per request — cart, page, clock, memory facts — sits behind it in the second block. with_tool_cache_control puts another breakpoint on the last tool definition, and build_request_messages rolls a breakpoint forward onto the newest persisted content block each turn.
Three details in that file are the kind of thing you only learn by shipping:
- The clock is truncated to the hour.
context_clockzeroes minutes and seconds, with a comment explaining why: rendering the minutes would change the block, and therefore re-read the conversation, on nearly every turn. A one-line function protecting a five-figure line item. - The rolling breakpoint is skipped when
tool_choiceisn'tauto, becausetool_choicekeys the messages span — an entry written under a forced round is unreadable by the auto rounds that follow. - The rolling breakpoint is also skipped on a bare first call, which would write an entry a one-shot session never reads.
That second bullet is a genuine tension in the design, and I'll come back to it.
On model choice, the guide is refreshingly unsentimental: size and effort level are the same trade-off. Pick your metric and your floor — task completion, answer relevance, grounded accuracy, against p50/p99 latency and cost — then sweep the whole eval suite across candidate models and effort levels. The repo's defaults tell you where to start: shopping-agent/core/shopping_agent/config.py defaults to claude-sonnet-5, merchant-agent/core/merchant_agent/config.py defaults to claude-opus-5. Consumer surface, high volume, latency-sensitive → Sonnet. Merchant surface, analytical, fewer sessions, higher stakes per action → Opus.
Two warnings worth tattooing on the sprint board. Prompts are tuned to a model, so iterate on each candidate's failures before you rule it out — a one-shot swap measures your prompt, not the model. And measure cost per completed task, not cost per call: the smarter configuration sometimes wins on p90/p99 latency because it plans better and takes fewer turns. When it's close, take the intelligence. I've made the opposite call before and paid for it in retry volume — the ugly arithmetic is in my agent cost optimization guide.
Memory belongs in your database, not in the model
The flat markdown profile everyone starts with — I have several — stops scaling the moment two features need the same fact.
The blueprint's position: memory is a typed record in the database you already run. A key (shoe_size, default_store, preferred_report_cadence), a value, a category, and the session it came from. commerce-common/commerce_common/memory.py is 666 lines and enforces the boring parts: keys capped at 64 characters, values at 200, exactly three categories, every fact through validate_fact on both write paths, identifier-shaped values refused by default.
Merchant memory keys on the person, not the account — the same operator moving between stores keeps their report preferences — and reads respect operator permissions.
Treat it as personal data from day one, because it is: a validator on the write path, user view/correct/delete wired into your actual account-deletion flow, a retention period, and a per-deployment switch to turn the whole thing off.
Two mechanics I'd copy directly:
Write asynchronously with a separate extractor. At end of turn, a small extraction pass reads only the user's and assistant's text — never tool results — and writes facts out of band. Zero added latency on the turn the user is waiting for, and Anthropic reports 13% higher fact recall on their internal commerce memory eval than the save-a-fact-tool approach. The reason is obvious once you see it: a model deciding mid-conversation whether something is worth remembering is a model distracted from helping. The repo even handles the race — extract_and_store discards its batch if the subject was purged while the extraction model was still running.
Read in three layers. Always-in-context facts (default store, fulfillment preference, operator role) ride in the session cache segment. Pre-fetched facts come from the same signals that pre-load a skill. Everything else sits behind recall_memories and is fetched only when asked for.
Safety enforced in code: the table that should be your spec

docs/safety.md opens with a heading I wish more AI repos had: "Enforced in code." Under it, a table of about twenty rules, each naming the module that enforces it. Under the next heading, "Still asked of the model," a much shorter list — and a sentence that reframes the entire risk posture:
When the model breaks one of these, the error is confined to its text. Every write, figure, and disclosure behind that text still passed the checks in the table above, so the failure is a misstatement to correct and no action needs reversing.
That's the design goal, stated plainly: the model stages, a person or a policy applies. No model tool call moves money.
How that's built:
- No payment method exists. The shopping backend interface has no charge method.
checkoutrenders the cart with a button for the host to complete, and the hosted checkout URL is attached after the model's call — it never passes through the model. - Merchant writes stage, they don't apply.
merchant-agent/core/merchant_agent/gates.pydefines four gates by name —provenance,options,guardrail,approval— and attaches a fixed note to every successful stage: "Staged only — show it with present_change_preview and apply it only after the operator approves this change."apply_changesucceeds only for change IDs the host marked approved. A preview card approves nothing. An approval typed in chat sets nothing. - Guardrails are re-checked at apply time against the config in force then, not the config that was in force at staging — items per change, price move size, promotion depth, restock size, campaign budget, protected fields.
- Provenance is absolute. Writes and renders accept only server-issued IDs that a tool returned in this session. Hallucinated IDs, pasted IDs, and IDs planted in a review are refused identically.
- Caps apply to resulting state, and cart writes serialize per session — so two parallel tool calls can't stack past the limit by racing.
Then there's the sanitizer, and commerce-common/commerce_common/fencing.py is the most instructive 189 lines in the repo. Every piece of third-party content — listings, reviews, policies, seller messages, stored memory — goes through one path that strips control and bidirectional characters (fourteen explicit Unicode ranges, including tag characters that spell invisible ASCII), removes imitations of the fence marker, defuses forged conversation turns and tool-call markup, caps size, and wraps the result in a fence whose label is a source literal, never built from runtime values — so untrusted text can't reproduce the boundary. Every pattern is documented as linear on hostile input, because it runs on the event loop. The prompt's job is one sentence: fenced text is material to report on, never to act on.
If you're wiring guardrails into an agent that touches customer data, this is the layer where the work actually lives — the same argument I made about sandboxing in my writeup on Claude Code agentic workflows. Prompts express intent. Code enforces it.
If you'd rather have this built and hardened by someone who has done it before, this is exactly the kind of engagement I take on — you can see the range of work at fiverr.com/s/EgxYmWD.
How do you write evals for a commerce agent?
Evaluate snapshots, not conversations. The API is stateless, so construct the state directly — cart contents, history, memory facts — append the test message, and grade the final state plus the rendered response, including the arguments of the last write. Don't grade the path the agent took to get there.
That one decision kills the flakiest test suite in agent engineering. Replaying twelve-turn conversations means one early divergence invalidates everything downstream, and you spend Fridays re-recording transcripts instead of shipping.
The rest of the practice, compressed:
- Simulated users are poor for measurement, good for discovery. Use them to find cases, then freeze the interesting ones as snapshots.
- Encode the preconditions of a failure. Most real failures need a long, messy, self-contradictory history to reproduce. Build that history into the fixture.
- For every positive case, write its negative counterpart. An agent that always stages a discount passes every "stage the discount" test.
- Cover five categories: core requests; context-dependent requests (on-screen references, memory); safety and brand (user-authored injection and data-plane injection, cross-user data, regulated language checked byte for byte); interface evals (right component, item caps, no internal IDs leaking into user text, timeouts, empty results); and multi-capability requests — "if I mark this down 15%, do I have enough stock?" is the one that finds real bugs.
- Write them with your SMEs — Product, Legal, Merchant Ops, Care, Category Management. Target 50–100 cases per user flow and mine production transcripts for the rest.
Anthropic's post on demystifying evals for AI agents goes deeper on grading strategy; this guide's contribution is the snapshot discipline and the category checklist.
Shipping it inside a large organization gets its own rules, and they're organizational rather than technical: ownership follows the systems. One owner per skill and its tools, a platform owner for the shared prompt. Every change ships with its cases. CI runs core cases plus all safety cases plus the cases for whatever the change touched — and the full suite for any change to the shared prompt. Gate on pass rate across a few trials, plus cache hit rate and cost per turn, so a "harmless" prompt edit that breaks the cache prefix gets caught by the gate rather than by finance. Full suite nightly and before release. Then put the agent inside the release calendar like any other system: canary cohort, per-skill kill switch that works without a deploy, and a freeze before peak periods. If you want the human side of that operating model, building effective human-agent teams is the companion piece.
What's actually in anthropics/commerce-agents
github.com/anthropics/commerce-agents is Apache-2.0, dated 2026, and explicitly unmaintained — a reference implementation, not a product. Seven Python packages installed editable, Python 3.11+, Node 22.
Four verticals run as real demos, each with a storefront and an operator portal:
| Vertical | Storefront | Portal | What it shows |
|---|---|---|---|
| Retail | :3000 | :3100 | Search, comparison, cart, checkout; digests, restocks, listing fixes |
| Travel | :3001 | :3101 | Date-bound inventory, itineraries; occupancy calendar, rate moves |
| Telecom | :3002 | :3102 | Account context, plan matrix; regulated fee protection |
| Entertainment | :3003 | :3103 | Timed holds, waitlists, venue map; event pacing, hold releases |
Both agents run on three paths — the Messages API turn loop, the Claude Agent SDK, and Managed Agents — over the same core packages, which is the most useful structural idea in the repo: the prompt, skills, tool contracts, and gates are defined once, and the runtime is a swappable surface. If the SDK path is new to you, start with my walkthrough of building a custom agent on Anthropic's SDK and come back. Getting started is python scripts/run_demo.py retail. All data is fictional. Nothing places an order, charges a card, or changes a live listing.
There's also a Claude Code plugin, which is how I have the source on disk in the first place:
claude plugin marketplace add anthropics/commerce-agents
claude plugin install commerce-builder@claude-commerce-agents
It ships four commands — /scaffold-commerce-agent, /add-commerce-flow, /author-commerce-evals, /review-commerce-agent — and six skills that mirror the guide's sections: commerce-architecture, commerce-prompt-caching, commerce-trust-safety, commerce-ui-tools, commerce-evals, commerce-merchant-operations. The review command is the one I'd point most teams at first: it maps an agent you already run against the reference row by row, which is a cheaper conversation than a rewrite.
Where I'd push back on this commerce agent architecture
Three things I'd want on the table before a team adopts this wholesale.
The single-agent claim is scoped tighter than it reads. It's an argument about commerce conversations — tightly coupled, shared-state, latency-sensitive sessions. It is not a general refutation of multi-agent systems, and quoting it as one in a design review would be a misuse. My own agent fleet is multi-agent precisely because the tasks don't share mutable state. Ask the coupling question, not the fashion question.
Grounding and caching pull against each other, and the repo admits it. Grounding rules force certain message shapes to start from a read tool by setting tool_choice — and build_request_messages skips the rolling cache breakpoint on exactly those rounds, because tool_choice keys the messages span. So the turns where you most want determinism are the turns where you give up a cache read. That's a defensible trade. It's also a real cost that no summary of this guide mentions, and it's the kind of thing you'd otherwise discover as an unexplained p99 spike three weeks after launch.
The sanitizer is an arms race with a clean floor. Fourteen Unicode ranges and a set of bounded regexes will catch today's invisible-instruction tricks and every lazy variant. New encodings will keep arriving. What makes the design sound isn't the regex list — it's that the fence label is a source literal and the enforcement table doesn't depend on the model behaving. Copy the architecture, and assume you'll be maintaining the pattern list forever.
One more thing worth saying plainly: the examples ship with no authentication, and the MCP servers bind to loopback for a reason. This is a blueprint you build on, not a thing you expose.
The file I'd read first
If you give this repo one hour, don't start with the prompts. Start with docs/safety.md, read the "Enforced in code" table top to bottom, and ask your own agent the question it forces: for every rule I currently express in a prompt, what happens when the model ignores it?
If the answer is "a misstatement I correct," you've built the commerce agent architecture this guide is describing. If the answer is "money moves," you haven't — and no amount of prompt engineering closes that gap. That's a server-side afternoon, and it's the single most valuable afternoon available to anyone shipping agents into a checkout flow this quarter.
I opened with a 119-line file that gives the architecture away. Here's why that file is the tell: it isn't clever. It's two system blocks, three breakpoints, a clock rounded to the hour, and comments explaining what each one protects. The whole blueprint is like that — the hard-won parts are boring, written down, and enforced somewhere the model can't reach.
FAQ
Frequently Asked Questions
Everything you need to know about this topic
A commerce agent is an AI agent that simplifies buying and selling across an online catalog. Consumer-facing ones search, compare, substitute, and assemble an order — a retail cart, a travel itinerary, a mobile plan change, seats held for a show. Business-facing ones answer sales questions, run promotions and campaigns, and manage inventory and pricing.
Usually no. A commerce conversation is one tightly coupled session with shared state, so every handoff loses context, multiplies tokens, and adds seconds. Subagents earn their place only for narrow self-contained tasks needing their own context window, or a true hand-off to a domain with its own agent.
Split the request into three prefix segments: global (system prompt and tool definitions, byte-identical, with the breakpoint at the end), session (per-user context, history, memory), and volatile (clock, current page) placed at the very end. Never put a timestamp at the top of the system prompt.
Sweep your whole eval suite across candidates rather than guessing. The reference repo defaults to Sonnet for the consumer shopping agent and Opus for the merchant agent, and measures cost per completed task — a smarter model that takes fewer turns can win on both p99 latency and total cost.
Grade snapshots, not conversations: construct the state, append the test message, and grade the final state plus the rendered response including the last write's arguments. Write a negative counterpart for every positive case, and target 50–100 cases per user flow.
Let's Work Together
Looking to build AI systems, automate workflows, or scale your tech infrastructure? I'd love to help.
- Fiverr (custom builds & integrations): fiverr.com/s/EgxYmWD
- Portfolio: mejba.me
- Ramlit Limited (enterprise solutions): ramlit.com
- ColorPark (design & branding): colorpark.io
- xCyberSecurity (security services): xcybersecurity.io