AI 购物代理
购物代理是一个充当客户私人购物助手的 AI 系统。它理解自然语言请求、搜索产品目录、根据偏好和约束条件提供推荐,并引导客户完成结账。在本课中,你将学习生产级购物代理背后的架构,并构建一个与 Shopify 的 Catalog API 和 Checkout Kit 集成的代理。
购物助手架构
一个设计良好的购物代理不是一个单体提示。它是多个专用组件协同工作的流水线:
核心组件
意图分类器:确定客户想做什么——浏览产品、比较选项、进行购买、询问运费或其他事情。这通常由 LLM 本身通过结构化输出来处理。
产品发现:将自然语言转换为带有适当过滤器和搜索词的 Catalog API 查询。
推荐引擎:根据客户偏好、购买历史和上下文信号对结果进行排序和过滤。
购物车管理器:在对话过程中维护购物车状态,处理添加、删除和数量变更。
结账流程:通过 Checkout Kit 编排购买过程,收集必要的买家信息并处理付款。
使用 Shopify 目录的产品推荐引擎
推荐引擎是你的代理在简单搜索之上增加价值的地方。它将 Catalog API 结果与上下文理解相结合,以呈现合适的产品。
// src/agents/recommendation-engine.ts
import Anthropic from '@anthropic-ai/sdk';
import { CatalogClient } from './catalog-client';
interface CustomerContext {
preferences: string[];
budget: { min: number; max: number } | null;
previousPurchases: string[];
conversationHistory: Message[];
}
interface ProductRecommendation {
productId: string;
title: string;
price: number;
reason: string; // Why this product was recommended
confidence: number;
}
export class RecommendationEngine {
private anthropic: Anthropic;
private catalog: CatalogClient;
constructor() {
this.anthropic = new Anthropic();
this.catalog = new CatalogClient();
}
async recommend(
query: string,
context: CustomerContext
): Promise<ProductRecommendation[]> {
// Step 1: Extract search parameters from natural language
const searchParams = await this.extractSearchParams(query, context);
// Step 2: Query the Catalog API
const rawProducts = await this.catalog.search({
query: searchParams.searchTerms,
filters: {
priceRange: context.budget || undefined,
categories: searchParams.categories,
availability: 'IN_STOCK',
},
limit: 25, // Fetch more than we need for ranking
});
// Step 3: Re-rank results using the LLM
const ranked = await this.rankProducts(rawProducts, query, context);
return ranked.slice(0, 5); // Return top 5 recommendations
}
private async extractSearchParams(
query: string,
context: CustomerContext
) {
const response = await this.anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 500,
messages: [
{
role: 'user',
content: `Extract search parameters from this shopping request.
Customer said: "${query}"
Known preferences: ${context.preferences.join(', ') || 'none'}
Budget: ${context.budget ? `$${context.budget.min}-$${context.budget.max}` : 'not specified'}
Previous purchases: ${context.previousPurchases.join(', ') || 'none'}
Return JSON with: searchTerms (string), categories (string[]), attributes (object)`,
},
],
});
return JSON.parse(response.content[0].type === 'text'
? response.content[0].text
: '{}');
}
private async rankProducts(
products: any[],
originalQuery: string,
context: CustomerContext
): Promise<ProductRecommendation[]> {
const response = await this.anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 2000,
messages: [
{
role: 'user',
content: `You are a personal shopping assistant. Rank these products for the customer.
Customer request: "${originalQuery}"
Preferences: ${context.preferences.join(', ')}
Budget: ${context.budget ? `$${context.budget.min}-$${context.budget.max}` : 'flexible'}
Products:
${products.map((p, i) => `${i + 1}. ${p.title} - $${p.price} - ${p.description}`).join('\n')}
Return a JSON array of objects with: productId, title, price, reason (one sentence explaining why this is a good match), confidence (0-1).
Order by confidence descending.`,
},
],
});
return JSON.parse(response.content[0].type === 'text'
? response.content[0].text
: '[]');
}
}
构建推荐流程时,使用 Promise.all() 并行发起 Catalog API 搜索和任何用户上下文查询(购买历史、保存的偏好)。LLM 重新排序步骤依赖于两者,但数据获取之间是相互独立的。
对话式商务流程
购物对话遵循可预测的模式。针对这些模式进行设计使你的代理感觉自然而非机械。
对话状态机
实现
// src/agents/shopping-agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { RecommendationEngine } from './recommendation-engine';
import { CartManager } from './cart-manager';
import { CheckoutFlow } from './checkout-flow';
type ConversationState =
| 'greeting'
| 'discovery'
| 'browsing'
| 'comparison'
| 'cart'
| 'checkout'
| 'confirmation';
interface AgentState {
conversationState: ConversationState;
cart: CartItem[];
searchContext: any;
recommendations: any[];
}
const SYSTEM_PROMPT = `You are a friendly, knowledgeable shopping assistant. Your job is to help customers find and purchase products they will love.
Guidelines:
- Ask clarifying questions when the request is vague
- Always explain WHY you recommend a product, not just WHAT it is
- Respect budget constraints -- never recommend products above the stated budget
- When showing products, highlight the key differentiators
- Be honest about trade-offs between options
- Guide customers toward checkout when they seem ready, but never be pushy
- If asked about something outside shopping, politely redirect
You have access to the following tools:
- search_products: Search the product catalog
- add_to_cart: Add a product to the shopping cart
- view_cart: Show current cart contents
- begin_checkout: Start the checkout process
- get_product_details: Get detailed information about a specific product`;
export class ShoppingAgent {
private anthropic: Anthropic;
private recommendations: RecommendationEngine;
private cartManager: CartManager;
private checkoutFlow: CheckoutFlow;
private state: AgentState;
private messages: Anthropic.MessageParam[] = [];
constructor(sessionId: string) {
this.anthropic = new Anthropic();
this.recommendations = new RecommendationEngine();
this.cartManager = new CartManager(sessionId);
this.checkoutFlow = new CheckoutFlow();
this.state = {
conversationState: 'greeting',
cart: [],
searchContext: {},
recommendations: [],
};
}
async handleMessage(userMessage: string): Promise<string> {
this.messages.push({ role: 'user', content: userMessage });
const response = await this.anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1500,
system: SYSTEM_PROMPT,
tools: this.getToolDefinitions(),
messages: this.messages,
});
// Process tool calls in a loop until the agent produces a text response
let currentResponse = response;
while (currentResponse.stop_reason === 'tool_use') {
const toolResults = await this.executeTools(currentResponse);
this.messages.push(
{ role: 'assistant', content: currentResponse.content },
{ role: 'user', content: toolResults }
);
currentResponse = await this.anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1500,
system: SYSTEM_PROMPT,
tools: this.getToolDefinitions(),
messages: this.messages,
});
}
const textContent = currentResponse.content.find(
(block) => block.type === 'text'
);
const assistantMessage = textContent?.text || '';
this.messages.push({ role: 'assistant', content: assistantMessage });
return assistantMessage;
}
private async executeTools(
response: Anthropic.Message
): Promise<Anthropic.ToolResultBlockParam[]> {
const toolUseBlocks = response.content.filter(
(block) => block.type === 'tool_use'
);
const results: Anthropic.ToolResultBlockParam[] = [];
for (const tool of toolUseBlocks) {
if (tool.type !== 'tool_use') continue;
let result: string;
switch (tool.name) {
case 'search_products':
const products = await this.recommendations.recommend(
tool.input.query as string,
{ preferences: [], budget: null, previousPurchases: [], conversationHistory: [] }
);
this.state.recommendations = products;
this.state.conversationState = 'browsing';
result = JSON.stringify(products);
break;
case 'add_to_cart':
await this.cartManager.addItem(
tool.input.productId as string,
tool.input.quantity as number
);
this.state.conversationState = 'cart';
result = JSON.stringify(await this.cartManager.getCart());
break;
case 'begin_checkout':
const checkoutUrl = await this.checkoutFlow.initiate(
await this.cartManager.getCart()
);
this.state.conversationState = 'checkout';
result = JSON.stringify({ checkoutUrl });
break;
default:
result = JSON.stringify({ error: 'Unknown tool' });
}
results.push({
type: 'tool_result',
tool_use_id: tool.id,
content: result,
});
}
return results;
}
private getToolDefinitions(): Anthropic.Tool[] {
return [
{
name: 'search_products',
description: 'Search for products in the catalog based on a natural language query',
input_schema: {
type: 'object' as const,
properties: {
query: { type: 'string', description: 'Natural language product search query' },
},
required: ['query'],
},
},
{
name: 'add_to_cart',
description: 'Add a product to the customer shopping cart',
input_schema: {
type: 'object' as const,
properties: {
productId: { type: 'string', description: 'The product variant ID' },
quantity: { type: 'number', description: 'Quantity to add' },
},
required: ['productId', 'quantity'],
},
},
{
name: 'begin_checkout',
description: 'Start the checkout process with the current cart',
input_schema: {
type: 'object' as const,
properties: {},
required: [],
},
},
];
}
}
Checkout Kit 集成
Checkout Kit 弥合了代理的产品推荐与实际购买之间的差距。以下是该集成在各平台上的工作方式。
JavaScript (Web Agents)
// src/checkout/web-checkout.ts
import { CheckoutKit } from '@shopify/checkout-kit-js';
export class WebCheckoutFlow {
private kit: CheckoutKit;
constructor(storeDomain: string, storefrontToken: string) {
this.kit = new CheckoutKit({
domain: storeDomain,
storefrontAccessToken: storefrontToken,
});
}
async createCheckout(cartItems: CartItem[], buyerEmail: string) {
// Create a cart via the Storefront API
const cart = await this.kit.createCart({
lines: cartItems.map((item) => ({
merchandiseId: item.variantId,
quantity: item.quantity,
})),
buyerIdentity: {
email: buyerEmail,
countryCode: 'US',
},
});
return {
cartId: cart.id,
checkoutUrl: cart.checkoutUrl,
totalAmount: cart.cost.totalAmount.amount,
currency: cart.cost.totalAmount.currencyCode,
};
}
}
Swift (iOS Agents)
// Sources/Checkout/ShopifyCheckout.swift
import ShopifyCheckoutKit
class AgentCheckoutManager {
let configuration: Configuration
init(storeDomain: String, storefrontToken: String) {
self.configuration = Configuration(
storeDomain: storeDomain,
storefrontAccessToken: storefrontToken
)
}
func presentCheckout(checkoutURL: URL, from viewController: UIViewController) {
ShopifyCheckoutKit.present(
checkout: checkoutURL,
from: viewController,
delegate: self
)
}
}
extension AgentCheckoutManager: CheckoutDelegate {
func checkoutDidComplete(event: CheckoutCompletedEvent) {
// Track successful purchase for agent analytics
Analytics.track("agent_purchase_completed", properties: [
"orderId": event.orderDetails.id,
"total": event.orderDetails.cart.totalAmount
])
}
func checkoutDidFail(error: CheckoutError) {
// Handle checkout failure gracefully
print("Checkout failed: \(error.localizedDescription)")
}
func checkoutDidCancel() {
// Customer backed out -- agent should follow up
}
}
Shopify 购物车在 24 小时不活跃后会过期。如果你的代理维护长时间运行的对话,始终在发起结账前验证购物车。如果购物车已过期,请从保存的购物车项目重新创建。永远不要假设之前创建的购物车仍然有效。
代理到结账的流水线
从客户意图到完成购买的完整流水线涉及多个步骤,每个步骤都有你的代理必须处理的潜在故障点:
在智能商务中,错误不仅仅是技术问题——它们是破碎的客户体验。你的代理必须处理:到 Catalog API 的网络超时、在结账时发现的缺货产品、付款被拒以及运输限制不匹配。对于每个错误,代理都应该清晰地沟通并提供替代的前进路径。
性能优化
购物代理必须快速响应。客户期望简单查询获得亚秒级响应,复杂产品搜索在三秒以内。以下是关键的优化策略:
- 预取热门类别 -- 在本地缓存常见类别中的前 100 个产品
- 流式 LLM 响应 -- 使用流式传输在代理处理时显示"正在输入"
- 并行工具执行 -- 当代理调用多个工具时,并发执行
- 连接池 -- 复用到 Catalog API 和 Checkout Kit 的 HTTP 连接
- 边缘缓存 -- 在边缘部署产品搜索缓存以获得全球性能
继续学习 工作流自动化,了解如何将 AI 代理与 Shopify Flow 结合以实现自动化商务运营。