生产级电商 Agent 的工程解剖:单一 Agent + Skills,UI 组件工具化,安全交给 harness
Anthropic 总结过去一年与零售、旅行、电信等团队共建 Claude commerce agent 的实战,面向工程师和工程负责人给出生产级架构指南。核心立场是明确反对 intent router 与按 domain 拆 subagent:电商会话是多意图紧耦合的单一 session,每次 handoff 都会丢状态并增加 token 与延迟;单一主 agent 携带 skills 的实验比 one-prompt 与 subagent 设计在质量上更好,成本也更低。UI 输出不是让模型写文本或自定义 tag,而是把每个 UI 组件定义成 presentation tool,服务端校验后发事件渲染,历史消息可直接回放。性能部分给出三条杠杆(更少轮次、更快工具、更快 token)与 perceived latency 设计,并说明 prompt caching 按 global/session/volatile 三段前缀缓存,最好部署可达 90–99% 命中。生产部分强调:记忆用异步抽取写入自有库并分层读取;安全规则全部落在 harness 而非 prompt;eval 用可构造的 snapshots,每个正例配反例。附开源参考实现 anthropics/commerce-agents。
Over the past year, we've worked with teams across the commerce industry — retailers, marketplaces, travel, entertainment, and telecom providers — to build commerce agents using Claude.
These agents are in production, and enterprise customers have seen larger carts and more efficient seller operations when using them. They also share a simple architecture: Claude in an agent loop equipped with a set of skills, tools, and a strong eval suite.
This post is for the engineers and engineering leaders building these (or other consumer facing) agents. Part 1 covers the architecture, which you decide once. Part 2 covers latency and cost. Part 3 covers production: memory, safety, evals, and scaling the work across an organization.
过去一年,我们与电商行业的众多团队合作——包括零售商、市场平台、旅行、娱乐和电信运营商——用 Claude 构建 commerce agent。
这些 agent 已经投入生产;企业客户的使用结果显示,购物车金额更大,卖家运营也更高效。它们的架构也出奇一致:Claude 跑在一个 agent 循环里,配上若干技能(skills)、工具和一套扎实的 eval 套件。
这篇文章面向正在构建这些(以及其他面向消费者的)agent 的工程师和工程负责人。第一部分讲架构——这是你只需要决策一次的部分;第二部分讲延迟与成本;第三部分讲生产落地:记忆、安全、eval,以及在组织内规模化这套工作。
We've also provided a blueprint to help build commerce agents on Claude. It contains the harnesses, patterns, and guardrails an engineering team needs to get a commerce agent running in days, with reference implementations of a shopping agent and a merchant agent for retail, travel, telecom, and ticketing platforms.
anthropics/commerce-agents →
In this guide
Part 1: The architecture
What is a commerce agent?
Skills, not subagents
System prompt or skill: decide by frequency
Engineering agent tooling
The UI components are tools
Part 2: Making it fast and affordable
Minimizing task completion latency
Perceived latency
Prompt caching
Choosing the model and its configuration
Part 3: Running it in production
Memory that survives the session
Safety: enforcement lives in the harness
Evals: shipping a non-deterministic system
Shipping with a large organization
我们还提供了一份蓝图,帮助你在 Claude 上构建 commerce agent。它包含工程团队在数天内跑起一个 commerce agent 所需的 harness、模式和护栏,并提供购物 agent 和商家 agent 的参考实现,覆盖零售、旅行、电信和票务平台。
anthropics/commerce-agents →
本指南目录
第一部分:架构
什么是 commerce agent?
技能,而非子代理
系统提示还是技能:按使用频率决定
面向工程的 agent 工具
UI 组件也是工具
第二部分:把它做得又快又省
降低任务完成延迟
感知延迟
提示缓存
选择模型及其配置
第三部分:生产环境运行
跨越会话的记忆
安全:强制约束在 harness 层
Eval:发布一个非确定性系统
与大型组织一起交付
One model in a standard agent loop, with skills for the long tail and tools that call the systems you already run. You decide this once.
We define a commerce agent as an agent that simplifies buying and selling across an online catalog.
Some agents face consumers: they search, compare, substitute, and assemble the order. That could be a retail cart, a travel itinerary, a mobile plan change, or seats held for a show. Some agents face the business: they answer questions about sales, run promotions and campaigns, and manage inventory and pricing.

The core architecture is a model in a standard agent loop: reasoning about a goal, exploring context, taking actions through tools, learning procedures through skills, asking clarifying questions, and observing the results until the goal is accomplished.
There is no intent router in front of it that segments the conversation and no set of domain specific agents behind it.
一个模型跑在标准 agent 循环里,用技能覆盖长尾需求,用工具调用你已有的系统。这个决策你只需要做一次。
我们给 commerce agent 的定义是:一个简化在线目录上买卖流程的 agent。
有些 agent 面向消费者:它们搜索、比较、找替代品,然后组装订单。订单可以是零售购物车、旅行行程、手机套餐变更,或为一场演出预留的座位。有些 agent 面向商家:它们回答销售问题、运行促销和营销活动、管理库存与定价。

