hoony's web study

728x90
반응형

요즘은 정말 AI 가 핫합니다. AI 월세라는 말이 있을 정도로 많은 사람들이 경험하고 사용하기 위해서 비용을 내고 있는것이 현실이기도 하구요. 
저는 AI 에 대해서 잘은 모르는 개발자입니다. 
딥러닝이라더지 학습이라는 의미도 잘 모르지만 문득 로컬에서 이런것도 해보는것이 어떨까해서 생성해본거에요. 
물론 저도 AI와 함께 하면서 이것을 만들었답니다. 
문득 생각이 든 것은 요즘은 이런것도 AI 를 통해서 공부하면서 해볼 수 있는 좋은 세상이고 이것을 잘 활용한다면 좋은 성과물이 나올것 같아요. 

이번 토이프로젝트는 PDF 문서를 기반으로 자연어 질문을 던지고 PDF 문서의 내용을 VectorDB에  Indexing 을 통해서 사용자의 질의에 대해서 대답할 수 있는 아주 간단한 것입니다. 
스터디용으로 생성한 것이고 아시는 분들은 너무 간단하게 생각할 수 있는 프로젝트입니다. 

기술스택 

LlamaIndex: 문서를 인덱싱하고 의미 기반 검색을 가능하게 하는 프레임워크.

ChromaDB: 문서 임베딩을 저장하는 벡터 데이터베이스.

Ollama: Mistral과 Nomic-Embed-Text 같은 LLM을 로컬에서 실행.

Streamlit: 직관적인 웹 인터페이스를 위한 파이썬 라이브러리.

Python: 모든 구성 요소를 연결, uv로 의존성 관리.

프로젝트 특징 

모든 것을 로컬에서 처리 및 무료로 구성하였습니다. 
Ollama 에 Nomic-Embed-Text LLM을 로컬에서 실행을 한 이유는  LlamaIndex가 기본적으로 chatGpt를 사용하고 있어서 API Key를 사용하게 되어서 이 모델을 로컬에서 실행을 했답니다. 

1단계 : 의존성 

uv를 사용해 의존성 관리를 위해 pyproject.toml의 내용은 아래와 같습니다.

[project]
name = "rag-ollama-app"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
    "llama-index==0.10.32",
    "chromadb==0.5.23",
    "llama-index-vector-stores-chroma",
    "llama-index-llms-ollama",
    "llama-index-embeddings-ollama",
    "pdfminer.six",
    "streamlit",
]

uv 에 설정된 의존성 설치 

uv pip install -r pyproject.toml

Ollama 설치 후 , 질문 답변용 mistral 모델과 임베딩 생성용 nomic-embed-text 모델 다운

ollama pull mistral
ollama pull nomic-embed-text
ollama run mistral

벡터 데이터 저장을 위한 chroma 구동 

chroma run --path ./chroma_index

chroma 가 구동중인지 확인하는 방법은 브라우저로도 가능하더라구요. 

2단계: PDF 인덱싱

pdf를 검색가능하기 위해서 임베딩을 생성해서 chromaDB에 저장합니다. 

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.embeddings.ollama import OllamaEmbedding
import chromadb
import os

PDF_DIR = "./pdfs"
CHROMA_DIR = "./chroma_index"
COLLECTION_NAME = "my_index"

def create_index():
    embed_model = OllamaEmbedding(model_name="nomic-embed-text")
    chroma_client = chromadb.HttpClient(host="localhost", port=8000)
    chroma_collection = chroma_client.get_or_create_collection(
        name=COLLECTION_NAME, metadata={"hnsw:space": "cosine"}
    )
    chroma_store = ChromaVectorStore(chroma_collection=chroma_collection, chroma_client=chroma_client)
    storage_context = StorageContext.from_defaults(vector_store=chroma_store)

    documents = SimpleDirectoryReader(PDF_DIR).load_data()
    index = VectorStoreIndex.from_documents(
        documents, storage_context=storage_context, embed_model=embed_model
    )

    os.makedirs(CHROMA_DIR, exist_ok=True)
    index.storage_context.persist(persist_dir=CHROMA_DIR)
    print(f"인덱스가 {CHROMA_DIR}에 저장되었습니다.")

if __name__ == "__main__":
    if not os.path.exists(PDF_DIR):
        print(f"에러: {PDF_DIR} 디렉토리가 없습니다.")
    else:
        create_index()

이 부분을 설명 드리면 
1. ./pdfs 폴더에서 PDF 를 읽어 드리구요. 
2. nomic-embed-text로 임베딩 생성 
3. ChromaDB에 my_index 컬렉션에 저장 
4. 메타데이터(docstore.json, index_store.json) 를 ./chroma_index에 저장

실행

uv run python index_pdfs.py

 

구동을 하시면 아마 폴더에 이런 것들이 생기실 거에요

사실 이 부분이 제일로 힘들었어요. 
json 이 생성되지 않아서 몇번이나 했는지 계속 수정하고 변경하고 하면서 얻은 소스에요 ^^ 

결론

nomic-embed-text 로 임베딩을 해서 ChromaDB에 저장하는 방법을 이제 알게 된것 같습니다. 

즐거운 코딩 되세요 

 

728x90

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading