**노코드(No code).** 모든 것을 하나의 YAML 파일에서 구성하거나 (`serviette wizard`로 생성할 수 있습니다). **모든 벡터 DB — 8개 백엔드.** DuckDB (임베디드, 제로 설정 — 기본값), pgvector, Qdrant, Milvus, ChromaDB, Weaviate, Pinecone 및 MongoDB Atlas Vector Search. 모든 백엔드는 **Pathway의 네이티브 커넥터**를 통해 작성되므로 파일 편집 및 삭제가 스토어에서 실제 upsert/delete로 처리됩니다. **제로 설정 기본값.** DuckDB는 기본 백엔드입니다: 외부 서비스 설치나 실행이 필요 없는 단일 로컬 파일 내 임베디드 데이터베이스이며, 벡터 검색 기능이 내장되어 있습니다. **실시간 및 증분적(Live & incremental).** Pathway를 기반으로 구축되었기 때문에 추가, 편집 및 삭제가 실시간으로 벡터 DB에 반영됩니다. 변경하는 모든 내용은 몇 초 후에 답변 가능합니다. 대규모 코퍼스가 메모리에 축적되는 대신 파이프라인을 통해 흐르므로, 큰 규모의 자료군(corpus)도 메모리에서 작게 유지됩니다. **다중 소스(Multiple sources).** 로컬 파일 시스템, Google Drive, S3/MinIO, SharePoint — 그리고 PyFilesystem 라이브러리가 열 수 있는 모든 것: FTP, SFTP, WebDAV, 심지어 ZIP 아카이브까지. 모두 실시간으로 감시되며 하나의 설정에서 자유롭게 혼합됩니다. **다중 모드(Multimodal) 기본 탑재.** 텍스트, 오피스 문서, PDF (테이블 및 레이아웃 포함), 스캔된 이미지 — 그리고 해당 API 키를 사용하면 오디오 녹음 및 심지어 비디오까지 가능합니다. 모든 형식은 기본적으로 활성화되어 있으며 API 키가 필요 없는 최적의 파서로 라우팅됩니다. 폴더에 파일을 넣기만 하면 어떤 문서와 마찬가지로 답변 가능합니다. 다중 모드(Multimodality)를 참조하세요. **Pathway의 LLM xpack 재사용.** 파서, 스플리터 및 임베더는 그대로 사용되며 — serviette는 자체 기능을 구현하지 않습니다.
다섯 가지 임베더 패밀리(OpenAI, LiteLLM, SentenceTransformers, Gemini, Bedrock)는 인덱서와 서버 측에서 동일하게 작동합니다. 여기에는 로컬 임베딩과 DuckDB를 사용한 완전히 로컬이며 자격 증명(credential-free)이 필요 없는 스택도 포함됩니다.**분리되고 확장 가능함.** 인덱서와 API 서버는 벡터 DB만 공유하는 독립적인 프로세스입니다. 서버는 상태가 없으며 수평적으로 확장할 수 있고, 인덱서는 하나의 설정 라인으로 워커 프로세스에 샤딩(sharding)됩니다. 모든 부분이 자체적으로 확장됩니다.**웹 채팅 UI, 동일 포트.** `serviette server`는 `/`에서 깔끔한 ChatGPT/Claude 스타일의 채팅 페이지를 제공하고, 버전이 지정된 API(`api/v1/...`) 옆에 위치합니다. 같은 오리진(same origin)이며 CORS가 필요 없고 추가로 실행할 것이 없습니다. 분리 배포(UI가 다른 호스트에 있는 경우)를 위해 독립적인 `serviette frontend` 프록시 계층이 있습니다.**Pathway 라이선스는 무료입니다.** https://pathway.com/framework/get-license에서 한 번의 클릭으로 받을 수 있습니다.
**두 부분은 완전히 분리되어 있습니다.** 인덱서(쓰기 경로)와 서버(읽기 경로)는 별도의 프로세스이며, 서로 대화하지 않는 다른 실행 파일입니다. 이들의 유일한 계약 관계는 벡터 데이터베이스 자체입니다:
**독립적인 확장성.** 서버는 상태가 없으며 읽기 전용이므로 로드 밸런서 뒤에 원하는 수의 인스턴스를 실행할 수 있으며, 각 인스턴스는 비용 없이 채팅 UI도 제공합니다. 인덱서는 별도로 확장됩니다: Pathway는 이를 워커 프로세스(`indexer.workers: 8`)에 샤딩합니다.
이것이 아래 벤치마크가 실행되는 방식이며, 스택의 모든 부분이 독립적으로 확장됩니다. 대용량 재인덱싱(Bulk re-indexing)은 쿼리 서비스 속도를 저하시키지 않으며, 쿼리 급증도 인덱싱을 중단시키지 않습니다.**실패 격리(Failure isolation).** 인덱서가 다운되어도 마지막으로 동기화된 데이터를 통해 서비스는 계속됩니다. 서버가 다운되어도 인덱싱은 데이터베이스를 최신 상태로 유지합니다. 양쪽 모두 독립적으로 재시작하거나 업그레이드할 수 있습니다 (인덱서는 재임베딩 없이 지속성(persistence)에서 이어서 진행).**데이터베이스는 사용자의 것.** 벡터는 평이하고 문서화된 스키마를 가진 *사용자* 저장소에 존재합니다. 다른 소비자들(BI, 다른 앱, 다른 검색 스택)은 동일한 컬렉션을 읽을 수 있으며, serviette가 이를 독점하지 않습니다. 그리고 기본 저장소가 임베디드 DuckDB 파일이기 때문에, 이것을 시도하는 데 드는 설정 비용은 전혀 없습니다.**선택적 세 번째 계층.** 분리된 배포(UI를 API와 다른 호스트에 두는 경우)의 경우, 독립적인 `serviette 프론트엔드`가 동일한 채팅 페이지를 서비스하고 API로 서버 측에서 프록시합니다.
유일하게 의도적으로 예외인 점은: 임베디드 DuckDB 백엔드는 이러한 분산성을 포기하는 대신 제로 설정(zero setup)을 제공한다는 것입니다. 로컬 파일 하나, 단일 작성자(single-writer)이며, 노트북과 데모에 이상적입니다 (동시성 관련 참고 사항은 문서를 참조하세요).
두 개의 독립적인 벤치마크가 각 축별로 존재합니다. 즉, 인덱싱이 시간과 메모리에 비용을 얼마나 발생시키는지, 그리고 검색의 정확도가 얼마나 높은지를 측정합니다.
독립적 벤치마크(docker-compose: Qdrant + indexer + server, 완전 로컬 임베딩, API 비용 제로)는 평문 위키피디아 코퍼스에 대해 수행되었습니다. 아래 모든 바이트는 추출된 텍스트이며 (동일한 텍스트 콘텐츠를 가진 PDF 컬렉션은 몇 배 더 무거울 것입니다), benchmarks/realtime-data-indexing을 참조하세요:
| 코퍼스 타겟 | ≈ 페이지 수 | 파일 수 | 청크 (근사치) | 인덱싱 시간 | 최대 메모리 (PSS, GiB) | Qdrant에 저장된 용량 (GiB) |
|---|---|---|---|---|---|---|
| 100 MB | 52,431 | 12,969 | 53,651 | 44초 | 6.86 | 0.61 |
| ... |
The table과 두 그림은 동일한 여섯 번의 실행에서 가져온 것입니다. 원본 요약(Raw summaries), 메모리 샘플(memory samples), 프로세스별 스냅샷(per-process snapshots) 및 해결된 구성(resolved configurations)은 results에 있습니다. 크기별로 최종 한 번의 실행이 게시되었으며, 실행 간 변동성(run-to-run variability)은 측정되지 않았습니다. 메모리 및 디스크 크기는 GiB를 사용하며, 입력 레이블은 명목상의 타겟입니다 (1 GB = 추출된 텍스트 1,000 MiB). 페이지는 약 2,000 바이트입니다. 청크는 완료 시점의 Qdrant 근사치 카운트이며, 크기별 곡선은 완료 감지기(completion detector)가 사용한 최대 관찰 카운트를 유지합니다.
문서는 파이프라인을 통해 흐르며 그 안에 축적되지 않습니다.
파일 감시 인덱스(file-watch index)는 편집 및 삭제를 감지하기 위해 감시된 파일별 메타데이터를 보존하므로, 비용은 파일 수에 따라 달라집니다. 오른쪽 패널은 커넥터 워커의 추가 사설 메모리(extra private memory)로부터 이 비용을 추정합니다: 적합된 기울기(fitted slope)는 **파일당 약 361 바이트**입니다. 이 프록시(proxy)에는 불균등한 워커 버퍼(unequal worker buffers)도 포함되므로, 개별 할당량을 분리하지는 않습니다.
임베딩 런타임(embedding runtime), 엔진 및 작업 버퍼(working buffers)가 워커의 기본값(baseline)을 차지합니다. 이 곡선들은 파일 감시 메타데이터와 버퍼가 증가하는 모든 단계를 포함하여 인덱싱 중 총 메모리가 어떻게 진화하는지를 보여줍니다. 파싱된 텍스트 캐시(Parsed-text cache), 영속성 스냅샷(persistence snapshots) 및 저장된 임베딩은 디스크에 존재합니다.
이러한 실행들을 통틀어 **500배 더 큰 입력 타겟은 최대 PSS 대비 2.1배의 비용**을 발생시킵니다.
파일 수와 텍스트 볼륨 모두 크기별로 변경되므로, 이 비교는 일정한 메모리 사용량을 확립하지 않습니다.
*모든 곡선과 표 값은 할당자 백그라운드 정리(allocator background purging)가 활성화된 동일한 실행에서 가져온 것입니다. 파일 감시 메모리는 다른 워커들의 중앙값 대비 rank-0의 사설 메모리 초과분으로부터 추정되었습니다.*
*샘플링된 최대 10 GB의 할당량(Attribution)을 기준으로 합니다. 로컬 임베더 메모리는 별도의 8개 프로세스 초기화 진단 및 라이브러리 매핑으로부터 추정되었습니다. 임베더 없이 이미 필요한 라이브러리는 잔여 메모리에 남아 있습니다. 로컬 임베더가 없는 상태에서의 3.27 GiB 추정치는 외부 임베더 인덱싱 실행에서 측정된 것이 아니라, 빼기를 통해 계산된 값입니다. 이 수치에는 임베딩 서비스는 포함되지 않으며, 클라이언트 오버헤드나 작업 버퍼의 변화량은 측정하지 않았습니다. 공유 페이지(Shared pages)는 PSS를 통해 비례적으로 고려되었습니다.*
설정: 96-logical-CPU 호스트 (저장된 CPU 모델 참조),
스트리밍 모드, 각 12개의 OpenMP 스레드를 가진 8개 워커 프로세스,
로컬 `static-retrieval-mrl-en-v1`
임베딩(embeddings), Matryoshka 방식으로 256 차원으로 축소됨,
512 토큰 청크, Qdrant, 그리고 jemalloc `background_thread:true`
.
임베딩 API 호출 없음. 설치된 버전: serviette 0.1.7, Pathway 0.33.0, PyTorch 2.14.1,
sentence-transformers 6.1.0.
최대 메모리(Peak memory)는 인덱서 컨테이너에서 합산된 최대 PSS입니다.
Qdrant 및 API 서버 메모리, 매핑되지 않은 파일 시스템 캐시 및 커널 메모리는 이 측정 범위 밖에 있으며, 전체 스택의 RAM 요구 사항은 아닙니다.
샘플 간의 짧은 최고점(brief peaks)은 누락될 수 있습니다.
인덱싱 시간은 Qdrant의 근사 청크 개수가 실행 최대치를 마지막으로 증가시킨 시점을 기준으로 추정됩니다. 이 순간은 최종 쓰기 작업보다 앞설 수 있으며, 출력 완전성 검사(output-completeness checks)는 타임스탬프를 수정하거나 백엔드 검색 인덱스가 준비되는 시간을 측정하지 않습니다. 기록된 환경, 캐시 조건 및 이를 재현하는 데 대한 제한 사항은 측정 노트에 설명되어 있습니다.
FRAMES (Google, 2024)에서의 종단 간(End-to-end) 평가: 답변을 2~15개의 영어 위키피디아 기사에서 조합해야 하는 824개의 다단계 질문(multi-hop questions) — benchmarks/frames 참조, 전체 기술 보고서는 REPORT.md에 있습니다:
측정 항목 | 결과
|---|---|
| gold-article recall — 논문의 지표, 논문의 코퍼스에서 | 0.50 대 논문에서 발표된 BM25 기준선에 대한 0.15 (우리가 재현한 값으로는 0.21)
| ... |
설정은 기술적으로 가능한 모든 부분에서 논문을 재현합니다: 동일한 Wikipedia dump (TFDS `wikipedia/20230601.en`)