개요

사용자의 자연어 질문을 받아 벡터 DB 유사도 검색 또는 엔티티 기반 DB 검색을 수행하고, 검색된 식당 후보를 LLM에 전달하여 최종 추천 답변을 생성하는 RAG(Retrieval-Augmented Generation) 파이프라인이다. 전체 흐름은 LangGraph의 StateGraph로 조율되며, OpenAI 임베딩 모델과 GPT 계열 LLM, SQLite 기반 벡터 DB가 연동된다.


전체 아키텍처 흐름

사용자 질문
    │
    ▼
route_node          ← 질문 유형 판단 (embedding / fixed)
    │
  ┌─┴──────────┐
  ▼            ▼
embedding_     fixed_
slot_node      slot_node    ← 슬롯(JSON) 추출
  └────┬────────┘
       ▼
connector_search_node       ← 슬롯 기반 DB 검색 (벡터 / 키워드)
       ▼
generate_node               ← LLM 최종 답변 생성
       ▼
     answer

파일 구성 및 역할

utils.py — DB 연동 및 벡터 검색 유틸리티

SQLite DB와의 모든 데이터 입출력 및 코사인 유사도 기반 벡터 검색을 담당하는 외부 유틸리티 파일. 임베딩 생성, DB 쿼리, 식당 상세 정보 조회, 검색 결과 반환까지 DB 레이어 전반을 처리한다.

# 텍스트를 OpenAI 임베딩 벡터로 변환
response = client.embeddings.create(model="text-embedding-3-small", input=text)
return np.array(response.data[0].embedding, dtype=np.float32)

OpenAI text-embedding-3-small 모델로 텍스트를 float32 벡터로 변환. DB에 저장된 벡터와 코사인 유사도를 계산하는 기준 벡터가 된다.

# 코사인 유사도로 상위 N개 코드 반환
similarities = cosine_similarity(query_vec.reshape(1, -1), filtered_embs)[0]
top_indices = similarities.argsort()[::-1]

sklearncosine_similarity로 질의 벡터와 DB 내 모든 임베딩을 비교해 유사도 높은 순으로 정렬. 중복 식당 코드는 제거하고 상위 N개를 반환한다.

# embedding 경로: 슬롯별로 유사도 검색 후 라운드로빈 병합
for k in embedding_search_keys:
    codes = search_embedding(k, indict[k], 8)
    rcodes = search_table(k, codes)
    buff.append(rcodes)

category, tag, menu, food, review 5개 슬롯 각각에 대해 벡터 검색을 수행하고, 결과를 라운드로빈 방식으로 합산해 편향 없이 다양한 후보를 확보한다.

# fixed 경로: 상호명/메뉴명/유저명 LIKE 검색 후 관계 테이블 경유
query = f"SELECT {k}_code FROM {table_name} WHERE name LIKE ?"
codes = pd.read_sql(query, conn, params=(f"%{indict[k]}%", ))
buff.extend(search_table(table_name, codes))

엔티티명으로 DB에서 코드를 찾고, search_table의 관계 테이블 체인(rel_restaurant_categoryrestaurant 등)을 타고 최종 식당 코드로 변환한다.


pipeline.py — LangGraph 파이프라인 진입점