Skip to main content

소매 및 전자상거래 분야의 AI 에이전트

이 글은 AI 에이전트가 고객 상호작용 자동화, 의사결정 최적화, LLM 기반 벡터 검색을 활용한 제품 추천 고도화를 통해 소매 산업을 어떻게 변화시키고 있는지 살펴봅니다. 이 글은 AI 번역본입니다. 오역이 의심되는 부분이 있으면 댓글로 알려주세요.
Created on September 12|Last edited on September 12
AI가 소매 산업을 변화시키고 있다, 핵심 프로세스를 자동화하여 고객 지원 개인화된 쇼핑 경험으로 이어지고 있습니다. 기업들은 효율을 최적화하고 참여도를 높이며 더 지능적이고 데이터 기반의 상호작용을 제공하기 위해 AI 기반 시스템을 점점 더 활용하고 있습니다. 이러한 기술이 계속 발전함에 따라 온라인과 오프라인을 막론하고 소매업체의 운영 방식과 고객의 쇼핑 경험이 새롭게 정의되고 있습니다.
우리는 어떻게 … 살펴보겠습니다 AI 에이전트 의사결정을 간소화하고 자동화하는 데 활용되고 있으며 워크플로우, 고객 상호작용을 개인화합니다. 또한 고객 문의를 더욱 효율적으로 관리하기 위한 AI 기반 이메일 분류 시스템과 a 추천 엔진 벡터 검색을 활용해 개인화된 상품 추천을 제공하고 LLM-생성 쿼리입니다. 이러한 구현과 함께 소매 분야에서 AI가 미치는 더 넓은 영향, 즉 이점과 과제, 그리고 지능형 자동화의 진화하는 역할을 살펴보겠습니다.


목차



AI 에이전트란 무엇이며, 소매 분야에서 왜 중요할까요?

소매 분야에서 AI 에이전트는 고객 접점과 백엔드 전반의 다양한 프로세스를 자동화하고 고도화하도록 설계된 지능형 시스템입니다. 이들은 챗봇, 음성 기반 어시스턴트, 퍼스널 쇼핑 경험을 구동하며, 고객의 니즈에 실시간으로 적응하는 매끄러운 상호작용을 제공합니다. 소매업체는 이러한 에이전트를 활용해 개인화된 상품 추천, 재고 관리, 고객 지원은 물론 동적 가격 책정 전략까지 수행할 수 있습니다.
소매 분야에서 AI는 기본적인 추천 시스템에서 벗어나, 고객 선호를 예측하고 의미 있는 대화를 나누며 의사 결정을 자동화하는 정교한 컨텍스트 인식 에이전트로 진화해 왔습니다. 이러한 변화는 다음과 같은 발전에 힘입었습니다 자연어 처리메모리 통합과 도구 활용을 통해 AI 에이전트는 데이터 기반 인사이트와 실제 행동 사이의 격차를 메우는 지능형 중개자로 기능할 수 있습니다. 전반적으로 AI 에이전트의 발전 경로는 이들이 비즈니스 운영과 고객 상호작용에 매끄럽게 통합되어 매출과 고객 만족도를 함께 끌어올리는 미래를 시사합니다.
이들이 작동하는 핵심에는 몇 가지 주요 구성 요소가 있습니다:
  • 도구: AI 에이전트는 API, 데이터베이스, 소프트웨어 시스템과 연결되어 상품 카탈로그, 주문 관리 플랫폼, 물류 네트워크에 접근함으로써 매끄러운 운영을 보장합니다.
  • 메모리이들은 고객의 선호와 과거 상호작용을 기억해 개인화된 추천을 제공하고, 더 나은 참여를 이끌며, 일관된 쇼핑 경험을 가능하게 합니다.
  • 연속 학습: AI 에이전트는 고객 행동, 시장 동향, 비즈니스 성과를 분석해 시간이 지날수록 전략을 고도화하며, 정확성과 효과성을 향상합니다.
  • 오케스트��이션: 이러한 에이전트는 복잡한 리테일 프로세스를 관리하며, 주문 처리와 공급업체 조율, 실시간 의사 결정을 자동화해 효율성을 높입니다.
자연어 처리, 멀티모달 AI, 적응형 학습이 계속 발전함에 따라 AI 에이전트는 리테일 운영에 더욱 핵심적인 역할을 하게 되며, 매출을 견인하고 고객 만족도를 높이며 비즈니스 프로세스를 간소화할 것입니다.

리테일러를 위한 AI 에이전트의 핵심 이점

