[AI] Redis 를 Langchain + RAG 에 적용해 봤다.

늘 공부하는 괴짜·2025년 6월 4일
0

AI : Langchain (RAG)

목록 보기
27/38

바로 직전에 wsl 에 설치하다가 암걸려 죽을뻔하고 집에 와서 macos 에 다시 도전한다. macos 용 Redis Stack도 존재하고 뭔가 든든하다.

1. Redis Stack 설치

% brew tap redis-stack/redis-stack
% brew install redis-stack

2. redis-stack-server 실행

% redis-stack-server

3. 서버 확인

% redis-cli ping

4. 모듈 확인

% redis-cli module list

5. 필요 패키지 설치

redis 버전을 지정하지 않으면 redis가 없다는 오류를 계속 보게 될 것이다.
redis==5.3.0 을 설치하자.

uv pip install langchain langchain_community langchain-core redis==5.3.0 tiktoken

6. insert redis data

그냥 내가 살고있는 인천을 검색해 봤다. 짜장면이 없다니!

from langchain_text_splitters import CharacterTextSplitter
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.vectorstores import Redis
from langchain_core.documents import Document

import redis

# --- 0. 환경 설정 ---
REDIS_URL = "redis://localhost:6379"
OLLAMA_EMBEDDING_MODEL = "nomic-embed-text"
INDEX_NAME = "incheon_detailed_rag_index"

# --- 1. Redis 모듈 상태 확인 ---
r = redis.Redis(host='localhost', port=6379)
modules = r.execute_command("MODULE LIST")
print("✅ Redis 모듈:")
for m in modules:
    print(f"  - {m[b'name'].decode()} (ver: {m[b'ver']})")

