카탈로그 혼란에서 실시간 추천으로: LLM과 Neo4j를 사용한 제품 그래프 구축
Source: Dev.to
대부분의 제품 추천 시스템은 기본적으로 고급 키워드 매처에 불과합니다. 클릭 데이터가 수백만 건이라면 그럭저럭 동작하지만, 다음과 같은 상황에서는 완전히 무너집니다:
- 상호작용 데이터가 전혀 없는 새로운 제품을 출시했을 때 📉
- 카탈로그가 일관성 없는 태그와 설명으로 엉망일 때 🤦
- 왜 해당 제품을 추천하는지 설명하고 싶을 때 (단순히 블랙박스 점수를 보여주는 것이 아니라)
저는 LLM과 그래프 데이터베이스를 활용해 제품을 실제로 이해하는 실시간 추천 엔진을 만들었습니다. 핵심 로직은 파이썬 100줄 정도에 불과합니다.
비밀 소스: 제품 분류 체계 + 지식 그래프
사용자 행동만을 의존하는 대신, LLM에게 다음을 이해하도록 가르칩니다:
- 제품이 실제로 무엇인지 (“사무용품”이 아니라 “젤펜” 같은 세분화된 분류)
- 사람들이 함께 구매하는 제품 (예: “젤펜” → “노트”, “펜꽂이” 같은 보완 제품)
이 모든 정보를 Neo4j 그래프 데이터베이스에 저장해 관계를 일급 시민으로 다룹니다. 이제 “이 젤펜과 보완적인 분류를 공유하는 모든 제품을 보여줘” 같은 쿼리를 할 수 있습니다.
실제 사례: 젤펜 문제
누군가 젤펜을 탐색하면 전통적인 추천 시스템은 다음을 보여줄 수 있습니다:
- 같은 카테고리의 다른 젤펜
- 판매량 기반 인기 아이템
- 충분한 데이터가 있다면 “고객이 함께 구매한” 무작위 아이템
우리 방식에서는 LLM이 제품 설명을 분석해 다음을 추출합니다:
- 주요 분류:
gel pen,writing instrument - 보완 분류:
notebook,pencil case,desk organizer
그래프가 이러한 관계를 알게 되므로, 젤펜을 볼 때 노트, 플래너, 정리함 등을 설명 가능한 연결과 함께 제시할 수 있습니다.
아키텍처 (단순화)
Product JSONs → CocoIndex Pipeline → LLM Extraction → Neo4j Graph
1. 제품을 스트림으로 ingest
자동 새로고침이 가능한 폴더의 제품 JSON 파일을 감시합니다:
data_scope["products"] = flow_builder.add_source(
cocoindex.sources.LocalFile(
path="products",
included_patterns=["*.json"]
),
refresh_interval=datetime.timedelta(seconds=5)
)
제품 파일이 변경될 때마다 파이프라인이 업데이트되며, 수동 재빌드가 필요 없습니다.
2. 데이터 정제 및 정규화
원시 JSON을 깔끔한 구조로 매핑합니다:
@cocoindex.op.function(behavior_version=2)
def extract_product_info(product: cocoindex.typing.Json, filename: str) -> ProductInfo:
return ProductInfo(
id=f"{filename.removesuffix('.json')}",
url=product["source"],
title=product["title"],
price=float(product["price"].lstrip("$").replace(",", "")),
detail=Template(PRODUCT_TEMPLATE).render(**product),
)
detail 필드는 LLM에 전달되는 마크다운 형태의 “제품 시트”가 됩니다.
3. LLM에게 무거운 작업을 맡기기
분류 계약을 데이터클래스로 정의합니다:
@dataclasses.dataclass
class ProductTaxonomy:
"""
A concise noun or short phrase based on core functionality.
Use lowercase, avoid brands/styles.
Be specific: "pen" not "office supplies".
"""
name: str
@dataclasses.dataclass
class ProductTaxonomyInfo:
taxonomies: list[ProductTaxonomy]
complementary_taxonomies: list[ProductTaxonomy]
그 다음 LLM을 호출합니다:
taxonomy = data["detail"].transform(
cocoindex.functions.ExtractByLlm(
llm_spec=cocoindex.LlmSpec(
api_type=cocoindex.LlmApiType.OPENAI,
model="gpt-4.1"
),
output_type=ProductTaxonomyInfo
)
)
LLM은 마크다운 설명을 읽고 스키마에 맞는 구조화된 JSON을 반환합니다—파싱 지옥이 없습니다.
4. Neo4j에 지식 그래프 구축
세 가지 요소를 내보냅니다:
- Product 노드:
id,title,price,url - Taxonomy 노드: “gel pen”, “notebook” 같은 고유 라벨
- 관계:
PRODUCT_TAXONOMY와PRODUCT_COMPLEMENTARY_TAXONOMY
product_node.export(
"product_node",
cocoindex.storages.Neo4j(
connection=conn_spec,
mapping=cocoindex.storages.Nodes(label="Product")
),
primary_key_fields=["id"],
)
Neo4j는 기본 키를 기준으로 자동 중복 제거를 수행합니다. 다섯 개의 제품이 모두 “notebook”을 보완 분류로 언급하면, 모두 동일한 Taxonomy 노드에 연결됩니다.
실시간 실행
Postgres(코코인덱스의 증분 처리용)와 Neo4j를 설정한 뒤 다음을 실행합니다:
pip install -e .
cocoindex update --setup main
다음과 같은 출력이 보일 것입니다:
documents: 9 added, 0 removed, 0 updated
그런 다음 http://localhost:7474에서 Neo4j Browser를 열고 실행합니다:
MATCH p=()-->() RETURN p
Boom—전체 제품 그래프가 시각화됩니다.
왜 실제로 동작하는가
- LLM은 텍스트 이해에 뛰어나다 – 스키마와 docstring을 통해 모델에게 복잡한 자연어 해석을 맡깁니다.
- 그래프는 관계에 최적화돼 있다 – 설명 가능한 연결을 얻고, PageRank, 커뮤니티 탐지, 최단 경로 등 그래프 알고리즘을 바로 적용할 수 있습니다.
- 증분 업데이트가 무료 – CocoIndex가 모든 파이프라인을 처리합니다; 제품 파일을 추가하면 그래프가 자동으로 업데이트됩니다.
다음에 만들 수 있는 것들
- 브랜드, 재질, 사용 사례 같은 분류를 별도 노드 타입으로 추가.
- 클릭스트림 데이터를 연결해 엣지 가중치를 부여하거나
FREQUENTLY_BOUGHT_WITH관계를 생성. - 필요에 따라 Ollama(온프레미스 LLM)로 OpenAI를 교체해 완전한 제어권 확보.
- 그래프 알고리즘을 레이어링해 제품 클러스터를 찾거나 트렌드 카테고리를 감지.
직접 해보기
전체 작업 코드는 오픈소스입니다:
👉 CocoIndex Product Recommendation Example
레포지토리에는 다음이 포함됩니다:
- 완전한 플로우 정의
- LLM 추출 연산
- Neo4j 매핑
- 샘플 제품 JSON
LLM‑네이티브 데이터 파이프라인이나 그래프 기반 추천을 실험하고 있다면, 여러분이 만들고 있는 것을 듣고 싶습니다. 댓글을 남기거나 저를 태그해 주세요!
P.S. 이 글이 도움이 되었다면 CocoIndex 레포에 별 ⭐을 주세요.
P.P.S. 파이프라인을 시각적으로 탐색하고 싶다면 CocoInsight (무료 베타)를 사용해 보세요 — 데이터 보관 없이 DevTools처럼 파이프라인을 살펴볼 수 있습니다.