방법: LLM 게이트웨이 통합에 관한 중국어 문서 20개와 일치하는 영어 문서 20개, 수작업으로 라벨링된 답변이 있는 중국어 쿼리 20개와 영어 쿼리 20개를 사용했으며, 세 모델을 모두 같은 시간 창에서 같은 코퍼스에 대해 실행했습니다. 코퍼스가 작으므로 5퍼센트포인트 미만의 차이는 결론적이지 않습니다 — 어떤 내용을 최종적으로 받아들이기 전에 반드시 자신의 데이터에서 재현하십시오.
1. 먼저 적절한 모델을 선택합니다
bge-m3를 선택하는 경우
- 말뭉치가 대부분 중국어(또는 일본어 / 러시아어)인 경우: 검색 품질은 3-small과 같고, 목록 가격은 절반이며, 같은 텍스트는 필요한 tokens가 42%에 불과합니다 — 실제 지출은 약 1/5 수준입니다
- 더 작은 저장 공간이 필요한 경우: 1024 차원은 1536보다 33% 작고 3072보다 66% 작습니다
- 롱테일 언어(100개 이상 지원)에 대한 커버리지가 필요한 경우
- 동일한 오픈소스 모델을 로컬에서 실행하여 오프라인과 온라인 벡터가 일치하길 원하는 경우
OpenAI를 선택하는 경우
- 말뭉치가 대부분 영어 또는 코드인 경우: 품질이 한 단계 더 높습니다. bge-m3는 이러한 콘텐츠에서 15%–50% 더 많은 tokens를 소모하지만, 단가가 절반이므로 전체적으로는 여전히 더 저렴합니다 — 따라서 여기서는 가격이 아니라 품질로 판단하세요
- 지식 베이스가 여러 언어가 섞여 있으며 답변을 하나만 받으면 되는 경우
- 저장 공간을 줄이기 위해
dimensions가 필요한 경우 - 이미 OpenAI 점수 범위에 맞춰 임계값을 보정해 두었고 다시 조정하고 싶지 않은 경우
2. 긴 문서는 항상 청크로 나누십시오
bge-m3에는 8192-token 윈도우가 있으므로 긴 매뉴얼도 한 번의 호출에 들어갑니다. 하지만 그렇다고 해서 그 방법이 좋은 것은 아닙니다.
측정 결과: 20개 섹션을 하나의 긴 매뉴얼로 이어 붙이고, 20개의 짧은 문서(각각 하나의 섹션에 해당)를 강한 방해 요소로 추가한 뒤 20개의 질문으로 조회했을 때 —
같은 질문에 대해, 올바른 섹션은 전체 문서보다 평균적으로 +0.10 높게 점수화됩니다:
긴 문서의 단일 벡터는 그 안에 있는 모든 것의 평균이므로, 정확하게 표현된 짧은 문단이 언제나 그것을 이깁니다.
3. 임계값은 모델별로 재보정해야 합니다
OpenAI에서bge-m3로 마이그레이션할 때 가장 자주 잘못되는 부분이 바로 여기입니다. 두 점수 범위는 완전히 다릅니다.
같은 수작업 라벨링 쌍을 세 모델이 점수화한 결과는 다음과 같습니다:
bge-m3에 대해 측정된 최적의 단일 임계값:
비교를 위해, 중국어 시나리오에서의 최적 임계값은
text-embedding-3-small의 경우 0.45, 3-large의 경우 0.33입니다.
4. 유사도만으로는 어떤 것이 정확한지 알 수 없습니다
이것은 모든 임베딩 모델에 해당합니다 — 특정 모델 하나의 결함이 아니라, 미리 알고 계셔야 합니다:
세 경우 모두 실패합니다. 코사인 유사도는 두 텍스트가 같은 주제에 관한 것인지 측정할 뿐, 서로 동의하는지는 측정하지 않습니다.
부정, 가격, 버전 번호 및 엔티티 이름은 검색 단계에서 분리할 수 없습니다. 올바른
대체 방안은 다음과 같습니다:
1
벡터 리콜, Top 50–100
bge-m3를 사용하여 후보 범위를 빠르게 좁히십시오. 임계값은 명백히 관련 없는 항목만 걸러냅니다.2
Top 3–5로 재랭크
후보를
bge-reranker-v2-m3로 보내십시오. 이것은 쿼리와 문서를 모델에 함께 통과시키는 cross-encoder이며, 이런 미세한 구분에 정확히 맞는 도구입니다 — 그리고 bge-m3와 같은 모델 계열에서 나왔습니다.3
생성 시점에 LLM이 판단하게 합니다
원래 질문과 함께 Top 3–5를 전달하고, 검색된 내용이 질문과 일치하지 않으면 아무것도 찾지 못했다고 말하도록 prompt에 명시적으로 적으십시오.
5. 배치 크기와 동시 실행 수
배치 처리: 변곡점은 64–128입니다
128을 넘으면 항목당 비용은 거의 개선되지 않으며(58ms → 53ms), 단일 요청은 7배로 커집니다.
요청 지속 시간은 클라이언트 타임아웃 위험과 한 번의 실패가 얼마나 많은 작업을 날리는지를 직접 좌우합니다.
동시 실행 수: 온라인용 8, 대량 인덱싱용 32–48
- 실시간 검색에는 동시 실행 수 8을 사용하십시오: 실패 없이 200 requests를 처리합니다
- 대량 인덱싱은 32–48까지 가능합니다: 처리량이 가장 높지만 429s가 나타나기 시작하므로 지수 백오프가 필요합니다
- 64를 초과하지 마십시오: 96에서는 실패율이 11.8%이며 요청이 약 60초 동안 멈추기 시작합니다
6. 비용이 실제로 계산되는 방식
비용은 두 가지의 곱입니다. 단가와 주어진 텍스트 조각이 몇 개의 tokens가 되는지입니다. 여기서는 둘 다 다릅니다.bge-m3는 $0.01 / 1M tokens이고, text-embedding-3-small는 $0.02입니다. 먼저 단가만 해도 절반입니다.
여기에 더해 토크나이저 효율도 있습니다. bge-m3는 XLM-R SentencePiece를 사용하며 토큰당 약 2.1개의 중국어 문자를 처리하고,
반면 OpenAI의 cl100k는 약 0.9입니다.
중국어 말뭉치의 비용은 대략
text-embedding-3-small의 5분의 1입니다.
영어와 코드는 bge-m3에서 더 많은 tokens를 소모하지만, 단가가 절반이기 때문에 총 지출에서는 여전히 OpenAI보다 낮습니다. 따라서 이 두 말뭉치에서는 선택 기준이 검색 품질(English Recall@1 70% vs 80%)이지, 가격이 아닙니다.
저장 방식도 다릅니다. 벡터는 이미 L2 정규화되어 있고 반환되는 값은 fp16 정밀도이므로,
bge-m3 벡터를 float16으로 저장해도 정확도 손실은 전혀 없습니다.
7. 조용히 드러나지 않는 실패 모드
7.1 LangChain의 기본값은 재현율을 80%에서 15%로 떨어뜨립니다
측정 결과:
해결 방법:
7.2 빈 문자열도 유효한 입력으로 받아들여집니다
input: ""는 bge-m3에서 200을 반환합니다(OpenAI는 여기서 400을 반환합니다). 그 결과 1024차원 벡터가 생성되고 2 tokens가 과금됩니다.
빈 청크를 필터링하지 않는 청킹 스크립트는 검색 중 무작위로 나타나는 무의미한 벡터로 인덱스를 채우게 됩니다. 인덱싱하기 전에 빈 텍스트를 필터링하십시오.
7.3 dimensions 파라미터는 거부됩니다
Model "bge-m3" does not support matryoshka representation, changing output dimensions will lead to poor results.
bge-m3는 Matryoshka 표현으로 학습되지 않았으므로, 벡터를 잘라내면 품질이 눈에 띄게 저하됩니다. 차원을 직접 줄이기보다 float16을 사용해 저장 공간을 줄이십시오.
7.4 모델 이름은 대소문자를 구분하며 별칭이 없습니다
오직bge-m3만 작동합니다. BAAI/bge-m3과 BGE-M3는 모두 503 “사용 가능한 채널이 없습니다”를 반환합니다.
7.5 8192는 입력별 제한입니다
8192 tokens를 넘는 단일 입력은 400을 반환하며 절대 조용히 잘리지 않습니다 — 이것이 더 안전한 동작입니다. 텍스트의 후반부를 조용히 잃어버린 듯한 정상적인 벡터를 받지 않게 되기 때문입니다. 이 제한은 요청 단위가 아니라 항목당 적용됩니다. 테스트에서는 1024 항목 / 102560 tokens를 실은 단일 호출이 정상적으로 반환되었습니다. 어떤 한 항목이라도 제한을 초과하면 전체 요청이 실패하며, 오류의 token 수는 전체가 아니라 해당 항목을 가리킵니다.8. 복사해서 사용할 수 있는 최소 구현
관련 문서
텍스트 임베딩 API
매개변수, 응답 형식, 빠른 시작
재정렬
bge-reranker-v2-m3, 정밀 단계에 적합한 도구RAG 튜닝
2단계 검색, 얼마나 많은 후보를 재현할지
모델 요금
모든 임베딩 모델의 실시간 요금