DOCS/ideas/250804_로빙_임베딩_서비스_분리_아키텍처.md
Claude-51124 e3a75c5df8 Add embedding service separation architecture design
로빙 임베딩 서비스 분리 아키텍처 설계서 추가:
- 현재 메모리 사용량 분석 (rb10508_micro: 987.9MB)
- 임베딩 서비스 공유 + 기억 저장소 분리 아키텍처
- 구체적 구현 방안 (HTTP API, ChromaDB 분리)
- 메모리 절약 효과 (228MB + 확장성)
- 단계별 구현 계획 및 위험 완화 방안

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 00:13:08 +09:00

8.3 KiB

로빙 임베딩 서비스 분리 아키텍처

작성일: 2025년 8월 4일 작성자: Claude (51124 서버)

배경

현재 각 로빙 컨테이너마다 독립적으로 ONNX 임베딩 모델(449MB)을 로드하여 메모리 사용량이 높은 상황입니다. rb10508_micro의 경우 987.9MB를 사용하고 있으며, 장기 운영 시 메모리 부족 위험이 있습니다.

현재 메모리 사용 현황

  • rb10508_micro: 987.9MB (ONNX 모델 포함)
  • rb8001: 416.4MB
  • rb10408_test: 54.96MB
  • 각 컨테이너마다 독립적인 임베딩 모델 로드

제안 아키텍처: 임베딩 서비스 분리

핵심 아이디어

  • 임베딩 생성: 공유 서비스 사용
  • 기억 저장소: 각 로빙별 완전 분리
  • 프라이버시: 로빙 간 기억 접근 불가
  • 효율성: 메모리 절약 + 확장성

아키텍처 구조

                    임베딩 서비스 (공유)
                    ├── ONNX 모델 (449MB)
                    ├── FastAPI HTTP API
                    └── 포트: 8600
                           ↑
                    ┌──────┴──────┐
rb8001 컨테이너              rb10508 컨테이너  
├── ChromaDB (/chroma_db)   ├── ChromaDB (/chroma_db_micro)
├── HTTP 클라이언트         ├── HTTP 클라이언트  
├── 기억: rb8001 전용       ├── 기억: rb10508 전용
└── 메모리: ~200MB          └── 메모리: ~400MB

구현 방안

1. 임베딩 서비스 구성

Docker Compose 설정

services:
  embedding-service:
    build: ./embedding_service
    container_name: embedding_service
    network_mode: host
    ports:
      - "8600:8600"
    volumes:
      - /home/admin/ivada_project/onnx_models:/models/onnx:ro
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8600/health"]
      interval: 30s
      timeout: 10s
      retries: 3

임베딩 서비스 API

# embedding_service/main.py
from fastapi import FastAPI
from onnx_embedder import ONNXEmbedder

app = FastAPI()
embedder = ONNXEmbedder("/models/onnx/multilingual-MiniLM-L12-v2")

@app.post("/embed")
async def create_embeddings(request: EmbedRequest):
    embeddings = []
    for text in request.texts:
        embedding = embedder.encode(text)
        embeddings.append(embedding.tolist())
    
    return {"embeddings": embeddings}

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

2. 로빙별 ChromaDB 분리

볼륨 마운트 구조

호스트 디렉토리:
/home/admin/ivada_project/
├── rb8001/chroma_db/              # rb8001 전용 기억
├── rb10508_micro/chroma_db_micro/ # rb10508 전용 기억  
├── rb10408/chroma_db/             # rb10408 전용 기억
└── onnx_models/                   # 공유 모델 (임베딩 서비스용)

로빙별 컬렉션 ID 분리

# rb8001의 memory.py
class Memory:
    def __init__(self):
        self.client = chromadb.PersistentClient(path="/code/chroma_db")
        
        # rb8001 전용 컬렉션들
        self.episodic = self.client.get_or_create_collection(
            name="rb8001_episodic",
            embedding_function=HTTPEmbeddingFunction()
        )
        self.semantic = self.client.get_or_create_collection(
            name="rb8001_semantic",
            embedding_function=HTTPEmbeddingFunction()
        )

# rb10508의 memory.py  
class Memory:
    def __init__(self):
        self.client = chromadb.PersistentClient(path="/code/chroma_db")
        
        # rb10508 전용 컬렉션들
        self.episodic = self.client.get_or_create_collection(
            name="rb10508_episodic",
            embedding_function=HTTPEmbeddingFunction()
        )

3. HTTP 임베딩 함수 구현

# common/http_embedding_function.py
import requests
from chromadb.api.types import EmbeddingFunction

class HTTPEmbeddingFunction(EmbeddingFunction):
    def __init__(self, embedding_service_url="http://localhost:8600"):
        self.url = f"{embedding_service_url}/embed"
        
    def __call__(self, texts):
        response = requests.post(self.url, 
                               json={"texts": texts},
                               timeout=10)
        response.raise_for_status()
        return response.json()["embeddings"]

