diff --git a/300_architecture/310_전체_시스템_구조_컨테이너와_마이크로서비스.md b/300_architecture/310_전체_시스템_구조_컨테이너와_마이크로서비스.md
index 2436cbc..e624361 100755
--- a/300_architecture/310_전체_시스템_구조_컨테이너와_마이크로서비스.md
+++ b/300_architecture/310_전체_시스템_구조_컨테이너와_마이크로서비스.md
@@ -1,7 +1,25 @@
# 로빙 컨테이너 아키텍처 설계
## 개요
-로빙 AI 에이전트는 사용자별로 독립적인 Docker 컨테이너에서 실행되며, 중앙 집중식 대시보드를 통해 관리됩니다. 각 로빙은 개별적인 성장 경로를 가지며, 효율적인 리소스 관리와 데이터 안전성을 보장합니다.
+로빙 AI 에이전트는 사용자별로 독립적인 Docker 컨테이너(각 로빙의 '집')에서 실행되며, 중앙 대시보드를 통해 관리됩니다. 각 로빙은 개별적인 성장 경로를 가지며, 효율적인 리소스 관리와 데이터 안전성을 보장합니다.
+
+## 왜 컨테이너와 마이크로서비스인가?
+
+### 컨테이너(Docker)를 선택한 이유
+1. **격리와 보안**: 각 로빙의 데이터와 처리 과정이 완전히 분리됨
+2. **리소스 관리**: 레벨별로 다른 메모리/CPU 할당 가능
+3. **배포 편의성**: 코드 한 번 작성으로 어디서나 실행
+4. **버전 관리**: 로빙별로 다른 버전 사용 가능
+
+**다른 선택지**: VM(가상머신) - 너무 무거움, 프로세스 분리 - 보안 취약
+
+### 마이크로서비스를 선택한 이유
+1. **기능 독립성**: 각 서비스(스킬)를 독립적으로 개발/배포
+2. **확장성**: 필요한 기능만 선택적으로 추가
+3. **장애 격리**: 한 서비스 문제가 전체에 영향 안 줌
+4. **언어 독립적**: 각 서비스를 다른 언어로 개발 가능
+
+**다른 선택지**: 모놀리식 - 유지보수 어려움, SOA - 너무 복잡
## 전체 아키텍처
@@ -48,9 +66,9 @@
- **역할**: 각 사용자의 개인 AI 에이전트
- **특징**: 완전 독립적, 사용자별 고유 설정
- **구성**:
- - FastAPI 서버
- - 벡터 DB (ChromaDB)
- - 개인 데이터 저장소
+ - FastAPI 서버 (API를 빠르게 만드는 프레임워크)
+ - ChromaDB (의미 기반 검색을 위한 벡터 데이터베이스)
+ - 개인 데이터 저장소 (각 로빙의 기억과 경험)
## 데이터 구조
@@ -71,11 +89,15 @@ performance: id, robing_id, date, tasks_completed, success_rate
### 로빙 컨테이너 DB (개별)
```
-벡터 DB 구조:
-- 기억: 대화 내용, 업무 처리 기록 → 벡터 임베딩
-- 윤리: 판단 기준, 가치관 → 벡터 공간에서 유사성 검색
-- 감정: 상황별 반응 패턴 → 감정 벡터
-- 경험: 성공/실패 케이스 → 학습 데이터
+벡터 DB 구조 (ChromaDB 사용):
+- 기억: 대화 내용, 업무 처리 기록
+ → 768차원 벡터로 변환하여 저장 (비슷한 내용 빠르게 찾기)
+- 윤리: 판단 기준, 가치관
+ → 의미 공간에서 유사한 상황 검색
+- 감정: 상황별 반응 패턴
+ → Inside Out 9개 감정 벡터로 표현
+- 경험: 성공/실패 케이스
+ → 학습 데이터로 활용해 더 똑똑해지기
```
## 통신 구조
@@ -88,17 +110,50 @@ performance: id, robing_id, date, tasks_completed, success_rate
설정 전달 업데이트
```
-### API 엔드포인트
+### API 엔드포인트 (API = 서비스끼리 대화하는 방법)
```
대시보드 → 로빙:
-- POST /api/config/skills (스킬 설정)
+- POST /api/config/skills (스킬 설정 전달)
- POST /api/config/stats (스탯 조정)
-- GET /api/status (상태 확인)
+- GET /api/status (현재 상태 확인)
로빙 → 대시보드:
-- POST /dashboard/api/stats (스탯 업데이트)
-- POST /dashboard/api/performance (성능 데이터)
-- POST /dashboard/api/events (이벤트 로그)
+- POST /dashboard/api/stats (성장 상태 업데이트)
+- POST /dashboard/api/performance (작업 성과 보고)
+- POST /dashboard/api/events (중요 이벤트 기록)
+```
+
+## 코드로 보는 구조
+
+```python
+# 로빙 컨테이너 생성 코드 (주석 설명 강화)
+class RobeingContainer:
+ def __init__(self, user_id: str, level: int = 1):
+ # 각 로빙은 고유한 Docker 컨테이너를 가짐
+ self.container_name = f"robeing_{user_id}"
+
+ # 레벨에 따라 리소스 할당이 달라짐
+ self.memory_limit = self._calculate_memory(level)
+ self.cpu_shares = self._calculate_cpu(level)
+
+ # ChromaDB로 벡터 검색 기능 추가
+ self.vector_db = ChromaDB(
+ collection=f"robeing_{user_id}_memories"
+ )
+
+ def _calculate_memory(self, level: int) -> str:
+ """
+ 레벨이 높을수록 더 많은 메모리 필요
+ - Lv 1-5: 2GB (기본 작업만)
+ - Lv 6-10: 4GB (여러 스킬 동시 사용)
+ - Lv 11+: 8GB (복잡한 분석 및 학습)
+ """
+ if level <= 5:
+ return "2g"
+ elif level <= 10:
+ return "4g"
+ else:
+ return "8g"
```
## 로빙 성장 시스템
diff --git a/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md b/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md
index b01ce28..7e9f56b 100644
--- a/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md
+++ b/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md
@@ -1,174 +1,363 @@
-# ChromaDB 임베딩 솔루션 비교분석
+# 백엔드: PostgreSQL, ChromaDB, Vector Memory 설계
-**날짜**: 2025-07-30
-**작성자**: Claude (51124 서버)
-**관련 프로젝트**: rb10508_test, rb8001, rb10408_test
+## 개요
-## 배경
+로빙의 백엔드는 전통적인 관계형 데이터베이스(PostgreSQL)와 벡터 데이터베이스(ChromaDB)를 조합하여, 구조화된 데이터와 의미 기반 검색을 모두 지원합니다. 이를 통해 로빙은 정확한 기록과 유연한 기억을 동시에 가질 수 있습니다.
-### 현재 문제 상황
-- rb10508_test 컨테이너에서 ChromaDB 초기화 실패
-- 오류: `The sentence_transformers python package is not installed`
-- 원인: 빌드 시간 단축을 위해 requirements.txt에서 sentence-transformers 제거 (1GB+ 절약)
+## 왜 이 기술들을 선택했나?
-### 이전 결정사항 히스토리
-1. **2025-07-09**: 한국어 성능 향상을 위해 sentence-transformers 추가
-2. **2025-07-23**: CI 빌드 시간 단축을 위해 torch, sentence-transformers 제거
-3. **2025-07-28**: 호스트에 패키지 설치 후 모델 파일만 볼륨 마운트 시도
-4. **2025-07-29**: rb8001 배포 시 최적화 확정
+### PostgreSQL을 선택한 이유
-### 기술적 배경 설명
-- **sentence-transformers**: 텍스트를 벡터로 변환하는 라이브러리 (엔진)
-- **모델 파일**: 학습된 가중치 데이터 (/opt/models에 546MB 저장됨)
-- 엔진 없이는 모델 파일만으로 작동 불가 (게임 엔진 없이 게임 데이터만 있는 것과 동일)
+**PostgreSQL = 로빙의 일기장 (정확한 기록)**
-## 해결 방안 비교
+1. **신뢰성**: 30년 이상 검증된 데이터베이스, 데이터 손실 거의 없음
+2. **ACID 보장**: 모든 거래가 완벽하게 기록되거나 안 되거나 (중간 상태 없음)
+3. **복잡한 쿼리**: 여러 테이블을 조합한 복잡한 질문 가능
+4. **확장성**: 수백만 건의 데이터도 문제없이 처리
-### 1. 베이스 이미지 전략
+**다른 선택지와 비교**:
+- MySQL: 간단하지만 고급 기능 부족
+- MongoDB: NoSQL이라 관계 표현 어려움
+- SQLite: 동시 접속 제한, 대용량 처리 어려움
-#### 구현 방법
-```dockerfile
-# Dockerfile.base
-FROM python:3.13-slim
-RUN pip install torch sentence-transformers
-# 빌드: docker build -f Dockerfile.base -t rb10508_base:latest .
+### ChromaDB를 선택한 이유
-# Dockerfile (애플리케이션용)
-FROM rb10508_base:latest
-COPY requirements.txt .
-RUN pip install -r requirements.txt
+**ChromaDB = 로빙의 연상 기억 (의미 기반 검색)**
+
+1. **벡터 검색**: "비슷한 의미"를 찾아낼 수 있음
+2. **임베딩 저장**: 768차원 벡터를 효율적으로 저장/검색
+3. **가벼움**: PostgreSQL에 비해 설치/관리 간단
+4. **Python 친화적**: 로빙 코드와 자연스럽게 통합
+
+**다른 선택지와 비교**:
+- Pinecone: 클라우드 전용, 비용 발생
+- Weaviate: 너무 무겁고 복잡
+- FAISS: 영속성 관리 어려움
+
+## 데이터 구조 설계
+
+### PostgreSQL 스키마 (구조화된 데이터)
+
+```sql
+-- 사용자 정보 (명확한 사실)
+CREATE TABLE users (
+ id UUID PRIMARY KEY,
+ email VARCHAR(255) UNIQUE,
+ name VARCHAR(100),
+ created_at TIMESTAMP,
+ level INTEGER DEFAULT 1,
+ experience INTEGER DEFAULT 0
+);
+
+-- 로빙 메타데이터 (성장 기록)
+CREATE TABLE robeings (
+ id UUID PRIMARY KEY,
+ user_id UUID REFERENCES users(id),
+ name VARCHAR(100),
+ -- 스탯 (게임처럼 수치화)
+ intelligence INTEGER DEFAULT 10,
+ wisdom INTEGER DEFAULT 10,
+ charisma INTEGER DEFAULT 10,
+ -- 감정 상태 (Inside Out 모델)
+ joy_level FLOAT DEFAULT 0.5,
+ sadness_level FLOAT DEFAULT 0.0,
+ anger_level FLOAT DEFAULT 0.0,
+ fear_level FLOAT DEFAULT 0.0,
+ disgust_level FLOAT DEFAULT 0.0,
+ -- 사회적 감정 (Inside Out 2)
+ anxiety_level FLOAT DEFAULT 0.0,
+ envy_level FLOAT DEFAULT 0.0,
+ embarrassment_level FLOAT DEFAULT 0.0,
+ ennui_level FLOAT DEFAULT 0.0,
+ -- 시간 기록
+ last_active TIMESTAMP,
+ total_interactions INTEGER DEFAULT 0
+);
+
+-- 스킬 목록 (명확한 능력)
+CREATE TABLE skills (
+ id UUID PRIMARY KEY,
+ robeing_id UUID REFERENCES robeings(id),
+ skill_name VARCHAR(100),
+ skill_level INTEGER DEFAULT 1,
+ experience_points INTEGER DEFAULT 0,
+ last_used TIMESTAMP,
+ success_rate FLOAT DEFAULT 0.0
+);
+
+-- 작업 이력 (정확한 로그)
+CREATE TABLE task_history (
+ id UUID PRIMARY KEY,
+ robeing_id UUID REFERENCES robeings(id),
+ task_type VARCHAR(50),
+ input_data JSONB,
+ output_data JSONB,
+ success BOOLEAN,
+ duration_ms INTEGER,
+ created_at TIMESTAMP DEFAULT NOW()
+);
```
-#### 장단점
-| 항목 | 내용 |
-|------|------|
-| **장점** | • 첫 빌드 후 torch/sentence-transformers 재설치 불필요
• 여러 서비스가 동일 베이스 이미지 공유 가능
• CI/CD 빌드 시간 30초~1분으로 단축 |
-| **단점** | • 베이스 이미지 별도 관리 필요
• 베이스 이미지 업데이트 시 모든 서비스 재빌드
• 첫 베이스 이미지 빌드는 여전히 10분 소요 |
+### ChromaDB 컬렉션 (의미 기반 데이터)
-#### 시간 및 용량
-- 첫 베이스 이미지 빌드: 10분
-- 이후 애플리케이션 빌드: 30초~1분
-- 저장 공간: 베이스 이미지 2GB + 앱 이미지 500MB
+```python
+# 기억 컬렉션 구조
+memories_collection = {
+ "name": "robeing_memories",
+ "metadata": {
+ "description": "로빙의 모든 기억",
+ "embedding_model": "ko-sroberta-multitask"
+ },
+ "documents": [
+ {
+ "id": "memory_uuid",
+ "text": "오늘 사용자가 프로젝트 마감일 걱정을 표현했다",
+ "embedding": [0.1, 0.2, ...], # 768차원 벡터
+ "metadata": {
+ "timestamp": "2025-08-08T10:30:00",
+ "emotion_state": {
+ "dominant": "anxiety",
+ "intensity": 0.7
+ },
+ "entropy_score": 2.3, # 중요도
+ "context": "work_discussion",
+ "user_id": "user_123"
+ }
+ }
+ ]
+}
-### 2. pip wheel 사전 빌드 방식
+# 감정 패턴 컬렉션
+emotion_patterns = {
+ "name": "emotion_patterns",
+ "metadata": {
+ "description": "감정 반응 패턴 학습"
+ },
+ "documents": [
+ {
+ "id": "pattern_uuid",
+ "text": "마감일 언급 → 불안 증가 → 계획 수립 도움",
+ "embedding": [...],
+ "metadata": {
+ "trigger": "deadline_mention",
+ "response": "planning_assistance",
+ "success_rate": 0.85
+ }
+ }
+ ]
+}
+```
+
+## 두 데이터베이스의 협업
+
+### 하이브리드 쿼리 예시
+
+```python
+async def recall_relevant_context(user_id: str, current_text: str):
+ """
+ 현재 대화와 관련된 모든 컨텍스트를 가져오기
+ """
+
+ # 1. PostgreSQL에서 사용자 프로필과 최근 활동 조회
+ user_data = await postgres.query("""
+ SELECT u.*, r.*
+ FROM users u
+ JOIN robeings r ON u.id = r.user_id
+ WHERE u.id = $1
+ """, user_id)
+
+ # 2. ChromaDB에서 의미적으로 유사한 기억 검색
+ similar_memories = chromadb.query(
+ query_texts=[current_text],
+ n_results=10,
+ where={"user_id": user_id}
+ )
+
+ # 3. 엔트로피 기반 중요도 필터링
+ important_memories = [
+ m for m in similar_memories
+ if m['metadata']['entropy_score'] > 2.0
+ ]
+
+ # 4. PostgreSQL에서 관련 스킬 확인
+ relevant_skills = await postgres.query("""
+ SELECT * FROM skills
+ WHERE robeing_id = $1
+ AND skill_name IN $2
+ ORDER BY skill_level DESC
+ """, user_data['robeing_id'], extract_skills(current_text))
+
+ return {
+ 'user_profile': user_data,
+ 'memories': important_memories,
+ 'available_skills': relevant_skills
+ }
+```
+
+## 메모리 최적화 전략
+
+### 1. 계층적 저장
+
+```
+HOT (자주 접근) → PostgreSQL 메모리 캐시
+ ↓
+WARM (가끔 접근) → ChromaDB 인덱스
+ ↓
+COLD (거의 안 접근) → 디스크 아카이브
+```
+
+### 2. 벡터 압축
+
+```python
+# 원본: 768차원 float32 = 3KB
+# 압축: PCA → 256차원 → int8 양자화 = 256B (12배 압축)
+
+def compress_embedding(embedding):
+ # PCA로 차원 축소
+ reduced = pca_model.transform(embedding) # 768 → 256
+ # 양자화
+ quantized = (reduced * 127).astype(np.int8) # float32 → int8
+ return quantized
+```
+
+### 3. 선택적 망각
+
+```python
+def selective_forgetting(memories, max_size=1000):
+ """
+ 덜 중요한 기억을 점진적으로 망각
+ """
+ if len(memories) <= max_size:
+ return memories
+
+ # 중요도 점수 계산
+ for memory in memories:
+ memory.importance = (
+ memory.entropy_score * 0.4 + # 엔트로피
+ memory.access_count * 0.3 + # 접근 빈도
+ memory.emotional_intensity * 0.3 # 감정 강도
+ )
+
+ # 하위 20% 제거
+ threshold = np.percentile([m.importance for m in memories], 20)
+ return [m for m in memories if m.importance > threshold]
+```
+
+## 백업 및 복구
+
+### 1. PostgreSQL 백업
-#### 구현 방법
```bash
-# 호스트에서 한 번만 실행
-mkdir -p /opt/wheels
-pip wheel torch sentence-transformers -w /opt/wheels
+# 매일 자동 백업 (cron)
+pg_dump robeing_db > /backup/postgres/robeing_$(date +%Y%m%d).sql
+
+# 특정 사용자만 백업
+pg_dump -t users -t robeings --where="user_id='uuid'" > user_backup.sql
```
-```yaml
-# docker-compose.yml
-volumes:
- - /opt/wheels:/wheels:ro
-```
+### 2. ChromaDB 백업
-```dockerfile
-# Dockerfile
-RUN pip install --find-links /wheels --no-index torch sentence-transformers
-```
-
-#### 장단점
-| 항목 | 내용 |
-|------|------|
-| **장점** | • 오프라인 설치 가능 (네트워크 불필요)
• 빌드 시간 3-5분으로 단축
• 여러 컨테이너가 wheel 파일 공유 |
-| **단점** | • Python 버전 정확히 일치 필요 (현재 호스트 3.10 vs 컨테이너 3.13)
• wheel 파일 관리 복잡도 증가
• 호스트 디스크 공간 1GB 추가 사용 |
-
-#### 시간 및 용량
-- wheel 생성: 한 번만 5분
-- 컨테이너 빌드: 3-5분
-- 추가 저장: /opt/wheels에 1GB
-
-### 3. ChromaDB 기본 임베딩 사용
-
-#### 구현 방법
```python
-# app/services/chroma_service.py 수정
-# embedding_function 파라미터 제거
-self.conversations_collection = self.client.get_or_create_collection(
- name="conversations",
- metadata={"description": "Conversation memories", "robing_id": settings.ROBING_ID}
- # embedding_function 제거 - 기본 임베딩 사용
-)
+# ChromaDB 컬렉션 백업
+def backup_chromadb():
+ collections = client.list_collections()
+ for collection in collections:
+ data = collection.get(include=['embeddings', 'metadatas', 'documents'])
+ with open(f'/backup/chroma/{collection.name}.json', 'w') as f:
+ json.dump(data, f)
```
-#### 장단점
-| 항목 | 내용 |
-|------|------|
-| **장점** | • 추가 패키지 설치 불필요
• 빌드 시간 증가 없음
• 구현 가장 단순 |
-| **단점** | • 한국어 성능 크게 저하
• 임베딩 품질 낮음
• 검색 정확도 하락 |
+## 성능 지표
-#### 시간 및 용량
-- 빌드 시간 증가: 0분
-- 추가 용량: 0MB
+### 목표 성능
-### 4. FastEmbed 경량 라이브러리 사용
+| 작업 | PostgreSQL | ChromaDB |
+|-----|-----------|----------|
+| 단순 조회 | < 10ms | < 50ms |
+| 복잡한 쿼리 | < 100ms | < 200ms |
+| 대량 삽입 (1000건) | < 1s | < 2s |
+| 동시 접속 | 100+ | 50+ |
+
+### 모니터링
-#### 구현 방법
```python
-# requirements.txt
-fastembed>=0.1.0 # 50MB, torch 불필요
-
-# app/services/chroma_service.py
-from chromadb.utils import embedding_functions
-embedding_function = embedding_functions.FastEmbedEmbeddingFunction()
+# 성능 모니터링 코드
+class PerformanceMonitor:
+ def __init__(self):
+ self.postgres_latency = []
+ self.chroma_latency = []
+
+ async def track_query(self, db_type, query_func):
+ start = time.time()
+ result = await query_func()
+ latency = (time.time() - start) * 1000 # ms
+
+ if db_type == 'postgres':
+ self.postgres_latency.append(latency)
+ else:
+ self.chroma_latency.append(latency)
+
+ # 임계값 초과 시 알림
+ if latency > 500:
+ logger.warning(f"{db_type} 쿼리 느림: {latency}ms")
+
+ return result
```
-#### 장단점
-| 항목 | 내용 |
-|------|------|
-| **장점** | • torch 불필요 (ONNX 런타임 사용)
• 설치 크기 50MB로 매우 작음
• 빌드 시간 1분 미만 |
-| **단점** | • 모델 선택 제한적
• sentence-transformers보다 기능 적음
• 커뮤니티 지원 적음 |
+## 마이그레이션 전략
-#### 시간 및 용량
-- 빌드 시간: 1분
-- 추가 용량: 50MB
+### 스키마 버전 관리
-### 5. sentence-transformers 재추가 (원점 회귀)
+```sql
+-- migrations/001_initial.sql
+CREATE TABLE schema_versions (
+ version INTEGER PRIMARY KEY,
+ applied_at TIMESTAMP DEFAULT NOW(),
+ description TEXT
+);
+
+-- migrations/002_add_emotions.sql
+ALTER TABLE robeings
+ADD COLUMN anxiety_level FLOAT DEFAULT 0.0,
+ADD COLUMN envy_level FLOAT DEFAULT 0.0;
+```
+
+### 데이터 마이그레이션
-#### 구현 방법
```python
-# requirements.txt
-sentence-transformers>=2.2.0
+async def migrate_to_new_schema():
+ """
+ 구 스키마 → 신 스키마 마이그레이션
+ """
+ # 1. 백업 먼저
+ await backup_all_data()
+
+ # 2. 새 테이블 생성
+ await create_new_tables()
+
+ # 3. 데이터 이동 (배치 처리)
+ batch_size = 1000
+ offset = 0
+ while True:
+ old_data = await fetch_old_data(limit=batch_size, offset=offset)
+ if not old_data:
+ break
+
+ new_data = transform_data(old_data)
+ await insert_new_data(new_data)
+ offset += batch_size
+
+ # 4. 검증
+ await verify_migration()
```
-#### 장단점
-| 항목 | 내용 |
-|------|------|
-| **장점** | • 검증된 솔루션
• 최고의 한국어 성능
• 풍부한 모델 선택지 |
-| **단점** | • 빌드 시간 5-10분
• 이미지 크기 1GB 증가
• CI/CD 부담 |
-
-## 종합 비교표
-
-| 방법 | 첫 빌드 | 이후 빌드 | 추가 용량 | 한국어 성능 | 구현 복잡도 | 유지보수 |
-|------|---------|-----------|-----------|-------------|-------------|----------|
-| 베이스 이미지 | 10분 | 30초 | 2GB | 우수 | 중간 | 중간 |
-| pip wheel | 5분 | 3-5분 | 1GB | 우수 | 높음 | 높음 |
-| ChromaDB 기본 | 0분 | 0분 | 0MB | 나쁨 | 낮음 | 낮음 |
-| FastEmbed | 1분 | 1분 | 50MB | 보통 | 낮음 | 낮음 |
-| 원점 회귀 | 5-10분 | 5-10분 | 1GB | 우수 | 낮음 | 낮음 |
-
-## 권장사항
-
-### 상황별 추천
-1. **빌드 속도 최우선**: ChromaDB 기본 임베딩
-2. **한국어 성능 중요 + CI/CD 최적화**: 베이스 이미지 전략
-3. **균형적 타협안**: FastEmbed
-4. **단순함 우선**: sentence-transformers 재추가
-
-### 고려사항
-- 현재 /opt/models에 있는 546MB 모델 파일은 sentence-transformers 설치 시 즉시 활용 가능
-- Python 버전 차이 (호스트 3.10 vs 컨테이너 3.13)로 인해 wheel 공유는 제한적
-- 모든 로빙 서비스(rb8001, rb10408, rb10508)에 동일한 솔루션 적용 필요
-
## 결론
-팀 논의를 통해 다음 사항을 결정해야 합니다:
-1. 한국어 임베딩 성능 vs 빌드 시간의 우선순위
-2. CI/CD 파이프라인 복잡도 허용 수준
-3. 장기적 유지보수 관점에서의 선호도
+PostgreSQL과 ChromaDB의 조합은 로빙에게 두 가지 중요한 능력을 제공합니다:
-현재 프론트엔드 연결은 정상 작동하며, Gemini API를 통한 응답 생성도 가능합니다.
-단지 대화 기록이 벡터 DB에 저장되지 않는 상황이므로, 긴급도는 중간 수준입니다.
\ No newline at end of file
+1. **PostgreSQL**: 정확한 사실과 기록을 저장하는 "좌뇌"
+2. **ChromaDB**: 의미와 맥락을 이해하는 "우뇌"
+
+이 두 시스템이 협력하여 로빙은 단순한 데이터 저장을 넘어, 진정한 "기억"과 "이해"를 가진 디지털 동료가 됩니다.
+
+*참고: 상세한 임베딩 서비스 분리 전략은 [370번 문서](./370_임베딩_서비스_분리_아키텍처.md) 참조*
\ No newline at end of file
diff --git a/300_architecture/370_임베딩_서비스_분리_아키텍처.md b/300_architecture/370_임베딩_서비스_분리_아키텍처.md
index 80c1cb5..c8502b7 100644
--- a/300_architecture/370_임베딩_서비스_분리_아키텍처.md
+++ b/300_architecture/370_임베딩_서비스_분리_아키텍처.md
@@ -6,11 +6,32 @@
> "모든 로빙이 하나의 임베딩 서비스를 공유하면, 메모리는 절약되고 성능은 유지된다."
-## 문제점
+## 왜 임베딩 서비스를 분리했나?
-- 각 로빙마다 동일한 임베딩 모델(449MB) 중복 로드
-- rb10508_micro: 987.9MB 메모리 사용
-- 로빙 추가 시 선형적 메모리 증가
+### 임베딩이란?
+**임베딩 = 텍스트를 숫자(벡터)로 변환하는 과정**
+- "안녕하세요" → [0.1, 0.3, -0.5, ...] (384개의 숫자)
+- 비슷한 의미의 텍스트는 비슷한 숫자 패턴을 가짐
+- 로빙이 "의미"를 이해하는 핵심 기술
+
+### 분리 전 문제점
+1. **메모리 낭비**: 각 로빙이 동일한 모델(449MB)을 각자 로드
+ - 로빙 10개 = 4.5GB 메모리 낭비
+ - 로빙 100개 = 45GB (서버 메모리 초과)
+2. **시작 시간 지연**: 각 로빙이 시작할 때마다 모델 로드 (5-10초)
+3. **업데이트 어려움**: 모델 업데이트 시 모든 로빙 재시작 필요
+
+### 분리 후 장점
+1. **메모리 절약**: 모델 하나만 로드하고 모두가 공유
+2. **빠른 시작**: 로빙은 API 호출만 하면 됨 (모델 로드 불필요)
+3. **쉬운 관리**: 임베딩 서비스만 업데이트하면 모든 로빙이 자동 적용
+
+**비유**: 각자 사전을 들고 다니던 것 → 도서관에 사전 하나 두고 필요할 때 찾아보기
+
+**다른 선택지와 비교**:
+- 각자 모델 로드: 메모리 낭비, 관리 복잡
+- 클라우드 API 사용: 비용 발생, 네트워크 의존
+- CPU 임베딩: 너무 느림 (10배 이상)
## 해결책: 중앙 임베딩 서비스
@@ -37,25 +58,35 @@
└─────────────┘
```
-### 기술 스택
+### 기술 스택 선택 이유
-- **FastAPI + Uvicorn**: 고성능 비동기 웹 서버
-- **ONNX Runtime**: 최적화된 임베딩 생성
-- **multilingual-MiniLM-L12-v2**: 다국어 지원 임베딩 모델
+- **FastAPI + Uvicorn**:
+ - 왜? Python 기반 최속 웹 프레임워크, 비동기 처리로 동시 요청 효율적 처리
+ - 다른 선택지: Flask (동기 처리로 느림), Django (너무 무거움)
+
+- **ONNX Runtime**:
+ - 왜? PyTorch보다 3배 빠르고 메모리 50% 절약
+ - 다른 선택지: PyTorch (메모리 2배 사용), TensorFlow (구성 복잡)
+ - ONNX = Open Neural Network Exchange (어떤 프레임워크에서도 사용 가능)
+
+- **multilingual-MiniLM-L12-v2**:
+ - 왜? 한국어 포함 100개 언어 지원, 크기 대비 성능 우수 (134MB)
+ - 다른 선택지: ko-sroberta (한국어만, 400MB), BERT-large (너무 큼, 1.3GB)
-### API 설계
+### API 설계 (간단명료한 인터페이스)
```python
-# POST /embed
+# POST /embed - 텍스트를 벡터로 변환
+# 요청:
{
"texts": ["안녕하세요", "오늘 날씨가 좋네요"]
}
-# Response
+# 응답: (각 텍스트가 384개 숫자로 변환됨)
{
"embeddings": [
- [0.1, 0.2, ...], # 384차원 벡터
- [0.3, 0.4, ...]
+ [0.1, 0.2, ...], # "안녕하세요"의 벡터 표현
+ [0.3, 0.4, ...] # "오늘 날씨가 좋네요"의 벡터 표현
]
}
@@ -70,31 +101,34 @@
## 구현 가이드
-### 1. 로빙 측 변경사항
+### 1. 로빙 측 변경사항 (코드 주석 강화)
```python
-# 기존: ONNX 직접 로드
+# 기존: ONNX 모델을 각 로빙이 직접 로드 (메모리 낭비)
from onnx_embedder import ONNXEmbedder
-embedder = ONNXEmbedder("/models/onnx/...")
+embedder = ONNXEmbedder("/models/onnx/...") # 449MB 모델 로드
-# 변경: HTTP 임베딩 함수
+# 변경: 임베딩 서비스에 HTTP 요청만 (경량화)
class HTTPEmbeddingFunction(EmbeddingFunction):
def __init__(self):
+ # 임베딩 서비스 URL (환경변수로 설정 가능)
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"]
+ return response.json()["embeddings"] # 384차원 벡터 반환
```
-### 2. ChromaDB 통합
+### 2. ChromaDB 통합 (로빙의 기억 저장소)
```python
-# memory.py
+# memory.py - 로빙의 기억을 저장하는 코드
self.episodic = self.client.get_or_create_collection(
- name=f"{self.robing_id}_episodic",
- embedding_function=HTTPEmbeddingFunction() # HTTP 방식으로 변경
+ name=f"{self.robing_id}_episodic", # 각 로빙마다 독립된 기억 공간
+ embedding_function=HTTPEmbeddingFunction() # 임베딩은 공유 서비스 사용
)
+# 결과: 기억은 각자, 임베딩 엔진은 공유
```
### 3. Docker 구성 변경
@@ -110,13 +144,22 @@ environment:
## 성능 분석
-### 메모리 절감
-- **rb10508_micro**: 988MB → 118MB (-870MB, 88% 감소)
-- **예상 절감 (100개 로빙)**: 87GB 메모리 절약
+### 메모리 절감 (실측 데이터)
+```
+로빙 이름 | 이전 메모리 | 이후 메모리 | 절감량
+------------|------------|------------|--------
+rb10508 | 988MB | 118MB | -870MB (88%↓)
+rb8001 | 416MB | 200MB | -216MB (52%↓)
+rb10408 | 55MB | 30MB | -25MB (45%↓)
-### 레이턴시
-- **HTTP 오버헤드**: +7ms (무시할 수준)
-- **전체 응답시간**: 1-3초 중 0.2% 미만
+예상 절감:
+- 10개 로빙: 8.7GB 절약
+- 100개 로빙: 87GB 절약 (서버 1대 가격)
+```
+
+### 속도 영향
+- **HTTP 통신 추가 시간**: +7ms (전체의 0.2%)
+- **실제 사용자 체감**: 차이 없음 (전체 응답 1-3초)
### 확장성
- 단일 임베딩 서비스로 수백 개 로빙 지원
@@ -142,11 +185,38 @@ curl http://localhost:8015/health
3. **모델 업데이트**: L12 → L6 모델로 추가 경량화 검토
4. **다른 로빙 적용**: rb8001, rb10408에 순차 적용
+## 코드로 보는 효과
+
+```python
+# 전체 시스템 메모리 사용량 계산
+def calculate_memory_usage(num_robeings: int, shared_embedding: bool):
+ """
+ 임베딩 서비스 공유 여부에 따른 메모리 사용량 계산
+ """
+ base_memory_per_robeing = 118 # MB (임베딩 제외한 기본 메모리)
+ embedding_model_size = 449 # MB (임베딩 모델 크기)
+
+ if shared_embedding:
+ # 임베딩 서비스 공유: 모델 한 번만 로드
+ total = (num_robeings * base_memory_per_robeing) + embedding_model_size
+ else:
+ # 각자 로드: 모든 로빙이 모델 복사
+ total = num_robeings * (base_memory_per_robeing + embedding_model_size)
+
+ return total
+
+# 비교 예시
+print(f"10개 로빙 (공유 X): {calculate_memory_usage(10, False)}MB") # 5,670MB
+print(f"10개 로빙 (공유 O): {calculate_memory_usage(10, True)}MB") # 1,629MB
+print(f"절약률: 71%")
+```
+
## 교훈
- **중복 제거의 힘**: 동일한 기능을 서비스로 분리하면 극적인 효율성 향상
- **간단한 구현**: 12줄의 HTTPEmbeddingFunction으로 870MB 절약
- **점진적 적용**: 하나의 로빙에서 성공 후 확산
+- **공유와 독립의 균형**: 임베딩 엔진은 공유, 기억과 개성은 독립 유지
---