Google의 첫 네이티브 멀티모달 임베딩 모델

gemini-embedding-2-preview 실제 테스트 및 토큰체크

by kimdonglin

Google이 최초의 네이티브 멀티모달 임베딩 모델 gemini-embedding-2-preview를 공개했다. 텍스트, 이미지, 비디오, 오디오, PDF를 하나의 벡터 공간에 매핑한다는 약속. 공식 문서에는 토큰 한도 8,192, 비디오 120초, 오디오 80초라고 적혀 있다.


그런데 실제로 테스트해보니 공식 문서의 숫자와 실제 동작 사이에 상당한 괴리가 있었다. 특히 Silent Truncation — 한도를 초과해도 에러 없이 앞부분만 처리하고 200 OK를 반환하는 동작 — 이 모달리티마다 다르게 작동하고 있었다.


이 글은 5개 모달리티를 직접 테스트하고, 잘림 경계를 1초/1페이지 단위로 추적한 결과를 공유한다.


핵심 발견

비디오: 공식 한도 120초이지만 실제 임베딩 반영은 앞 31초뿐. 나머지는 Silent Truncation.

오디오: 유일하게 정직한 동작. 한도 초과 시 에러, 한도 내 전체 반영.

PDF: 4페이지까지만 반영. 5페이지는 조용히 잘림, 6페이지부터 에러.

task_type 파라미터가 embedding-2-preview에서 완전히 무시됨 (breaking change).


테스트 환경

SDK: google-genai 1.57.0

임베딩 모델: gemini-embedding-2-preview, gemini-embedding-001

토큰 카운트: gemini-2.0-flash-lite (countTokens API는 무료)

비디오/오디오 소스: TED Talk (yt-dlp + ffmpeg 분할)

PDF: reportlab로 페이지 수별 테스트 파일 생성


가격

001 대비 33% 비싸지만 멀티모달을 지원한다. 양쪽 모두 Batch API 50% 할인을 지원한다.

* embedding-2-preview 모달리티별 가격: Text $0.20 (Batch $0.10) | Image $0.45 | Audio $6.50 | Video $12.00 /1M tokens


Silent Truncation이란?

입력이 모델의 토큰 한도를 초과할 때, API가 에러를 반환하지 않고 한도까지의 앞부분만 처리한 뒤 정상 응답을 반환하는 동작이다. 초과된 뒷부분은 임베딩에 전혀 반영되지 않으며, 개발자는 잘림이 발생했는지 알 수 없다.


검증 방법

1단계 - 경계 탐색: 동일 소스를 다양한 길이로 잘라 임베딩한다. 길이가 달라도 벡터가 동일해지는 지점이 잘림 (예: 30초/31초/32초/.../60초 클립 -> 32초 이상 클립은 벡터 완전 동일(cos=1.0) -> 잘림 경계 = 31~32초)

2단계 - 완전 폐기 검증: '뒷부분 완전 폐기'인지 '앞부분 가중치 쏠림'인지 구분하기 위해, 영상의 서로 다른 구간에서 클립을 추출하여 교차 비교한다. A(0~30초) vs B(300~360초) 유사도 = A(0~30초) vs B(300~330초) 유사도 -> B의 330~360초 구간은 벡터에 전혀 영향 없음 -> 완전 폐기 확정

3단계 - 에러 여부 확인: 모달리티마다 초과 시 에러를 반환하는지, 조용히 잘리는지 확인한다.


모달리티별 결과


텍스트: 8,192 토큰에서 Silent Truncation

동일한 접두사(45,000글자, 8192토큰) 뒤에 완전히 다른 내용을 붙여 임베딩했다.

- prefix(45,000자, 8192토큰) + 영어 11만자 vs prefix만 -> cos=1.000000, 벡터 동일

- prefix(45,000자, 8192토큰) + 한글 7만자 vs prefix만 -> cos=1.000000, 벡터 동일

한국어 주의: 한국어는 영어 대비 글자당 토큰 소모가 2.5~3배다. 영어 22,500자 = 5,002토큰이지만, 한국어 26,500자 = 14,003토큰. 단순 계산으로는 8,192토큰 = 한국어 약 15,000자이지만, 실제 텍스트 구성(공백, 문장 부호, 혼용 영어 등)에 따라 편차가 크므로 안전 마진을 두고 청킹해야 한다.


비디오: 공식 한도 128초, 실제 반영 31~32초


공식 문서에는 비디오 한도가 128초라고 적혀 있다. 실제로 300초 영상도 에러 없이 임베딩이 '성공'한다. 하지만 벡터를 비교하면:

32초 클립과 300초 클립의 벡터가 완전히 동일하다. 앞 31~32초 이후는 전부 버려진다.

교차 검증 결과도 명확하다. 영상 앞부분(0~30초)과 뒷부분(300~360초)에서 추출한 클립을 비교했을 때, 60초 클립의 30~60초 구간이 유사도에 전혀 영향을 주지 않았다. 완전 폐기 확정.

참고: 생성 모델(gemini-2.0-flash-lite) 기준 32초 = ~9,700토큰. 생성 모델과 임베딩 모델의 내부 토크나이저가 다르기 때문에 정확히 환산할 수는 없지만, 임베딩 모델 내부에서도 8,192 토큰 한도가 비디오에 동일하게 적용되는 것으로 추정된다.


오디오: Hard Reject - 가장 정직한 동작


공식 문서에는 오디오 한도가 80초로 적혀 있다. 실측 결과 81초까지는 성공, 82초에서 에러가 발생했다. 공식 한도와 1초 차이.