# --- 2. 문서 로드 및 분할 ---
raw_documents = [
    Document(page_content="""인천광역시는 대한민국 서해안에 자리 잡은, 역사와 미래가 공존하는 역동적인 도시입니다.
    서울에서 서쪽으로 약 30km 떨어져 수도권의 핵심 축을 이루고 있으며, 광활한 서해 바다를 품고 있어 일찍이 **국제적인 관문 도시**로서의 역할을 수행해 왔습니다.
    현재 인구 약 300만에 달하는 이 도시는 한국의 경제 발전과 글로벌화를 이끄는 중요한 동력원 중 하나로 손꼽힙니다.

    ---

    ### 지리적 특징과 중요성
    인천은 서해에 면한 지리적 특성 덕분에 **다양한 섬과 해안선**을 가지고 있습니다.
    백령도, 연평도, 덕적도 등 수많은 유인도와 무인도가 점점이 흩어져 있으며, 이들 섬은 천혜의 자연경관과 함께 독특한 역사적, 문화적 가치를 지니고 있습니다.
    특히, 썰물 때 광활하게 펼쳐지는 갯벌은 생태계의 보고이자 귀한 자연유산으로, 국제적으로도 그 중요성을 인정받고 있습니다.
    조수간만의 차가 큰 서해의 특성 때문에 과거에는 대형 선박의 접안에 어려움이 있었으나, 갑문식 도크 건설(예: 인천항 갑문)을 통해 이를 극복하며 항만 기능을 고도화했습니다.

    ---

    ### 역사의 흔적과 근대화의 상징
    인천은 한국 근대화의 여정에서 매우 중요한 의미를 지닙니다.
    1883년 개항 이후, 이곳은 서구 문물이 유입되는 주요 통로가 되었고, 일본, 중국, 서양 각국과의 교류가 활발히 이루어졌습니다.
    **자유공원**은 한국 최초의 근대식 공원으로, 인천항 개항 당시 외국인 거류민들을 위해 조성되었습니다.
    이곳에는 맥아더 장군 동상이 우뚝 서 있어 한국전쟁 당시 인천상륙작전의 역사적 의미를 되새기게 합니다.
    또한, **차이나타운**은 개항기 중국인들이 모여 살기 시작하면서 형성된 한국 최초의 차이나타운으로, 이국적인 풍경과 맛있는 음식으로 관광객들을 유혹합니다.
    월미도는 개항기의 근대사와 한국전쟁의 아픔을 간직한 곳으로, 현재는 유원지와 문화 공간으로 거듭나 많은 사람들의 발길이 이어지고 있습니다.

    ---

    ### 경제와 산업의 중심지
    인천은 **국제공항과 국제항만을 동시에 보유한 세계적인 물류 허브 도시**입니다.
    **인천국제공항**은 세계 최고 수준의 서비스와 인프라를 자랑하며, 연간 수천만 명의 승객과 수백만 톤의 화물을 처리하는 동북아시아의 핵심 공항입니다.
    이 공항은 단순히 교통의 요지를 넘어, 인천을 글로벌 비즈니스와 관광의 중심지로 만드는 데 결정적인 역할을 하고 있습니다.

    또한, **인천항**은 컨테이너 물동량이 꾸준히 증가하며 국가 경제에 기여하고 있습니다.
    이러한 물류 인프라를 기반으로 인천은 **송도국제도시, 청라국제도시, 영종국제도시** 등 3대 경제자유구역을 조성하여 바이오, 첨단 IT, 항공, 물류 등 미래 산업을 육성하고 있습니다.
    특히 송도국제도시는 스마트시티 기술이 적용된 친환경 도시로, 국제기구와 글로벌 기업들이 입주하며 국제 비즈니스의 중심지로 발돋움하고 있습니다.

    ---

    ### 문화와 관광, 그리고 미래
    인천은 과거와 현재, 그리고 미래가 조화롭게 어우러진 문화 도시입니다.
    **다양한 테마를 가진 박물관, 미술관, 공연장** 등이 도시 곳곳에 자리하고 있으며, 매년 다채로운 축제와 행사가 열려 시민들과 방문객들에게 풍성한 즐거움을 선사합니다.
    **소래포구 어시장**에서는 싱싱한 해산물을 맛볼 수 있고, 강화도와 같은 곳에서는 고인돌 유적지 등 역사 깊은 유적들을 탐방하며 한국의 오랜 역사를 경험할 수 있습니다.

    쾌적한 도시 환경을 위한 노력도 지속되고 있습니다.
    넓은 공원과 녹지 공간은 시민들에게 휴식과 여유를 제공하며, 해양 자원을 활용한 워터프론트 개발 등 친수 공간 조성에도 힘쓰고 있습니다.

    인천은 명실상부한 대한민국의 미래 성장 동력입니다.
    국제적인 개방성과 역동성을 바탕으로 끊임없이 변화하고 발전하며, 세계 속의 명품 도시로 도약하기 위한 노력을 멈추지 않을 것입니다.
    """)
]

text_splitter = CharacterTextSplitter(chunk_size=300, chunk_overlap=50)
documents = text_splitter.split_documents(raw_documents)

print(f"\n 문서 로드 완료: 원본 {len(raw_documents)}개 → 청크 {len(documents)}개")

# --- 3. Ollama 임베딩 모델 초기화 ---
print(f"\n Ollama 임베딩 모델 초기화 중: '{OLLAMA_EMBEDDING_MODEL}'")
embeddings = OllamaEmbeddings(model=OLLAMA_EMBEDDING_MODEL)

# --- 4. Redis Vector Store 저장 ---
print(f"\n Redis에 벡터 저장 시작 (index: '{INDEX_NAME}')")
try:
    vectorstore = Redis.from_documents(
        documents=documents,
        embedding=embeddings,
        redis_url=REDIS_URL,
        index_name=INDEX_NAME
    )
    print("Redis 저장 및 인덱싱 완료!")
except Exception as e:
    print(f"Redis 저장 오류: {e}")
    exit()

