사용자의 자연어 질문을 받아 벡터 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]
sklearn의cosine_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,review5개 슬롯 각각에 대해 벡터 검색을 수행하고, 결과를 라운드로빈 방식으로 합산해 편향 없이 다양한 후보를 확보한다.
# 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_category→restaurant등)을 타고 최종 식당 코드로 변환한다.
pipeline.py — LangGraph 파이프라인 진입점