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

13장. 임베딩과 벡터 DB

임베딩 — 뜻을 숫자 목록으로 바꾼다

한 줄 요약

임베딩(embedding) 은 글을 정해진 길이의 숫자 목록으로 바꾸는 일입니다. 뜻이 비슷한 글은 비슷한 숫자 목록이 됩니다. 그래서 두 글의 뜻이 얼마나 가까운지를 숫자로 계산할 수 있습니다.


1. 글이 숫자 목록이 된다

임베딩 모델에 문장 하나를 넣으면 숫자 목록 하나가 나옵니다. 이 숫자 목록을 벡터(vector) 라고 부릅니다. 「따라하기」에서 실제로 나오는 값입니다.

'반품 배송비는 얼마인가요?'
  차원 수: 3072
  앞 5개 값: [0.0072, 0.0106, 0.0045, -0.0558, -0.0273]

문장 하나가 실수 3,072개가 되었습니다. 이 개수를 차원(dimension) 이라고 합니다.

용어 뜻
임베딩 글을 숫자 목록으로 바꾸는 일, 또는 그 결과
벡터 숫자 목록. 파이썬에서는 float의 리스트
차원 그 목록의 길이. 우리가 쓰는 모델은 3,072
임베딩 모델 이 변환을 하는 모델. 답변을 쓰는 모델(gemini-3.8-flash)과는 다른 모델

숫자 하나하나가 무슨 뜻인지는 사람이 읽을 수 없습니다. 중요한 것은 값 자체가 아니라 두 벡터가 얼마나 가까운가입니다.


2. 지도에 비유하면

지도 위의 한 지점은 숫자 두 개(위도, 경도)로 나타냅니다. 가까운 동네는 숫자도 비슷합니다.

임베딩 모델은 모든 문장을 뜻의 지도 위에 점으로 찍습니다. 다만 이 지도는 축이 두 개가 아니라 3,072개입니다.

  • "반품 배송비는 얼마인가요?"와 "환불할 때 택배비 누가 내요?"는 이웃한 자리에 찍힙니다.
  • "오늘 점심 뭐 먹지?"는 먼 자리에 찍힙니다.

뜻으로 검색한다는 것은 질문의 자리를 찍고, 그 근처에 있는 청크를 찾는 일입니다.


3. 가까움을 재는 자 — 코사인 유사도

두 벡터가 얼마나 가까운지 재는 대표적인 값이 코사인 유사도(cosine similarity) 입니다. 두 벡터가 가리키는 방향이 같으면 1, 방향이 다를수록 작아집니다.

같은 문장을 기준으로 네 문장과의 유사도를 재 본 결과입니다(실습문제 1에서 직접 돌려 봅니다).

유사도 1.000 | '반품 배송비는 얼마인가요?' vs '반품 배송비는 얼마인가요?'
유사도 0.872 | '반품 배송비는 얼마인가요?' vs '환불할 때 택배비 누가 내요?'
유사도 0.573 | '반품 배송비는 얼마인가요?' vs '포인트는 언제 적립되나요?'
유사도 0.538 | '반품 배송비는 얼마인가요?' vs '오늘 점심 뭐 먹지?'
  • 같은 문장은 1.000입니다.
  • 둘째 줄을 보세요. "반품"과 "환불", "배송비"와 "택배비". 겹치는 단어가 하나도 없는데 0.872입니다. 단어 검사로는 0건이었을 쌍입니다.
  • 상관없는 문장도 0이 되지는 않습니다. 0.538입니다. 그래서 유사도는 값 하나만 보지 않고 다른 후보와 견주어 읽습니다.

유사도 0.5가 "절반쯤 비슷하다"는 뜻은 아닙니다. 이 모델에서는 상관없는 문장끼리도 0.5 근처가 나옵니다. 숫자의 눈금은 모델마다 다릅니다.


4. 질문과 청크는 같은 모델로 바꾼다

뜻의 지도는 모델마다 다릅니다. A 모델의 지도에 찍어 둔 청크 사이에 B 모델로 찍은 질문을 놓으면, 거리는 계산되지만 그 값에는 의미가 없습니다. 축척도 방위도 다른 두 지도를 겹쳐 놓은 것과 같습니다.

여기서 지켜야 할 규칙 두 가지가 나옵니다.

  • 질문과 청크는 반드시 같은 임베딩 모델로 바꿉니다.
  • 임베딩 모델을 바꾸면 저장해 둔 벡터를 전부 다시 만듭니다. 이것을 재색인(re-indexing) 이라고 합니다.

그래서 임베딩 모델의 이름은 코드 여기저기에 적지 않고 config.py의 EMBEDDING_MODEL 한 곳에 둡니다.


5. 우리가 쓰는 임베딩 모델

항목 값
모델 이름 models/gemini-embedding-001
차원 3,072 (기본값)
입력 글만 받습니다. 한 번에 2,048토큰까지
지원 종료 예정일 2028년 5월 14일

2026년 10월 기준입니다. 모델 목록과 종료 일정은 바뀌므로 공식 문서에서 확인합니다.

  • 임베딩 안내: https://ai.google.dev/gemini-api/docs/embeddings
  • 지원 종료 일정: https://ai.google.dev/gemini-api/docs/deprecations

공식 문서에는 더 새로운 임베딩 모델도 올라와 있습니다. 그래도 이 과정에서는 모델을 바꾸지 않습니다. 바꾸는 순간 4절의 규칙대로 전부 재색인해야 하기 때문입니다.


6. 코드에서는 이렇게 쓴다

임베딩 모델은 LangChain(11장)의 GoogleGenerativeAIEmbeddings로 감싸서 씁니다.

from langchain_google_genai import GoogleGenerativeAIEmbeddings

embeddings = GoogleGenerativeAIEmbeddings(
    model=EMBEDDING_MODEL,                    # models/gemini-embedding-001
    google_api_key=os.getenv("GOOGLE_API_KEY"),
)

vec = embeddings.embed_query("반품 배송비는 얼마인가요?")   # float 3,072개
메서드 넣는 것 쓰는 때
embed_query(문장) 문장 하나 질문을 벡터로 바꿀 때
embed_documents(문장 목록) 문장 여러 개 청크를 한꺼번에 벡터로 바꿀 때

둘은 같은 모델을 부르지만, 모델에게 "이것은 질문이다", "이것은 찾을 대상 문서다"라는 표시를 다르게 붙여 보냅니다. 이 구분은 라이브러리가 알아서 합니다.


핵심 정리

  • 임베딩은 글을 숫자 목록(벡터)으로 바꿉니다. 우리 모델은 문장 하나를 3,072개의 숫자로 바꿉니다.
  • 뜻이 비슷하면 벡터가 가깝습니다. 겹치는 단어가 없는 두 질문의 코사인 유사도가 0.872였습니다.
  • 상관없는 문장도 0.538이 나옵니다. 유사도는 다른 후보와 견주어 읽습니다.
  • 질문과 청크는 같은 모델로 바꿉니다. 모델을 바꾸면 전부 재색인합니다.
  • 질문은 embed_query, 청크는 embed_documents로 바꿉니다.
← 이전 절왜 임베딩이 필요한가 — 청크는 있는데, 찾을 방법이 없다다음 절 →벡터 DB와 Chroma — 벡터를 저장하고 꺼내는 곳
오명운 · macro@prag-ai.com