# 370. 임베딩 서비스 분리 아키텍처 ## 개요 각 로빙이 독립적으로 ONNX 임베딩 모델을 로드하던 구조에서 중앙 임베딩 서비스를 공유하는 구조로 전환하여, 메모리 사용량을 극적으로 감소시키고 확장성을 확보했습니다. ## 문제점 - 각 로빙마다 동일한 임베딩 모델(449MB) 중복 로드 - rb10508_micro: 987.9MB 메모리 사용 - 로빙 추가 시 선형적 메모리 증가 ## 해결책: 중앙 임베딩 서비스 ### 아키텍처 ``` 이전: 이후: ┌─────────────┐ ┌─────────────┐ │ rb10508 │ │ rb10508 │ │ ONNX Model │ │ (118MB) │──┐ │ (988MB) │ └─────────────┘ │ └─────────────┘ │ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ rb8001 │ │ rb8001 │ │ skill-embedding │ │ ONNX Model │ → │ (200MB) │──│ (874.4MB) │ │ (416MB) │ └─────────────┘ │ - ONNX Model │ └─────────────┘ │ - Port 8015 │ ▼ └─────────────────┘ ┌─────────────┐ ┌─────────────┐ │ rb10408 │ │ rb10408 │ │ ONNX Model │ │ (30MB) │──┘ │ (55MB) │ └─────────────┘ └─────────────┘ ``` ### 기술 스택 - **FastAPI + Uvicorn**: 고성능 비동기 웹 서버 - **ONNX Runtime**: 최적화된 임베딩 생성 - **multilingual-MiniLM-L12-v2**: 다국어 지원 임베딩 모델 ### API 설계 ```python # POST /embed { "texts": ["안녕하세요", "오늘 날씨가 좋네요"] } # Response { "embeddings": [ [0.1, 0.2, ...], # 384차원 벡터 [0.3, 0.4, ...] ] } # GET /health { "status": "healthy", "service": "skill-embedding", "model": "multilingual-MiniLM-L12-v2", "uptime": 3600.5 } ``` ## 구현 가이드 ### 1. 로빙 측 변경사항 ```python # 기존: ONNX 직접 로드 from onnx_embedder import ONNXEmbedder embedder = ONNXEmbedder("/models/onnx/...") # 변경: HTTP 임베딩 함수 class HTTPEmbeddingFunction(EmbeddingFunction): def __init__(self): self.url = f"{os.getenv('SKILL_EMBEDDING_URL', 'http://localhost:8015')}/embed" def __call__(self, input: List[str]) -> List[List[float]]: response = requests.post(self.url, json={"texts": input}, timeout=30) return response.json()["embeddings"] ``` ### 2. ChromaDB 통합 ```python # memory.py self.episodic = self.client.get_or_create_collection( name=f"{self.robing_id}_episodic", embedding_function=HTTPEmbeddingFunction() # HTTP 방식으로 변경 ) ``` ### 3. Docker 구성 변경 ```yaml # 불필요한 볼륨 제거 # - /opt/models:/models:ro # 더 이상 필요 없음 # 환경변수 추가 environment: - SKILL_EMBEDDING_URL=http://localhost:8015 ``` ## 성능 분석 ### 메모리 절감 - **rb10508_micro**: 988MB → 118MB (-870MB, 88% 감소) - **예상 절감 (100개 로빙)**: 87GB 메모리 절약 ### 레이턴시 - **HTTP 오버헤드**: +7ms (무시할 수준) - **전체 응답시간**: 1-3초 중 0.2% 미만 ### 확장성 - 단일 임베딩 서비스로 수백 개 로빙 지원 - 수평 확장 가능 (로드밸런서 적용 시) ## 모니터링 ```python # 주요 메트릭 - 임베딩 생성 요청 수 - 평균 응답 시간 - 메모리 사용량 - 에러율 # 헬스체크 curl http://localhost:8015/health ``` ## 다음 단계 1. **캐싱 레이어 추가**: Redis로 자주 사용되는 임베딩 캐싱 2. **배치 처리 최적화**: 대량 텍스트 임베딩 성능 개선 3. **모델 업데이트**: L12 → L6 모델로 추가 경량화 검토 4. **다른 로빙 적용**: rb8001, rb10408에 순차 적용 ## 교훈 - **중복 제거의 힘**: 동일한 기능을 서비스로 분리하면 극적인 효율성 향상 - **간단한 구현**: 12줄의 HTTPEmbeddingFunction으로 870MB 절약 - **점진적 적용**: 하나의 로빙에서 성공 후 확산 --- *"임베딩은 공유하되, 기억은 분리하라"*