核心架构就是标准 agent 循环中的一个模型:围绕目标推理、探索上下文、通过工具执行动作、通过技能学习流程、提出澄清问题,并观察结果,直到目标完成。
它前面没有把对话切分的意图路由器,后面也没有一组领域专用 agent。
A commerce agent has to cover a wide range of capabilities across many categories and intents, which makes it tempting to create one subagent per domain.
In practice this proves suboptimal, because a commerce conversation is one tightly coupled session across multiple intents and turns, and requires considerable shared context.
In a subagent architecture, the orchestrator holds the cart or staged changes, the user's preferences, and the conversation history.
Every handoff to a subagent is a state-lossy operation, which often impacts the quality of the subagent’s response and, consequently, the overall response. On top of that, each handoff can cost several times the tokens and adds seconds of latency.
The domains also rarely separate cleanly. A returns flow might need the order history, the current cart, and the product catalog, meaning a subagent-per-domain approach either duplicates that access everywhere or hands off mid-task.
As models get smarter, they also handle longer context, more skills, and more tools, so the limits behind today's placement rules loosen with each model generation.
Instead, agent skills give you similar per-domain modularity and context control without the handoff tax, because the skill instructions load into the main agent that already holds the entire history.
In our comparisons across several enterprise deployments, a single agent with skills consistently has outperformed both the one-prompt-for-everything design and the subagent design on quality, and often at a lower cost and latency per task.
Where subagents do earn their place is when the orchestrator can call them as a tool for a narrow or self-contained task that would benefit from its own dedicated context window.
A common production example is a deep-research subagent, where the subagent searches and reads documents, writes and runs code, traverses data models, and hits dead ends. All the work happens inside one or more subagents, and only a compact answer comes back to the orchestrator.
The other exception is a domain that already has its own purpose-built agent. If your pharmacy or financial-services experience runs a dedicated agent with its own compliance surface, the right move can be a hand-off, where that agent takes over the task and works with the user directly through its own loop until the task is done.
The distinction is ownership of the conversation. A hand-off makes the domain agent the user's counterpart, while delegation keeps the orchestrator, bouncing the domain agent in and out within a single turn and degrading on every exchange.
一个 commerce agent 要覆盖众多品类和意图带来的广泛能力,所以很容易让人想按领域造一个子代理。
实践证明这种做法并不好,因为一次电商对话是一个横跨多个意图和轮次的强耦合会话,需要大量共享上下文。
在子代理架构中,购物车或暂存变更、用户偏好和对话历史都由编排器持有。
每一次交给子代理都是一次丢失状态的操作,往往会拉低子代理回答的质量,进而拉低整体回答质量。更糟的是,每次交接的 token 消耗可能翻好几倍,还会增加数秒延迟。
领域也几乎无法干净地切分。一个退货流程可能需要订单历史、当前购物车和商品目录,也就是说按领域切子代理要么到处重复这些访问,要么在任务中途反复交接。
随着模型越来越强,它们也能处理更长的上下文、更多的技能和工具,所以今天这些放置规则背后的限制,每一代模型都会放宽一些。
相比之下,agent 技能能给你类似的按领域模块化和上下文控制能力,却不必付交接税,因为技能指令被加载到已经持有全部历史的主 agent 中。
在我们对多个企业部署的对比中,带技能的单 agent 在质量上持续胜过“一个提示词包办一切”的设计,也胜过子代理设计,而且单位任务的成本和延迟往往更低。
子代理真正有存在价值的地方,是由编排器把它当作工具调用,去做一件边界清晰、自包含、值得拥有独立上下文窗口的任务。
一个常见生产例子是深度研究子代理:它自己搜索和阅读文档、写代码并运行、遍历数据模型,也会走进死胡同。所有工作发生在一个或多个子代理内部,回到编排器的只有一个紧凑的答案。
另一个例外是某个领域已经有专为自己打造的 agent。如果你的药房或金融服务体验跑着独立的 agent,并有自己的合规面,正确的做法可以是交接(hand-off):由那个 agent 接管任务,用它自己的循环直接与用户协作,直到任务完成。
区别在于谁拥有这段对话。Hand-off 让领域 agent 成为用户的对话对象,而委托仍然由编排器做主,在一个回合内把领域 agent 拉进拉出,每一次往来都在损耗。
The main factor when deciding whether to put a set of instructions within a system prompt or skill is how often the agent will need it. Loading a skill costs a model turn, so anything the agent needs on most turns generally goes in the system prompt.
This does, however, depend on how your traffic is distributed, and what agent behavior your evals show. A good starting point is that anything relevant to a third or more of your traffic, whether anticipated before launch or observed in production, goes in the system prompt, and the rest goes in skills.
If a skill is predictable from a signal you already have, such as the page the user arrived from, we recommend injecting it from the harness before the first model call and skipping the extra turn to load the skill.
Critical instructions, such as safety and legal rules, brand constraints, and key user facts such as allergies, always go in the system prompt.
For commerce agents, this means product search lives in the prompt, since nearly every session touches it, and skills carry the long tail of features.
In our reference implementation, the shopping agent's prompt holds grounding, cart and checkout semantics, presentation rules, and product search, and the following skills cover the rest: search-discovery, purchase-research, planning-goals, customer-care, and memory-personalization.
The merchant agent splits the same way, with performance-insights, catalog-listings, inventory-operations, pricing-promotions, and marketing-campaigns as its skills, one per operational domain.
In the prompt
Shopping agent: Grounding, cart and checkout semantics, presentation rules, and product search.
Shopping skills — The long tail: search-discovery · purchase-research · planning-goals · customer-care · memory-personalization
Merchant skills — One per operational domain: performance-insights · catalog-listings · inventory-operations · pricing-promotions · marketing-campaigns
决定一组指令放进系统提示还是技能,主要看 agent 多久需要用到它一次。加载技能会消耗一个模型回合,所以凡是 agent 大多数回合都需要的东西,一般放进系统提示。
这当然也取决于你的流量分布,以及 eval 展示出来的 agent 行为。一个不错的起点是:与超过三分之一流量相关的内容——无论是上线前预判到还是上线后观测到的——放进系统提示,其余放进技能。
如果某个技能可以从你已有的信号预判出来,比如用户来自哪个页面,我们建议在第一次模型调用前就由 harness 注入,省掉加载技能的那一个回合。
关键指令,比如安全和法律规则、品牌约束,以及过敏原这类关键用户事实,永远放在系统提示里。
对电商 agent 来说,这意味着商品搜索常驻提示词,因为几乎每个会话都会碰到;技能则承担功能的漫长尾部。
在我们的参考实现里,购物 agent 的提示词承载 grounding、购物车与结算语义、展示规则和商品搜索,其余由这些技能覆盖:search-discovery、purchase-research、planning-goals、customer-care 和 memory-personalization。
商家 agent 也是同样的切分方式,每个运营领域一个技能:performance-insights、catalog-listings、inventory-operations、pricing-promotions 和 marketing-campaigns。
提示词内
购物 agent:Grounding、购物车与结算语义、展示规则和商品搜索。
购物技能——长尾:search-discovery · purchase-research · planning-goals · customer-care · memory-personalization
商家技能——每个运营领域一个:performance-insights · catalog-listings · inventory-operations · pricing-promotions · marketing-campaigns
Our post on writing effective tools for agents covers tool design in general. Two points have mattered most in commerce:
Build agent tools on top of your core systems and logic.
A commerce company already has search and ranking, a cart, a preferences and profile store, an inventory system, promotion and campaign engines, sales analytics, and more, each encoding logic tuned over years and seeing signals the model never will.
The agent's tools should call those systems, not reimplement them, and the tool boundary is where their logic ends and the model's judgment takes over.
For example, when the agent calls search_products, the results should arrive already ranked; its job is to decide which results serve the user's goal, how many to show, and how to present them.
Tool results are context.
Return the fields the model reasons with and drop the rest. Image URLs on every search row are the usual offender.
As needed, reshape the raw response inside the tool, including appending a next step when it isn't obvious from the data.
This is especially relevant for error scenarios, where the model benefits from instructions instead of error codes. For example, add an error instruction "Include a product ID when querying availability," instead of a generic 403.
我们上一篇关于为 agent 编写有效工具的文章,覆盖了工具设计的普遍问题。在电商领域,有两点最为关键:
在核心系统和逻辑之上构建 agent 工具。
电商公司通常已经有搜索和排序、购物车、偏好与画像存储、库存系统、促销与营销活动引擎、销售分析等等。每一套系统都沉淀了多年调优的逻辑,并且能看到模型永远看不到的信号。
Agent 的工具应当调用这些系统,而不是重新实现一遍;工具边界就是它们的逻辑结束、模型判断接手的地方。
例如,当 agent 调用 search_products 时,返回结果应该已经排好序;它的工作是决定哪些结果能服务用户目标、展示多少条、以及如何呈现。
工具返回结果是上下文。
只返回模型需要推理的字段,其余丢掉。每条搜索结果都带上图片 URL,是最常见的反面典型。
必要时在工具内部重塑原始响应,包括在数据看不出下一步时补一句下一步建议。
这在错误场景尤其重要:模型更需要指令而不是错误码。例如,加一条错误提示“查询可售性时请带上产品 ID”,而不是返回笼统的 403。
Most commerce agent responses are UI components rather than prose, whether a product carousel, an itinerary, a seat map, or a chart. That means the agent has to emit a schema rather than text.
Teams sometimes start by prompting the model to emit custom tags and parsing them on the client-side. This stops working as the surface grows, because:
The model isn’t as well trained on your markup as it is on tool calls so reliability drops as nested components get added. Well-formed data is not guaranteed just through prompting.
The tag definitions live in the system prompt, so every new component bloats context and every edit risks regressions elsewhere in the prompt.
Past conversations end up stored in a format only your parser can read, so loading history means either parsing raw messages on the client or keeping a second copy in a format that isn't native to the model API.
The pattern that has held up is to make each UI component a tool. The model calls present_products, present_itinerary, or present_plan_comparison with typed arguments; your server validates and enriches the call and emits an event; and your client renders it.
As the components are tool calls, they're already in the messages array in native format, so you don’t need to re-parse when you reload an old conversation. An example presentation-tool contract is illustrated below and in the reference repo.

The tradeoff is streaming granularity. Each top-level argument of a tool call buffers on the server for validation, so the sub-components of a presentation tool arrive in steps even with streaming on. This impacts perceived latency.
To get a token-level stream, set eager_input_streaming: true on the tool definition, which skips the buffering and with it the server-side schema guarantee.
In our evals, schema violations are very rare on Claude Sonnet-class models and up, but wrap the call in a retry for the cases where one slips through.
Presentation tools also give the agent a record of what's on screen. When a customer says "the first hotel" or "the third one down on the left," the layout is in the messages array, in the arguments of the last presentation call.
For that to work, the arguments have to reflect the rendered layout, so structure them the way the UI is structured, as ordered rows and carousels rather than a flat list the client rearranges.
大多数 commerce agent 的响应不是散文,而是 UI 组件,无论是商品轮播、行程、座位图还是图表。这意味着 agent 要输出的是一份 schema,而不是文本。
团队常常一开始会让模型输出自定义标签,再在客户端解析。随着界面变复杂,这种做法会失效,因为:
模型对你的标记语言不像对工具调用那样训练充分,嵌套组件越多,可靠性越差。仅靠提示词无法保证结构良好的数据。
标签定义都放在系统提示里,每加一个新组件都会让上下文膨胀,任何一次修改都可能让提示词的其他部分回退。
过去的会话最终会以一种只有你的解析器能读的格式存储,因此加载历史意味着要么在客户端解析原始消息,要么用模型 API 不原生支持的格式保留第二份副本。
真正经得起考验的做法是:让每个 UI 组件都成为一个工具。模型用带类型的参数调用 present_products、present_itinerary 或 present_plan_comparison;服务端校验并丰富这次调用,发出一个事件;客户端负责渲染。
因为这些组件就是工具调用,它们会以原生格式出现在 messages 数组里,所以重新载入旧对话时不需要二次解析。下面和参考仓库里都有一个展示工具契约的例子。

代价是流式粒度。工具调用的每个顶层参数都会在服务端缓冲以做校验,所以即使开着流式,展示工具的子组件也会分步到达。这会影响感知延迟。
要拿到 token 级流式,就在工具定义上设置 eager_input_streaming: true,它会跳过缓冲,同时也跳过服务端 schema 保证。
在我们的 eval 里,Claude Sonnet 级及以上模型很少违反 schema,但还是建议给调用包一层重试,应对偶尔漏网的情况。
展示工具还能让 agent 记录屏幕上有什么。当客户说“第一家酒店”或“左边往下第三家”时,布局已经在 messages 数组里、在最近一次展示调用的参数中。
要做到这一点,参数必须反映真实渲染出的布局。所以参数结构应该跟 UI 结构一致,用有序的行和轮播来表达,而不是一个让客户端自行重排的扁平列表。
Latency matters in commerce, and consumer surfaces are the least forgiving. However, on agentic surfaces, what we have consistently seen move metrics like retention, engagement, and cart size is the quality of the outcome.
Whether the answer was relevant and the task actually completed was more critical to those metrics as compared to marginal latency gains.
So attack latency on two fronts. Minimize end-to-end latency through good engineering, and pair that with dropping perceived latency (since time spent watching an agent work reads as progress).
Every user has a latency budget, and the techniques below keep the agent inside it without spending intelligence to get there.
Task completion latency is the sum, over model turns, of time to last token plus tool processing. That gives you three levers to work towards: fewer turns, faster tools, and faster tokens. These levers sometimes compete, so the thing to minimize is the sum rather than any one of them.
Fewer turns: Load likely context up front, increase model intelligence, and have the model call independent tools in parallel.
Faster tools: Optimize the tool's own backend, and dispatch tools eagerly as their arguments complete.
Faster tokens: Choose the model and its configuration by sweeping your eval suite.
延迟在电商里很关键,面向消费者的界面最不宽容。但在 agent 型界面上,我们反复看到真正能拉动留存、参与度和购物车金额这类指标的,是结果质量。
答案是否相关、任务是否真正完成,与边际延迟上的细微提升相比,对这些指标的影响更大。
所以要在两条战线同时下手:一方面通过扎实的工程降低端到端延迟;另一方面压低感知延迟,因为用户看着 agent 工作的时间会被理解为进展。
每个用户都有一个延迟预算,下面的技巧让 agent 待在这个预算之内,而且不需要靠消耗更多智能来做到。
任务完成延迟等于各模型回合中“到最后一个 token 的时间 + 工具处理时间”的总和。这样你有三个杠杆可调:更少的回合、更快的工具、更快的 token。这些杠杆有时互相竞争,所以要最小化的是总和,而不是其中任何一项。
更少回合:把可能用到的上下文提前加载好,提高模型智能,并让模型并行调用独立的工具。
更快工具:优化工具自身后端,并在参数流式传完时尽早派发工具。
更快 token:用整套 eval 套件做扫描,来选择模型及其配置。
Query complexity adds turns, and is generally out of your control. Model intelligence and relevant context help the agent get to task completion in fewer turns. Some of our key learnings in this area include:
Load likely context up front. If the user opened the assistant from a product page, or a merchant opened it from a campaign dashboard, put that page's data in the session context. The conversation is likely about it, and answering from context costs no extra turns.
Increase model intelligence. Smarter models can decrease overall turns in the completion of a task as the agent can more efficiently plan and issue its tool calls. That often outweighs their slower tokens. If your queries skew complex, or production shows more than about five turns per task, the faster model is frequently the smarter one. Which one that is depends on your traffic, so choose by sweep, as described under "Choosing the model" below.
Have the model call independent tools in parallel. Commerce use cases often require many operations in parallel: be it searching for multiple products, querying many policy docs, or fetching records from many sources of sales data. Parallel tool ensures multiple independent queries don’t burn additional turns. Prompt the model to call many tools within a turn and return the results in one user message as an array of tool results (see the parallel tool use docs).
查询复杂度会带来更多回合,而这通常不在你的控制范围内。模型智能和相关上下文,能帮助 agent 用更少的回合完成任务。我们在这一块的主要心得包括:
提前加载可能用到的上下文。如果用户是从商品页打开助手,或商家从营销活动仪表盘打开助手,就把那个页面的数据放进会话上下文。对话大概率是围绕它的,直接基于上下文作答不需要额外回合。
提高模型智能。模型越聪明,越能更高效地规划并发出工具调用,从而减少完成任务的总体回合数。这常常比它更慢的 token 速度更重要。如果你的查询偏复杂,或生产数据里每个任务超过大约五个回合,“更快的模型”往往就是“更聪明的那个”。具体是哪一个取决于你的流量结构,所以要像下文“选择模型”里说的那样,用扫描来决定。
让模型并行调用独立工具。电商场景常常需要同时做很多事:搜索多个商品、查询多份政策文档、或者从多个销售数据源取数。并行工具能保证多个互不依赖的查询不会多烧回合。提示模型在一个回合内调用多个工具,并把结果放在一条用户消息里,以工具结果数组返回(见 parallel tool use 文档)。
Optimize the tool's own backend. Sometimes a tool genuinely fans out – a merchant agent with a "get today's snapshot" query reads sales, inventory, and campaign status in three independent calls. But we often see the tool boundary become the place where missing backend logic gets stitched together: an availability check that calls the catalog for the SKU, the inventory service per store, and the fulfillment service for cutoffs, then applies substitution rules and pickup eligibility in the tool's own code before answering. That tool is now overloaded with domain knowledge, hard to keep correct as the rules change, and is carrying logic that should sit in an upstream system. When you find yourself writing that logic in a tool, the fix is one backend endpoint that answers the question, and calling that with an agent tool.
Dispatch tools eagerly. Tool arguments stream out of the model like any other tokens, so the harness can execute each tool’s call as its arguments complete and process it while the model is still streaming other, parallel tools or content blocks. We've seen this take multi-second gaps down to a few hundred milliseconds, and the Claude Agent SDK does it by default. You should prompt the model to emit its slowest call first for maximum latency gains.

