Custom Vector Stores: 벡터 저장소를 내 손으로

Custom Vector Stores: 벡터 저장소를 내 손으로

기본적으로 인덱스는 파싱→청킹→임베딩→관리형 벡터 저장소 쓰기까지 전부 자동으로 처리해요. 그런데 벡터 저장소를 꼭 직접 갖고 싶을 때가 있어요 — LlamaCloud가 관리하지 않는 데이터베이스에 자체 호스팅 중이거나, 데이터를 내 인프라에만 두고 싶을 때죠. 이럴 때 관리형 내보내기를 끄고 raw 파싱 출력을 직접 내려 받을 수 있어요.

출처: 공식문서

이 모드에서도 LlamaCloud가 디렉토리 관리·파싱·동기화 오케스트레이션은 계속 처리해요. 대신 청킹·임베딩·DB 쓰기를 내가 맡는 거예요.

parse-only 인덱스 만들기

인덱스를 만들 때 vector_target="DISABLED"를 넘겨요. 파이프라인이 다운로드 전용 내보내기로 전환되면서, 파싱 출력이 관리형 벡터 저장소 대신 객체 스토리지에 기록돼요.

index = await client.beta.indexes.create(
    source_directory_id=directory.id,
    vector_target="DISABLED",
)

일반 인덱스와 같은 방식으로 ready 상태가 될 때까지 기다리면 돼요 (getting started guide 참고).

파싱 출력 다운로드

소스 디렉토리의 각 파일은 페이지별 마크다운(header, text, footer), 구조화 항목, 첨부, 출처 메타데이터를 담은 JSON 페이로드를 만들어요. 디렉토리 파일을 나열하고 각 페이로드를 presigned URL로 내려 받아요.

import httpx

# 소스 디렉토리의 파일 나열
files_resp = await client.beta.directories.files.list(
    directory_id=directory.id,
)

# 각 디렉토리 파일 ID는 스토리지의 parsed-output JSON 객체를 가리킨다.
# presigned URL을 해석해 다운로드한다.
async with httpx.AsyncClient() as http:
    for f in files_resp.items:
        presigned = await client.files.get(f.file_id)
        response = await http.get(presigned.url)
        response.raise_for_status()
        parsed = response.json()
        # parsed["parse"] holds the parsed output payload

동기화 후 이 루프를 다시 돌려 변경분을 반영하세요. 각 페이로드의 content_fingerprint_hashsource_modified_at 필드로 마지막 내보내기 이후 변경되지 않은 파일은 건너뛸 수 있어요.

청킹·임베딩·내 저장소로 밀어넣기

여기부터는 일반적인 커스텀 RAG 파이프라인과 같아요.

  1. 페이지 경계를 문자 오프셋으로 추적하면서 페이지별 마크다운을 연결해요.
  2. 선호하는 청커로 텍스트를 청킹해요.
  3. 선호하는 임베딩 모델로 청크를 임베딩해요.
  4. (parsed_directory_file_id, export_config_id) 키로 DB에 청크를 써요. 재파싱된 파일이 이전 내보내기를 중복 없이 완전히 대체하도록 하기 위함이에요.

레퍼런스 구현

index-v2-data-sinks 저장소에 여러 데이터베이스용 완전한 실행 가능한 레퍼런스 익스포터가 있어요.

  • MongoDB (Atlas vector search)
  • PostgreSQL + pgvector
  • Qdrant
  • Pinecone
  • Turbopuffer
  • Azure AI Search

각 익스포터는 인덱스/컬렉션 프로비저닝, 멱등 재내보내기를 위한 결정적 ID, 파일별 delete-then-insert, 증분 동기화용 스냅샷 목록을 처리해요. export/base.py의 공유 Exporter 프로토콜이 새 싱크를 추가할 때 구현해야 하는 계약이에요.

class Exporter(Protocol):
    async def export(self, outputs, *, project_id, embeddings=None, embedding_model=None): ...
    async def delete_file(self, *, parsed_directory_file_id, export_config_id): ...
    async def delete_files(self, *, parsed_directory_file_ids, export_config_id): ...
    async def list_snapshots(self, *, export_config_id): ...

코딩 에이전트를 이 저장소에 가리켜 작업 예제로 삼거나, 마음에 드는 싱크를 복사해 시작점으로 쓰거나, 원하는 스토어에 맞춰 프로토콜을 구현하면 돼요.

주의사항

  • client.beta.*의 검색·채팅·파일 작업은 관리형 벡터 저장소를 질의해요. vector_target="DISABLED"면 이 엔드포인트는 질의할 대상이 없어요 — 검색은 내 DB에서 처리해야 해요.
  • 소스 디렉토리에서 파일이 제거될 때 내 DB의 레코드 정리는 내 책임이에요. 레퍼런스 구현의 list_snapshots() 패턴이 현재 디렉토리 상태와 대조하는 방법을 보여줘요.

더 알아보기