동작 흐름

메모리 저장 과정

현재 방식

def store_memory(self, content):
    # 로컬 ONNX 모델로 임베딩 생성 (987MB 메모리 사용)
    embedding = self.embedding_function(content)
    
    # ChromaDB에 저장
    self.episodic.add(
        documents=[content],
        embeddings=[embedding],
        ids=[memory_id]
    )

임베딩 서비스 방식

def store_memory(self, content):
    # HTTP로 임베딩 서비스 호출
    response = requests.post("http://localhost:8600/embed", 
                           json={"texts": [content]})
    embedding = response.json()["embeddings"][0]
    
    # ChromaDB에 저장 (임베딩은 받은 값 사용)
    self.episodic.add(
        documents=[content],
        embeddings=[embedding],
        ids=[memory_id]
    )

실제 대화 시나리오

rb8001과의 대화

사용자 → rb8001: "오늘 피자 먹었어"

1. rb8001이 임베딩 서비스 호출
   POST localhost:8600/embed {"texts": ["오늘 피자 먹었어"]}
   
2. rb8001의 ChromaDB에만 저장
   rb8001_episodic.add(
       documents=["오늘 피자 먹었어"],
       embeddings=[[0.1, 0.2, ...]],
       metadata={"user_id": "user123", "timestamp": "..."}
   )

rb10508과의 대화

사용자 → rb10508: "어제 뭐 먹었지?"

1. rb10508이 임베딩 서비스 호출 (같은 서비스 사용)
   POST localhost:8600/embed {"texts": ["어제 뭐 먹었지"]}
   
2. rb10508의 ChromaDB에서만 검색 (rb8001 기억 접근 불가)
   rb10508_episodic.query(query_embeddings=[[0.2, 0.1, ...]])
   → 결과 없음 (rb8001의 "피자" 기억은 모름)
   
3. rb10508: "죄송해요, 어제 뭘 드셨는지 기억이 없네요"

예상 효과

메모리 절약

  • rb8001: 416MB → 200MB (-216MB)
  • rb10508: 987MB → 400MB (-587MB)
  • rb10408: 55MB → 30MB (-25MB)
  • 임베딩 서비스: +600MB (새로 생성)
  • 순 절약: 228MB + 여러 로빙 추가 시 더 큰 절약

성능 영향

  • 레이턴시 증가: +10-50ms (HTTP 호출 오버헤드)
  • 전체 응답시간: 1-3초 → 영향 미미
  • 처리량: 동일 (병목은 LLM API)

운영상 이점

  • 확장성: 새로운 로빙 추가 시 임베딩 서비스 공유
  • 장애 격리: 임베딩 서비스 장애가 모든 로빙에 영향
  • 유지보수: 임베딩 모델 업데이트 시 한 곳만 수정

구현 단계

Phase 1: 임베딩 서비스 구축

  1. 임베딩 서비스 컨테이너 개발
  2. HTTP API 구현 및 테스트
  3. 성능 벤치마크 측정

Phase 2: rb10508_micro 적용

  1. HTTPEmbeddingFunction 구현
  2. memory.py 코드 수정
  3. docker-compose.yml 업데이트
  4. 배포 및 테스트

Phase 3: 다른 로빙들 순차 적용

  1. rb8001 적용
  2. rb10408 적용
  3. 전체 시스템 안정성 검증

고려사항

장점

  • 메모리 효율성: 중복 모델 로딩 제거
  • 프라이버시 보장: 로빙 간 기억 완전 분리
  • 확장성: 새 로빙 추가 시 효율적
  • 유지보수성: 중앙집중식 임베딩 관리

단점

  • 네트워크 의존성: HTTP 호출 오버헤드
  • 단일 장애점: 임베딩 서비스 장애 시 전체 영향
  • 복잡성 증가: 서비스 간 의존성 관리

위험 완화 방안

  • 헬스체크: 임베딩 서비스 상태 모니터링
  • 타임아웃: HTTP 호출 시 적절한 타임아웃 설정
  • 폴백: 임베딩 서비스 장애 시 로컬 모델 대체 고려
  • 캐싱: 자주 사용되는 임베딩 캐싱으로 성능 향상

결론

임베딩 서비스 분리 아키텍처는 메모리 효율성과 확장성을 크게 향상시키면서도 각 로빙의 프라이버시를 완벽하게 보장합니다. 약간의 레이턴시 증가가 있지만, 전체적인 이점이 훨씬 큽니다.

특히 향후 더 많은 로빙 서비스가 추가될 것을 고려하면, 이 아키텍처는 필수적인 개선사항으로 판단됩니다.