# --- 5. 유사도 검색 테스트 ---
query = "인천의 국제공항과 항만 역할은?"
print(f"\n유사도 검색 실행 (쿼리: '{query}')")
results = vectorstore.similarity_search(query, k=3)

for i, doc in enumerate(results, 1):
    print(f"\n[결과 {i}]\n{doc.page_content.strip()}")

6-1. 데이터 insert 결과

7. select redis data

from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.chat_models import ChatOllama
from langchain_community.vectorstores import Redis
from langchain.chains import RetrievalQA
import sys

# --- 0. 환경 설정 ---
# Redis 서버 주소
REDIS_URL = "redis://localhost:6379"

# Ollama 모델 설정
OLLAMA_EMBEDDING_MODEL = "nomic-embed-text"
OLLAMA_LLM_MODEL = "exaone3.5:2.4b"

# --- 1. 임베딩 모델 및 LLM 초기화 (Ollama 사용) ---
print(f"\nOllama 임베딩 모델 '{OLLAMA_EMBEDDING_MODEL}' 초기화 중...")
embeddings = OllamaEmbeddings(model=OLLAMA_EMBEDDING_MODEL)

print(f"Ollama LLM 모델 '{OLLAMA_LLM_MODEL}' 초기화 중...")
llm = ChatOllama(model=OLLAMA_LLM_MODEL, temperature=0.7)


# --- 2. Redis 인덱스 스키마 정의 ---
# nomic-embed-text 모델의 임베딩 차원을 정확히 확인하여 설정
# 일반적으로 nomic-embed-text는 768 차원
EMBEDDING_DIMENSION = 768 # 명시적으로 768로 설정되어 있음

# --- 변경된 스키마 정의 방식! ---
# 이 스키마는 LangChain의 RedisVectorStore가 내부적으로 기대하는 형식
schema = {
    # 텍스트 필드 정의
    "text_fields": [
        {"name": "content"}, # 문서 내용이 저장된 필드 이름
        # {"name": "other_metadata_field"} # 추가적인 텍스트 메타데이터 필드가 있다면
    ],
    # 벡터 필드 정의
    "vector_fields": [
        {
            "name": "vector", # 임베딩 벡터가 저장된 필드 이름 (기본적으로 "vector")
            "dims": EMBEDDING_DIMENSION,
            "distance_metric": "COSINE", # 임베딩 생성 시 사용한 거리 측정 방식에 맞춤.
            "algorithm": "FLAT" # 또는 "COSINE", "L2", "IP" 등 (인덱스 생성 시 설정한 값)
        }
    ]
}

# --- 3. Redis Vector Store 연결 ---
# 인덱스 이름은 기존에 사용한 것과 동일
index_name = "incheon_detailed_rag_index"

try:
    print(f"\n데이터를 가져오기 위해 Redis 인덱스 '{index_name}'에 연결 중...")
    vectorstore = Redis.from_existing_index(
        embedding=embeddings,
        redis_url=REDIS_URL,
        index_name=index_name,
        schema=schema
    )
    print(f"기존 Redis 인덱스 '{index_name}'에 성공적으로 연결")

except Exception as e:
    print(f"Redis 인덱스 '{index_name}' 연결 중 오류 발생: {e}")

# --- 4. RAG 체인 구축 ---
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever()
)

# --- 5. 질문 실행 ---
query = "인천의 지리적 특징은 무엇인가요?"
print(f"\n질문: {query}")
response = qa_chain.invoke({"query": query})
print(f"답변: {response['result']}")

7-1. 결과

8. redis 를 그냥 설치할시

8-1. redis 삭제

8-2. redis 재설치(버전 없음)

8-3. insert redis data 시에 오류 발생

8-4. redis==6.0.0 부터 오류발생 확인

5.3.0 버전으로 설치하도록 하자.

결국 redis 버전때문에 또또 개고생 할뻔...

profile
인공지능이라는 옷을 입었습니다. 뭔가 멋지면서도 잘 맞습니다.

0개의 댓글