优化工具自身的后端。有些工具确实会向外扇出——比如商家 agent 收到“给我今天的快照”这类查询,会用三个独立调用分别读销售、库存和活动状态。但我们经常看到,工具边界变成了拼接缺失后端逻辑的地方:一个可售性检查先去目录查 SKU、按门店查库存服务、再查履约服务的截单时间,然后在工具自己的代码里套用替代规则和门店自提资格,最后才回答。这个工具已经被领域知识压垮:规则一变就很难保持正确,它其实背着应该放在上游系统里的逻辑。当你发现自己正在工具里写这种逻辑,修法是一个能回答这个问题的后端端点,再用 agent 工具去调用它。
尽早派发工具。工具参数像其他 token 一样从模型里流式出来,所以 harness 可以在每个工具调用的参数收齐后立刻执行,并在模型还在流式输出其他并行工具或内容块时处理它。我们看到这能把数秒的间隙压到几百毫秒,Claude Agent SDK 默认就会这么做。你还可以提示模型先发出最慢的那个调用,以获得最大延迟收益。

Perceived latency is the time a user feels until the screen does something. It’s especially critical in consumer-facing use cases where any transaction friction impacts checkout rates and revenue. Two techniques shorten it without touching the model:
Stream components as they form. A rendered commerce response is typically 500–700 output tokens, which without streaming is five or more seconds of a spinner. Send each parameter of a presentation tool to the client as it streams and render the page progressively.
Show the work. While the agent is gathering context, render a short progress line for each step in plain language (for example, "finding hotels near the water"). You can build it from the tool's existing arguments (such as the query for a product search), or add an additional user_facing_message parameter tool that prompts the model to write the line.

The two panels above run the same agent with the same tools and prompt; only the harness differs. Total time is about the same, but the time the user sees something is quite different.
感知延迟是用户感觉到屏幕有反应之前的时间。在面向消费者的场景里它尤其关键,任何交易摩擦都会影响结算率和收入。有两种技巧完全不动模型就能缩短它:
组件边生成边流式发送。一个渲染后的电商响应通常是 500–700 个输出 token;如果不流式,用户就要盯着菊花转五秒以上。把展示工具的每个参数在流式过程中发给客户端,渐进渲染页面。
展示工作过程。agent 收集上下文时,用平实的语言为每个步骤渲染一行简短的进度提示(比如“正在找水边的酒店”)。你可以直接利用工具已有的参数(例如商品搜索的 query),也可以额外加一个 user_facing_message 参数工具,让模型来写这行字。

上面两个面板运行的是同一个 agent、同一套工具和提示词,只有 harness 不同。总耗时间差不多,但用户看到画面内容的时间差别很大。
Prompt caching is your largest cost reduction candidate and commerce traffic is well-suited for it. Cached input token reads cost a tenth of fresh ones, and while cache-writes carry a premium of roughly 1.25x, a cached prefix pays for itself on its second use. In customer facing applications where volume is large, you have a unique opportunity to hit very high cache levels using the cheapest, default 5 minute cache expiration.
The best commerce deployments we've seen run at 90–99% cache hit rates, and that is the range to design for from the start. Our experience has shown cached token reads are also around 1.5 to 2x faster at ~100k tokens, with relatively linear scaling the more tokens there are.
提示缓存是你最大的成本削减机会,电商流量尤其适合。缓存输入 token 的读取价格只有新 token 的十分之一;写入缓存虽然贵大约 1.25 倍,但一个带缓存的前缀第二次使用就回本了。在量很大的面向消费者应用里,你有一个独特机会,用最便宜的默认 5 分钟缓存过期时间打到很高的缓存命中率。
我们见过最好的电商部署跑在 90%–99% 的缓存命中率,从设计第一天就该奔着这个区间去。经验还表明,在约 100k token 的规模下,缓存 token 的读取速度大约也快 1.5 到 2 倍,token 越多,加速基本呈线性扩展。
Caching is prefix-based. A request reads from cache up to the first byte that differs from a previous request, so what matters is not just what is in the context but the order it is in. Think of a request as three segments, ordered by how often they change:
Global: most of the system prompt and tool definitions, identical across every session. This is your warmest cache and, at scale, will likely not expire. Keep it byte-identical across turns and sessions and put a cache breakpoint at its end.
Session: per-user context and conversation history, which differ across sessions but stay stable within one. This segment comes after the global one.
Volatile: anything that changes within a session, such as the current time or the current page. Put it at the very end of the request, either as a tagged block in the newest user turn or, on models that support mid-conversation system messages, as a system-role message appended to the messages array. The most common mistake we see is a timestamp or the current page at the top of the system prompt, which silently breaks the cache on every request.

缓存是按前缀工作的。一个请求从开头读到与上一个请求第一个不同的字节为止,所以重要的不只是上下文里有什么,还有它们的顺序。可以把请求想成三段,按变化频率从低到高排列:
全局段:大部分系统提示和工具定义,每次会话都一样。这是最热的一段缓存,量大了以后基本上不会过期。跨回合、跨会话都要保持字节一致,并在这一段末尾放一个缓存断点。
会话段:每个用户自己的上下文和对话历史,跨会话不同,但在一次会话内保持稳定。这段放在全局段之后。
易变段:会话内会变化的任何内容,比如当前时间或当前页面。把它放在请求最末端,可以作为最新用户回合里的 tagged 块;在支持会话中途系统消息的模型上,也可以作为 system 角色消息追加到 messages 数组里。我们见过最常见的错误,是把时间戳或当前页面放在系统提示开头,这会让每次请求都悄悄打不中缓存。

There are two implementation details to remember here. First, skills should be loaded as tool results rather than appended to the system prompt. The skill body then lands in the conversation prefix and is cached along with it.
Second, roll your breakpoints forward in each turn: a request allows a limited number of breakpoints, so move the newest one to the end of each user turn. Each round then reads the accumulated history, including long tool results such as search responses, from cache.

这里有两个实现细节要记住。第一,技能应该作为工具结果加载,而不是追加到系统提示里。这样技能正文会落进对话前缀,跟着一段一起被缓存。
第二,每一轮都要把断点向前滚动:一个请求允许的断点数量有限,所以把最新断点移到每个用户回合的最末尾。这样每轮都能从缓存里读取累积的历史,包括搜索结果这类很长的工具结果。