AI 에이전트는 전통적인 시스템의 복잡성을 줄여 리테일 운영을 단순화합니다. 과거에는 리테일러가 시장 변화를 따라가기 위해 수작업 규칙 설정, 대규모 엔지니어링, 잦은 조정을 의존했습니다. 대규모 언어 모델(LLM)로 구동되는 AI 시스템은 데이터를 동적으로 해석하고 인사이트를 생성하며 최소한의 인간 개입으로 의사 결정을 자동화합니다. 이러한 적응력 덕분에 리테일러는 지속적인 재프로그램 없이도 변화하는 트렌드, 고객 행동, 운영 과제에 신속하게 대응할 수 있습니다.
AI의 가장 큰 장점 중 하나는 개인화된 고객 상호작용입니다. AI 기반 추천, 챗봇, 그리고 가상 비서 개인의 선호에 맞춰 제품 추천, 프로모션, 지원 응답을 정교하게 조정합니다. 거래 데이터에만 의존하던 이전의 추천 엔진과 달리, 최신 AI 모델은 탐색 기록, 구매 패턴, 심지어 자연어 상호작용까지 분석해 맥락을 이해한 추천을 제공합니다. 이러한 고도화된 개인화는 전환율을 높이고, 고객 유지율을 개선하며, 브랜드 충성도를 강화합니다.
AI는 또한 반복적인 문의에 대한 응답을 자동화하고 중요한 이슈를 우선순위화함으로써 고객 서비스를 혁신합니다. AI 기반 이메일 분류 시스템은 메시지를 분류하고 긴급도를 평가하며 시급한 사안을 실시간으로 에스컬레이션할 수 있습니다. 챗봇과 보이스 어시스턴트는 일상적인 질문을 처리하여 고객에게 빠르고 정확하며 일관된 지원을 제공하는 동시에, 인간 상담사의 업무 부담을 줄입니다. 이러한 자동화는 응답 시간을 단축하고 운영 비용을 낮추며, 공감과 문제 해결이 필요한 고부가가치 상호작용에 인간 직원이 집중할 수 있도록 해줍니다.
이러한 핵심 기능을 자동화함으로써 AI는 운영 간접비를 줄이고 효율성을 높이며, 리테일 전반에서 데이터 기반 의사 결정을 강화합니다.

AI 에이전트를 구성하는 핵심 ���소와 기술

AI 에이전트는 대규모 언어 모델(LLM), 메모리, 외부 도구, 오케스트레이션 시스템을 활용해 데이터를 처리하고 의사 결정을 내리며 적응합니다. 핵심적으로 LLM은 AI 에이전트가 인간과 유사한 언어를 이해하고 생성하도록 해 주어, 고객 상호작용과 의사 결정에 필수적입니다. 이러한 에이전트는 상호 연결된 시스템 안에서 동작하며, 이를 통해 적응성과 정확성을 보장합니다.
AI 에이전트의 핵심 구성 요소는 다음과 같습니다:
  • 도구 통합: AI 에이전트는 외부 시스템에 연결해 실시간 데이터에 접근하고 고객 문의 응답과 같은 작업을 수행합니다. 예를 들어, Shopify의 API와 통합된 AI 에이전트는 제품 재고 상황을 즉시 확인할 수 있으며, Salesforce에 연결된 에이전트는 고객의 구매 이력을 조회해 개인화된 추천을 제공할 수 있습니다.
  • 메모리: 에이전트가 과거 상호작용을 기억하도록 하여 일관되고 개인화된 응답을 제공할 수 있게 합니다. 예를 들어, 가상 쇼핑 어시스턴트는 이전 세션에서 고객의 선호도를 기억해, 고객이 처음부터 다시 설명하지 않아도 자연스럽게 맞춤형 상품을 추천할 수 있습니다.
  • 오케스트레이션: 여러 AI 기능을 조율해 복잡한 리테일 워크플로를 관리합니다. 여기에는 주문 처리, 공급업체 조율, 실시간 의사 결정이 포함되며, 운영을 간소화하고 수작업을 줄여 줍니다.
  • 관측 가능성: AI 에이전트의 투명성, 디버깅, 지속적 최적화를 보장합니다. Weights & Biases Weave와 같은 플랫폼을 사용하면 리테일러는 응답 정확도를 추적하고 환각을 탐지하며, 실시간으로 AI 행동을 개선해 성능과 신뢰를 동시에 높일 수 있습니다.
이러한 구성 요소가 갖춰지면 AI 에이전트는 더 신뢰할 수 있고 문맥을 이해하며 확장 가능한 자동화를 제공하여 리테일 운영을 혁신하고 고객 경험을 개선합니다.

리테일 에이전트에서 LLM의 역할

대규모 언어 모델은 현대 AI 에이전트의 핵심 동력으로, 언어를 처리하고 응답을 생성하며 실제 상호작용에 유연하게 적응하도록 만들어 줍니다. 전통적인 규칙 기반 AI와 달리, LLM은 딥러닝과 방대한 데이터셋을 활용해 텍스트를 예측하고 이해하며 동적으로 생성합니다. 자연스러운 대화를 처리하고 정보를 요약하며 개인화된 추천을 제공하는 능력 덕분에, AI 에이전트는 단순한 자동화 도구를 넘어 지능적인 의사결정 주체로 발전했습니다.
LLM의 발전으로 리테일 분야에서 AI 에이전트의 반응성과 효율성이 크게 향상되었습니다. 몇샷 학습과 같은 기법은 검색 증강 생성, 그리고 파인튜닝 이러한 모델이 비즈니스 요구에 맞게 적응하고, 개인화된 고객 상호작용을 제공하며, 인간의 개입 없이 지원 업무를 자동화하도록 합니다.
LLM이 계속 발전함에 따라 리테일 기술의 한계를 넓히고, 더 똑똑한 자동화와 한층 깊어진 개인화, 그리고 더욱 효율적인 운영을 이끌 것입니다.

보안, 컴플라이언스, 그리고 확장성

