电商 Agent 卡在哪一步
用 Claude 搭购物 Agent 看似一句话,真跑起来才发现库存查询、价格比对、支付回调全是坑。本文拆解 anthropics/commerce-agents 三个核心工程问题:tool schema 不一致、上下文爆炸、异步 webhook 丢失,每个问题都给出可运行代码。
anthropics/commerce-agents 是 Anthropic 官方推出的电商 Agent 参考蓝图,仓库里直接给出了从商品浏览、加购、下单到支付回调的完整可运行示例。截至 2026-09-04,这个项目已经在 GitHub Trending 冲到 ⭐1475。它之所以火,是因为过去一年大模型 Agent 在”工具调用”层面已经成熟,但”商业场景”几乎没人提供能直接抄作业的工程模板——commerce-agents 填补了从 demo 到生产的最后一段空白。
问题一:商品检索返回结构混乱,LLM 解析直接崩
现象:Agent 调用 MCP 工具拉商品列表,每个商家返回的 JSON schema 都不一样,Claude 把 price 误读成字符串,把 in_stock 误读成 0/1,整条对话直接进入死循环。
根因:commerce-agents 的解法是用 Zod 在工具边界做一次强类型归一化,把所有外部 payload 折叠成同一个内部 schema,让上游 LLM 只面对稳定结构。
解法:
import { z } from "zod";
const ProductSchema = z.object({
id: z.string(),
title: z.string(),
price_cents: z.number().int().positive(),
currency: z.enum(["USD", "EUR", "CNY"]),
in_stock: z.boolean(),
});
export const searchProducts = tool({
name: "search_products",
input: z.object({ query: z.string(), merchant_id: z.string() }),
output: z.array(ProductSchema),
handler: async ({ query, merchant_id }) => {
const raw = await merchantClient(merchant_id).search(query);
return raw.map(item => ProductSchema.parse(item));
},
});
跑 npm install && npm run dev 就能看到效果:不管接 Shopify、Magento 还是自研 ERP,Agent 看到的永远是一份干净的 Product[],重试率能掉到 1% 以下。
问题二:购物车会话上下文爆炸
现象:用户加 20 件商品后,token 占用从 4k 飙到 60k,单次调用成本翻 6 倍,首 token 延迟也明显上升。
根因:每轮 tool_result 都把整个购物车序列化回填到 messages,重复商品数据累计。
解法:commerce-agents 用 diff 摘要策略,只把”本轮变化”塞进上下文:
type CartDiff = {
added: Product[];
removed: string[];
updated: Array<{ id: string; qty: number }>;
};
function cartDiff(prev: Cart, curr: Cart): CartDiff {
return {
added: curr.items.filter(i => !prev.items.find(p => p.id === i.id)),
removed: prev.items.filter(i => !curr.items.find(p => p.id === i.id)).map(i => i.id),
updated: curr.items
.filter(i => prev.items.find(p => p.id === i.id && p.qty !== i.qty))
.map(i => ({ id: i.id, qty: i.qty })),
};
}
messages.push({ role: "tool", content: JSON.stringify(cartDiff(prevCart, currCart)) });
实测下来,长会话的 token 量能砍掉 70%,单次账单金额直接腰斩。
问题三:结账异步回调丢失,订单卡在 PENDING
现象:用户点击支付,Stripe webhook 几秒后到达,但 Agent 上下文早已被压缩或被新对话覆盖,订单停留在 PENDING 一整晚。
根因:commerce-agents 明确把”对话”和”订单状态机”拆成两个独立服务。LLM 只负责触发 checkout,webhook 单独驱动状态机,两者通过 order_id 解耦。
解法:
// 1. Agent 端:用 prompt caching 稳住 system prompt
await anthropic.beta.messages.create({
model: "claude-opus-4-1",
system: [{
type: "text",
text: SHOPPING_SYSTEM_PROMPT,
cache_control: { type: "ephemeral" },
}],
tools: [searchProductsTool, addToCartTool, checkoutTool],
messages,
});
// 2. Webhook 端:单独跑状态机
app.post("/webhooks/stripe", (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body, req.headers["stripe-signature"], SECRET
);
orderStateMachine.transition(event.data.object.metadata.order_id, event.type);
res.sendStatus(200);
});
这样做的好处是:即使 Agent 会话被截断或重启,订单依然能可靠地走到 PAID 或 FAILED,不会再出现”用户付了钱但系统不知道”的事故。
问题四:多商家租户隔离,A 店库存推给 B 店
现象:Agent 给商家 A 的用户推荐了商家 B 的商品,违反合同,被法务找上门。
根因:tool 调用没绑定 merchant_id,上下文里又塞了多个商家的数据。
解法:commerce-agents 在 tool schema 层强制注入 tenant 字段,并在 handler 里断言:
const searchProducts = tool({
name: "search_products",
input: z.object({
query: z.string(),
merchant_id: z.string().describe("Tenant-bound, set by router"),
}),
handler: async ({ query, merchant_id }, ctx) => {
assert(ctx.user.accessibleMerchants.includes(merchant_id), "tenant violation");
return merchantClient(merchant_id).search(query);
},
});
问题五:怎么验证 Agent 真的能下单成功
现象:本地 demo 跑通,但生产环境失败率高,没法量化,也无法做回归。
根因:commerce-agents 提供 evals/run.ts,跑 200 个真实购物场景(搜、加、删、改、结账、退款),CI 卡阈值。
解法:
npm run eval -- --suite=checkout --model=claude-opus-4-1
跑完会输出 success_rate、avg_tokens、p95_latency 三个核心指标,低于 0.92 的 success_rate 直接 fail pipeline,强制开发者先复盘再上线。
Sources
本文参考:
- anthropics/commerce-agents GitHub README 与
examples/目录 - GitHub Trending 2026-09-04 trending 榜
- Anthropic 官方文档 Tool Use 与 Prompt Caching
- r/ClaudeAI 周讨论:“shipping agents in prod” 置顶帖