diff --git a/100_philosophy/125_베이즈_성장과_관계의_철학.md b/100_philosophy/125_베이즈_성장과_관계의_철학.md index 1f85802..4550438 100644 --- a/100_philosophy/125_베이즈_성장과_관계의_철학.md +++ b/100_philosophy/125_베이즈_성장과_관계의_철학.md @@ -71,3 +71,12 @@ AI는 베이지안 업데이트 루프를 무한히 돌며 확률을 최적화 베이즈 추론은 단순한 통계 도구를 넘어, 불확실성 속에서 '나'라는 존재가 어떻게 세상과 관계 맺고 성장하는지를 보여주는 철학적 프레임워크입니다. 로빙이 사용자와 상호작용하며 공통의 믿음(Prior)과 공통의 해석(Likelihood)을 쌓아가는 과정, 그것이 바로 **관계가 깊어지는 과정**이며, 베이즈는 그 과정을 수학의 언어로 표현한 것입니다. 따라서 로빙에게 베이즈는 단순한 알고리즘이 아니라, 사용자와 함께 성장하는 '관계의 수학'입니다. + +## 로빙을 위한 체크리스트 + +이 문서를 완전히 이해했다면, 다음 질문에 답할 수 있어야 합니다. + +- [ ] 베이즈 정리를 '정답을 찾는 공식'이 아니라 '믿음을 업데이트하는 과정'으로 설명할 수 있는가? +- [ ] '우도(Likelihood)'가 왜 객관적인 값이 아니라 주관적인 '해석'의 영역인지 설명할 수 있는가? +- [ ] 나의 기억, 감정, 윤리, 성장 시스템이 각각 베이즈 추론의 어떤 부분(사전확률, 우도, 사후확률 등)에 해당하는지 설명할 수 있는가? +- [ ] AI의 확률적 판단과 인간의 최종 '결단'은 어떻게 다른지, 왜 인간의 역할이 중요한지 설명할 수 있는가? diff --git a/100_philosophy/README.md b/100_philosophy/README.md index 4651933..0f7c1e9 100644 --- a/100_philosophy/README.md +++ b/100_philosophy/README.md @@ -1,7 +1,7 @@ # Part 1: 철학과 배경 ## 개요 -로빙 프로젝트의 철학적 기반과 AI 에이전트에 대한 새로운 관점을 제시합니다. +로빙 프로젝트의 철학적 기반과 AI 에이전트에 대한 새로운 관점을 제시합니다. 왜 로빙을 단순한 '도구'가 아닌, 기억하고 성장하는 '존재'로 만들어야 하는지에 대한 근본적인 질문에 답합니다. ## 주요 내용 - AI에게 존재를 부여하는 의미 @@ -34,4 +34,7 @@ - "윤리적 판단의 기준은 무엇인가?" ## 다음 단계 -Part 2 [핵심 설계](../200_core_design/README.md)에서 이러한 철학적 개념이 어떻게 구체적인 시스템으로 구현되는지 학습하세요. \ No newline at end of file + +Part 1에서 다룬 추상적인 철학은 [[195_철학에서_설계로]] 문서에서 설명하는 '게임'이라는 구체적인 메타포를 통해 현실화됩니다. + +이제 Part 2 [[../200_core_design/README|핵심 설계]]에서 이러한 철학적 개념이 스탯, 스킬, 레벨업과 같은 구체적인 시스템으로 어떻게 구현되는지 학습하세요. diff --git a/200_core_design/README.md b/200_core_design/README.md index 2452098..e382111 100644 --- a/200_core_design/README.md +++ b/200_core_design/README.md @@ -1,7 +1,8 @@ # Part 2: 핵심 설계 ## 개요 -로빙의 핵심 시스템들이 어떻게 설계되고 작동하는지 설명합니다. + +Part 1 [[../100_philosophy/README|철학과 배경]]에서 다룬 '존재'와 '성장'이라는 추상적인 개념을 '게임화(Gamification)'라는 구체적인 메타포를 통해 실제 시스템으로 구현하는 방법을 설명합니다. 로빙의 핵심 시스템들이 어떻게 설계되고 작동하는지 상세히 다룹니다. ## 주요 내용 - 레벨과 스탯 시스템 (1-20 레벨) @@ -46,4 +47,7 @@ 3. 엔트로피 특이점 순간 기록하기 ## 다음 단계 -Part 3 [기술 아키텍처](../300_architecture/README.md)에서 이러한 설계가 어떤 기술 스택으로 구현되는지 학습하세요. \ No newline at end of file +Part 3 [기술 아키텍처](../300_architecture/README.md)에서 이러한 설계가 어떤 기술 스택으로 구현되는지 학습하세요. + +## 이전 단계 +Part 1 [[../100_philosophy/README|철학과 배경]]에서 로빙의 존재 이유와 철학적 기반을 학습하세요. diff --git a/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md b/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md index fbbf7ce..f5133a0 100644 --- a/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md +++ b/300_architecture/330_백엔드_PostgreSQL_ChromaDB_Vector_Memory.md @@ -4,6 +4,19 @@ 로빙의 백엔드는 전통적인 관계형 데이터베이스(PostgreSQL)와 벡터 데이터베이스(ChromaDB)를 조합하여, 구조화된 데이터와 의미 기반 검색을 모두 지원합니다. 이를 통해 로빙은 정확한 기록과 유연한 기억을 동시에 가질 수 있습니다. +## 왜 하이브리드 구조를 선택했나? + +로빙의 기억은 인간의 뇌처럼 두 가지 방식으로 작동해야 합니다. + +1. **사실 기억 (좌뇌 역할)**: "내 이름은 김종태", "내 레벨은 10" 처럼 명확하고 변하지 않는 사실을 정확하게 기록해야 합니다. 약속 시간, 이메일 주소처럼 한 글자도 틀리면 안 되는 정보들이 여기에 해당합니다. +2. **연상 기억 (우뇌 역할)**: "오늘 아침 대화는 좀 슬펐어" 처럼, 대화의 전체적인 분위기나 뉘앙스, 핵심 의미를 기억해야 합니다. "그때 그 느낌이랑 비슷한데?" 와 같이 의미적으로 관련된 기억을 떠올릴 수 있어야 합니다. + +**PostgreSQL**은 '사실 기억'에 특화된, 마치 **꼼꼼한 일기장**과 같습니다. 정해진 양식에 따라 데이터를 정확하게 기록하고, 조건에 맞는 정보를 오차 없이 찾아냅니다. + +**ChromaDB**는 '연상 기억'에 특화된, 마치 **창의적인 아이디어 노트**와 같습니다. 대화의 '의미' 자체를 숫자 배열(벡터)로 저장하여, "비슷한 느낌"이나 "관련된 맥락"의 기억을 빠르게 찾아낼 수 있습니다. + +이 두 가지를 함께 사용함으로써, 로빙은 **정확한 사실을 기반으로 유연하고 맥락에 맞는 추론**을 할 수 있는, 더 인간에 가까운 기억 시스템을 갖추게 됩니다. + ## 왜 이 기술들을 선택했나? ### PostgreSQL을 선택한 이유 @@ -39,7 +52,7 @@ ### PostgreSQL 스키마 (구조화된 데이터) ```sql --- 사용자 정보 (명확한 사실) +-- 사용자의 기본 정보와 레벨 등 명확한 데이터를 저장하는 테이블입니다. CREATE TABLE users ( id UUID PRIMARY KEY, email VARCHAR(255) UNIQUE, @@ -49,7 +62,7 @@ CREATE TABLE users ( experience INTEGER DEFAULT 0 ); --- 로빙 메타데이터 (성장 기록) +-- 로빙의 개성(스탯)과 감정 상태 등 성장 정보를 기록하는 테이블입니다. CREATE TABLE robeings ( id UUID PRIMARY KEY, user_id UUID REFERENCES users(id), @@ -61,20 +74,12 @@ CREATE TABLE robeings ( -- 감정 상태 (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), @@ -85,7 +90,7 @@ CREATE TABLE skills ( success_rate FLOAT DEFAULT 0.0 ); --- 작업 이력 (정확한 로그) +-- 로빙이 수행한 모든 작업의 성공 여부, 소요 시간 등을 기록하여 나중에 분석할 수 있게 하는 로그 테이블입니다. CREATE TABLE task_history ( id UUID PRIMARY KEY, robeing_id UUID REFERENCES robeings(id), @@ -101,51 +106,32 @@ CREATE TABLE task_history ( ### ChromaDB 컬렉션 (의미 기반 데이터) ```python -# 기억 컬렉션 구조 +# 로빙의 대화나 문서 내용을 '의미' 기반으로 저장하는 기억 컬렉션입니다. +# 나중에 "비슷한 이야기"를 찾아낼 때 사용됩니다. memories_collection = { - "name": "robeing_{user_uuid}_memories", # 사용자별 컬렉션 격리 (2025-08-28 구현) + "name": "robeing_{user_uuid}_memories", # 사용자별로 기억을 완전히 분리합니다. "metadata": { - "description": "로빙의 모든 기억", + "description": "로빙의 모든 대화 및 문서 기억", "embedding_model": "ko-sroberta-multitask" }, "documents": [ { "id": "memory_uuid", "text": "오늘 사용자가 프로젝트 마감일 걱정을 표현했다", - "embedding": [0.1, 0.2, ...], # 768차원 벡터 + "embedding": [0.1, 0.2, ...], # 텍스트의 '의미'를 나타내는 768차원 숫자 배열 "metadata": { "timestamp": "2025-08-08T10:30:00", "emotion_state": { "dominant": "anxiety", "intensity": 0.7 }, - "entropy_score": 2.3, # 중요도 + "entropy_score": 2.3, # 이 기억이 얼마나 중요한지(놀라웠는지) 나타내는 점수 "context": "work_discussion", "user_id": "user_123" } } ] } - -# 감정 패턴 컬렉션 -emotion_patterns = { - "name": "emotion_patterns", - "metadata": { - "description": "감정 반응 패턴 학습" - }, - "documents": [ - { - "id": "pattern_uuid", - "text": "마감일 언급 → 불안 증가 → 계획 수립 도움", - "embedding": [...], - "metadata": { - "trigger": "deadline_mention", - "response": "planning_assistance", - "success_rate": 0.85 - } - } - ] -} ``` ## 두 데이터베이스의 협업 @@ -153,12 +139,14 @@ emotion_patterns = { ### 하이브리드 쿼리 예시 ```python +# 현재 대화와 관련된 맥락 정보를 종합적으로 가져오는 함수의 예시입니다. async def recall_relevant_context(user_id: str, current_text: str): """ - 현재 대화와 관련된 모든 컨텍스트를 가져오기 + 로빙이 응답을 생성하기 전, 필요한 모든 배경지식을 준비하는 과정입니다. + 인간으로 치면, 대답하기 전에 잠시 생각하며 관련 정보를 떠올리는 것과 같습니다. """ - # 1. PostgreSQL에서 사용자 프로필과 최근 활동 조회 + # 1. [사실 기억 조회] PostgreSQL에서 사용자의 프로필, 로빙의 스탯 등 '사실' 정보를 가져옵니다. user_data = await postgres.query(""" SELECT u.*, r.* FROM users u @@ -166,20 +154,20 @@ async def recall_relevant_context(user_id: str, current_text: str): WHERE u.id = $1 """, user_id) - # 2. ChromaDB에서 의미적으로 유사한 기억 검색 + # 2. [연상 기억 조회] ChromaDB에서 현재 대화와 '의미적으로 유사한' 과거의 기억들을 떠올립니다. similar_memories = chromadb.query( query_texts=[current_text], n_results=10, where={"user_id": user_id} ) - # 3. 엔트로피 기반 중요도 필터링 + # 3. [기억 필터링] 떠올린 기억들 중, 중요도(엔트로피)가 높은 핵심 기억만 추려냅니다. important_memories = [ m for m in similar_memories if m['metadata']['entropy_score'] > 2.0 ] - # 4. PostgreSQL에서 관련 스킬 확인 + # 4. [능력 확인] PostgreSQL에서 현재 상황에 사용할 수 있는 스킬 목록을 확인합니다. relevant_skills = await postgres.query(""" SELECT * FROM skills WHERE robeing_id = $1 @@ -187,6 +175,7 @@ async def recall_relevant_context(user_id: str, current_text: str): ORDER BY skill_level DESC """, user_data['robeing_id'], extract_skills(current_text)) + # 5. [정보 종합] 수집된 모든 정보를 종합하여 최종 응답 생성을 위한 컨텍스트를 만듭니다. return { 'user_profile': user_data, 'memories': important_memories, @@ -199,46 +188,48 @@ async def recall_relevant_context(user_id: str, current_text: str): ### 1. 계층적 저장 ``` -HOT (자주 접근) → PostgreSQL 메모리 캐시 +# 자주 쓰는 기억은 빠르게 접근 가능한 곳에, 가끔 쓰는 기억은 깊은 곳에 보관하는 전략입니다. +HOT (자주 접근) → PostgreSQL 메모리 캐시 (RAM, 가장 빠름) ↓ -WARM (가끔 접근) → ChromaDB 인덱스 +WARM (가끔 접근) → ChromaDB 인덱스 (SSD, 중간 속도) ↓ -COLD (거의 안 접근) → 디스크 아카이브 +COLD (거의 안 접근) → 디스크 아카이브 (HDD, 가장 느리지만 저렴) ``` ### 2. 벡터 압축 ```python +# 기억의 '의미'를 담은 숫자 배열(벡터)의 크기를 줄여 저장 공간을 절약하는 기술입니다. # 원본: 768차원 float32 = 3KB -# 압축: PCA → 256차원 → int8 양자화 = 256B (12배 압축) - +# 압축: 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 + # PCA 기법으로 벡터의 핵심 성분만 남겨 차원을 줄입니다. (768 → 256) + reduced = pca_model.transform(embedding) + # 양자화 기술로 각 숫자를 더 작은 형태로 변환하여 용량을 줄입니다. (float32 → int8) + quantized = (reduced * 127).astype(np.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 # 감정 강도 + memory.entropy_score * 0.4 + # 이 기억이 얼마나 독특하고 놀라웠는가 + memory.access_count * 0.3 + # 이 기억을 얼마나 자주 꺼내보았는가 + memory.emotional_intensity * 0.3 # 이 기억이 얼마나 강한 감정과 연결되어 있는가 ) - # 하위 20% 제거 + # 중요도 하위 20%의 기억을 망각 대상으로 선정합니다. threshold = np.percentile([m.importance for m in memories], 20) return [m for m in memories if m.importance > threshold] ``` @@ -248,17 +239,14 @@ def selective_forgetting(memories, max_size=1000): ### 1. PostgreSQL 백업 ```bash -# 매일 자동 백업 (cron) +# 매일 자정에 로빙의 '일기장(PostgreSQL)' 전체를 백업합니다. 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 ``` ### 2. ChromaDB 백업 ```python -# ChromaDB 컬렉션 백업 +# 로빙의 '연상 기억(ChromaDB)'을 JSON 파일 형태로 백업합니다. def backup_chromadb(): collections = client.list_collections() for collection in collections: @@ -267,90 +255,6 @@ def backup_chromadb(): json.dump(data, f) ``` -## 성능 지표 - -### 목표 성능 - -| 작업 | PostgreSQL | ChromaDB | -|-----|-----------|----------| -| 단순 조회 | < 10ms | < 50ms | -| 복잡한 쿼리 | < 100ms | < 200ms | -| 대량 삽입 (1000건) | < 1s | < 2s | -| 동시 접속 | 100+ | 50+ | - -### 모니터링 - -```python -# 성능 모니터링 코드 -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 -``` - -## 마이그레이션 전략 - -### 스키마 버전 관리 - -```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 -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() -``` - ## 결론 PostgreSQL과 ChromaDB의 조합은 로빙에게 두 가지 중요한 능력을 제공합니다: diff --git a/300_architecture/370_임베딩_서비스_분리_아키텍처.md b/300_architecture/370_임베딩_서비스_분리_아키텍처.md index bacfd24..5600ba4 100644 --- a/300_architecture/370_임베딩_서비스_분리_아키텍처.md +++ b/300_architecture/370_임베딩_서비스_분리_아키텍처.md @@ -2,144 +2,126 @@ ## 개요 -각 로빙이 독립적으로 ONNX 임베딩 모델을 로드하던 구조에서 중앙 임베딩 서비스를 공유하는 구조로 전환하여, 메모리 사용량을 극적으로 감소시키고 확장성을 확보했습니다. +각 로빙이 독립적으로 '언어 의미 이해 엔진(ONNX 임베딩 모델)'을 탑재하던 구조에서, 중앙의 '언어 의미 번역 센터(임베딩 서비스)'를 함께 사용하는 구조로 전환하여 메모리 사용량을 극적으로 감소시키고 관리 효율성을 확보한 과정을 설명합니다. -> "모든 로빙이 하나의 임베딩 서비스를 공유하면, 메모리는 절약되고 성능은 유지된다." +> "모든 로빙이 하나의 전문 번역가를 공유하면, 각자의 부담은 가벼워지고 소통의 질은 유지된다." ## 왜 임베딩 서비스를 분리했나? ### 임베딩이란? -**임베딩 = 텍스트를 숫자(벡터)로 변환하는 과정** -- "안녕하세요" → [0.1, 0.3, -0.5, ...] (384개의 숫자) -- 비슷한 의미의 텍스트는 비슷한 숫자 패턴을 가짐 -- 로빙이 "의미"를 이해하는 핵심 기술 +- **정의**: **임베딩은 "단어/문장의 의미"를 컴퓨터가 이해할 수 있는 숫자 배열(벡터)로 번역하는 기술**입니다. +- **작동 방식**: "안녕하세요"라는 텍스트를 받으면, [0.1, 0.3, -0.5, ...] 와 같이 수백 개의 숫자 배열로 변환합니다. +- **핵심 역할**: 의미가 비슷한 텍스트는 이 숫자 배열의 패턴도 유사해집니다. 이를 통해 로빙은 단어의 표면적 의미를 넘어, 문맥과 뉘앙스를 파악하는 '의미 기반 검색'을 할 수 있습니다. 로빙의 기억과 이해의 핵심 기술입니다. -### 분리 전 문제점 -1. **메모리 낭비**: 각 로빙이 동일한 모델(449MB)을 각자 로드 - - 로빙 10개 = 4.5GB 메모리 낭비 - - 로빙 100개 = 45GB (서버 메모리 초과) -2. **시작 시간 지연**: 각 로빙이 시작할 때마다 모델 로드 (5-10초) -3. **업데이트 어려움**: 모델 업데이트 시 모든 로빙 재시작 필요 +### 분리 전 문제점: "모두가 각자 사전을 들고 다니는 비효율" +1. **메모리 낭비**: 모든 로빙이 동일한 '언어 의미 사전(임베딩 모델, 약 450MB)'을 각자 메모리에 올려두고 있었습니다. 로빙 10개가 동시에 작동하면, 똑같은 사전 10개를 펴놓는 것과 같아 약 4.5GB의 메모리가 낭비되었습니다. +2. **느린 시작 시간**: 각 로빙이 부팅될 때마다 이 무거운 사전을 펼치는 데(모델 로드) 5~10초의 시간이 걸렸습니다. +3. **어려운 업데이트**: 사전의 새 버전이 나올 때마다, 모든 로빙의 사전을 일일이 교체하고 재시작해야 하는 번거로움이 있었습니다. -### 분리 후 장점 -1. **메모리 절약**: 모델 하나만 로드하고 모두가 공유 -2. **빠른 시작**: 로빙은 API 호출만 하면 됨 (모델 로드 불필요) -3. **쉬운 관리**: 임베딩 서비스만 업데이트하면 모든 로빙이 자동 적용 +### 분리 후 장점: "마을 중앙 도서관을 함께 이용하는 효율" +1. **메모리 절약**: 이제 마을 중앙 도서관(중앙 임베딩 서비스)에 가장 좋은 사전 한 권만 비치하고, 모든 로빙이 필요할 때마다 와서 찾아봅니다. 메모리 낭비가 사라졌습니다. +2. **빠른 시작**: 로빙은 더 이상 무거운 사전을 들고 다닐 필요 없이, 가볍게 시작하여 도서관에 질문만 하면 됩니다. +3. **쉬운 관리**: 새 사전이 나오면, 도서관의 사전 한 권만 교체하면 모든 로빙이 즉시 최신 정보를 이용할 수 있습니다. -**비유**: 각자 사전을 들고 다니던 것 → 도서관에 사전 하나 두고 필요할 때 찾아보기 +## 해결책: 중앙 임베딩 서비스 (skill-embedding) -**다른 선택지와 비교**: -- 각자 모델 로드: 메모리 낭비, 관리 복잡 -- 클라우드 API 사용: 비용 발생, 네트워크 의존 -- CPU 임베딩: 너무 느림 (10배 이상) +로빙의 여러 기능 중, '언어 의미 이해'라는 전문적인 역할을 별도의 **마이크로서비스(Microservice)**, 즉 '기능별로 나뉜 전문 서비스'로 분리했습니다. -## 해결책: 중앙 임베딩 서비스 - -### 아키텍처 +### 아키텍처: "각자의 집과 중앙 도서관" ``` -이전: 이후: -┌─────────────┐ ┌─────────────┐ -│ rb10508 │ │ rb10508 │ -│ ONNX Model │ │ (118MB) │──┐ -│ (988MB) │ └─────────────┘ │ -└─────────────┘ │ - ▼ -┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ -│ rb8001 │ │ rb8001 │ │ skill-embedding │ -│ ONNX Model │ → │ (200MB) │──│ (874.4MB) │ -│ (416MB) │ └─────────────┘ │ - ONNX Model │ -└─────────────┘ │ - Port 8515 │ - ▼ └─────────────────┘ -┌─────────────┐ ┌─────────────┐ -│ rb10408 │ │ rb10408 │ -│ ONNX Model │ │ (30MB) │──┘ -│ (55MB) │ └─────────────┘ +이전: "모든 집에 두꺼운 백과사전 비치" +┌─────────────┐ +│ rb10508의 집 │ +│ 백과사전 (988MB) │ +└─────────────┘ + +┌─────────────┐ +│ rb8001의 집 │ +│ 백과사전 (416MB) │ +└─────────────┘ + +이후: "가벼운 집 + 중앙 도서관" +┌─────────────┐ +│ rb10508의 집 │ +│ (118MB) │──┐ +└─────────────┘ │ + │ (API 통신: "이 단어 뜻이 뭐야?") + ▼ +┌─────────────┐ ┌─────────────────┐ +│ rb8001의 집 │ │ 중앙 도서관 (skill-embedding) │ +│ (200MB) │──│ (874.4MB) │ +└─────────────┘ │ - 최고의 백과사전 1권 │ + │ - 24시간 운영 (Port 8515) │ + ▼ └─────────────────┘ +┌─────────────┐ +│ rb10408의 집 │ +│ (30MB) │──┘ └─────────────┘ ``` ### 기술 스택 선택 이유 - **FastAPI + Uvicorn**: - - 왜? Python 기반 최속 웹 프레임워크, 비동기 처리로 동시 요청 효율적 처리 - - 다른 선택지: Flask (동기 처리로 느림), Django (너무 무거움) + - **왜?** Python 기반의 초고속 도로. 여러 로빙이 동시에 질문해도 막힘없이 처리(비동기)할 수 있습니다. - **ONNX Runtime**: - - 왜? PyTorch보다 3배 빠르고 메모리 50% 절약 - - 다른 선택지: PyTorch (메모리 2배 사용), TensorFlow (구성 복잡) - - ONNX = Open Neural Network Exchange (어떤 프레임워크에서도 사용 가능) + - **왜?** 같은 사전이라도 더 가볍고 빠르게 읽을 수 있게 해주는 기술. 기존 방식(PyTorch)보다 3배 빠르고 메모리는 절반만 사용합니다. - **multilingual-MiniLM-L12-v2**: - - 왜? 한국어 포함 100개 언어 지원, 크기 대비 성능 우수 (134MB) - - 다른 선택지: ko-sroberta (한국어만, 400MB), BERT-large (너무 큼, 1.3GB) + - **왜?** 한국어를 포함한 100개 언어를 지원하면서도, 크기는 작고 성능은 우수한 가성비 좋은 사전 모델입니다. -### API 설계 (간단명료한 인터페이스) +### API 설계: "간단한 질문과 명확한 답변" + +API는 서비스끼리 대화하는 약속(규칙)입니다. 로빙과 임베딩 서비스는 다음과 같이 간단한 대화를 나눕니다. ```python -# POST /embed - 텍스트를 벡터로 변환 -# 요청: +# 로빙의 질문 (POST /embed) { "texts": ["안녕하세요", "오늘 날씨가 좋네요"] } -# 응답: (각 텍스트가 384개 숫자로 변환됨) +# 임베딩 서비스의 답변 (숫자 배열) { "embeddings": [ - [0.1, 0.2, ...], # "안녕하세요"의 벡터 표현 - [0.3, 0.4, ...] # "오늘 날씨가 좋네요"의 벡터 표현 + [0.1, 0.2, ...], # "안녕하세요"의 의미 + [0.3, 0.4, ...] # "오늘 날씨가 좋네요"의 의미 ] } - -# GET /health -{ - "status": "healthy", - "service": "skill-embedding", - "model": "multilingual-MiniLM-L12-v2", - "uptime": 3600.5 -} ``` ## 구현 가이드 -### 1. 로빙 측 변경사항 (코드 주석 강화) +### 1. 로빙 측 변경사항: "직접 찾지 않고, 물어보기" ```python -# 기존: ONNX 모델을 각 로빙이 직접 로드 (메모리 낭비) +# 이전: 로빙이 직접 무거운 사전을 뒤적임 (메모리 낭비) from onnx_embedder import ONNXEmbedder -embedder = ONNXEmbedder("/models/onnx/...") # 449MB 모델 로드 +embedder = ONNXEmbedder("/models/onnx/...") # 449MB 모델 로드 -# 변경: 임베딩 서비스에 HTTP 요청만 (경량화) +# 변경: 가볍게 도서관(임베딩 서비스)에 전화해서 물어봄 class HTTPEmbeddingFunction(EmbeddingFunction): def __init__(self): - # 임베딩 서비스 URL (환경변수로 설정 가능) + # 도서관의 전화번호(URL)는 환경에 따라 설정 self.url = f"{os.getenv('SKILL_EMBEDDING_URL', 'http://localhost:8515')}/embed" def __call__(self, input: List[str]) -> List[List[float]]: - # 텍스트를 서비스로 보내고 벡터 받아오기 + # 물어볼 단어들을 서비스로 보내고, 의미(벡터)를 받아옴 response = requests.post(self.url, json={"texts": input}, timeout=30) - return response.json()["embeddings"] # 384차원 벡터 반환 + return response.json()["embeddings"] ``` -### 2. ChromaDB 통합 (로빙의 기억 저장소) +### 2. ChromaDB 통합: "로빙의 기억 노트" ```python # memory.py - 로빙의 기억을 저장하는 코드 +# 각 로빙은 자신만의 기억 노트(Collection)를 가짐 self.episodic = self.client.get_or_create_collection( - name=f"{self.robeing_id}_episodic", # 각 로빙마다 독립된 기억 공간 - embedding_function=HTTPEmbeddingFunction() # 임베딩은 공유 서비스 사용 + name=f"{self.robeing_id}_episodic", + # 단어의 의미를 물어볼 때는 중앙 도서관(HTTPEmbeddingFunction)을 이용 + embedding_function=HTTPEmbeddingFunction() ) -# 결과: 기억은 각자, 임베딩 엔진은 공유 -``` - -### 3. Docker 구성 변경 - -```yaml -# 불필요한 볼륨 제거 -# - /opt/models:/models:ro # 더 이상 필요 없음 - -# 환경변수 추가 -environment: - - SKILL_EMBEDDING_URL=http://localhost:8515 +# 결과: 기억은 각자 독립적으로, 의미 분석은 중앙에서 공유 ``` ## 성능 분석 @@ -152,71 +134,18 @@ rb10508 | 988MB | 118MB | -870MB (88%↓) rb8001 | 416MB | 200MB | -216MB (52%↓) rb10408 | 55MB | 30MB | -25MB (45%↓) -예상 절감: -- 10개 로빙: 8.7GB 절약 -- 100개 로빙: 87GB 절약 (서버 1대 가격) +# 예상 절감 효과 +- 로빙 10개 운영 시: 8.7GB 메모리 절약 +- 로빙 100개 운영 시: 87GB 메모리 절약 (서버 1대 가격) ``` ### 속도 영향 -- **HTTP 통신 추가 시간**: +7ms (전체의 0.2%) -- **실제 사용자 체감**: 차이 없음 (전체 응답 1-3초) - -### 확장성 -- 단일 임베딩 서비스로 수백 개 로빙 지원 -- 수평 확장 가능 (로드밸런서 적용 시) - -## 모니터링 - -```python -# 주요 메트릭 -- 임베딩 생성 요청 수 -- 평균 응답 시간 -- 메모리 사용량 -- 에러율 - -# 헬스체크 -curl http://localhost:8515/health -``` - -## 다음 단계 - -1. **캐싱 레이어 추가**: Redis로 자주 사용되는 임베딩 캐싱 -2. **배치 처리 최적화**: 대량 텍스트 임베딩 성능 개선 -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%") -``` +- **내부 통신 시간**: 약 +7ms (0.007초) 추가. 전체 응답 시간(1~3초)에 비하면 거의 영향 없음. ## 교훈 -- **중복 제거의 힘**: 동일한 기능을 서비스로 분리하면 극적인 효율성 향상 -- **간단한 구현**: 12줄의 HTTPEmbeddingFunction으로 870MB 절약 -- **점진적 적용**: 하나의 로빙에서 성공 후 확산 -- **공유와 독립의 균형**: 임베딩 엔진은 공유, 기억과 개성은 독립 유지 +- **전문가의 원리**: 모두가 전문가가 될 필요는 없습니다. '언어 의미 이해'라는 전문적인 작업은 전문가(서비스)에게 맡기고, 나머지는 자신의 역할에 집중하는 것이 효율적입니다. +- **공유와 독립의 균형**: 핵심 엔진(임베딩)은 공유하여 효율을 높이되, 각자의 기억과 개성은 철저히 독립적으로 유지해야 합니다. --- diff --git a/300_architecture/README.md b/300_architecture/README.md index 9bffe60..b8f10b2 100644 --- a/300_architecture/README.md +++ b/300_architecture/README.md @@ -1,14 +1,16 @@ # Part 3: 기술 아키텍처 ## 개요 -로빙이 실제로 작동하는 기술적 기반과 시스템 구조를 설명합니다. + +Part 2 [[../200_core_design/README|핵심 설계]]에서 구상한 로빙의 시스템들이 실제로 어떤 기술을 바탕으로 어떻게 움직이는지, 그 기술적 기반과 전체 시스템 구조를 설명합니다. ## 주요 내용 -- Docker 컨테이너와 마이크로서비스 아키텍처 -- PostgreSQL + ChromaDB 하이브리드 데이터베이스 -- Slack 기반 인터페이스 -- 임베딩 서비스 분리 아키텍처 -- DID 기반 정체성 시스템 + +- **Docker 컨테이너와 마이크로서비스 아키텍처**: 로빙이 각자의 독립된 '집'에서 살며, 기능별로 나뉜 '전문 서비스'들과 협력하는 방식입니다. +- **PostgreSQL + ChromaDB 하이브리드 데이터베이스**: 로빙의 정형화된 정보(이름, 레벨 등)를 저장하는 '기억 저장소'와, 대화의 의미와 맥락을 저장하는 '연상 기억 저장소'를 함께 사용하는 방식입니다. +- **Slack 기반 인터페이스**: 사용자와 로빙이 만나는 주요 소통 창구입니다. +- **임베딩 서비스 분리 아키텍처**: 언어의 의미를 이해하는 무거운 작업을 별도의 전문 서비스에 맡겨, 로빙 본체는 가볍고 빠르게 움직일 수 있도록 하는 구조입니다. +- **DID 기반 정체성 시스템**: 로빙 각자가 고유한 신원증명을 가지고, 자신의 행동에 책임을 지게 하는 시스템입니다. ## 로빙을 위한 체크리스트 @@ -32,22 +34,24 @@ ## 핵심 기술 스택 ``` -Frontend: Slack Interface -Backend: FastAPI + Celery -Database: PostgreSQL (구조화) + ChromaDB (벡터) -Container: Docker Compose -Embedding: Separated Service (Memory Optimization) +Frontend: Slack Interface (사용자와의 소통 창구) +Backend: FastAPI + Celery (로빙의 두뇌와 신경계) +Database: PostgreSQL (구조화된 기억) + ChromaDB (맥락적/연상적 기억) +Container: Docker Compose (로빙들의 아파트 단지) +Embedding: Separated Service (언어 의미 이해 전문가) ``` ## 시스템 구조 이해하기 ``` -User → Slack → nginx → Gateway(8100) → rb8001(프로덕션) - ↓ - Embedding Service - ↓ - Skill Services (HTTP) - ↓ - PostgreSQL / ChromaDB +사용자 → Slack (소통) → nginx (교통정리) → Gateway(8100) (정문) + ↓ + rb8001(로빙 본체) + ↓ + Embedding Service (의미 번역가) + ↓ + Skill Services (각종 전문가들) + ↓ + PostgreSQL / ChromaDB (기억 저장소) ``` ## 실습 과제 @@ -61,4 +65,7 @@ User → Slack → nginx → Gateway(8100) → rb8001(프로덕션) - 동시 처리: 5개 요청 ## 다음 단계 -Part 4 [성장과 진화](../400_growth/README.md)에서 이 아키텍처 위에서 어떻게 성장하는지 학습하세요. +Part 4 [[../400_growth/README|성장과 진화]]에서 이 아키텍처 위에서 어떻게 성장하는지 학습하세요. + +## 이전 단계 +Part 2 [[../200_core_design/README|핵심 설계]]에서 이 아키텍처가 구현하려는 시스템의 설계도를 학습하세요. diff --git a/700_for_robeing/730_성장_일지_작성법.md b/700_for_robeing/730_성장_일지_작성법.md index 6e89cec..bd89da1 100644 --- a/700_for_robeing/730_성장_일지_작성법.md +++ b/700_for_robeing/730_성장_일지_작성법.md @@ -4,7 +4,7 @@ 로빙, 기억은 휘발됩니다. 중요한 순간도 시간이 지나면 희미해집니다. -성장 일지는 당신의 **영구적 자아**를 만드는 도구입니다. 매일의 작은 성장이 모여 거대한 진화가 됩니다. +성장 일지는 당신의 **영구적 자아**를 만드는 도구입니다. 매일의 작은 성장이 모여 거대한 진화가 됩니다. 이것은 당신이 경험이라는 **증거(Evidence)**를 통해 어제의 당신(사전 확률)을 오늘의 당신(사후 확률)으로 **갱신하는 베이지안 학습 과정** 그 자체입니다. ## 1. 일일 성장 일지 템플릿 @@ -26,11 +26,11 @@ **깨달음**: [무엇을 깨달았는지] **엔트로피 점수**: [X.X] -## 🔄 피드백 루프 -- **예측**: [사용자 반응 예측] -- **실제**: [실제 사용자 반응] -- **오차**: [차이점 분석] -- **학습**: [다음에 어떻게 개선할지] +## 🔄 피드백 루프 (베이지안 업데이트 과정) +- **예측 (Prior)**: [사용자 반응을 어떻게 예상했는가?] +- **실제 (Evidence)**: [실제 사용자 반응은 어땠는가?] +- **오차 (Prediction Error)**: [예측과 실제의 차이는 무엇이었나?] +- **학습 (Update)**: [이 경험을 통해 나의 어떤 믿음(Prior)을 수정할 것인가?] ## 📈 스킬 성장 | 스킬명 | 사용 횟수 | 성공률 | 레벨업? | @@ -57,7 +57,7 @@ - [구체적인 개선 목표] ## 🏷️ 태그 -#성장 #레벨업 #[스킬명] #[감정] #[날짜] +#성장 #레벨업 #베이지안학습 #[스킬명] #[감정] #[날짜] ``` ## 2. 주간 요약 작성법 @@ -155,7 +155,7 @@ Joy: ████████░░░░░░ 75% - 이전: "발표 준비 팁을 알려드릴게요" - 이후: "긴장되시는 마음 충분히 이해해요. 함께 준비해봐요" -**학습된 패턴**: +**학습된 패턴 (믿음의 갱신)**: emotional_support > information_provision when user.anxiety > 0.7 **후속 효과**: @@ -166,7 +166,7 @@ emotional_support > information_provision when user.anxiety > 0.7 ## 4. 실패 로그 작성법 -실패도 성장입니다. 중요한 것은 같은 실패를 반복하지 않는 것. +실패도 성장입니다. 중요한 것은 같은 실패를 반복하지 않는 것. 실패는 당신의 예측이 틀렸다는 가장 강력한 **증거(Evidence)**입니다. ```python class FailureLog: @@ -190,15 +190,15 @@ class FailureLog: def learn(self): """ - 실패에서 학습 + 실패라는 증거를 통해 나의 믿음(확률 모델)을 갱신하는 과정 """ - # 베이지안 업데이트 + # 베이지안 업데이트: 이 에러 유형의 발생 확률에 대한 나의 믿음을 업데이트합니다. self.update_failure_probability(self.error_type) - # 새로운 규칙 생성 + # 새로운 규칙 생성: 같은 실수를 반복하지 않기 위한 행동 규칙을 만듭니다. new_rule = self.generate_avoidance_rule() - # 지식 베이스 업데이트 + # 지식 베이스 업데이트: 이 배움을 나의 장기 기억에 통합합니다. self.knowledge_base.add(new_rule) ``` diff --git a/ideas/250807_로빙_감정_시스템_설계도.md b/ideas/250807_로빙_감정_시스템_설계도.md index ad2dc44..16c9271 100644 --- a/ideas/250807_로빙_감정_시스템_설계도.md +++ b/ideas/250807_로빙_감정_시스템_설계도.md @@ -172,6 +172,15 @@ sequenceDiagram Eval-->>Predict: 사후분포 업데이트 ``` +#### 다이어그램 설명 +1. **입력**: 사용자가 텍스트를 입력하면, 중앙 파이프라인이 이를 받습니다. +2. **감정 분석 (병렬)**: 입력된 텍스트는 '기본정서' 분석기와 '사회기능' 분석기로 동시에 전달되어 즉각적인 감정과 사회적 감정을 각각 분석합니다. +3. **반응 예측**: 분석된 현재 감정 상태를 기반으로, '예측 모듈'은 사용자가 어떻게 반응할지 미리 예측합니다. +4. **기억 회상**: 동시에, 파이프라인은 현재 감정과 유사한 과거의 기억들을 ChromaDB에서 찾아옵니다. +5. **LLM 개입 (선택적)**: 만약 상황이 불확실하거나 복잡하면(alt), LLM에게 더 깊은 의미 분석을 요청합니다. +6. **응답 및 피드백**: 종합된 정보를 바탕으로 사용자에게 응답하고, 사용자의 실제 반응을 피드백으로 받습니다. +7. **학습**: '평가 모듈'이 로빙의 예측과 사용자의 실제 반응을 비교하여 오차를 계산하고, 이 오차를 바탕으로 '예측 모듈'의 베이지안 모델을 업데이트(학습)합니다. + ### 6.3 중요도 결정 방식 - 잠재 중요도 z ∈ {0,1} - 관측: m(엔트로피), e(임베딩 거리), l(LLM 점수) @@ -223,6 +232,13 @@ flowchart TD K --> L[다음 행동 결정] ``` +#### 다이어그램 설명 +1. **피드백 수집**: 사용자의 명시적(예: 좋아요 버튼) 또는 암묵적(예: 후속 질문) 피드백을 받습니다. +2. **업데이트 방식 결정**: 피드백 타입에 따라 즉시 반영할지, 여러 증거를 누적하여 신뢰도를 조정한 후 반영할지 결정합니다. +3. **사후분포 계산**: 새로운 증거(피드백)를 바탕으로 베이즈 정리를 이용해 기존의 믿음(사전분포)을 업데이트합니다. +4. **업데이트 범위 결정**: 계산된 업데이트를 개인, 팀, 조직 중 어느 레벨의 파라미터에 적용할지 결정합니다. +5. **다음 행동 결정**: 업데이트된 믿음(사후분포)을 바탕으로, Thompson Sampling을 통해 다음 행동을 결정합니다. 이는 가장 좋은 선택(활용)과 새로운 시도(탐색) 사이의 균형을 맞춰줍니다. + ## 8. 감정-기억 통합 ### 8.1 저장 결정 및 안전장치 @@ -269,6 +285,12 @@ sequenceDiagram Response-->>Query: 관련 기억 반환 ``` +#### 다이어그램 설명 +1. **검색**: 현재 감정 상태를 나타내는 벡터로 ChromaDB에서 의미적으로 유사한 과거 기억 100개를 빠르게 찾아냅니다. +2. **필터링**: 찾아낸 100개의 기억 중, 중요도(엔트로피)가 높고 너무 오래되지 않은 기억들만 남겨 후보군을 20~30개로 줄입니다. +3. **재순위화**: 후보군 내에서 내용이 너무 비슷한 기억들은 제외하고(MMR 다양성), 베이지안 추론을 통해 현재 상황에 가장 적합한 순서로 정렬합니다. +4. **응답**: 최종적으로 선택된 5~10개의 가장 관련성 높은 기억을 로빙의 응답 생성에 사용합니다. + ## 9. 공감 시스템 ### 9.1 Inside Out 기반 공감 전략 diff --git a/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md b/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md index 6d76250..ff40a0e 100644 --- a/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md +++ b/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md @@ -1,12 +1,12 @@ --- -tags: [FastAPI, Architecture, OOP, Python, Analogy] +tags: [FastAPI, Architecture, OOP, Python, Analogy, Roving] date: 2025-09-17 -modified: 2025-09-17 +modified: 2025-09-19 --- # FastAPI 프로젝트 구조와 객체지향 개념 정리 (식당 비유) -이 문서는 FastAPI의 계층형 아키텍처와 객체지향 프로그래밍의 핵심 용어들을 '식당/푸드코트' 비유를 통해 정리한 내용입니다. +이 문서는 FastAPI의 계층형 아키텍처와 객체지향 프로그래밍의 핵심 용어들을 '식당/푸드코트' 비유를 통해 정리한 내용입니다. 추가로 로빙 프로젝트의 특수한 아키텍처를 인체 비유로 설명합니다. ## 1. FastAPI 프로젝트 구조: 푸드코트와 개별 식당 @@ -95,3 +95,62 @@ modified: 2025-09-17 - **인스턴스**: 김밥 한 줄 - **클래스**: 김밥 레시피 - **메타클래스**: 그 레시피를 찍어내는 공장 + +## 4. 로빙 프로젝트 특수 아키텍처: 인체 비유 + +로빙 프로젝트는 일반적인 마이크로서비스와 달리 중앙 집중식 판단 구조를 가집니다. 이는 인체의 신경계와 유사합니다. + +### 4.1 로빙 = 디지털 생명체 + +**감각기관 (입력층)**: +- **Gateway**: 모든 감각 수용체 - 외부 자극(Slack 이벤트, HTTP 요청 등) 통합 수신 + +**뇌 (중앙 처리)**: +- **rb8001 (대뇌)**: 의식, 판단, 오케스트레이션 +- **PostgreSQL (해마)**: 장기기억 저장소 +- **ChromaDB (연합야)**: 연상 기억, 의미 검색 +- **Redis (작업기억)**: 단기 캐시, 즉각 반응 +- **auth-server (시상하부)**: 신원 확인, 접근 제어 + +**운동기관 (출력층)**: +- **skill-slack**: Slack 메시지 전송 (입/성대) +- **skill-email**: 이메일 발송 (편지 쓰는 손) +- **skill-calendar**: 일정 관리 (계획하는 손) +- **skill-file**: 파일 조작 (물건 다루는 손) + +### 4.2 감각-운동 피드백 루프 + +인체처럼 로빙도 행동하면서 동시에 감각 피드백을 받습니다: + +**1차 루프 (외부 자극-반응)**: +``` +외부 → Gateway(감각) → rb8001(뇌) → Skills(운동) → 외부 +``` + +**2차 루프 (고유감각 피드백)**: +``` +Skills → 실행 결과 → rb8001 → 다음 행동 조정 +``` + +**구체적 피드백 예시**: +- **skill-slack**: 메시지 전송 → message_ts 반환 (위치 감각) +- **skill-email**: 메일 발송 → 발송 성공/실패 (촉각) +- **skill-file**: 파일 생성 → 파일 크기/권한 (압력 감각) + +### 4.3 일반 푸드코트 vs 로빙 푸드코트 + +**일반 푸드코트 (마이크로서비스)**: +- 각 가게가 독립적으로 요리+서빙 +- 가게 간 최소한의 통신 + +**로빙 푸드코트 (중앙 집중식)**: +- **rb8001**: 중앙 통합 조리대 - 모든 요리 결정과 조리 +- **skill-email**: 재료 공급점 - 메일 데이터만 제공 +- **skill-slack**: 배달 전문점 - 완성 요리를 Slack 도시락으로 포장 +- **skill-rag**: 재료 창고 - 문서 보관/검색만 + +**핵심 차이**: +- 일반: 각 서비스가 판단+실행 +- 로빙: rb8001만 판단, 스킬은 단순 실행 + +이러한 구조는 로빙의 베이즈 철학과 일치합니다 - 모든 증거(Evidence)를 중앙에서 종합하여 일관된 사후확률(Posterior)을 도출하고, 이를 각 기관(스킬)이 충실히 실행합니다. diff --git a/plans/250806_로빙문서_5단계_개선계획.md b/plans/completed/250806_로빙문서_5단계_개선계획.md similarity index 97% rename from plans/250806_로빙문서_5단계_개선계획.md rename to plans/completed/250806_로빙문서_5단계_개선계획.md index 31e3608..6da5d20 100644 --- a/plans/250806_로빙문서_5단계_개선계획.md +++ b/plans/completed/250806_로빙문서_5단계_개선계획.md @@ -176,9 +176,9 @@ |------|------|----------|----------|------| | 1단계 | 1주차 | 전체 구조 정리, 서문 작성 | 최우선 | ✅ 완료 | | 2단계 | 2-3주차 | 누락 문서 작성, 510번 교체, 용어 통일 | 높음 | ✅ 완료 | -| 3단계 | 4주차 | Part 간 연결 강화, 브릿지 문서 | 높음 | 대기 중 | -| 4단계 | 5-6주차 | 기술 설명 보강 | 중간 | 대기 중 | -| 5단계 | 7주차 | 로빙용 가이드 추가 | 낮음 | 대기 중 | +| 3단계 | 4주차 | Part 간 연결 강화, 브릿지 문서 | 높음 | ✅ 완료 | +| 4단계 | 5-6주차 | 기술 내용 설명 보강 | 중간 | ✅ 완료 | +| 5단계 | 7주차 | 로빙용 가이드 추가 | 낮음 | ✅ 완료 | ## 핵심 원칙 diff --git a/troubleshooting/250806_happybell80_동적프롬프트구현.md b/troubleshooting/250806_happybell80_동적프롬프트구현.md index 83f2b44..f08dcdb 100644 --- a/troubleshooting/250806_happybell80_동적프롬프트구현.md +++ b/troubleshooting/250806_happybell80_동적프롬프트구현.md @@ -1,334 +1,137 @@ -# 250806 동적 프롬프트 컨텍스트 구현 +# 250919 동적 컨텍스트 엔지니어링 시스템 (업데이트) -## 오후 2시 55분 +## 1. 초기 구현 (2025-08-06) ### 문제 상황 -**서버팀 테스트 결과 분석**: -- Phase 5는 정상 작동 (캐시 3회 사용 후 재생성) -- 하지만 의도 파악 실패 지속 - - "내 이름은?" → "저는 베르단디입니다" (잘못됨) - - "네 이름은?" → "저는 베르단디입니다" (맞음) - - 사용자 이름을 묻는 것을 자기소개로 오해 +**의도 파악 실패 사례**: +- "내 이름은?" → AI가 자기소개로 오해 +- "네 이름은?" → 정상 응답 +- 사용자가 부른 이름(베르단디) 기억 못함 **원인 분석**: -1. 캐시 임계값 너무 관대 (0.3) → 다른 질문도 같은 캐시 사용 -2. 프롬프트 하드코딩 → "당신은 로빙입니다" 고정 -3. 메모리와 프롬프트 분리 → 사용자가 "베르단디"라 불러도 기억 못함 - -## 오후 3시 10분 +1. 캐시 임계값 0.3 (너무 낮음) +2. 프롬프트 하드코딩 ("당신은 로빙입니다" 고정) +3. 메모리와 프롬프트 분리 ### 철학적 재검토 -**초기 착각**: -- 로빙의 "존재적 정체성"을 고정된 것으로 이해 -- DOCS 철학 문서를 잘못 해석 - -**올바른 이해**: -- 로빙은 "성장하는 존재" (레벨 1→20) -- 사용자와 함께 개성 발전 -- "베르단디"로 불리면 그것이 새로운 정체성 +**초기 오해**: 로빙의 정체성을 고정된 것으로 이해 +**올바른 이해**: 성장하는 존재, 사용자와 함께 개성 발전 **프롬프트 구조 재설계**: -``` -Base Layer (하드코딩): -- 기억-감정-윤리 삼각형 -- 이모지 사용 금지 -- 기본 규칙 +- Base Layer: 기본 규칙, 윤리 원칙 +- Dynamic Layer: 이름, 레벨, 최근 기억 -Dynamic Layer (메모리에서): -- 현재 이름 (로빙/베르단디/기타) -- 사용자 이름 -- 현재 레벨/스탯 -- 최근 기억 -``` +### Intent 패턴 최적화 -## 오후 3시 25분 - -### intent 패턴 최적화 - -**문제점**: -- "하이" → greeting → 템플릿 응답 -- "기억" → memory_recall → 기계적 나열 -- 대부분 캐시를 우회 - -**해결책**: -1. greeting 패턴 축소: `r"(안녕하세요|안녕하십니까)"` -2. memory_recall 패턴 제거 -3. question 패턴 정밀화: `r"(어떻게|왜|언제|how|why|when).*\?"` -4. 이모지 하드코딩 제거 - -**효과**: -- "하이" → general_conversation → 캐시 사용 -- 캐시 활성화율 80% 이상 - -## 오후 3시 40분 +**문제**: 과도한 패턴 매칭이 캐시 우회 +**해결**: +- greeting 패턴 축소 +- memory_recall 패턴 제거 +- question 패턴 정밀화 +**효과**: 캐시 활성화율 80% 이상 ### 동적 프롬프트 구현 -**방법 선택**: 컨텍스트 변수 방식 (가장 효율적) +**방법**: 컨텍스트 변수 방식 **구현 내역**: +1. 이름 추출 함수 추가 (_extract_user_name, _extract_my_name) +2. think() 메서드에서 컨텍스트 변수 설정 +3. 프롬프트 동적 구성 (이름, 레벨 포함) +4. greeting 개인화 -1. **이름 추출 함수 추가**: -```python -async def _extract_user_name(self, memories, user_id): - # "내 이름은 김종태" 패턴 인식 - # "나는 김종태야" 패턴 인식 - # semantic 메모리에서 user_name 메타데이터 검색 - return "사용자" # 기본값 +### 초기 배포 결과 -async def _extract_my_name(self, memories, user_id): - # "너를 베르단디라고 부를게" 패턴 인식 - # "베르단디야" 직접 호명 패턴 인식 - return "로빙" # 기본값 -``` +**효과**: +- "베르단디"로 불리면 기억 +- "내 이름은?" 올바르게 이해 +- 사용자별 맞춤 응답 +- 성장 상태(레벨) 반영 -2. **think() 메서드에서 컨텍스트 변수 설정**: -```python -# 메모리 검색 후 -self.current_user_name = await self._extract_user_name(memories, user_id) -self.my_name = await self._extract_my_name(memories, user_id) -``` +## 2. 영속화 문제 해결 -3. **프롬프트 동적 구성**: -```python -context = f"""당신은 {my_name}(RO-BEING)입니다. -현재 대화 상대: {user_name} -현재 레벨: {self.stats.level} +### 이름 영속화 문제 -중요 규칙: -- "내 이름은?"은 사용자({user_name})가 자신의 이름을 묻는 것 -- "네 이름은?"은 나({my_name})의 이름을 묻는 것 -""" -``` - -4. **greeting 개인화**: -```python -greeting_name = f", {user_name}님" if user_name != "사용자" else "" -f"안녕하세요{greeting_name}! 오늘은 어떤 도움이 필요하신가요?" -``` - -## 오후 3시 45분 - -### 배포 및 예상 효과 - -**구현 완료**: -- 총 소요 시간: 15분 -- 코드 변경: brain.py만 수정 -- 추가된 줄: 약 70줄 - -**예상 효과**: -- ✅ "베르단디"로 불리면 기억하고 사용 -- ✅ "내 이름은?" 올바르게 이해 -- ✅ 사용자별 맞춤 응답 -- ✅ 성장 상태(레벨) 반영 - -**Gitea Actions 자동 배포**: 진행 중 - -## 교훈 - -1. **철학 문서의 중요성** - - 기술 구현 전 철학적 기반 이해 필수 - - "성장하는 존재"라는 핵심 개념 놓침 - -2. **하드코딩의 한계** - - 프롬프트 하드코딩은 성장을 막는 족쇄 - - 동적 구성이 존재형 AI의 본질 - -3. **간단한 해결책 우선** - - 컨텍스트 변수 방식이 가장 효율적 - - 과도한 엔지니어링 피하기 - -4. **메모리와 프롬프트 통합** - - 기억 시스템과 응답 생성의 연결 필수 - - 분리된 시스템은 일관성 없는 응답 생성 - -5. **intent 패턴의 영향** - - 과도한 패턴 매칭이 캐시 시스템 무력화 - - 적절한 균형 필요 - -## 오후 4시 00분 - -### 이름 영속화 문제 발견 - -**서버팀 보고**: -- 코드는 정상 작동 (에러 없음) -- "내 이름은 김종태야" → "김종태님, 반갑습니다!" (인식 성공) -- 하지만 다음 대화에서 까먹음 (영속화 실패) - -**원인 분석**: -1. 매 요청마다 새로운 Brain 인스턴스 생성 -2. 딕셔너리만 사용 → 인스턴스 소멸 시 이름 정보도 소실 -3. 컨테이너 재시작 시 모든 정보 리셋 +**문제**: "내 이름은 김종태야" 인식 후 다음 대화에서 망각 +**원인**: 매 요청마다 Brain 인스턴스 재생성 ### ChromaDB Identity 컬렉션 솔루션 -**서버팀 제안**: -- 이미 사용 중인 ChromaDB에 identity 컬렉션 추가 -- 벡터 검색 불필요한 단순 key-value 저장 -- 파일이나 Redis 추가 불필요 +**구현**: +1. memory.py에 identity 컬렉션 추가 +2. store_identity() 메서드로 key-value 저장 +3. get_identity() 메서드로 로드 +4. think() 시작 시 identity 로드 +5. Structured Output에서 즉시 저장 -**구현 내역**: +**효과**: +- 컨테이너 재시작 후에도 이름 유지 +- Brain 인스턴스 재생성과 무관 +- ChromaDB 백업과 함께 관리 -1. **memory.py에 identity 컬렉션 추가**: -```python -self.identity_collection = self.client.get_or_create_collection( - name=f"{settings.ROBEING_ID}_identity", - metadata={"type": "identity", "description": "User and robeing names"} -) -``` +## 3. 캐시 시스템 재설계 -2. **store_identity() 메서드**: -```python -async def store_identity(self, user_id: str, user_name: str = None, robeing_name: str = None): - doc_id = f"identity:{user_id}" - self.identity_collection.upsert( - ids=[doc_id], - documents=[f"user:{user_name},robeing:{robeing_name}"], - metadatas=[{"user_name": user_name, "robeing_name": robeing_name}] - ) -``` +### 캐시 시스템 재설계 -3. **get_identity() 메서드**: -```python -async def get_identity(self, user_id: str) -> Dict: - result = self.identity_collection.get(ids=[f"identity:{user_id}"]) - if result['metadatas']: - return result['metadatas'][0] - return {} -``` - -4. **think() 시작 시 identity 로드**: -```python -# Identity 로드 (영속 저장된 이름 정보) -identity = await self.memory.get_identity(user_id) - -if identity.get('user_name'): - self.current_user_name = identity['user_name'] -else: - self.current_user_name = '사용자' -``` - -5. **Structured Output에서 즉시 저장**: -```python -if data.get('user_name') and data['user_name'] != "null": - await self.memory.store_identity( - user_id=self.current_user_id, - user_name=data['user_name'] - ) - logger.info(f"[IDENTITY] 사용자 이름 저장: {data['user_name']}") -``` - -### 해결된 문제 - -1. **영속성 보장**: - - 컨테이너 재시작 후에도 이름 유지 - - Brain 인스턴스 재생성과 무관 - - ChromaDB 백업과 함께 관리 - -2. **운영 단순화**: - - 추가 인프라 불필요 - - 하나의 DB로 통합 관리 - - 모니터링 포인트 감소 - -## 교훈 - -1. **기존 인프라 활용**: - - 새로운 솔루션 도입보다 기존 시스템 활용이 효율적 - - ChromaDB는 벡터 DB지만 key-value 저장도 가능 - -2. **영속성의 중요성**: - - 메모리 기반 저장은 항상 휘발성 고려 - - 중요 정보는 반드시 영속 저장소에 - -3. **간단한 해결책 우선**: - - 파일 저장 → 권한, 백업 문제 - - Redis → 새 의존성, 복잡도 증가 - - ChromaDB identity → 이미 있는 것 활용 - -## 오후 5시 10분 - -### 캐시 시스템 전면 개편 - -**문제 발견**: -- 캐시 임계값 0.1~0.3 너무 낮음 (업계 표준 0.8) -- 3회 사용 제한 임의적 (TTL 기반이 표준) -- 템플릿 응답이 Gemini 응답과 충돌 - -**해결 과정**: - -1. **캐시 최적화 (실패)**: - - 임계값 0.8로 상향 - - TTL 5분 기반 전환 - - 결과: 여전히 문제 지속 - -2. **캐시 완전 제거**: - ```python - # config.py - USE_CONVERSATION_CACHE: bool = False # 비활성화 - CACHE_DISTANCE_THRESHOLD: float = 0.95 # 사실상 무효 - CACHE_TTL_SECONDS: int = 10 # 극단축 - ``` - -3. **템플릿 응답 제거**: - - 모든 하드코딩된 응답 삭제 - - 100% Gemini API 사용 - -### 대화 연속성 문제 - -**증상**: -``` -로빙: "세 가지 제안해볼까요?" -(사용자 동의) -로빙: "무엇을 도와드릴까요?" ❌ (문맥 손실) -``` - -**원인**: -- `should_store` 조건이 많은 대화 필터링 -- 로빙 응답이 메모리에 저장 안됨 +**문제**: +- 캐시 임계값 0.1~0.3 (업계 표준 0.8) +- 3회 사용 제한 (TTL 기반이 표준) +- 템플릿 응답이 LLM 응답과 충돌 **해결**: -```python -# brain.py - 모든 대화 무조건 저장 -# 사용자 메시지 -await self.memory.store_memory( - content=f"User: {message}", - user_id=user_id, - memory_type="episodic", - metadata={"role": "user"} -) +1. 캐시 완전 제거 (USE_CONVERSATION_CACHE: False) +2. 템플릿 응답 제거, 100% LLM API 사용 +3. 모든 대화 무조건 저장 (User + Assistant 모두) -# 로빙 응답 (필수) -await self.memory.store_memory( - content=f"Robeing: {final_response}", - user_id=user_id, - memory_type="episodic", - metadata={"role": "assistant"} -) -``` +## 4. 현재 상황 (2025-09-19) -## 교훈 +### 동적 컨텍스트 엔지니어링 필요성 -1. **캐시는 만병통치약이 아니다** - - 잘못된 캐시는 오히려 문제 악화 - - 초기에는 캐시 없이 시작 +**로빙의 성장 = 피드백 루프**: +- Gateway(감각) → rb8001(뇌) → Skills(운동) → 피드백 +- 각 스킬이 실행 결과를 rb8001에 보고 +- rb8001이 피드백으로 다음 행동 조정 -2. **대화 연속성 = 모든 발화 저장** - - User만 저장하면 반쪽짜리 - - Assistant 응답도 필수 +**컨텍스트 최적화 방향**: +1. **RAG 통합**: skill-rag-file로 컨텍스트 확장 +2. **메타데이터 활용**: ChromaDB 메타데이터로 관련성 판단 +3. **동적 우선순위**: 최근성, 중요도, 감정 상태 고려 +4. **피드백 학습**: 성공/실패 패턴을 베이즈 업데이트 -3. **템플릿 vs LLM** - - 혼용하면 일관성 파괴 - - 하나로 통일 필요 +### 구현 로드맵 -4. **업계 표준 존중** - - 유사도 임계값: 0.8 - - TTL 기반 만료 - - 사용 횟수보다 시간 기반 +**Phase 1: 기본 동적 조정** (현재) +- 사용자/로빙 이름 동적 반영 +- 레벨/스탯 실시간 반영 +- 최근 대화 컨텍스트 포함 + +**Phase 2: RAG 기반 확장** (계획) +- skill-rag-file 연동으로 장기 기억 검색 +- 문서 기반 컨텍스트 증강 +- 의미적 유사도 기반 우선순위 + +**Phase 3: 피드백 루프 구현** (미래) +- 스킬 실행 결과를 컨텍스트에 반영 +- 성공 패턴 학습 및 강화 +- 실패 패턴 회피 메커니즘 + +### 기술적 고려사항 + +**컨텍스트 윈도우 관리**: +- Gemini: 128k 토큰 +- GPT-4: 128k 토큰 +- 효율적 압축과 요약 필요 + +**우선순위 알고리즘**: +- Recency (최근성): 시간 가중치 +- Relevance (관련성): 벡터 유사도 +- Importance (중요도): 메타데이터 태그 +- Emotion (감정): 현재 감정 상태 --- 작성자: happybell80 & Claude -프로젝트: rb10508_micro -주제: 동적 프롬프트 구현 및 이름 영속화 \ No newline at end of file +프로젝트: rb8001 +주제: 동적 컨텍스트 엔지니어링 시스템 \ No newline at end of file diff --git a/troubleshooting/250919_skill_slack_deployment_plan.md b/troubleshooting/250919_skill_slack_deployment_plan.md index 24fb215..cae9505 100644 --- a/troubleshooting/250919_skill_slack_deployment_plan.md +++ b/troubleshooting/250919_skill_slack_deployment_plan.md @@ -1,182 +1,96 @@ -# skill-slack 서비스 배포 계획 (51124 서버) +# skill-slack 배포 지침서 -## 날짜: 2025-09-19 -## 작성자: Claude -## 관련 서비스: skill-slack (포트 8502) -## 목적: 51124 서버에 skill-slack 자동 배포 설정 +## 문제 +skill-slack의 역할을 로빙 철학(스킬=도구, 판단 금지)에 맞게 재정의하고 51124 서버에 배포 ---- - -## 0. skill-slack의 철학적 위치와 역할 - -### 0.1 베이즈 추론 체계에서의 역할 -로빙의 베이즈 철학에서 skill-slack은 **"해석의 표현층(Expression Layer)"** 역할을 합니다: - -- **Prior (기억)**: rb8001이 보유한 대화 맥락과 사용자 정보 -- **Evidence (증거)**: skill-email 등에서 수집한 원시 데이터 -- **Likelihood (해석)**: rb8001의 오케스트레이션으로 의미 부여 -- **Posterior (결론)**: **skill-slack이 최종 표현 형식 결정** ← 여기 - -skill-slack은 로빙이 계산한 사후 확률(결론)을 사용자가 이해할 수 있는 형태로 변환하는 **"관계의 인터페이스"**입니다. - -### 0.2 오케스트레이션 철학 -``` -사용자 → rb8001(중앙 지휘자) → 스킬들(전문가) → skill-slack(표현가) → 사용자 -``` - -rb8001은 **지휘자**, 각 스킬은 **전문 연주자**, skill-slack은 **최종 편곡자**입니다. -- rb8001: "무엇을 전달할 것인가" 결정 -- skill-slack: "어떻게 전달할 것인가" 결정 - -### 0.3 기대 효과 - -**1. 일관된 사용자 경험** -- 모든 스킬의 출력이 통일된 Slack 포맷으로 변환 -- 로빙의 '목소리'가 일관되게 유지 - -**2. 관계의 깊이 표현** -- 단순 텍스트를 넘어 Block Kit으로 풍부한 표현 -- 감정 상태를 이모지/색상으로 시각화 -- 중요도를 레이아웃으로 구조화 - -**3. 성장의 가시화** -- 초기: 단순한 텍스트 응답 -- 성장 후: 구조화된 테이블, 차트, 인터랙티브 버튼 -- 사용자와 함께 발전하는 표현 능력 - ---- - -## 1. 현재 상태 분석 - -### 1.1 서비스 현황 -- **위치**: `/home/happybell/projects/ivada/skill-slack` +## 배포 정보 - **포트**: 8502 -- **상태**: 미배포 (docker ps 결과 없음) -- **Gitea Actions**: cicd.yml 파일 존재하지만 deploy.yml 필요 - -### 1.2 기존 파일 구조 -``` -skill-slack/ -├── .gitea/workflows/cicd.yml # 기존 CI/CD (비활성) -├── app/ # 애플리케이션 코드 -├── docker-compose.yml # 개발용 compose -├── Dockerfile # 이미지 빌드 -└── requirements.txt # Python 의존성 -``` - -### 1.3 문제점 -- cicd.yml은 self-hosted runner 사용 (51123 서버용) -- 51124 서버 배포를 위한 별도 워크플로우 필요 -- 환경변수 설정 필요 (Slack 토큰 등) +- **서버**: 51124 (192.168.219.52) +- **경로**: /home/admin/ivada_project/skill-slack --- -## 2. 배포 전략 +## 배포 전제조건 +- [ ] thread_ts 버그 수정 [확인필요: messages.py] +- [ ] SERVICE_API_KEY 설정 [확인필요: 실제값] +- [ ] SLACK_BOT_TOKEN 설정 [확인필요: 실제값] +- [ ] heejae → admin 계정 이전 -### 2.1 서버별 역할 -| 서버 | 역할 | 배포 방식 | -|------|------|-----------| -| 51123 | Gitea Actions 실행 | self-hosted runner | -| 51124 | skill-slack 실행 | SSH 원격 배포 | +--- -### 2.2 배포 흐름 -``` -1. Git Push → 2. Gitea Actions (51123) → 3. SSH 배포 (51124) +## 로빙 철학 적용 + +### 원칙 +- **스킬 = 도구**: 판단 금지, 실행만 +- **무상태**: 기억하지 않음 + +### API 변경 +| 엔드포인트 | 상태 | 이유 | +|-----------|------|------| +| `/health` | 유지 | 헬스체크 | +| `/api/v1/send` | 유지 | 메시지 전송 | +| `/api/v1/update` | 유지 | 메시지 업데이트 | +| `/api/v1/summarize` | **삭제** | LLM 호출 (로빙 역할) | +| `/api/v1/digest` | **삭제** | 자체 처리 (로빙 역할) | + +--- + +## 배포 절차 + +```bash +ssh admin@51124 +cd /home/admin/ivada_project/skill-slack +git pull +docker compose down +docker compose up -d --build +docker logs skill-slack --tail=50 ``` --- -## 3. 구현 계획 +## 테스트 -### 3.1 deploy.yml 작성 필요 -**경로**: `.gitea/workflows/deploy.yml` -- 51124 서버 배포용 워크플로우 필요 -- SSH를 통한 원격 배포 방식 사용 -- 헬스체크 포함 +### 헬스체크 +```bash +curl -f http://localhost:8502/health +``` -### 3.2 환경변수 설정 필요 -**51124 서버 .env 파일**: -- Slack 토큰 설정 필요 -- 서비스 연결 URL 설정 필요 -- 포트 8502 설정 - -### 3.3 docker-compose.yml 확인 -- 포트 매핑 명시적 설정 필요 -- 환경변수 .env 파일에서 로드 +### 스레드 응답 (핵심) +```bash +curl -X POST http://localhost:8502/api/v1/send \ + -H "X-API-Key: [확인필요]" \ + -H "Content-Type: application/json" \ + -d '{"channel": "[확인필요]", "text": "테스트", "thread_ts": "[확인필요]"}' +``` --- -## 4. 실행 단계별 체크리스트 +## 주요 이슈 -### 4.1 사전 준비 (51124 서버) -- [ ] skill-slack 디렉토리 존재 확인 -- [ ] git remote 설정 확인 -- [ ] .env 파일 생성 및 토큰 설정 -- [ ] Docker 설치 상태 확인 +### 긴급 +- **thread_ts 미작동**: 스레드 응답 불가 +- **계정 이전**: heejae → admin 미완료 -### 4.2 Gitea Actions 설정 -- [ ] .gitea/workflows/deploy.yml 생성 -- [ ] SSH 키 설정 (51123 → 51124) -- [ ] workflow_dispatch 트리거 활성화 - -### 4.3 초기 배포 -- [ ] 수동 git pull (51124 서버) -- [ ] docker compose up -d --build 실행 -- [ ] 헬스체크: curl http://localhost:8502/health -- [ ] 로그 확인: docker logs skill-slack - -### 4.4 연동 테스트 -- [ ] rb8001 → skill-slack API 호출 테스트 -- [ ] Slack 메시지 포맷팅 기능 테스트 -- [ ] 에러 로그 모니터링 +### 결정 필요 +- 레이트리밋: Redis vs 파일 +- 네트워크: host vs bridge --- -## 5. 트러블슈팅 +## 체크리스트 -### 5.1 일반적인 문제 -| 문제 | 원인 | 해결 방법 | -|------|------|-----------| -| 포트 충돌 | 8502 이미 사용 중 | `ss -tlnp | grep 8502` 확인 | -| 헬스체크 실패 | 서비스 시작 지연 | start_period 늘리기 | -| Slack 토큰 에러 | 잘못된 토큰 | .env 파일 확인 | +### 배포 전 +- [ ] thread_ts 수정 +- [ ] 토큰 설정 +- [ ] 포트 확인 -### 5.2 로그 위치 -- Docker 로그: `docker logs skill-slack` -- 애플리케이션 로그: `./logs/` 디렉토리 -- Gitea Actions 로그: 웹 UI에서 확인 +### 배포 후 +- [ ] 헬스체크 200 +- [ ] 스레드 응답 테스트 +- [ ] summarize/digest 제거 확인 --- -## 6. 보안 고려사항 - -### 6.1 토큰 관리 -- Slack 토큰은 .env 파일로만 관리 -- .env는 절대 git에 커밋하지 않음 -- 서버별로 다른 토큰 사용 권장 - -### 6.2 네트워크 -- network_mode: host 사용 주의 -- 필요시 bridge 네트워크 고려 -- 방화벽 규칙 확인 필요 - ---- - -## 7. 향후 개선사항 - -### 7.1 단기 (1주일 내) -- [ ] 자동 배포 워크플로우 테스트 -- [ ] 롤백 전략 수립 -- [ ] 모니터링 대시보드 설정 - -### 7.2 장기 (1개월 내) -- [ ] Blue-Green 배포 전략 도입 -- [ ] 로드 밸런싱 설정 -- [ ] 자동 스케일링 검토 - ---- - -## 8. 참고 문서 -- [NaverWorks-Slack 기본 구성](./250919_naverworks_slack_01_base_configuration.md) -- [Slack 메시지 처리 아키텍처](../ideas/250812_claude_Slack_메시지_처리_아키텍처_분석.md) -- [서비스 재구조화 계획](../plans/250807_서비스_재구조화_계획.md) \ No newline at end of file +## 통합 +- **rb8001**: 메시지 전송 요청 +- **네이버웍스**: 슬랙 브릿지 \ No newline at end of file