리테일 사용 사례에서는 AI 에이전트가 구매 이력과 결제 정보 등 민감한 고객 데이터를 자주 처리하므로 보안과 규정 준수가 매우 중요합니다. 신뢰를 유지하고 법적 리스크를 피하기 위해, 리테일 기업은 GDPR, CCPA 및 기타 데이터 보호 법규를 준수하고, 암호화, 안전한 저장, 사용자 동의 메커니즘을 구현해야 합니다. 개인화와 같은 영역에서 AI 기반 시스템의 무단 사용을 방지하기 위해서는 엄격한 접근 제어가 필수적입니다. 사기 탐지, 그리고 결제 처리.
규정 준수를 넘어, AI 시스템은 사이버 위협 의사 결정을 훼손할 수 있습니다. 적대적 공격은 모델을 조작해 잘못된 예측을 내리게 만들 수 있으며, 프롬프트 인젝션 허위 정보 유포나 무단 데이터 접근으로 이어질 수 있습니다. 이러한 위험을 완화하려면 실시간 이상 징후 탐지와 자동화된 쿼리 필터링이 중요합니다. AI 모니터링 도구는 비정상적인 행동을 표시하고 이상을 식별해, 보안 침해가 확산되기 전에 차단할 수 있습니다.
신뢰를 구축하려면 AI 에이전트는 투명하고 감사가 가능해야 합니다. 고급 모니터링 시스템은 의사 결정 과정을 추적하고 상호작용을 기록하며, 실시간으로 불일치를 탐지합니다. 리테일 기업은 다른 AI 에이전트를 감독하기 위해 AI 에이전트를 활용해, 응답이 정확하고 정책을 준수하며 환각이 없도록 보장할 수 있습니다. 다음과 같은 플랫폼은 W&B Weave 모델 성능을 추적하고 응답을 개선하며 시스템 신뢰성을 유지해 감독을 강화하세요. 적절한 모니터링이 없으면 AI 에이전트가 오해의 소지가 있는 응답을 생성하거나 편향된 추천을 하고 민감한 정보를 노출해 브랜드 평판을 훼손할 수 있습니다.
확장성도 큰 과제이며, 특히 블랙 프라이데이 같은 피크 세일 기간에 두드러집니다. AI 에이전트는 트래픽 급증을 처리하고, 거래를 처리하며, 재고를 실시간으로 관리해야 합니다. 효율적으로 확장하지 못하면 시스템 지연, 거래 실패, 열악한 고객 경험 위험이 커집니다. RAG(검색 기반 생성) 같은 솔루션은 대규모 모델 쿼리에 대한 의존도를 낮춰 효율을 최적화하고, 응답 속도를 높이며 비용을 절감합니다. 모델 양자화는 정확도를 유지한 채 AI 모델을 압축해 성능을 더욱 끌어올려, 높은 수요 상황에서도 효율적으로 확장할 수 있도록 보장합니다.
대규모로 AI를 도입하는 리테일 기업에게 보안, 컴플라이언스, 성능은 선택 사항이 아니라 필수입니다. AI 기반 시스템은 보안성, 규정 준수, 확장성을 지속적으로 보장할 수 있도록 상시 모니터링되고 최적화되어야 합니다. 이러한 요소가 없다면 보호 장치이러한 보호 장치가 없다면 AI 에이전트는 자산이 아니라 오히려 부담이 되어, 애초에 제공하도록 설계된 효율성과 개인화를 스스로 훼손할 수 있습니다.

튜토리얼: AI로 이메일 우선순위 자동화

리테일 기업은 기술 문제, 결제 분쟁, 기능 요청, 일반 문의 등 방대한 양의 고객 이메일을 처리합니다. 이러한 이메일을 수작업으로 검토하고 분류하는 방식은 비효율적이며, 긴급한 이슈에 대한 응답을 지연시킬 수 있습니다. AI 기반 자동화는 이메일을 분석하고 긴급도를 평가해 가장 시급한 사안을 우선 처리하도록 하여 이 과정을 효율화합니다.
이 튜토리얼에서는 다음과 같은 기능을 갖춘 AI 기반 이메일 우선순위 시스템을 구축합니다:
  • 언어적 단서와 비즈니스 영향도를 분석해 긴급도를 감지하고 HIGH, MEDIUM, LOW 우선순위를 부여합니다.
  • 이메일을 기술 문제, 결제, 연동, 고객 서비스와 같은 사전에 정의된 비즈니스 영역으로 분류합니다.
  • 긴급도와 타임스탬프를 기준으로 이메일의 우선순위를 매기고 순위를 정해, 중요한 이슈가 먼저 처리되도록 합니다.
  • 자주 제기되는 고객 우려 사항에 대한 보고서를 생성하여, 기업이 제품과 서비스를 최적화하도록 돕습니다.
W&B Weave를 통한 실시간 모니터링으로이 시스템은 투명성을 유지하고, 우선순위 부여 로직을 동적으로 개선하며, 리테일 운영에 맞춰 효율적으로 확장됩니다.
다음 코드 AI 기반 시스템이 이메일을 동적으로 분석하고 분류하며 우선순위를 지정하는 방법을 보여줍니다. 이는 다음을 사용합니다 OpenAI의 GPT-4o 추론에는 를, 실시간 모니터링에는 Weave를 사용합니다. 이 튜토리얼은 모의 데이터셋을 사용하지만, 실제로는 API를 통해 실제 받은편지함에 연결해 사용할 수 있습니다.
import json
from datetime import datetime, timedelta
from litellm import completion
import os
import weave; weave.init('retail-email-agent')


os.environ["OPENAI_API_KEY"] = "your api key"


