실무 Multi-Agent 오케스트레이션 13장 · 임베딩과 벡터 DB 3 / 7 ← 이전목차다음 → TechLead Cro

13장. 임베딩과 벡터 DB

벡터 DB와 Chroma — 벡터를 저장하고 꺼내는 곳

한 줄 요약

벡터 DB(vector database) 는 벡터를 저장해 두고, 질문 벡터와 가까운 것을 찾아 주는 저장소입니다. 우리는 폴더 하나에 저장되는 Chroma를 씁니다. 청크를 벡터로 바꿔 넣는 일을 색인(indexing) 이라고 합니다.


1. 왜 따로 저장소가 필요한가

원리만 보면 저장소 없이도 검색할 수 있습니다. 질문을 벡터로 바꾸고, 청크 벡터 전부와 유사도를 계산해 높은 것부터 고르면 됩니다. 그런데 문제가 둘 있습니다.

문제 설명
매번 다시 임베딩할 수 없다 청크를 벡터로 바꾸려면 임베딩 API를 호출해야 합니다. 프로그램을 켤 때마다 모든 청크를 다시 보내는 것은 시간과 비용의 낭비입니다
전부 비교하면 느려진다 청크 12개는 전부 비교해도 됩니다. 수십만 개가 되면 전부 비교하는 방식은 쓸 수 없습니다

벡터 DB는 이 두 가지를 맡습니다. 벡터를 파일로 남겨 두고, 전부 비교하지 않고도 가까운 것을 빨리 찾는 색인 구조를 만들어 둡니다.


2. 두 시점 — 색인은 미리, 검색은 그때그때

12장에서 본 RAG의 두 시점이 여기서 구체적인 모습이 됩니다.

시점 언제 하는 일 임베딩 API 호출
색인 미리, 문서가 바뀔 때만 청크 12개 → 벡터 12개 → chroma_db/에 저장 청크 수만큼
검색 질문이 올 때마다 질문 1개 → 벡터 1개 → 가까운 청크 찾기 질문 하나만큼

이번 장의 파일은 색인을 하고, 잘 들어갔는지 검색으로 확인합니다. 14장부터의 파일은 색인을 하지 않고 저장된 폴더를 불러오기만 합니다.

13장을 실행해야 chroma_db/ 폴더가 생깁니다. 14장 이후의 파일은 모두 이 폴더가 있어야 제대로 동작합니다.


3. Chroma

벡터 DB는 여러 가지가 있습니다. 서버를 따로 띄우는 것도 있고, 기존 데이터베이스에 기능을 덧붙이는 것도 있습니다. 우리는 Chroma를 씁니다.

Chroma의 성질 우리에게 좋은 점
서버가 필요 없다 설치 외에 준비할 것이 없습니다
폴더 하나에 저장된다 무엇이 어디 저장됐는지 눈으로 볼 수 있습니다
본문과 메타데이터를 함께 저장한다 12장에서 붙인 출처(문서 이름, 조항)가 검색 결과에 그대로 따라옵니다

원리는 어느 벡터 DB든 같습니다. "벡터를 넣는다, 가까운 것을 찾는다" 두 가지입니다.

Chroma 안에서 벡터 묶음 하나를 컬렉션(collection) 이라고 부릅니다. 데이터베이스의 표 하나에 해당합니다. 우리 컬렉션의 이름은 haru_policy입니다.


4. 색인하는 코드

docs = [Document(page_content=c["text"], metadata=c["metadata"], id=c["id"])
        for c in chunks]

vectorstore = Chroma.from_documents(
    documents=docs,
    embedding=embeddings,
    collection_name=COLLECTION,
    persist_directory=CHROMA_DIR,
)

Document는 LangChain이 문서 한 조각을 담는 표준 그릇입니다. 세 가지를 담습니다.

칸 넣는 것 12장 청크의 어느 값
page_content 본문. 이것이 벡터로 바뀝니다 text
metadata 본문에 딸린 정보. 벡터로 바뀌지 않고 그대로 저장됩니다 metadata
id 이 조각의 고유한 이름 id (policy-000 ~ policy-011)

Chroma.from_documents(...) 한 줄이 세 가지 일을 합니다.

  1. 본문 12개를 임베딩 모델로 보내 벡터 12개를 받습니다.
  2. 벡터, 본문, 메타데이터, id를 컬렉션에 넣습니다.
  3. persist_directory 폴더에 파일로 저장합니다.

5. 다시 실행하면 중복해서 들어가는가

같은 파일을 두 번 실행하면 청크가 24개가 될까요? 되지 않습니다. 12개 그대로입니다.

이유는 Document에 넣은 id입니다. Chroma는 이미 있는 id가 다시 들어오면 새로 추가하지 않고 덮어씁니다. 처음 실행한 뒤와 한 번 더 실행한 뒤에 컬렉션의 개수를 세어 보면 두 번 모두 12개입니다.

상황 결과
id를 넣고 다시 실행 같은 id를 덮어씁니다. 개수가 늘지 않습니다
id를 넣지 않고 다시 실행 실행할 때마다 새 id가 만들어져 같은 내용이 쌓입니다. 그래서 우리는 id를 넣습니다

알아 둘 점이 두 가지 더 있습니다.

  • 다시 실행해도 임베딩 API는 다시 호출됩니다. 덮어쓸 벡터를 만들어야 하기 때문입니다. 중복은 생기지 않지만 호출은 공짜가 아닙니다.
  • 덮어쓰기는 같은 id에만 일어납니다. 문서가 줄어 청크가 12개에서 10개가 되면, 예전의 policy-010, policy-011은 지워지지 않고 남습니다. 문서를 바꿨을 때는 chroma_db/ 폴더를 지우고 다시 실행하는 것이 가장 확실합니다.

핵심 정리

  • 벡터 DB는 벡터를 저장하고, 가까운 벡터를 찾아 줍니다.
  • 색인은 미리 한 번, 검색은 질문마다 합니다. 색인은 문서가 바뀔 때만 다시 합니다.
  • Chroma는 서버 없이 폴더 하나(chroma_db/)에 저장됩니다.
  • Document에는 본문, 메타데이터, id를 담습니다. 벡터가 되는 것은 본문뿐입니다.
  • id를 넣었기 때문에 다시 실행해도 중복되지 않습니다. 다만 임베딩 API는 다시 호출됩니다.
← 이전 절임베딩 — 뜻을 숫자 목록으로 바꾼다다음 절 →유사도 검색과 거리 읽기 — 점수는 작을수록 가깝다
오명운 · macro@prag-ai.com