Skip to main content

AI Shopping Agents

A shopping agent is an AI system that acts as a personal shopper for customers. It understands natural language requests, searches product catalogs, makes recommendations based on preferences and constraints, and guides the customer through checkout. In this lesson, you will learn the architecture behind production-grade shopping agents and build one that integrates with Shopify's Catalog API and Checkout Kit.

쇼핑 어시스턴트 아키텍처

A well-designed shopping agent is not a single monolithic prompt. It is a pipeline of specialized components that work together:

핵심 컴포넌트

Intent Classifier: Determines what the customer wants to do -- browse products, compare options, make a purchase, ask about shipping, or something else. This is typically handled by the LLM itself with structured output.

Product Discovery: Translates natural language into Catalog API queries with appropriate filters and search terms.

Recommendation Engine: Ranks and filters results based on customer preferences, purchase history, and contextual signals.

Cart Manager: Maintains shopping cart state across the conversation, handling additions, removals, and quantity changes.

Checkout Flow: Orchestrates the purchase process through Checkout Kit, collecting necessary buyer information and processing payment.

Shopify 카탈로그를 사용한 상품 추천 엔진

The recommendation engine is where your agent adds value beyond simple search. It combines Catalog API results with contextual understanding to surface the right products.

// 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
: '[]');
}
}
Reduce Latency with Parallel Requests

When building recommendation flows, fire the Catalog API search and any user-context lookups (purchase history, saved preferences) in parallel using Promise.all(). The LLM re-ranking step depends on both, but the data fetches are independent of each other.

대화형 커머스 흐름

A shopping conversation follows predictable patterns. Designing for these patterns makes your agent feel natural rather than robotic.

대화 상태 머신

구현

// 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 bridges the gap between your agent's product recommendations and actual purchases. Here is how the integration works across platforms.

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
}
}
Cart Expiration

Shopify carts expire after 24 hours of inactivity. If your agent maintains long-running conversations, always validate the cart before initiating checkout. If the cart has expired, recreate it from the saved cart items. Never assume a previously created cart is still valid.

에이전트-결제 파이프라인

The complete pipeline from customer intent to completed purchase involves several steps, each with potential failure points that your agent must handle:

Error Handling is Critical

In agentic commerce, errors are not just technical problems -- they are broken customer experiences. Your agent must handle: network timeouts to the Catalog API, out-of-stock products discovered at checkout time, payment declines, and shipping restriction mismatches. For every error, the agent should communicate clearly and offer an alternative path forward.

성능 최적화

Shopping agents must respond quickly. Customers expect sub-second responses for simple queries and under three seconds for complex product searches. Here are key optimization strategies:

  1. Pre-fetch popular categories -- Cache the top 100 products in common categories locally
  2. Stream LLM responses -- Use streaming to show the agent "typing" while it processes
  3. Parallel tool execution -- When the agent calls multiple tools, execute them concurrently
  4. Connection pooling -- Reuse HTTP connections to the Catalog API and Checkout Kit
  5. Edge caching -- Deploy product search caches at the edge for global performance
Next Steps

Continue to Workflow Automation to learn how to combine AI agents with Shopify Flow for automated commerce operations.