임베딩 관리
기술 스택
- 모델:
text-embedding-3-large (OpenAI)
- 차원: 3072 (기본값,
dimensions 옵션으로 축소 가능)
- 입력 한도: 8K 토큰
- 패키지:
openai
- API 키:
OPENAI_API_KEY 환경변수 필수
LLM 추상화 미사용
임베딩은 packages/core/src/llm/ 추상화 계층을 사용하지 않고, packages/core/src/embedding/client.ts에서 openai 패키지를 직접 호출한다. 따라서 임베딩은 OpenAI 전용이며, LLM 프로바이더 설정(ANKI_SPLITTER_DEFAULT_LLM_PROVIDER)과 독립적으로 동작한다.
모듈 구조 (packages/core/src/embedding/)
| 파일 |
역할 |
client.ts |
OpenAI 임베딩 API 클라이언트 (text-embedding-3-large) |
cosine.ts |
코사인 유사도 계산 (0-100%) |
cache.ts |
파일 기반 증분 캐시 (호환성 검사, 레거시 마이그레이션 포함) |
index.ts |
모듈 re-export |
주요 상수
export const EMBEDDING_PROVIDER = "openai";
export const EMBEDDING_MODEL = "text-embedding-3-large";
export const EMBEDDING_EXPECTED_DIMENSION = 3072;
// 레거시 (Gemini → OpenAI 마이그레이션 감지용)
export const LEGACY_EMBEDDING_PROVIDER = "gemini";
export const LEGACY_EMBEDDING_MODEL = "gemini-embedding-001";
주요 함수
// 단일 텍스트 임베딩
const embedding = await getEmbedding(text, options?); // number[] (3072차원)
// 의미적 유사도
const similarity = await getSemanticSimilarity(text1, text2, options?); // 0-100 (%)
// 배치 임베딩 (BATCH_SIZE=100, rate limit 딜레이 자동 적용)
const embeddings = await getEmbeddings(texts, options?, onProgress?); // number[][]
// 유사성 검사 (임베딩 모드)
const result = await checkSimilarity(targetCard, allCards, {
useEmbedding: true, deckName: '덱명', threshold: 85
});
EmbeddingOptions는 dimensions?: number 필드 하나만 가진다. 지정하면 모델이 차원 축소된 벡터를 반환한다.
배치 처리 및 재시도
- BATCH_SIZE: 100건씩 OpenAI API 호출
- 배치 간 딜레이: 350ms (
EMBEDDING_BATCH_DELAY_MS)
- 재시도: 최대 2회 (
EMBEDDING_MAX_ATTEMPTS), 재시도 간 350ms 대기
- 재시도 대상: 408, 429, 500, 502, 503, 504 상태코드 및 네트워크 오류
텍스트 전처리
preprocessTextForEmbedding() — 임베딩 생성 전 반드시 적용 (자동):
- Cloze 구문에서 내용만 추출 (
{{c1::DNS}} → DNS)
- HTML 태그 제거
- 컨테이너 구문 제거 (
::: tip 등)
- nid 링크에서 제목만 추출
캐시 전략
API 엔드포인트
| Method |
Path |
설명 |
| POST |
/api/embedding/generate |
덱 전체 임베딩 생성 (증분, 마이그레이션 자동) |
| GET |
/api/embedding/status/:deckName |
캐시 상태 + 호환성 확인 |
| DELETE |
/api/embedding/cache/:deckName |
캐시 삭제 |
| POST |
/api/embedding/single |
단일 텍스트 임베딩 (디버깅) |
자주 발생하는 문제
- OPENAI_API_KEY 미설정:
OPENAI_API_KEY가 설정되지 않았습니다 에러 → .env에 키 추가
- Rate Limit 429: 배치 처리 시 자동 딜레이 적용, 서버 라우트에서도 10건마다 400ms 대기
- 캐시 마이그레이션: Gemini에서 OpenAI로 변경 후 첫 실행 시 자동 감지 및 재생성
- 차원 불일치: 레거시 캐시는 768차원 (Gemini), 현재는 3072차원 (OpenAI) — 호환성 검사가 자동 처리
- 캐시 위치 혼동: 덱 이름을 MD5 해시로 변환하여 파일명 생성
상세 참조
references/embedding-system.md — OpenAI text-embedding-3-large, 캐시 호환성/마이그레이션 상세
references/preprocessing.md — Cloze/HTML/컨테이너 제거 로직
references/troubleshooting.md — OPENAI_API_KEY, rate limit, 캐시 마이그레이션
1---2name: managing-embeddings3description: USE THIS SKILL for anything related to embeddings, vector similarity, or semantic search. Triggers: "임베딩 생성", "코사인 유사도", "캐시 어디에", "임베딩 상태", "의미 유사도", "text-embedding-3-large", "openai embedding", "벡터 차원", "3072", "임베딩 캐시", "embedding 마이그레이션", "레거시 캐시", "embedding fallback", "유사도 검사 임베딩 모드", "임베딩 배치", "rate limit 429". Covers OpenAI embedding API, file-based cache with migration, and text preprocessing.4---56# 임베딩 관리78## 기술 스택910- **모델**: `text-embedding-3-large` (OpenAI)11- **차원**: 3072 (기본값, `dimensions` 옵션으로 축소 가능)12- **입력 한도**: 8K 토큰13- **패키지**: `openai`14- **API 키**: `OPENAI_API_KEY` 환경변수 필수1516## LLM 추상화 미사용1718> 임베딩은 `packages/core/src/llm/` 추상화 계층을 사용하지 않고, `packages/core/src/embedding/client.ts`에서 `openai` 패키지를 직접 호출한다. 따라서 임베딩은 OpenAI 전용이며, LLM 프로바이더 설정(`ANKI_SPLITTER_DEFAULT_LLM_PROVIDER`)과 독립적으로 동작한다.1920## 모듈 구조 (packages/core/src/embedding/)2122| 파일 | 역할 |23|------|------|24| `client.ts` | OpenAI 임베딩 API 클라이언트 (`text-embedding-3-large`) |25| `cosine.ts` | 코사인 유사도 계산 (0-100%) |26| `cache.ts` | 파일 기반 증분 캐시 (호환성 검사, 레거시 마이그레이션 포함) |27| `index.ts` | 모듈 re-export |2829## 주요 상수3031```typescript32export const EMBEDDING_PROVIDER = "openai";33export const EMBEDDING_MODEL = "text-embedding-3-large";34export const EMBEDDING_EXPECTED_DIMENSION = 3072;3536// 레거시 (Gemini → OpenAI 마이그레이션 감지용)37export const LEGACY_EMBEDDING_PROVIDER = "gemini";38export const LEGACY_EMBEDDING_MODEL = "gemini-embedding-001";39```4041## 주요 함수4243```typescript44// 단일 텍스트 임베딩45const embedding = await getEmbedding(text, options?); // number[] (3072차원)4647// 의미적 유사도48const similarity = await getSemanticSimilarity(text1, text2, options?); // 0-100 (%)4950// 배치 임베딩 (BATCH_SIZE=100, rate limit 딜레이 자동 적용)51const embeddings = await getEmbeddings(texts, options?, onProgress?); // number[][]5253// 유사성 검사 (임베딩 모드)54const result = await checkSimilarity(targetCard, allCards, {55 useEmbedding: true, deckName: '덱명', threshold: 8556});57```5859`EmbeddingOptions`는 `dimensions?: number` 필드 하나만 가진다. 지정하면 모델이 차원 축소된 벡터를 반환한다.6061## 배치 처리 및 재시도6263- **BATCH_SIZE**: 100건씩 OpenAI API 호출64- **배치 간 딜레이**: 350ms (`EMBEDDING_BATCH_DELAY_MS`)65- **재시도**: 최대 2회 (`EMBEDDING_MAX_ATTEMPTS`), 재시도 간 350ms 대기66- **재시도 대상**: 408, 429, 500, 502, 503, 504 상태코드 및 네트워크 오류6768## 텍스트 전처리6970`preprocessTextForEmbedding()` — 임베딩 생성 전 반드시 적용 (자동):711. Cloze 구문에서 내용만 추출 (`{{c1::DNS}}` → `DNS`)722. HTML 태그 제거733. 컨테이너 구문 제거 (`::: tip` 등)744. nid 링크에서 제목만 추출7576## 캐시 전략7778- **저장 위치**: `EMBEDDING_CACHE_DIR` 환경변수 또는 `output/embeddings/` (기본)79- **파일명**: `{deckNameHash}.json` (MD5 앞 12자)80- **캐시 구조**:81 ```typescript82 interface EmbeddingCache {83 schemaVersion: number; // EMBEDDING_CACHE_SCHEMA_VERSION = 184 deckName: string;85 provider: string; // "openai"86 model: string; // "text-embedding-3-large"87 dimension: number; // 307288 lastUpdated: number;89 embeddings: Record<string, CachedEmbedding>;90 }91 ```92- **증분 업데이트**: MD5 해시로 텍스트 변경 감지, 변경된 카드만 재생성93- **호환성 검사**: `getCacheIncompatibilityReason(cache)` — schema/provider/model 불일치 감지94 - 불일치 시 자동으로 캐시 재생성 (레거시 Gemini 캐시 → OpenAI 캐시 마이그레이션)95- **캐시 확인**: `GET /api/embedding/status/:deckName`96- **캐시 삭제**: `DELETE /api/embedding/cache/:deckName`9798## API 엔드포인트99100| Method | Path | 설명 |101|--------|------|------|102| POST | /api/embedding/generate | 덱 전체 임베딩 생성 (증분, 마이그레이션 자동) |103| GET | /api/embedding/status/:deckName | 캐시 상태 + 호환성 확인 |104| DELETE | /api/embedding/cache/:deckName | 캐시 삭제 |105| POST | /api/embedding/single | 단일 텍스트 임베딩 (디버깅) |106107## 자주 발생하는 문제108109- **OPENAI_API_KEY 미설정**: `OPENAI_API_KEY가 설정되지 않았습니다` 에러 → `.env`에 키 추가110- **Rate Limit 429**: 배치 처리 시 자동 딜레이 적용, 서버 라우트에서도 10건마다 400ms 대기111- **캐시 마이그레이션**: Gemini에서 OpenAI로 변경 후 첫 실행 시 자동 감지 및 재생성112- **차원 불일치**: 레거시 캐시는 768차원 (Gemini), 현재는 3072차원 (OpenAI) — 호환성 검사가 자동 처리113- **캐시 위치 혼동**: 덱 이름을 MD5 해시로 변환하여 파일명 생성114115## 상세 참조116117- `references/embedding-system.md` — OpenAI text-embedding-3-large, 캐시 호환성/마이그레이션 상세118- `references/preprocessing.md` — Cloze/HTML/컨테이너 제거 로직119- `references/troubleshooting.md` — OPENAI_API_KEY, rate limit, 캐시 마이그레이션