# Mock email database
MOCK_EMAILS = [
{
"id": "1",
"from": "customer@example.com",
"subject": "App keeps crashing",
"body": "The mobile app has been crashing constantly for the past week. This is frustrating as I rely on it for work.",
"timestamp": (datetime.now() - timedelta(hours=2)).isoformat(),
"urgency": None,
"category": None
},
{
"id": "2",
"from": "support@partner.com",
"subject": "Urgent: Service integration issue",
"body": "We're experiencing problems with the API integration. Several customers are affected. Need immediate assistance.",
"timestamp": (datetime.now() - timedelta(hours=1)).isoformat(),
"urgency": None,
"category": None
},
{
"id": "3",
"from": "user123@example.com",
"subject": "Billing overcharge",
"body": "I was charged twice for my monthly subscription. Please refund the extra charge. Your billing system needs better validation.",
"timestamp": (datetime.now() - timedelta(minutes=30)).isoformat(),
"urgency": None,
"category": None
},
{
"id": "4",
"from": "enterprise@bigcorp.com",
"subject": "Missing enterprise features",
"body": "We need better team management features and role-based access control. Currently managing large teams is very manual.",
"timestamp": (datetime.now() - timedelta(minutes=45)).isoformat(),
"urgency": None,
"category": None
}
]

# Urgency levels mapped to descriptors
URGENCY_LEVELS = {
2: "HIGH", # Critical issues, system down, multiple users affected
1: "MEDIUM", # Important but not critical
0: "LOW" # Regular requests, no immediate impact
}

# Categories mapped to integers
CATEGORIES = {
0: "Technical Issues", # App crashes, bugs
1: "Performance", # Speed, reliability
2: "Billing/Pricing", # Payment problems
3: "User Experience", # UI/UX issues
4: "Feature Requests", # New features
5: "Security/Privacy", # Security concerns
6: "Integration", # API issues
7: "Customer Service", # Support general
8: "Documentation", # Help docs
9: "Enterprise" # Large customer needs
}

class EmailSystem:
def __init__(self, model_id="openai/gpt-4o"):
self.emails = MOCK_EMAILS.copy()
self.model_id = model_id
def _run_inference(self, prompt):
"""Run inference using the specified model"""
try:
response = completion(
model=self.model_id,
messages=[{"role": "user", "content": prompt}],
temperature=0.1
)
return response["choices"][0]["message"]["content"].strip()
except Exception as e:
print(f"Inference error: {str(e)}")
return "7" # Default to Customer Service
def get_category(self, email):
"""Get category as integer 0-9"""
categories = "\n".join(f"{k}: {v}" for k, v in CATEGORIES.items())
prompt = f"""Categorize this email into exactly ONE category by responding with ONLY its number (0-9):

Categories:
{categories}

Email subject: {email['subject']}
Email body: {email['body']}

Respond with ONLY a single number 0-9:"""
try:
response = self._run_inference(prompt)
category = int(response)
if category not in CATEGORIES:
return 7 # Default to Customer Service
return category
except:
return 7
@weave.op
def get_urgency(self, email):
"""Get urgency as integer 0-2"""
prompt = f"""Rate this email's urgency with ONLY ONE number:
2: HIGH - Emergency, system down, multiple users affected
1: MEDIUM - Important issue but not critical
0: LOW - Regular request, no immediate impact

Email subject: {email['subject']}
Email body: {email['body']}

Respond with ONLY the number (2, 1, or 0):"""
try:
response = self._run_inference(prompt)
urgency = int(response)
if urgency not in [0, 1, 2]:
return 0
return urgency
except:
return 0

def process_emails(self):
"""Process all emails and return summary"""
urgency_map = {2: "HIGH", 1: "MEDIUM", 0: "LOW"}
results = []
for email in self.emails:
# Get category
category_num = self.get_category(email)
category_name = CATEGORIES[category_num]
# Get urgency
urgency_num = self.get_urgency(email)
urgency_name = urgency_map[urgency_num]
results.append({
"subject": email["subject"],
"category_num": category_num,
"category": category_name,
"urgency_num": urgency_num,
"urgency": urgency_name
})
return results

class UrgencyRater:
def __init__(self, model_id="openai/gpt-4o"):
self.model_id = model_id
def _run_inference(self, prompt):
"""Run inference using the specified model"""
try:
response = completion(
model=self.model_id,
messages=[{"role": "user", "content": prompt}],
temperature=0.1
)
return response["choices"][0]["message"]["content"].strip()
except Exception as e:
print(f"Inference error: {str(e)}")
return "0" # Default to LOW urgency
@weave.op
def rate_urgency(self, email):
"""Rate email urgency from 0-2"""
prompt = f"""Analyze this email's urgency and respond with ONLY ONE number:

2: HIGH URGENCY - Critical issues:
- System down/major outage
- Multiple users/customers affected
- Security incidents
- Significant revenue impact
- Words like "urgent", "emergency", "immediate"

1: MEDIUM URGENCY - Important issues:
- Single user blocked
- Bug affecting functionality
- Billing problems
- Integration issues
- Performance problems

0: LOW URGENCY - Regular requests:
- Feature requests
- Documentation
- General questions
- Non-blocking issues
- Future planning

Email subject: {email['subject']}
Email body: {email['body']}

Respond with ONLY the number (2, 1, or 0):"""
try:
response = self._run_inference(prompt)
urgency = int(response)
if urgency not in URGENCY_LEVELS:
return 0
return urgency
except:
return 0
def batch_rate_emails(self, emails):
"""Rate urgency for a batch of emails"""
results = []
for email in emails:
urgency_num = self.rate_urgency(email)
results.append({
"subject": email["subject"],
"urgency_num": urgency_num,
"urgency": URGENCY_LEVELS[urgency_num],
"timestamp": email["timestamp"]
})
# Sort by urgency (high to low) and then by timestamp
return sorted(results,
key=lambda x: (-x["urgency_num"], x["timestamp"]))

