스토리지 커스터마이징 — 저장 계층을 원하는 백엔드로 바꾸기

스토리지 커스터마이징 — 저장 계층을 원하는 백엔드로 바꾸기

LlamaIndex는 기본적으로 복잡한 설정을 감춰두고, 몇 줄이면 데이터를 바로 질의할 수 있게 해줘요.

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("Summarize the documents.")

그런데 내부에는 이 인제스트된 문서(Node 객체)와 임베딩 벡터, 인덱스 메타데이터가 어디에 저장될지 바꿀 수 있는 저장 계층이 숨어 있어요. 기본값이면 충분할 때도 있지만, 프로덕션에서는 외부 DB를 연결하고 싶을 때가 많죠. 이 페이지에서는 그 방법을 다룹니다.

출처: 공식문서

저수준 API로 저장 계층 제어하기

VectorStoreIndex.from_documents(documents) 같은 고수준 API 대신, 좀 더 세밀한 제어를 주는 저수준 API를 쓸 수 있어요. 각 스토어를 직접 인스턴스화해 StorageContext에 담아 넘기면 됩니다.

from llama_index.core.storage.docstore import SimpleDocumentStore
from llama_index.core.storage.index_store import SimpleIndexStore
from llama_index.core.vector_stores import SimpleVectorStore
from llama_index.core.node_parser import SentenceSplitter

# create parser and parse document into nodes
parser = SentenceSplitter()
nodes = parser.get_nodes_from_documents(documents)

# create storage context using default stores
storage_context = StorageContext.from_defaults(
    docstore=SimpleDocumentStore(),
    vector_store=SimpleVectorStore(),
    index_store=SimpleIndexStore(),
)

# create (or load) docstore and add nodes
storage_context.docstore.add_documents(nodes)

# build index
index = VectorStoreIndex(nodes, storage_context=storage_context)

# save index
index.storage_context.persist(persist_dir="<persist_dir>")

# can also set index_id to save multiple indexes to the same folder
index.set_index_id("<index_id>")
index.storage_context.persist(persist_dir="<persist_dir>")

# to load index later, make sure you setup the storage context
# this will loaded the persisted stores from persist_dir
storage_context = StorageContext.from_defaults(persist_dir="<persist_dir>")

# then load the index object
from llama_index.core import load_index_from_storage

loaded_index = load_index_from_storage(storage_context)

# if loading an index from a persist_dir containing multiple indexes
loaded_index = load_index_from_storage(storage_context, index_id="<index_id>")

# if loading multiple indexes from a persist dir
loaded_indicies = load_index_from_storage(
    storage_context, index_ids=["<index_id>", ...]
)

이 코드에서 핵심은 StorageContext.from_defaults(...)에 넘기는 스토어 세 개예요. 문서 스토어·벡터 스토어·인덱스 스토어를 원하는 구현으로 바꿔 끼우면, 한 줄만 바꿔도 저장 백엔드가 달라집니다. 여러 인덱스를 같은 폴더에 저장하려면 index.set_index_id(...)로 ID를 지정해 구분할 수 있어요.

벡터 스토어 통합과 저장 동작

대부분의 벡터 스토어 통합은 인덱스 전체(벡터 + 텍스트)를 벡터 스토어 자체에 저장해요. 이렇게 되면 위에서 본 것처럼 인덱스를 명시적으로 영속할 필요가 없어지는 큰 장점이 생깁니다. 벡터 스토어가 이미 호스팅되어 데이터를 영속하고 있기 때문이에요.

이런 방식을 지원하는 벡터 스토어로는 AzureAISearchVectorStore, ChatGPTRetrievalPluginClient, CassandraVectorStore, ChromaVectorStore, EpsillaVectorStore, DocArrayHnswVectorStore, DocArrayInMemoryVectorStore, JaguarVectorStore, LanceDBVectorStore, MetalVectorStore, MilvusVectorStore, MyScaleVectorStore, OpensearchVectorStore, PineconeVectorStore, QdrantVectorStore, TablestoreVectorStore, RedisVectorStore, UpstashVectorStore, WeaviateVectorStore 등이 있어요.

Pinecone을 쓰는 작은 예시를 볼게요.

import pinecone
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.pinecone import PineconeVectorStore

# Creating a Pinecone index
api_key = "api_key"
pinecone.init(api_key=api_key, environment="us-west1-gcp")
pinecone.create_index(
    "quickstart", dimension=1536, metric="euclidean", pod_type="p1"
)
index = pinecone.Index("quickstart")

# construct vector store
vector_store = PineconeVectorStore(pinecone_index=index)

# create storage context
storage_context = StorageContext.from_defaults(vector_store=vector_store)

# load documents
documents = SimpleDirectoryReader("./data").load_data()

# create index, which will insert documents/vectors to pinecone
index = VectorStoreIndex.from_documents(
    documents, storage_context=storage_context
)

여기서 포인트는 VectorStoreIndex.from_documents(...)를 호출하는 순간 문서·벡터가 Pinecone에 삽입된다는 거예요. 별도로 persist()를 부르지 않아도 됩니다.

이미 데이터가 들어 있는 기존 벡터 스토어가 있다면, 그 스토어에 연결해서 바로 VectorStoreIndex를 만들 수도 있어요.

index = pinecone.Index("quickstart")
vector_store = PineconeVectorStore(pinecone_index=index)
loaded_index = VectorStoreIndex.from_vector_store(vector_store=vector_store)

더 알아보기

  • Document Stores — 원본 문서 청크를 담는 스토어
  • Vector Stores — 벡터 임베딩을 저장하는 스토어와 기능 비교
  • Index Stores — 인덱스 메타데이터를 담는 스토어
  • Persisting & Loading Data — 저장·불러오기 전반