데이터 저장과 불러오기 — 인덱스를 디스크·원격 저장소에 영속하기

데이터 저장과 불러오기 — 인덱스를 디스크·원격 저장소에 영속하기

기본적으로 LlamaIndex는 데이터를 인메모리로만 유지해요. 즉 프로세스가 끝나면 사라집니다. 이 인메모리 데이터를 디스크나 원격 백엔드에 영속하고, 다시 불러오는 방법을 이 페이지에서 다뤄요.

출처: 공식문서

데이터 저장하기

인메모리 데이터를 원하는 시점에 명시적으로 저장할 수 있어요.

storage_context.persist(persist_dir="<persist_dir>")

이렇게 하면 지정한 persist_dir(기본값은 ./storage) 아래에 데이터가 디스크로 저장돼요. 같은 디렉토리에 여러 인덱스를 저장하려면, 로드할 때 인덱스 ID를 기억해 두기만 하면 됩니다.

여러분이 MongoDB 같은 대체 스토리지 백엔드를 구성하면 기본으로 데이터가 영속돼요. 이 경우 storage_context.persist()를 호출해도 아무 일도 일어나지 않습니다.

주의할 점이 하나 있어요. 저장은 StorageContext를 기준으로 이뤄지는데, 문서 스토어·벡터 스토어·인덱스 스토어가 각각 어떻게 저장되는지는 그 저장소 구현에 따라 달라져요.

데이터 불러오기

데이터를 다시 불러오려면 같은 구성(같은 persist_dir이나 같은 벡터 스토어 클라이언트)으로 스토리지 컨텍스트를 다시 만들면 돼요.

storage_context = StorageContext.from_defaults(
    docstore=SimpleDocumentStore.from_persist_dir(persist_dir="<persist_dir>"),
    vector_store=SimpleVectorStore.from_persist_dir(
        persist_dir="<persist_dir>"
    ),
    index_store=SimpleIndexStore.from_persist_dir(persist_dir="<persist_dir>"),
)

그다음 몇 가지 편의 함수로 StorageContext에서 특정 인덱스를 로드할 수 있어요.

from llama_index.core import (
    load_index_from_storage,
    load_indices_from_storage,
    load_graph_from_storage,
)

# load a single index
# need to specify index_id if multiple indexes are persisted to the same directory
index = load_index_from_storage(storage_context, index_id="<index_id>")

# don't need to specify index_id if there's only one index in storage context
index = load_index_from_storage(storage_context)

# load multiple indices
indices = load_indices_from_storage(storage_context)  # loads all indices
indices = load_indices_from_storage(
    storage_context, index_ids=[index_id1, ...]
)  # loads specific indices

# load composable graph
graph = load_graph_from_storage(
    storage_context, root_id="<root_id>"
)  # loads graph with the specified root_id

한 디렉토리에 여러 인덱스가 저장돼 있다면 index_id를 지정해야 하는 점, 인덱스가 하나뿐이면 지정하지 않아도 되는 점을 기억해 두면 돼요. 복합 그래프를 저장했다면 load_graph_from_storageroot_id를 지정해 다시 불러올 수 있어요.

원격 백엔드 사용하기

기본적으로 LlamaIndex는 로컬 파일시스템으로 파일을 저장·불러와요. 하지만 fsspec.AbstractFileSystem 객체를 넘기면 이 기본 동작을 바꿀 수 있어요.

예를 들어 S3 호환 스토리지를 쓰는 상황을 생각해 봅시다. 먼저 s3fs.S3FileSystem을 구성합니다.

import dotenv
import s3fs
import os

dotenv.load_dotenv("../../../.env")

# load documents
documents = SimpleDirectoryReader(
    "../../../examples/paul_graham_essay/data/"
).load_data()
print(len(documents))
index = VectorStoreIndex.from_documents(documents)

이 시점까지는 평범한 로컬 흐름과 같아요. 이제 S3 파일시스템을 만들어서 그쪽으로 저장·로드를 해보죠.

# set up s3fs
AWS_KEY = os.environ["AWS_ACCESS_KEY_ID"]
AWS_SECRET = os.environ["AWS_SECRET_ACCESS_KEY"]
R2_ACCOUNT_ID = os.environ["R2_ACCOUNT_ID"]

assert AWS_KEY is not None and AWS_KEY != ""

s3 = s3fs.S3FileSystem(
    key=AWS_KEY,
    secret=AWS_SECRET,
    endpoint_url=f"https://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com",
    s3_additional_kwargs={"ACL": "public-read"},
)

# If you're using 2+ indexes with the same StorageContext,
# run this to save the index to remote blob storage
index.set_index_id("vector_index")

# persist index to s3
s3_bucket_name = "llama-index/storage_demo"  # {bucket_name}/{index_name}
index.storage_context.persist(persist_dir=s3_bucket_name, fs=s3)

# load index from s3
index_from_s3 = load_index_from_storage(
    StorageContext.from_defaults(persist_dir=s3_bucket_name, fs=s3),
    index_id="vector_index",
)

핵심은 fs=s3 파라미터예요. 이걸 넘기면 저장·로드가 로컬이 아니라 S3 파일시스템으로 이뤄집니다. 파일시스템을 넘기지 않으면 기본적으로 로컬 파일시스템을 가정한다는 점을 기억하세요.

더 알아보기

  • Customizing Storage — 저장 계층을 바꾸는 방법
  • Vector Stores — 벡터를 저장하는 스토어
  • Index Stores — 인덱스 메타데이터를 담는 스토어
  • Document Stores — 원본 문서 청크를 담는 스토어