- 81초까지: 성공. 모든 길이에서 벡터가 다름 -> 전체 내용 반영

- 82초부터: 즉시 에러 (400 INVALID_ARGUMENT)

Silent Truncation이 없다. 한도를 넘으면 에러로 알려주고, 한도 내에서는 전체를 반영한다. 오디오의 초당 토큰은 ~32로 비디오의 약 1/10이며, 81초 = 2,595토큰으로 8,192 토큰 한도 내에 여유 있게 들어간다.



이미지: 입력 방식에 따라 동작이 다르다


공식 문서에는 요청당 이미지 6장 한도로 적혀 있다. 이미지 1장당 258토큰(해상도 무관). 그런데 입력 방식이 두 가지이며 동작이 다르다.


개별 입력 (contents=[part1, part2, ...]): 이미지별 개별 벡터 반환. 8장까지 성공.

통합 벡터 (contents=[Content(parts=[...])]): 단일 벡터. 여기서 문제가 발생한다.


이미지를 1장씩 누적하며 테스트한 결과:

- 1장 -> 2장: 벡터 변화 (cos=0.676) -> 2장째 반영됨

- 2장 -> 3장: cos=1.000 -> 3장째부터 무시

- 3~6장: 모두 2장 벡터와 동일

- 7장: 에러

토큰 수(516)는 8,192 한도에 한참 못 미치므로, 이미지 전용 내부 처리 한도가 별도로 존재하는 것으로 보인다.


PDF: 혼합 패턴 - 공식 한도 6페이지, 실제 반영 4페이지


공식 문서에는 PDF 한도가 6페이지로 적혀 있다. 하지만 실측 결과는 더 복잡하다.

- 4페이지까지: 정상 반영 (4p vs 4p_v2 = cos 0.9538, 내용 차이 반영됨)

- 5페이지: API가 수용하지만 5번째 페이지 내용은 Silent Truncation

(4p vs 5p = cos 1.0000, 동일 벡터)

- 6페이지 이상: 에러 반환


5페이지 PDF가 가장 위험하다. 에러도 없이 성공하지만, 마지막 페이지는 임베딩에 반영되지 않는다.

해상도와 품질은 토큰 수에 영향을 주는가?

이미지 해상도를 높이거나 오디오 비트레이트를 올리면 토큰을 더 소모할까?


이미지 해상도: 토큰 수 불변


동일한 이미지를 64px부터 3840px까지 리사이즈하여 테스트했다.

토큰 수는 해상도와 무관하게 고정 259토큰. 모델이 내부적으로 고정 크기로 리사이즈하여 처리. 단,

극저해상도(64px)에서는 cos 0.959까지 하락하므로 적절한 해상도 유지 필요.


오디오 비트레이트: 토큰 수 불변


동일한 30초 오디오를 32kbps~320kbps로 인코딩하여 테스트했다.

비트레이트 10배 차이에도 토큰 수 동일. 저비트레이트(32kbps)에서 cos 0.917 하락 -> 압축 손실이 임베딩에 반영됨.


오디오 샘플레이트 & 포맷

샘플레이트에 따라 +/-5 토큰의 미세 차이만 존재. MP3/WAV 포맷 차이도 토큰 수에 실질적 영향 없음.


결론: 이미지 해상도와 오디오 품질은 토큰 소모량에 영향을 주지 않는다. 모델이 내부적으로 정규화하여 처리하기 때문. 단, 극저품질 입력은 임베딩 벡터 품질을 떨어뜨리므로 검색 정확도에 영향.



세 가지 패턴으로 정리된다:

패턴 A (Silent Truncation): 텍스트, 비디오, 이미지(통합). 에러 없이 앞부분만 처리. 가장 위험.

패턴 B (Hard Reject): 오디오. 한도 초과 시 즉시 에러. 한도 내 전체 반영. 가장 안전.

패턴 C (혼합): PDF. 일부 구간 Silent Truncation + 일정 수 초과 시 에러.


embedding-001 vs embedding-2-preview


001에서 2-preview로 마이그레이션할 때 주의할 점:


1. 토큰 한도 4배 증가: 001의 2,048토큰 -> 2-preview의 8,192토큰. 기존 청킹 전략을 재조정할 수 있다.

2. task_type 무시: 2-preview에서는 task_type 파라미터가 완전히 무시된다. RETRIEVAL_DOCUMENT든, CLUSTERING이든 동일한 벡터가 나온다.

3. MRL 차원 축소: 양 모델 모두 128~3,072 차원을 지원한다. 저장 비용 최적화가 필요하면 128이나 256

차원도 사용 가능.



실전 청킹 시

핵심 원칙: 공식 문서 한도가 아닌 실측 반영 범위 기준으로 청킹해야 한다. 비디오를 128초 단위로 잘라 넣으면 각 청크의 앞 31초만 임베딩에 반영되고, 나머지 97초는 조용히 버려진다.


마무리


Gemini Embedding 2는 멀티모달 통합 벡터 공간이라는 매력적인 기능을 제공하지만, Silent Truncation은 프로덕션에서 심각한 데이터 손실을 일으킬 수 있다. 특히 비디오에서 공식 한도(128초)와 실제 반영 범위(31초)의 4배 차이는 반드시 인지하고 대응해야 한다. 테스트 없이 공식 문서만 믿고 청킹 전략을 세우면 안 된다.

매거진의 이전글AI시대 새로운 낭만, Entrepreneurship