if __name__ == "__main__":
# Test both systems
print("=== Category System Test ===")
category_system = EmailSystem()
category_results = category_system.process_emails()
for r in category_results:
print(f"\nSubject: {r['subject']}")
print(f"Category: {r['category_num']} ({r['category']})")
print(f"Urgency: {r['urgency_num']} ({r['urgency']})")
print("\n=== Urgency Rater Test ===")
urgency_system = UrgencyRater()
urgency_results = urgency_system.batch_rate_emails(MOCK_EMAILS)
for r in urgency_results:
print(f"\nSubject: {r['subject']}")
print(f"Urgency: {r['urgency_num']} ({r['urgency']})")
print(f"Timestamp: {r['timestamp']}")
이 시스템은 LLM을 사용해 고객 이메일을 분류하고 긴급도를 부여하여 처리합니다. 이메일이 도착하면 시스템은 이메일의 제목과 본문을 포함한 프롬프트를 생성해 LLM에 전달합니다. 모델은 카테고리 ID(예: 결제, 기술 문제, 고객 서비스)와 긴급도 점수(LOW, MEDIUM, 또는 HIGH) 미리 정의된 기준에 따라.
The EmailSystem 클래스는 구조화된 프롬프트로 모델을 호출해 분류와 긴급도 점수를 처리합니다. The UrgencyRater 클래스는 긴급도를 나타내는 핵심 문구와 맥락을 감지해 긴급도 분류를 정교화합니다. 분류가 완료되면 이메일은 긴급도와 타임스탬프 기준으로 정렬되어, 결제 분쟁이나 시스템 장애와 같은 시급한 이슈가 즉시 처리되도록 합니다.
효율성을 높이기 위해 시스템은 통합합니다 Weave 실시간 모니터링을 통해 기업이 AI 성능을 추적하고 이상 징후를 감지하며 우선순위 부여 로직을 동적으로 개선할 수 있습니다. 이 튜토리얼은 목업 데이터셋을 사용하지만, API를 통해 실제 수신함에 연결하면 실제 고객 이메일을 자동으로 처리해 수작업을 줄이고 응답 시간을 개선할 수 있습니다.
이메일 분석에 AI를 활용하면 소매업체는 효율적이고 선제적인 고객 서비스를 제공할 수 있으며, 궁극적으로 고객 만족도와 운영 효율을 높일 수 있습니다.

소매업을 위한 AI 기반 상품 추천 시스템

소매업체는 개인화된 추천을 통해 고객 참여와 매출을 높입니다. AI 기반 추천 시스템은 제품 유사성, 탐색 행동, 과거 상호작용을 분석해 관련성이 높은 상품을 제안합니다. 이 튜토리얼에서는 Burberry의 상품 카탈로그를 포함한 목업 소매 웹사이트를 만들고, AI로 구동되는 추천 시스템을 함께 구현합니다.
웹사이트가 어떻게 보일지 보여주는 스크린샷입니다:

웹사이트에서는 사용자가 카테고리를 탐색하고 개별 상품 페이지를 확인하며, 상호작용에 따라 동적으로 생성되는 추천을 받을 수 있습니다. 이를 위해 제품명, 이미지, 가격, 카테고리 정보를 포함한 Burberry의 제품 데이터세트를 사용합니다. 추천 엔진은 사용자의 탐색 행동을 추적하고 대규모 언어 모델을 사용해 검색 쿼리를 생성한 뒤, 벡터 데이터베이스를 통해 유사한 상품을 검색합니다.
이러한 추천을 구현하기 위해 먼저 OpenAI의 text-embedding 모델로 생성한 제품 임베딩을 저장하는 벡터 데이터베이스를 만듭니다. 이를 통해 단순한 키워드가 아니라 의미적 유사성에 기반해 제품을 비교할 수 있습니다. 이후 Flask 기반의 웹 애플리케이션에 통합하여, 사용자가 제품을 탐색하고 개인화된 추천을 받을 수 있도록 하겠습니다.
또한 Weave를 사용해 AI가 추천을 선택하고 순위를 매기는 과정을 추적하여 투명성과 지속적인 최적화를 보장합니다. LLM이 생성한 검색 쿼리, 벡터 기반 유사도 검색, 실시간 추적을 결합함으로써 이 시스템은 매우 유연하고 개인화된 쇼핑 경험을 제공합니다.

벡터 데이터베이스 구축

효과적인 추천을 제공하려면, 시스템은 단순한 키워드 매칭을 넘어 제품을 비교할 수 있는 방법이 먼저 필요합니다. 이를 위해 OpenAI의 text-embedding 모델을 사용해 제품 설명을 임베딩으로 변환합니다. 이렇게 생성된 임베딩은 인메모리 저장소에 보관되며 벡터 데이터베이스, 유사도 기반 검색을 효율적으로 수행할 수 있도록 합니다.
이 과정은 제품 데이터를 로드하고, 제품 설명을 수치 벡터로 변환한 뒤, 메타데이터를 나중에 사용할 수 있도록 저장하는 단계로 이루어집니다. 이렇게 임베딩을 저장해 두면, 정확한 문구 일치가 아니라 의미적 유사성에 기반해 관련 상품을 찾아낼 수 있습니다.
import os
import pandas as pd
import pickle
from datasets import load_dataset
from langchain_openai import OpenAIEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_core.documents import Document

# Set up OpenAI API Key (Replace with your actual API key)
os.environ["OPENAI_API_KEY"] = "your api key"

# Load dataset from Hugging Face
dataset = load_dataset('DBQ/Burberry.Product.prices.United.States')
df = pd.DataFrame(dataset['train'])