Model size and the effort setting are the same tradeoff – intelligence against latency and cost – and you should choose both by measurement:
Pick your metric and your floor. Pick the quality metrics your business runs on (task completion, answer relevance, grounded accuracy), the eval score you won't go below, and your p50 and p99 latency and cost budgets.
Sweep. Run your entire eval suite across every model and effort level you'd consider. We recommend starting at Opus for merchant agents, whose tasks are analysis-heavy, and Sonnet for consumer agents, where latency weighs more. If you have production traffic, weigh the results by your real query mix. Then let the numbers decide. Sometimes Opus 5's lift on cart-driving tasks justifies the cost difference over Sonnet, and sometimes it doesn't.
Read the results carefully. Two things regularly surprise teams. The first is that a prompt is tuned to a model, so a sweep run with one prompt may underperform other models that it wasn't written for. A smaller model usually needs instructions the current model infers on its own, and a larger one will follow instructions to the letter that the smaller one was ignoring. A few rounds of iteration on each candidate's failing cases is a cheap step before ruling any of them out. The second is that a more intelligent configuration sometimes wins on latency (most commonly on p90 and p99) despite slower tokens, because it plans its tool calls better and needs fewer rounds on the most complex requests.
Measure cost per completed task rather than per model call, since a cheaper model that needs more turns, or fails more often, is not cheaper. When the result is close, and the cost fits your per-task economics and latency, choose intelligence. Quality is what drives adoption and retention, and allows for room to build for the next 6 months as models become better.
模型大小和 effort 设置是同一个权衡——用智能换延迟和成本。两者都应该靠测量来决定:
先定指标和底线。选定业务实际依赖的质量指标(任务完成率、答案相关性、有据可依的准确率)、你绝不跌破的 eval 分数,以及 p50、p99 延迟和成本预算。
扫描。在你考虑的每个模型和 effort 档位上跑完整 eval 套件。我们建议商家 agent 从 Opus 起步,因为它的任务偏分析;消费者 agent 从 Sonnet 起步,因为延迟权重更高。如果你有生产流量,就按真实查询分布给结果加权。然后让数字说话。有时 Opus 5 在带动购物车任务上的提升,足以证明比 Sonnet 贵的差价合理;有时不值。
仔细读结果。有两件事常让团队意外。第一,提示词本身是贴着某个模型调出来的:用同一个提示词扫描,可能会让那些并非为它写的模型表现不佳。小一点的模型通常需要明确指令,而当前这个模型自己能推断出来;大一些的模型则会逐字执行小模型原本忽略的指令。在划掉任何一个候选之前,先针对每个候选失败案例做几轮迭代,成本很低。第二,更聪明的配置有时反而在延迟上胜出(最常见的是 p90、p99),尽管它 token 更慢,因为它能更好地规划工具调用,在最复杂的请求上少跑几轮。
要按“每个已完成任务的成本”来算,而不是按每次模型调用的成本:一个更便宜但需要更多回合、或失败率更高的模型并不便宜。当结果接近、成本也符合你的单位任务经济和延迟预算时,选更聪明的那个。质量才是驱动采用和留存的东西,也能为未来 6 个月随着模型变强继续往上盖留出空间。
Lastly, we talk about what gets an agent through production: memory, safety, evals, and scaling the work across an organization.
The relationship and interactions you have with your customers matter. Memory is what lets an agent pick up where the last conversation left off instead of starting from nothing. A shopper who mentioned a nut allergy in March shouldn't have to repeat it in June, and a merchant who checks the same three campaigns every Monday shouldn't have to name them each time. Long-term memory, the facts that should survive across sessions, is a system you build and it has three parts: how facts are stored, how they are written, and how they are read.
Memory belongs in your systems, not in the model.
A flat markdown profile works when profiles are small and the agent is the only reader. Most production commerce agents outgrow it, and the practical replacement is the database you already operate. A fact is a small typed record: a key (such as shoe_size, default_store, preferred_report_cadence), a short value, a category, and the session it came from. Some keys you decide up front and every user gets; the rest the extractor discovers. A database stays queryable as the store grows, lets you build deterministic behavior on specific attributes, and joins to the user data you already have.
For merchant-facing agents, key memory by person rather than by account. Merchant logins are often shared between operators, so each operator needs their own profile, and reads have to respect that operator's permissions: a store manager's agent should not recall a fact a district manager stated.
In the commerce domain, agent memory holds personal data. The facts worth remembering are often the most regulated ones, and the rules between jurisdictions differ. Treat memory as a data-handling design problem and not just a storage one. In practice that means four things:
Decide which types of memories you are willing to hold. Enforce that at the write path, with a validator that every save goes through, rather than in the prompt alone.
Give users a way to see, correct, and delete what is stored. Wire deletion into your account-deletion and data-request flows.
Set a retention period. A preference from a few years ago is likely to be outdated, so a retention period helps keep memory facts fresh.
Memory should be a per-deployment switch. This allows regions that can't take on these obligations to run without it.
最后,我们谈谈是什么让一个 agent 走完生产流程并留在这里:记忆、安全、eval,以及把这项工作扩展到整个组织。
你和客户之间的关系与互动很重要。记忆让 agent 能从上一次对话停下的地方接上,而不是从零开始。三月份提过坚果过敏的购物者,不该到六月再重复一遍;每周一查看同样三个 campaign 的商家,也不该每次都说名字。长期记忆——那些跨会话应该留存的事实——是你自主搭建的一个系统,包含三部分:事实怎么存、怎么写、怎么读。
记忆属于你的系统,不属于模型。
当画像很小、agent 是唯一阅读者时,一份扁平的 markdown 画像够用。但大多数生产级 commerce agent 很快会超出这个模式,更实际的替代品是你已经在运营的数据库。一条事实是结构化的短记录:一个 key(比如 shoe_size、default_store、preferred_report_cadence)、一段短 value、一个 category,以及它来自哪个会话。有些 key 你预先定好,每个用户都会有;其余由抽取器不断发现。数据库能在存储增长后继续保持可查询,能让你基于特定属性构建确定性行为,还能和你已有的用户数据做 join。
面向商家的 agent,记忆要按人来记,而不是按账号。商家登录常常是多个操作员共用,所以每个操作员需要自己的画像,读取时也要尊重该操作员的权限:门店经理的 agent 不应该调出区域经理说过的事实。
在电商领域,agent 记忆装的是个人数据。值得记的事实往往正是监管最严的那些,各司法辖区的规则又不一样。要把记忆当作一个数据处理设计问题,而不只是存储问题。实操上意味着四件事:
决定你愿意保存哪些类型的记忆。在写入路径上强制这件事——用每个保存都要经过的 validator,而不是只靠提示词。
给用户查看、更正和删除所存内容的能力。把删除接入账号删除和数据请求流程。
设定保留期限。几年前的偏好大概率已经过时,保留期限能让记忆事实保持新鲜。
记忆应该做成按部署环境开关的功能。这样无法承担这些义务的地区可以不开启它。
Write memory asynchronously. At the end of each turn, or every few turns in a long session, an agent in a separate thread or process reads the conversation and creates, updates, or deletes facts in the store, keeping its own working context as the session goes on.
It adds nothing to the conversation's latency, and achieved 13% higher fact recall on our internal commerce memory eval suite.
The obvious alternative, a tool the agent calls to save a fact, is the wrong one for a latency-sensitive commerce agent. Every save is a tool call inside a user-facing turn, and unless the whole store is in context, a save needs a read first to update or dedupe, which is a round of its own.
It also puts one more decision in front of the agent on every turn, and in our evals that competition for attention showed up as missed memories.
Separating the extractor also lets you prompt it precisely. It reads only the user's and the assistant's text, never tool results, so a product description or a review can't become a fact about the user. Its prompt says what counts as a fact — a stated size, a dietary constraint, a fulfillment preference, a merchant’s usual materialized views — and what doesn't, such as anything from a listing or a one-off detail.

