| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Class
- data structure
- XML
- control
- function
- DS
- MIPS
- react
- CSS
- web
- Java
- system
- for
- github
- openai
- python
- Pipelining
- Rag
- computer
- DATAPATH
- architecture
- DB
- mysql
- javascript
- instruction
- html
- Linux
- Algorithm
- AI
- php
- Today
- Total
YYYEJI
[MAC] 의미론적 검색(Semantic Search)을 기반으로 한 RAG 살펴보기 본문
RAG는 사용자의 질문을 검색기로 찾은 문서를 LLM이 답변을 생성해서 유저에게 보여줍니다.
이때 사용하는 검색기!!에 다양한 종류가 있습니다!
1) 의미론적 검색(Semantic Search)
2) 키워드 검색 (Keyword Search)
[MAC] 의미론적 검색(Semantic Search)을 기반으로 한 RAG 살펴보기
RAG는 사용자의 질문을 검색기로 찾은 문서를 LLM이 답변을 생성해서 유저에게 보여줍니다.이때 사용하는 검색기!!에 다양한 종류가 있습니다! 1) 의미론적 검색(Semantic Search)2) 키워드 검색 (Keyword
yyyeji.tistory.com
3) 하이브리드 검색 (Hybrid Search)
[MAC] 하이브리드 검색(Hybrid Search)을 기반으로 한 RAG 살펴보기
RAG는 사용자의 질문을 검색기로 찾은 문서를 LLM이 답변을 생성해서 유저에게 보여줍니다.이때 사용하는 검색기!!에 다양한 종류가 있습니다! 1) 의미론적 검색(Semantic Search)2) 키워드 검색 (Keyword
yyyeji.tistory.com
지금 이 글에서는 의미론적 검색을 기반으로 한 RAG 검색기 코드를 살펴볼 예정입니다!
의미론적 검색(Semantic Search)는
* Vector Store을 기반으로 한 검색 방식으로, 텍스트의 의미적 유사성을 고려하여 검색을 수행하고,
* 임베딩 벡터 간의 유사도를 계산하여 의미적으로 관련성이 높은 문서를 찾아내는 특징이 있고,
* 동의어나 문맥적 의미를 파악할 수 있어 자연어 질의에 효과적입니다!!
* 자연어 질의에 강점을 보이며 기존 키워드 검색의 한계를 보완하고,
* 전통적인 검색 방식과는 달리 의미 기반 매칭으로 더 정확하고 포괄적인 검색 결과를 제공합니다!!
코드를 살펴보겠습니다

벡터 저장소를 초기화시킬게요!
아래 코드는 RAG 파이프라인에서 데이터 로드, 청크 분할 단계를 담당하고 있습니다!
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
import os
from glob import glob
# 데이터 로드
def load_text_files(txt_files):
data = []
for text_file in txt_files:
loader = TextLoader(text_file, encoding='utf-8')
data.extend(loader.load())
return data
# 문장을 구분하여 분할 - 정규표현식 사용 (문장 구분자: 마침표, 느낌표, 물음표 다음에 공백이 오는 경우)
korean_txt_files = glob(os.path.join('data', '*_KR.md'))
korean_data = load_text_files(korean_txt_files)
# 가져온 데이터를 chunk 한다.
text_spilitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base",
chunk_size=300,
chunk_overlap=50,
)
korean_chunks = text_spilitter.split_documents(korean_data)
print("한국어 청크 수:", len(korean_chunks))
TextLoader()를 통해 지정된 파일을 읽어서 LangChain의 Document 객체 리스트로 반환합니다.
UTF-8로 읽기 때문에 한글도 문제 없이 처리됩니다!!
loader.load() 결과를 data 리스트에 누적합니다.
파일 경로는 data 폴더 안에서 _KR.md로 끝나는 파일을 모두 가져오라는 뜻입니다.
RecursiveCharacterTextSplitter()를 통해 문서를 토큰 기준으로 나누되 문장 경계를 고려하고,
하나의 청크가 최대 300토큰까지, 50토큰 씩 겹치게 해서 문맥 손실을 최소화합니다.!!
그리고 text_spilitter.split_documents()를 통해 청크 단위로 쪼갭니다!!
다음은 문서들을 메타데이터와 포맷을 추가하는 작업입니다
from langchain_core.documents import Document
# Document 객체에 메타데이터 추가
korean_docs = []
for chunk in korean_chunks:
doc = Document(page_content=chunk.page_content, metadata=chunk.metadata)
# 메타 타이터 추가
doc.metadata['company'] = '테슬라' if '테슬라' in doc.metadata['source'] else '리비안'
doc.metadata['language'] = 'ko'
doc.page_content = f"<Document>\n{doc.page_content}\n</Document>\n<Source>이 문서는 미국 전기차 회사인 '{doc.metadata['company']}'에 대한 문서입니다.</Source>"
korean_docs.append(doc)
doc 파일에 텍스트와 기존 메타데이터를 그대로 복사해서 새로운 Document를 생성합니다!
원본 문서의 source 경로/이름에 "테슬라"라는 단어가 있으면 '테슬라'로 태그, 아니면 '리비안'으로 태그하게 됩니다.
import json
# korean_docs 파일을 jsonlines 파일로 저장
def save_jsonlines(docs, filename):
with open(filename, 'w', encoding='utf-8') as f:
for doc in docs:
f.write(json.dumps(doc.model_dump_json(), ensure_ascii=False) + '\n')
save_jsonlines(korean_docs, 'data/korean_docs_final.jsonl') # jsonl format으로 file 저장
korean_docs 리스트를 JSON Lines(.jsonl) 형식으로 저장하는 코드입니다!!
다음은 임베딩을 할 예정인데요..!
from langchain_chroma import Chroma
from langchain_openai import AzureOpenAIEmbeddings
# OpenAI Embeddings 모델을 로드
embeddings = AzureOpenAIEmbeddings(model="text-embedding-3-small")
# Chroma 벡터 저장소 생성하기
chroma_db = Chroma.from_documents(
documents=korean_docs,
embedding=embeddings,
collection_name="db_korean_cosine_metadata",
persist_directory="./chroma_db",
collection_metadata = {'hnsw:space': 'cosine'}, # l2, ip, cosine 중에서 선택
)
임베딩 모델은 text-embedding-3-small 모델을 사용했고,
이 모델이 문서를 벡터로 변환해줍니다!
from_documents → 주어진 Document 리스트를 임베딩 후 DB에 넣고,
collection_name → 해당 컬렉션의 이름 (검색 시 구분 가능)
persist_directory →이 기능 사용하면 프로그램 껐다 켜도 데이터 유지
collection_metatdata{'hnsw:space':'cosine'} → 유사도 계산 방식을 코사인으로 결정
이제 벡터 저장소에서 로드합니다
# 벡터 저장소 로드
from langchain_chroma import Chroma
from langchain_openai import AzureOpenAIEmbeddings
embeddings = AzureOpenAIEmbeddings(model="text-embedding-3-small")
chroma_db = Chroma( # 정보 불러오기
collection_name="db_korean_cosine_metadata",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
Chroma DB를 사용할 때도 같은 임베딩 모델을 불러와야 검색 결과가 일관되어야 합니다!!
저장 시 로드 시 모델이 다르면 검색 품질이 깨질수도 있어요 ,,
collection_name → 저장할 때 지정했던 이름 그대로 사용
embedding_function → 불러온 임베딩 모델 연결 (쿼리를 벡터화할 때 필요)
persist_directory → 저장된 DB가 있는 경로를 지정
# 검색기 지정하여 테스트
chroma_k_retriever = chroma_db.as_retriever(search_kwargs={"k": 2}) # 2개 리턴
query = "리비안은 언제 사업을 시작했나요?"
retriever_docs = chroma_k_retriever.invoke(query)
query(질문)을 임베딩을 사용해서 벡터로 바꾸고,
chroma_db에서 코사인 유사도를 기준으로 비슷한 문자 2개(k=2)를 찾아
그 결과 retriever_docs 리스트에 Document 객체로 담게 됩니다!
그리고 출력을 하면!!
print("검색어", query)
print("="*200)
for doc in retriever_docs:
print(doc.page_content)
print("="*200)

이러한 결과를 볼 수 있어요!!!!!!

'인공지능 챗봇' 카테고리의 다른 글
| [MAC] 하이브리드 검색(Hybrid Search)을 기반으로 한 RAG 살펴보기 (4) | 2025.08.06 |
|---|---|
| [MAC] 키워드 검색(Keyword Search)을 기반으로 한 RAG 살펴보기 (4) | 2025.08.06 |
| [MAC] Naive RAG 구현 코드 구현하기 (13) | 2025.08.05 |
| [MAC] 벡터 저장소 기반 RAG 검색기 사용하기 (6) | 2025.08.05 |
| [MAC] 벡터 저장소 FAISS(Facebook AI Similarity Search) 사용한 코드 살펴보기 (2) | 2025.08.05 |