# Drop missing values
df = df.dropna(subset=["title", "imageurl", "category2_code", "category3_code", "product_code"])

# Convert product descriptions into embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
documents = [
Document(
page_content=f"{row['title']} - Category: {row['category2_code']} > {row['category3_code']}",
metadata={
"title": row["title"],
"imageurl": row["imageurl"],
"price": row["price"],
"category": row["category2_code"],
"product_code": str(row["product_code"])
}
) for _, row in df.iterrows()
]

# Store only document data (not the full vector store)
with open("documents.pkl", "wb") as f:
pickle.dump(documents, f)

# Save metadata separately
with open("metadata.pkl", "wb") as f:
pickle.dump(df, f)

print("Documents and metadata saved successfully! You can now run the Flask app.")


웹사이트와 추천 시스템 구축

벡터 데이터베이스가 구축되면, 시스템은 개인화된 추천 엔진을 구동합니다.
Flask를 사용해 간단한 프론트엔드를 구축하여 사용자가 상품 카테고리를 탐색하고, 개별 상품 페이지를 확인하며, AI가 생성한 추천을 받을 수 있게 합니다. 이 웹사이트는 Burberry의 상품 카탈로그를 동적으로 구성하여 이미지, 가격, 상세 정보를 직관적인 인터페이스로 보여줍니다. 사용자가 상품을 열람하면, 시스템은 언어 모델을 사용해 검색 쿼리를 생성하고 이를 통해 유사한 상품을 조회합니다. 이 쿼리는 이전에 열람한 모든 상품을 기반으로 하거나 현재 선택한 상품만을 기준으로 생성될 수 있습니다. 이후 시스템은 벡터 데이터베이스를 검색해 가장 관련성 높은 상품을 찾고, 상품 상세 페이지에 표시할 순서로 랭킹하여 개인화되고 끊김 없는 상품 탐색을 보장합니다.
웹사이트 코드는 다음과 같습니다:
from flask import Flask, request, render_template_string
import pickle
import os
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_openai import OpenAIEmbeddings
import random
from langchain.chat_models import ChatOpenAI
import weave; weave.init("retail-agent")

# Initialize LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)

# Set up OpenAI API Key
os.environ["OPENAI_API_KEY"] = "your api key"


app = Flask(__name__)

# Load stored documents (precomputed embeddings)
with open("documents.pkl", "rb") as f:
documents = pickle.load(f)

# Recreate the vector store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = InMemoryVectorStore(embeddings)
_ = vector_store.add_documents(documents)

# Load metadata (original product details)
with open("metadata.pkl", "rb") as f:
df = pickle.load(f)

# Track viewed products
viewed_products = []

# Home page template
home_template = """
<!DOCTYPE html>
<html>
<head>
<title>Product Categories</title>
<style> body { font-family: Arial, sans-serif; } </style>
</head>
<body>
<h1>Product Categories</h1>
<ul>
{% for category in categories %}
<li><a href="{{ url_for('subcategory_page', category=category) }}">{{ category }}</a></li>
{% endfor %}
</ul>
</body>
</html>
"""

# Subcategory page template
subcategory_template = """
<!DOCTYPE html>
<html>
<head>
<title>{{ category }} Subcategories</title>
<style> body { font-family: Arial, sans-serif; } </style>
</head>
<body>
<h1>{{ category }} Subcategories</h1>
<ul>
{% for subcategory in subcategories %}
<li><a href="{{ url_for('product_page', category=category, subcategory=subcategory) }}">{{ subcategory }}</a></li>
{% endfor %}
</ul>
</body>
</html>
"""

# Product listing page template
product_template = """
<!DOCTYPE html>
<html>
<head>
<title>{{ subcategory }} Products</title>
<style>
body { font-family: Arial, sans-serif; }
.product { display: flex; align-items: center; margin-bottom: 20px; }
.product img { width: 100px; margin-right: 10px; }
</style>
</head>
<body>
<h1>{{ subcategory }} Products</h1>
<ul>
{% for product in products %}
<li class="product">
<a href="{{ url_for('product_detail', category=category, subcategory=subcategory, product_id=product['product_code']) }}">
<img src="{{ product['imageurl'] }}" alt="{{ product['title'] }}">
</a>
<div>
<p><strong>{{ product['title'] }}</strong></p>
<p>Price: ${{ product['price'] }}</p>
</div>
</li>
{% endfor %}
</ul>
</body>
</html>
"""
product_detail_template = """
<!DOCTYPE html>
<html>
<head>
<title>{{ product['title'] }}</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.page-container {
display: flex;
gap: 30px;
max-width: 1400px;
margin: 0 auto;
}
.product-container {
flex: 0 0 600px;
position: sticky;
top: 20px;
align-self: flex-start;
}
.product-container img {
width: 100%;
max-width: 500px;
height: auto;
}
.recommendations {
flex: 1;
min-width: 0;
}
.recommendations-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-top: 20px;
}
.recommendation-item {
text-align: center;
}
.recommendation-item img {
width: 100%;
height: 200px;
object-fit: contain;
}
.recommendation-item p {
margin: 5px 0;
font-size: 14px;
}
.back-link {
display: block;
margin-top: 20px;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
h2 {
font-size: 20px;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div class="page-container">
<div class="product-container">
<h1>{{ product['title'] }}</h1>
<img src="{{ product['imageurl'] }}" alt="{{ product['title'] }}">
<p><strong>Category:</strong> {{ product['category2_code'] }} - {{ product['category3_code'] }}</p>
<p><strong>Price:</strong> ${{ product['price'] }}</p>
<a href="{{ url_for('product_page', category=category, subcategory=subcategory) }}" class="back-link">Back to Products</a>
</div>

<div class="recommendations">
<h2>Recommended Products</h2>
<div class="recommendations-grid">
{% for rec in recommendations %}
<div class="recommendation-item">
<a href="{{ url_for('product_detail', category=rec['category'], subcategory=rec['subcategory'], product_id=rec['product_code']) }}">
<img src="{{ rec['imageurl'] }}" alt="{{ rec['title'] }}">
</a>
<p><strong>{{ rec['title'] }}</strong></p>
<p>${{ rec['price'] }}</p>
</div>
{% endfor %}
</div>
</div>
</div>
</body>
</html>
"""
@app.route("/")
def home():
"""Display the main categories."""
categories = df["category2_code"].dropna().unique()
return render_template_string(home_template, categories=categories)