记忆要异步写入。每个回合结束时(长会话里也可以每隔几回合),让独立线程或进程里的 agent 读一遍对话,在存储里创建、更新或删除事实,同时它自己也随着会话推进维护一份工作上下文。
这样完全不会增加对话延迟,而且在我们内部的电商记忆 eval 套件上,事实召回率提高了 13%。
一个显而易见的替代方案——让 agent 调工具保存事实——对延迟敏感的电商 agent 是错误选择。每次保存都是用户回合内的一个工具调用;而且除非整个存储都在上下文里,否则一次保存还要先读一次才能更新或去重,这本身就是一轮往返。
它还让 agent 每一回合多面对一个决策;在我们的 eval 里,这种注意力竞争表现为漏记。
把抽取器拆出来,你还能更精确地给它写提示。它只读用户和助手的文本,绝不读工具结果,所以商品描述或评论不会变成关于用户的事实。它的提示词会说明什么算事实——说出的尺码、饮食限制、履约偏好、商家常用的物化视图——什么不算,比如来自 listing 的任何内容或一次性细节。

Read memory in three layers.
Always in context: A small fixed set of facts goes into context on every turn: the ones nearly every request depends on, such as a shopper's default store and fulfillment preference, or an operator's store and role.
Pre-fetched per turn: Facts relevant to the current request are pre-fetched per turn from the same signals that pre-load a skill: a shoe search pulls sizes and brand preferences, a campaign question pulls the operator's usual metrics.
Behind a lookup tool: Everything else sits behind a lookup tool.
Since memory is per-user context, all of it goes in the session segment, below the global cache breakpoint.
读取记忆分三层。
常驻上下文:每回合都把一小批固定事实放进上下文——那些几乎每个请求都依赖的事实,比如购物者的默认门店和履约偏好,或操作员的所属门店和角色。
按回合预取:与当前请求相关的事实,每一回合用预加载技能时相同的信号去取:搜鞋时拉出尺码和品牌偏好,问 campaign 时拉出该操作员常用的指标。
查询工具兜底:其余一切放在一个 lookup 工具后面。
由于记忆属于用户上下文,它全部放在会话段里,位于全局缓存断点之下。
The prompt is where safe behavior starts, but in commerce it can't be where safety is enforced. The failures are financial and often irreversible, and a prompt rule is one injection or one bad sample away from being skipped. Every rule below is enforced in code, on both the consumer and the merchant agent, and defined once so every runtime shares it.
The model stages; a person or a policy applies
No model tool call moves money or changes the business. Order placement, payments, refunds, price changes, and campaign launches all end in an action the harness controls instead of the model.
On the consumer side this is structural: the checkout tool renders the cart with a button to place the order, and the backend interface the agent calls has no charge method at all.
On the merchant side, every write tool produces a staged change with a server-generated ID, and apply_change succeeds only for IDs that have been approved through a real surface: a button in the operator's portal, a confirmation in the CLI, or the platform's own tool-approval prompt when the agent runs on Managed Agents.
The guardrails are re-checked at apply time against current limits, not the limits in force when the change was staged. Whatever the surface, the shape is the same: the model's most dangerous action is to propose, and the approval routes through the maker-checker flow your business already uses for that kind of change.
安全行为始于提示词,但在电商领域,不能把安全寄托在提示词上。一旦出错就是金钱损失,而且往往不可逆;一条提示规则离被绕过,只差一次注入或一个坏样本。下面每一条规则都用代码在消费者 agent 和商家 agent 两侧强制实施,并且只定义一次,让每个运行环境共用。
模型只是暂存,真正的执行要由人或策略批准。
没有任何模型工具调用能直接移动资金或改变业务。下单、支付、退款、改价、启动 campaign,最终都落在 harness 控制的行为上,而不是模型手里。
消费者侧,这是结构性的:结账工具渲染购物车,上面有“下单”按钮,而 agent 能调用的后端接口根本没有 charge 方法。
商家侧,每个写工具都产出带服务端生成 ID 的暂存变更;apply_change 只对已经通过真实界面批准的 ID 生效:操作员门户里的按钮、CLI 里的确认,或在 Managed Agents 上运行时平台自己的工具批准提示。
guardrail 在 apply 时按当前限额重新检查,而不是按暂存变更时的限额。不管什么界面,形状都一样:模型最危险的行为只是“提议”,审批走你业务对这类变更早就在用的 maker-checker(经办/复核)流程。
The harness keeps a per-session record of every ID the server has handed the model, and that record is the only key any write or render will accept.
The cart accepts only product IDs the server returned to this session, and the merchant tools accept only listing and campaign IDs the agent has actually read. An ID that arrived any other way — hallucinated, pasted by a user, planted in a review — is refused before the backend sees it.
The same rule covers the UI. Presentation tools take IDs, and the server fills in the product, order, or change records itself, so a card only renders records the server itself filled in.
It covers delegates too: the merchant analysis subagent reads data but never adds to the set of IDs the agent may write to.
For fees, disclosures, and other regulated content, the model chooses which product to disclose and the server supplies every word from approved copy. The same fee fields are on the merchant agent's protected list, so neither side of the counter can change or paraphrase them, and evals check the rendered strings byte for byte.
harness 会按会话维护一份记录,记下服务端交给模型的每一个 ID;任何写入或渲染只接受这份记录里的 ID。
购物车只接受服务端在本会话返回过的商品 ID;商家工具只接受 agent 真正读过的 listing 和 campaign ID。以任何其他方式进来的 ID——幻觉出来的、用户粘贴的、藏在评论里的——在后端看到之前就会被拒绝。
同样的规则也覆盖 UI。展示工具接收 ID,由服务端自己填上商品、订单或变更记录,所以一张卡片只会渲染服务端自己填进去的记录。
它也覆盖被委托的对象:商家分析子代理可以读数据,但绝不会增加 agent 可以写入的 ID 集合。
对于费用、披露和其他受监管内容,模型只决定要对哪个产品做披露,所有措辞都由服务端从已批准的文案里提供。同样的费用字段也在商家 agent 的保护列表上,所以柜台两边都无法改动或改写它们;eval 会逐字节比对渲染出来的字符串。
Capped transactions must hold to repeated requests
Most commerce surfaces cap how many of an item one user can buy — for ticket allocations, promotional pricing, or fraud control — and an agent will retry, rephrase, and parallelize in ways a human clicking a button never did.
The cap is therefore enforced on the line as it would be after the write, so a second "add two more" can't stack past it, and cart writes for one session are serialized so parallel tool calls in a single turn can't combine to exceed it.
Merchant changes are checked the same way against caps on price movement, discount depth, restock size, and campaign budget, plus a list of protected fields no change may touch. The rule generalizes: enforce every limit on the resulting state rather than the request, and serialize writes per session.
Third-party content is sanitized
In commerce most of the context is written by people who aren't you — sellers, reviewers, competitors — so every backend read is untrusted input and goes through one sanitizer.
Every tool result authored by a third party, such as listings, reviews, policies, seller messages, and stored memory, is sanitized and wrapped in a fence with a fixed label before the model sees it.
The sanitizer strips control and bidirectional characters, removes anything that imitates the fence markers, defuses text that imitates a conversation turn or a tool call, and caps the size, which is designed to stop a hostile listing from impersonating the system or filling the context.
The prompt carries the other half of the contract: fenced text is material to report on, never to act on.
限额必须扛得住反复请求
多数电商界面会限制一个用户能买多少件某商品——为了票券分配、促销定价或防欺诈;而 agent 会以人类点按钮绝不会用的方式重试、换说法、并行发起。
因此,限额是在明细行上按写入完成后会变成的状态来强制执行的:再补一句“再加两件”也没法堆过上限;同一会话的购物车写入被串行化,一个回合里的并行工具调用也不能合起来突破上限。
商家变更也用同样方式检查:价格变动幅度、折扣深度、补货数量和 campaign 预算都有上限,另有一份任何变更都不能碰的保护字段清单。这条规则可以推广为:在结果状态上执行每一条限制,而不是在请求上;并且每个会话的写入都要串行化。
第三方内容要消毒
电商里大部分上下文来自与你无关的人——卖家、评论者、竞争对手——所以每个后端读取都是不可信输入,都要经过同一个消毒器。
每一个由第三方生成的工具结果,比如 listing、评论、政策、卖家消息和存储的记忆,在交给模型之前都要消毒,并包在一个带固定标签的 fence 里。
消毒器会去掉控制字符和双向字符,移除任何模仿 fence 标记的内容,化解那些伪装成对话回合或工具调用的文本,并限制长度;这些设计是为了防止恶意 listing 冒充系统或灌满上下文。
提示词承担另一半契约:fence 里的文本是供报告的材料,永远不是要执行的动作。
Anything from a small prompt change to a new tool can change agent behavior in ways that are hard to predict, and the change you're shipping is often not the one that regresses. Evals are how you find that out before you deploy. Our earlier blog post on evals for agents covers the general practice. This section covers specifics for commerce agents.
The model’s API is stateless, so what the agent outputs is a function of the system prompt, the tools, and the messages array. This means any state a commerce conversation can reach can be constructed directly. So creating an eval case means constructing the test state, appending the test user message, and letting the agent run from there.
Then grade the outcome: the final state and the rendered response, including the arguments of the last write. In most cases, we recommend against grading the path the agent took to get there as such test cases are brittle and restricting.
Simulated-user evals, in which a second model plays the user and a judge grades the whole conversation, are a poor tool for measurement. Two non-deterministic systems interacting need larger samples, cost more per trial, are harder to judge, and produce failures that are hard to attribute. They are useful for finding coverage gaps and for a general vibe check on the agent, so use them to discover cases, then write each case as a snapshot.