@app.route("/category/<category>")
def subcategory_page(category):
"""Display subcategories under a main category."""
subcategories = df[df["category2_code"] == category]["category3_code"].dropna().unique()
return render_template_string(subcategory_template, category=category, subcategories=subcategories)

@app.route("/category/<category>/<subcategory>")
def product_page(category, subcategory):
"""Display products under a specific subcategory."""
products = df[(df["category2_code"] == category) & (df["category3_code"] == subcategory)][["title", "imageurl", "price", "product_code"]].head(20).to_dict(orient="records")
return render_template_string(product_template, category=category, subcategory=subcategory, products=products)



@app.route("/category/<category>/<subcategory>/<product_id>")
def product_detail(category, subcategory, product_id):
"""Display product details with recommendations from two separate LLM queries."""
product = df[df["product_code"].astype(str) == product_id].iloc[0].to_dict()

# Add product to viewed list
if product not in viewed_products:
viewed_products.append(product)

# Generate two separate recommendation queries
search_query_all_products = generate_recommendation_query(all_products=True)
search_query_current_product = generate_recommendation_query(current_product=product)

recommended_products = []

# Fetch general recommendations from all viewed products
if search_query_all_products:
search_results_all = vector_store.similarity_search(search_query_all_products, k=50)
sampled_results_all = random.sample(search_results_all, min(10, len(search_results_all)))

recommended_products.extend([
{
"title": doc.metadata.get("title", "Unknown Title"),
"imageurl": doc.metadata.get("imageurl", ""),
"price": doc.metadata.get("price", "N/A"),
"category": doc.metadata.get("category2_code", "Unknown"),
"subcategory": doc.metadata.get("category3_code", "Unknown"),
"product_code": doc.metadata.get("product_code", "Unknown")
}
for doc in sampled_results_all
])

# Fetch 5 recommendations similar to the currently viewed product
if search_query_current_product:
search_results_current = vector_store.similarity_search(search_query_current_product, k=5)
recommended_products.extend([
{
"title": doc.metadata.get("title", "Unknown Title"),
"imageurl": doc.metadata.get("imageurl", ""),
"price": doc.metadata.get("price", "N/A"),
"category": doc.metadata.get("category2_code", "Unknown"),
"subcategory": doc.metadata.get("category3_code", "Unknown"),
"product_code": doc.metadata.get("product_code", "Unknown")
}
for doc in search_results_current
])

return render_template_string(product_detail_template, category=category, subcategory=subcategory, product=product, recommendations=recommended_products)

@weave.op
def generate_recommendation_query(all_products=False, current_product=None):
"""Generate search queries for product recommendations using an LLM."""
if all_products and viewed_products:
sampled_products = random.sample(viewed_products, min(3, len(viewed_products)))
product_descriptions = [
f"Product: {p.get('title', '')}, Category: {p.get('category2_code', '')}, Subcategory: {p.get('category3_code', '')}"
for p in sampled_products
]
prompt = f"""
The user has viewed the following products:
{', '.join(product_descriptions)}

Based on these products, generate a concise search query to find similar items with related styles, names, or categories.
You can suggest other product categories that the user might be interested in.
RESPOND ONLY with the query.
"""
elif current_product:
prompt = f"""
The user is currently viewing:
Product: {current_product.get('title', '')}, Category: {current_product.get('category2_code', '')}, Subcategory: {current_product.get('category3_code', '')}

Generate a concise search query to find similar products with related styles, names, or categories.
RESPOND ONLY with the query.
"""
else:
return None

response = llm.invoke(prompt)
return response.content.strip()


if __name__ == "__main__":
app.run(debug=True)

시스템은 추천을 정교화하기 위해 사용자의 탐색 기록을 추적하여, 과거 상호작용이 향후 제안에 반영되도록 합니다. 협업 필터링이나 정적 규칙에만 의존하는 기존 추천 시스템과 달리, 이 시스템은 LLM을 사용해 검색 쿼리를 동적으로 생성합니다. 사용자가 자주 구매한 상품을 단순히 매칭하는 대신, LLM은 사용자가 열람한 상품의 의미를 해석하고 그들의 탐색 행동에 맞춘 검색 쿼리를 생성합니다.
시스템은 세션 내에서 사용자가 열람한 상품을 추적하고, LLM을 사용해 두 가지 별도의 추천 쿼리를 생성합니다. 첫 번째 쿼리는 사용자의 열람 기록에서 무작위로 선택한 최대 세 개의 상품을 기반으로 합니다. LLM은 이들 상품 설명을 활용해 공통된 테마, 스타일, 카테고리를 포착하는 검색 쿼리를 생성합니다. 두 번째 쿼리는 현재 보고 있는 단일 상품만을 바탕으로 생성되어, 해당 상품의 속성에 근거한 정밀한 추천을 보장합니다.
이러한 쿼리가 생성되면, 이를 사용해 상품 데이터베이스에서 벡터 유사도 검색을 수행합니다. 첫 번째 쿼리의 경우, 시스템은 최대 50개의 일치 상품을 가져온 뒤 변화를 주기 위해 그중 10개를 무작위로 선택하여 표시합니다. 두 번째 쿼리의 경우, 순수하게 벡터 유사도에 따라 가장 관련성이 높은 5개를 조회합니다. 이 접근 방식은 일반화(전체 탐색 행동을 기반으로 한 추천)와 특이성(현재 상품을 기반으로 한 추천) 사이의 균형을 맞춰, 더 관련성 높고 개인화된 상품 제안을 보장합니다.
우리 추천 시스템의 핵심 대부분은 명확하게 확인할 수 있습니다 generate_recommendation_query 우리 벡터 데이터베이스를 검색하는 데 사용할 쿼리를 생성하는 함수입니다:
def generate_recommendation_query(all_products=False, current_product=None):
"""Generate search queries for product recommendations using an LLM."""
if all_products and viewed_products:
sampled_products = random.sample(viewed_products, min(3, len(viewed_products)))
product_descriptions = [
f"Product: {p.get('title', '')}, Category: {p.get('category2_code', '')}, Subcategory: {p.get('category3_code', '')}"
for p in sampled_products
]
prompt = f"""
The user has viewed the following products:
{', '.join(product_descriptions)}


Based on these products, generate a concise search query to find similar items with related styles, names, or categories.
You can suggest other product categories that the user might be interested in.
RESPOND ONLY with the query.
"""
elif current_product:
prompt = f"""
The user is currently viewing:
Product: {current_product.get('title', '')}, Category: {current_product.get('category2_code', '')}, Subcategory: {current_product.get('category3_code', '')}


Generate a concise search query to find similar products with related styles, names, or categories.
RESPOND ONLY with the query.
"""
else:
return None


response = llm.invoke(prompt)
return response.content.strip()
이러한 상호작용을 추적하고 시간이 지남에 따라 추천을 정교화하기 위해, 시스템은 검색 쿼리, 사용자 상호작용, 추천 결과를 기록하는 Weave를 통합합니다. 이를 추가함으로써 @weave.op LLM 추론 함수에 데코레이터를 추가하면, Weave 내부에서 함수의 모든 입력과 출력을 추적할 수 있습니다. LLM이 검색 쿼리를 생성할 때마다 Weave가 해당 과정을 기록하여 실시간 모니터링과 조정을 가능하게 합니다. 이러한 추적은 추천 시스템의 투명성을 보장하고, 사용자 선호 패턴을 감지하며, 검색 쿼리를 정교화해 정확도를 향상시키는 데 도움을 줍니다.
웹사이트에掲載된 Weave 트레이스 중 하나의 스크린샷입니다.

LLM을 활용해 검색 쿼리를 생성하면 기존 추천 방식보다 여러 가지 이점이 있습니다. 협업 필터링과 같은 방식은 방대한 사용자 데이터가 필요하고, 신규 혹은 틈새 상품에는 취약합니다. 반면 LLM 기반 접근법은 경직된 패턴이 아니��� 의미에 기반해 추천을 생성하므로 더 유연하고 반응성이 뛰어납니다. 문맥을 이해하는 쿼리를 생성할 수 있어, 탐색 이력이 거의 없는 사용자에게도 관련성 높은 제안을 제공할 수 있습니다. AI와 Weave를 통한 실시간 추적을 활용하면, 소매업체는 적응적이고 통찰력이 있으며 참여와 매출을 효과적으로 이끄는 추천 시스템을 구축할 수 있습니다.

결론

AI 에이전트는 운영을 자동화하고 고객 참여를 강화하며 실시간 개인화 경험을 제공함으로써 소매 산업을 혁신하고 있습니다. 이메일 분류부터 지능형 추천 시스템까지, AI 기반 솔루션은 프로세스를 간소화하고 의사 결정을 고도화하며 매끄러운 고객 상호작용을 구현합니다. 고정된 규칙 기반 시스템에서 적응형 AI 에이전트로의 전환을 통해 소매업체는 민첩성을 유지하고 최소한의 인력 개입으로 워크플로를 최적화할 수 있습니다.
이 프로젝트의 AI 기반 추천 시스템은 벡터 검색, LLM 생성 쿼리, 그리고 Weave를 통한 실시간 추적으로 고도로 개인화된 쇼핑 경험을 제공할 수 있습니다. 사용자 행동에 대한 이해를 지속적으로 정교화함으로써, AI는 상품 탐색을 개선하고 고객 만족도를 높입니다.
AI 기술이 발전함에 따라 소매 업계에 미치는 영향은 더욱 커질 것입니다. 대화형 AI와 예측 분석부터 자율형 쇼핑 어시스턴트에 이르기까지, AI 기반 솔루션은 고객이 브랜드와 상호작용하는 방식을 재정의할 것입니다. 이러한 혁신을 수용하는 소매업체는 효율성과 매출을 높이는 데 그치지 않고, 개인화되고 지능적인 쇼핑 경험의 새로운 기준을 세우게 될 것입니다.


이 글은 AI 번역본입니다. 오역이 의심되는 부분은 댓글로 알려주세요. 원문 보고서는 다음 링크에서 확인할 수 있습니다: 원문 보고서 보기