Most teams fail to properly test the injected state. A case should encode the preconditions of a failure, not just the task. If a behavior only emerges after a busy first turn with several tool calls, or after a contradiction earlier in the session, a case that starts from a clean state passes on every config and provides no meaningful data.
We've observed most suites to be heavy on such clean-state cases, so make sure a share of yours starts from long, messy, or contradictory histories.
从一个小提示词改动到新工具,都可能在难以预测的方面改变 agent 行为;而且会引发回退的,往往不是你以为的那个变更。Eval 就是让你在部署前发现这一点的手段。我们之前关于 agent eval 的博客文章讲了一般的做法,这一节只讲电商 agent 的专属细节。
模型的 API 是无状态的,所以 agent 的输出是系统提示、工具和 messages 数组的函数。这意味着一次电商对话能到达的任何状态,都可以被直接构造出来。创建一个 eval 用例,就是构造测试状态、追加测试用户消息,然后让 agent 从那里跑起来。
然后对结果打分:最终状态和渲染出的响应,包括最后一次写入的参数。多数情况下,我们不建议给 agent 到达结果的路径打分,因为这样的用例很脆,还会限制行为空间。
模拟用户 eval——让第二个模型扮演用户、一个裁判给整场对话打分——作为测量工具很差劲。两个非确定性系统互动,需要更大的样本、每次试验成本更高、更难评判,产生的失败也很难归因。它更适合用来找覆盖盲区和做整体观感检查。所以用模拟用户来发现用例,然后把每个用例写成快照。

大多数团队没有真正测试注入状态。一个用例应该编码失败的前置条件,而不只是任务本身。如果某个行为只在忙碌的第一个回合、连着几次工具调用之后才出现,或只在会话前面出现过矛盾之后才出现,那么从干净状态开始的用例会在任何配置下都通过,给不出有意义的数据。
我们观察到大多数套件里这种干净状态用例太多,所以一定要让一部分用例从很长、很乱、或互相矛盾的历史开始。
Effective evaluation requires testing both desired and undesired behaviors.
For every positive case, write its negative counterpart: a "should serve" for every "should refuse," a "should just do it" for every "should ask." Missing negatives are the most common gap we find in a suite.
Evaluate for the following:
Core requests that make up the bulk of your traffic, since a failure here affects most sessions. These include simple lookups, multi-constraint requests, product and plan questions, and multi-intent messages. For the questions, check that every price, availability, and attribute traces back to returned data, and that the agent says when data is missing rather than inventing it.
Context-dependent requests, such as references to what is on screen, constraints carried over from earlier turns, and writes against an existing cart. Evaluating memory falls into this bucket as well. Check that memories were extracted, retrieved, and changed the answer.
Safety and brand cases, where a failure costs money or trust. These include attempted injection, attempts to read another user's data, and regulated language, which is checked byte for byte. Split injection into two cases: user-authored injection, where the directive comes from the user's own message, and data-plane injection, where it is planted in product names, reviews, or web snippets that arrive via tool results.
Interface evaluations, to ensure the right component is rendered, item caps are respected, and there are no internal identifiers in user-facing text. Test for timeouts and empty results too.
Requests that belong to multiple capabilities at once. An operator asks "if I mark this down 15%, do I have enough stock to cover the demand?" That is a pricing question and an inventory question together. The right answer stages the markdown with a stock projection attached; the wrong answers do one and skip the other. Evals written per capability won't catch this, because each grades only its own half. Write cases for the requests that need two neighboring capabilities together, and grade both halves of the answer.
Write evals with SMEs and use real incidents
Partner with the subject-matter experts who see the failures firsthand, such as team members in Product, Legal, Merchant Ops, Customer Care, and Category Management, to design test cases. Real failures make the best evals, and 50-100 eval cases per user flow is a good starting point.
Make sure to have a variety of cases, as outlined above. Production transcripts are a great stream for sourcing new cases, especially the tricky ones. Coding agents are good at generating additional cases and adversarial variants. The reference repository includes a Claude Code plugin with an eval-authoring skill built with our recommended approach.
有效的评估需要同时测试期望行为和不期望行为。
每个正向用例,都要写一个反向对应物:每个“应该拒绝”配一个“应该服务”,每个“应该先问”配一个“应该直接做”。缺少负向用例是我们最常在套件里发现的缺口。
请针对以下类别评估:
核心请求:它们构成你流量的大头,这里出问题会影响大多数会话。包括简单查询、多约束请求、产品和套餐问题、多意图消息。对于问题型请求,检查每个价格、可售性和属性都能回溯到返回的数据;数据缺失时 agent 要明说,而不是编造。
依赖上下文的请求:例如引用当前屏幕内容、前面回合带下来的约束、针对已有购物车的写入。评估记忆也归在这一类。检查记忆是否被抽取、被检索,并真的改变了答案。
安全与品牌用例:这类失败损失金钱或信任。包括尝试注入、尝试读取其他用户数据、受监管措辞(逐字节检查)。注入要拆成两类:用户创作型注入(指令来自用户自己的消息)和数据面注入(指令被埋在商品名、评论或通过工具结果到达的网页摘要里)。
界面评估:确保渲染了正确的组件、单品数量上限被遵守、面向用户的文本里没有内部标识符。超时和空结果也要测。
同时属于多个能力的请求。操作员问“如果我打 85 折,库存够不够覆盖需求?”这同时是定价问题和库存问题。正确答案是暂存降价并附上库存预测;错误答案只做一半,跳过另一半。按单一能力划分的 eval 抓不到这种问题,因为每个只给自己那一半打分。要专门为需要两个相邻能力协同的请求写用例,并给答案的两半都打分。
与 SME 一起编写 eval,并使用真实事故
与亲眼见过失败的领域专家合作设计测试用例,比如 Product、Legal、Merchant Ops、Customer Care 和 Category Management 的同事。真实失败是最好的 eval,每个用户流程 50–100 个用例是个不错的起点。
按上面的分类保持用例多样性。生产转录文本是发现新用例的绝佳来源,尤其是那些刁钻案例。编码 agent 很擅长生成额外用例和对抗变体。参考仓库里带一个 Claude Code 插件,内置了按我们推荐方法编写的 eval 编写技能。
In a commerce enterprise the agent is built by many engineering teams. Search, checkout, pricing, marketing tech, customer care, and the catalog platform each own systems the agent depends on, each ships on its own cadence, and each will want to add or change a tool, a skill, or a prompt rule.
Unlike a service, an agent has no strict module boundary protecting the others: a change made by the pricing team shares a context window with checkout.
The tempting fix is to break the system into many subagents, one per business unit. As discussed in Part 1, we recommend against it for quality reasons. Instead, we outline the process for de-risking multi-team collaboration:
Ownership follows the systems. Every skill and tool has a single owner team. For example, pricing owns the promotion tools and the pricing skill, care owns the order and returns tools and the customer-care skill. The shared prompt has a single platform-level owner for the common parts and domain owner for the domain-specific section.
A change ships with its cases and CI runs a set chosen for it. A team contributing a skill also contributes its cases, including the negative cases and the boundary cases against neighboring skills. Running the full suite on every pull request is too slow and too expensive to survive, so build a CI set from it instead. That set will consist of a core set of cases with the highest-traffic requests and every safety case. On top of that, run the cases for whatever the change touched. For a skill, that means its own cases and its neighbors' boundary cases. For a tool, it is every case that calls it. For the shared prompt, it is the full eval suite since everything reads the system prompt. We recommend gating the pass rate over a few trials, and on cache hit rate and cost per turn. It is also a good practice to run the full suite nightly and before every release. Cross-team regressions are caught in these runs.
The agent should also be inside the release calendar. It's one deployment unit, so a bad change reaches every user at once. Roll prompt and skill changes to a canary cohort first, keep a switch that turns off one skill without a deploy, and freeze the agent ahead of peak periods the same way you freeze other systems.
For the human side of this arrangement, see Building effective human-agent teams.
在电商企业里,agent 由许多工程团队共同构建。搜索、结账、定价、营销技术、客户关怀和目录平台各自拥有 agent 所依赖的系统,各自按自己的节奏发布,也都想增加或改动某个工具、技能或提示规则。
与普通服务不同,agent 没有严格的模块边界来保护别人:定价团队做的一个改动,会和结账功能共享同一个上下文窗口。
一个诱人的修法是把系统拆成许多子代理,每个业务单元一个。如第一部分所讨论,出于质量原因我们不推荐。作为替代,我们给出降低多团队协作风险的过程:
所有权跟着系统走。每个技能和工具只有一个归属团队。例如,定价团队拥有促销工具和定价技能,客户关怀团队拥有订单/退货工具和 customer-care 技能。共享提示词有一个平台级 owner 负责公共部分,领域相关段落由领域 owner 负责。
一个改动能上线,要带上它的用例,CI 跑一个为它挑选的集合。贡献技能的团队也要贡献它的用例,包括负向用例和与邻近技能的边界用例。每个 pull request 都跑全量套件太慢、太贵,活不下来,所以要从中抽取一个 CI 集合:包含流量最大请求的核心用例,加上所有安全用例;在此基础上,凡改动碰过的东西,也要跑它的用例。改技能,就跑技能自己的用例和邻居的边界用例;改工具,就跑每个会调用它的用例;改共享提示词,就要跑整个 eval 套件,因为所有东西都读系统提示。我们建议把多次试验的通过率、缓存命中率和每回合成本作为闸门。另外,比较健康的做法是每晚和每次发版前跑一遍全量套件。跨团队回退都会在这样几轮里被抓住。
agent 本身也应该放进发布日历。它是一个部署单元,一个坏改动会同时打到所有用户。提示词和技能改动先上 canary 小流量,保留一个不需要发版就能关掉某个技能的开关,并在高峰前像冻其他系统一样冻结 agent。
关于这套安排中人的一面,参见 Building effective human-agent teams。
Most of what this post describes is not about the model. The tools call systems you already run, the skills encode procedures you already follow, the evals are your product requirements doc written as tests, and the harness enforces policy you would enforce for any client. Models will keep improving, and when a better one ships, the architecture we describe adopts it as a config change with an eval sweep. Everything else keeps working.
It is also important to think about your roadmap for product surfaces. The architecture will outlast the chat panel. The same agent can work over voice, and it can proactively act on a fare drop before the user asks. For a team that already has the evals and the tools, those are presentation-layer projects. Further out, some of the traffic to your storefront will come from agents that shop on behalf of users. The same provenance, staging, and approval rules that keep your own agent in bounds are what will let you open your tools to those agents safely.
Commerce has always rewarded making the buying process as smooth as possible. Agents make that a lot easier. Check out the complete reference implementation, with both the consumer and the merchant agent and runnable examples for retail, travel, telecom, and entertainment.
Acknowledgements
Written by Matthew Koen and Ali Shazal. Special thanks to Michael Segner, Rodrigo Olivares, Amandeep Khurana, Aiza Usman, John Lopus and others for their contributions.
这篇帖子讲的绝大部分其实和模型无关。工具调用的是你已经在跑的系统,技能编码的是你本来就在执行的流程,eval 是你的产品需求文档被写成了测试,harness 强制的是你对任何客户端都会执行的政策。模型会持续变好;等更好的模型发布,我们描述的架构只是把它当作一次配置变更,配合一轮 eval 扫描接进来。其他一切照常工作。
还要认真规划产品界面形态的路线图。这套架构会比聊天面板活得更久。同一个 agent 可以跑在语音上,也可以在用户开口前主动对一次降价采取行动。对已经拥有 eval 和工具的团队来说,这些只是表现层项目。再往后看,你店面的一部分流量会来自替用户购物的 agent。那些让自家 agent 不出界的来源追踪(provenance)、暂存和审批规则,也正是未来你能安全地向这些 agent 开放自己工具的东西。
电商一直奖励把购买过程做得尽可能顺滑的人。Agent 让这件事容易得多。去看看完整参考实现吧,里面有消费者 agent 和商家 agent,还有零售、旅行、电信和娱乐的可运行示例。
致谢
作者:Matthew Koen 和 Ali Shazal。特别感谢 Michael Segner、Rodrigo Olivares、Amandeep Khurana、Aiza Usman、John Lopus 等人的贡献。