Update robeing terminology and main_db references

This commit is contained in:
happybell80 2025-08-21 00:00:13 +09:00
parent 35c456571e
commit b2f8ae637f
69 changed files with 396 additions and 396 deletions

View File

@ -79,11 +79,11 @@ class Inventory:
equipped: Dict[str, Item] # 현재 장착 아이템 equipped: Dict[str, Item] # 현재 장착 아이템
storage: List[Item] # 보관 중인 아이템 storage: List[Item] # 보관 중인 아이템
def can_equip(self, item: Item, robing_stats: Stats) -> bool: def can_equip(self, item: Item, robeing_stats: Stats) -> bool:
"""아이템 장착 가능 여부 확인""" """아이템 장착 가능 여부 확인"""
return ( return (
robing_stats.level >= item.level_req and robeing_stats.level >= item.level_req and
all(robing_stats[stat] >= req for stat, req in item.stat_req.items()) all(robeing_stats[stat] >= req for stat, req in item.stat_req.items())
) )
``` ```

View File

@ -158,7 +158,7 @@ rm -rf /home/heejae/rb8001/chroma_db
```python ```python
async def chat(self, message: str, ...): async def chat(self, message: str, ...):
# Memory search # Memory search
memory_manager = MemoryManager(self.robing_name) memory_manager = MemoryManager(self.robeing_name)
memories = await memory_manager.search_memories( memories = await memory_manager.search_memories(
query=message, query=message,
n_results=3 n_results=3
@ -286,7 +286,7 @@ docker compose up -d
```bash ```bash
# 로그 확인 # 로그 확인
docker logs rb8001 -f docker logs rb8001 -f
docker logs robing_state -f docker logs robeing_state -f
# 컨테이너 재시작 # 컨테이너 재시작
docker compose restart docker compose restart

View File

@ -30,7 +30,7 @@ User → rb8001 → skill-email → Gmail API
"message": "이메일 보내줘", "message": "이메일 보내줘",
"user_id": "U091UNVE41M", "user_id": "U091UNVE41M",
"channel": "C123456", "channel": "C123456",
"robing_id": "rb8001", "robeing_id": "rb8001",
"action": "compose", "action": "compose",
"execution_plan": {...} "execution_plan": {...}
} }
@ -66,7 +66,7 @@ User → rb8001 → skill-email → Gmail API
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_tokens (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id VARCHAR(100) UNIQUE NOT NULL, user_id VARCHAR(100) UNIQUE NOT NULL,
robing_id VARCHAR(50), robeing_id VARCHAR(50),
token TEXT NOT NULL, token TEXT NOT NULL,
refresh_token TEXT NOT NULL, refresh_token TEXT NOT NULL,
token_uri VARCHAR(255), token_uri VARCHAR(255),
@ -105,7 +105,7 @@ async def route_email(message):
"original_message": message, "original_message": message,
"hints": hints, "hints": hints,
"slack_user_id": user_id, "slack_user_id": user_id,
"robing_id": robing_id "robeing_id": robeing_id
}) })
# skill-email - 이메일 생성 및 발송 # skill-email - 이메일 생성 및 발송
@ -185,7 +185,7 @@ async def compose_email(request):
- [ ] 로빙별 발신 계정 설정 - [ ] 로빙별 발신 계정 설정
```python ```python
ROBING_GMAIL_MAPPING = { ROBEING_GMAIL_MAPPING = {
"rb8001": "robeing.main", "rb8001": "robeing.main",
"rb10408": "robeing.test", "rb10408": "robeing.test",
"rb10508": "robeing.micro" "rb10508": "robeing.micro"
@ -288,7 +288,7 @@ class DBCredentialsProvider:
## 로그 예시 ## 로그 예시
``` ```
INFO: Calling service: http://172.17.0.1:8501/send INFO: Calling service: http://172.17.0.1:8501/send
with payload keys: ['message', 'user_id', 'channel', 'robing_id', 'action', 'execution_plan'] with payload keys: ['message', 'user_id', 'channel', 'robeing_id', 'action', 'execution_plan']
ERROR: 422 Unprocessable Entity ERROR: 422 Unprocessable Entity
DETAIL: 토큰 파일이 없습니다: /app/auth-server/tokens/test_gmail.json DETAIL: 토큰 파일이 없습니다: /app/auth-server/tokens/test_gmail.json
``` ```

View File

@ -34,7 +34,7 @@
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │ │ │ 공통 DB │ │
│ │ users, robings, stats │ │ │ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
@ -78,13 +78,13 @@
users: id, name, email, created_at users: id, name, email, created_at
-- 로빙 메타데이터 -- 로빙 메타데이터
robings: id, user_id, name, level, stats, container_id, status robeings: id, user_id, name, level, stats, container_id, status
-- 스킬 및 아이템 설정 -- 스킬 및 아이템 설정
skills: id, robing_id, skill_type, config, enabled skills: id, robeing_id, skill_type, config, enabled
-- 성능 통계 -- 성능 통계
performance: id, robing_id, date, tasks_completed, success_rate performance: id, robeing_id, date, tasks_completed, success_rate
``` ```
### 로빙 컨테이너 DB (개별) ### 로빙 컨테이너 DB (개별)

View File

@ -160,7 +160,7 @@ class HTTPEmbeddingFunction(EmbeddingFunction):
#### 로빙 브레인의 새로운 역할 #### 로빙 브레인의 새로운 역할
```python ```python
class RobingBrain: class RobeingBrain:
"""경량화된 로빙 브레인 - 라우터 역할만 수행""" """경량화된 로빙 브레인 - 라우터 역할만 수행"""
def __init__(self): def __init__(self):
@ -309,7 +309,7 @@ volumes:
```python ```python
# State Service에서 중앙 관리 # State Service에서 중앙 관리
class ConfigService: class ConfigService:
async def get_robing_config(self, robing_id: str): async def get_robeing_config(self, robeing_id: str):
return { return {
"log_level": "INFO", "log_level": "INFO",
"memory_limit": "512m", "memory_limit": "512m",
@ -427,9 +427,9 @@ def process_pure_function(message: str) -> dict:
### 메트릭 수집 ### 메트릭 수집
```python ```python
# Prometheus 메트릭 # Prometheus 메트릭
robing_request_count = Counter('robing_requests_total', 'Total requests') robeing_request_count = Counter('robeing_requests_total', 'Total requests')
robing_memory_usage = Gauge('robing_memory_bytes', 'Memory usage') robeing_memory_usage = Gauge('robeing_memory_bytes', 'Memory usage')
robing_response_time = Histogram('robing_response_seconds', 'Response time') robeing_response_time = Histogram('robeing_response_seconds', 'Response time')
``` ```
### 보안 강화 ### 보안 강화

View File

@ -125,7 +125,7 @@ class HTTPEmbeddingFunction(EmbeddingFunction):
```python ```python
# memory.py - 로빙의 기억을 저장하는 코드 # memory.py - 로빙의 기억을 저장하는 코드
self.episodic = self.client.get_or_create_collection( self.episodic = self.client.get_or_create_collection(
name=f"{self.robing_id}_episodic", # 각 로빙마다 독립된 기억 공간 name=f"{self.robeing_id}_episodic", # 각 로빙마다 독립된 기억 공간
embedding_function=HTTPEmbeddingFunction() # 임베딩은 공유 서비스 사용 embedding_function=HTTPEmbeddingFunction() # 임베딩은 공유 서비스 사용
) )
# 결과: 기억은 각자, 임베딩 엔진은 공유 # 결과: 기억은 각자, 임베딩 엔진은 공유

View File

@ -75,7 +75,7 @@ SELECT
u.username, u.username,
u.name, u.name,
u.email, u.email,
wm.robing_id, wm.robeing_id,
sum.slack_user_id, sum.slack_user_id,
gt.is_equipped as gmail_equipped gt.is_equipped as gmail_equipped
FROM users u FROM users u
@ -120,7 +120,7 @@ SELECT
u.name as user_name, u.name as user_name,
u.email, u.email,
wm.role, wm.role,
wm.robing_id wm.robeing_id
FROM workspaces w FROM workspaces w
JOIN workspace_members wm ON w.id = wm.workspace_id JOIN workspace_members wm ON w.id = wm.workspace_id
JOIN users u ON wm.user_id = u.id JOIN users u ON wm.user_id = u.id
@ -185,7 +185,7 @@ user_id로 권한 확인
- `users.email` - 중복 이메일 방지 - `users.email` - 중복 이메일 방지
- `users.username` - 중복 사용자명 방지 - `users.username` - 중복 사용자명 방지
- `slack_workspaces.team_id` - 중복 Slack 팀 방지 - `slack_workspaces.team_id` - 중복 Slack 팀 방지
- `robing_settings.robing_id` - 중복 설정 방지 - `robeing_settings.robeing_id` - 중복 설정 방지
### 3. NULL 허용 정책 ### 3. NULL 허용 정책
- 필수 관계: NOT NULL (user_id, workspace_id 등) - 필수 관계: NOT NULL (user_id, workspace_id 등)

View File

@ -51,8 +51,8 @@
| workspace_id | UUID | NO | | 워크스페이스 ID (FK → workspaces) | | workspace_id | UUID | NO | | 워크스페이스 ID (FK → workspaces) |
| user_id | UUID | NO | | 사용자 ID (FK → users) | | user_id | UUID | NO | | 사용자 ID (FK → users) |
| role | ENUM | YES | member | 역할 (admin/member) | | role | ENUM | YES | member | 역할 (admin/member) |
| robing_id | VARCHAR(50) | YES | | 할당된 로빙 ID | | robeing_id | VARCHAR(50) | YES | | 할당된 로빙 ID |
| robing_url | VARCHAR(255) | YES | | 로빙 서비스 URL | | robeing_url | VARCHAR(255) | YES | | 로빙 서비스 URL |
| is_active | BOOLEAN | YES | true | 활성 상태 | | is_active | BOOLEAN | YES | true | 활성 상태 |
| joined_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 가입 시각 | | joined_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 가입 시각 |
| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | | updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 |
@ -157,18 +157,18 @@
| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | | created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | | updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 |
### robing_stats (구버전) ### robeing_stats (구버전)
- **용도**: 구버전 로빙 통계 (사용 중단 예정) - **용도**: 구버전 로빙 통계 (사용 중단 예정)
- **설명**: robeing_stats로 마이그레이션 필요 - **설명**: robeing_stats로 마이그레이션 필요
### robing_settings ### robeing_settings
- **용도**: 로빙 설정 정보 - **용도**: 로빙 설정 정보
- **Primary Key**: id (SERIAL) - **Primary Key**: id (SERIAL)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 | | 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------| |--------|------|------|--------|------|
| id | SERIAL | NO | | 설정 ID | | id | SERIAL | NO | | 설정 ID |
| robing_id | VARCHAR(50) | NO | | 로빙 ID (UNIQUE) | | robeing_id | VARCHAR(50) | NO | | 로빙 ID (UNIQUE) |
| settings | JSONB | YES | | 설정 JSON | | settings | JSONB | YES | | 설정 JSON |
| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | | created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | | updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 |
@ -229,7 +229,7 @@
### 개선 필요 사항 ### 개선 필요 사항
1. gmail_audit_logs.user_id를 UUID로 변경 필요 (현재 VARCHAR) 1. gmail_audit_logs.user_id를 UUID로 변경 필요 (현재 VARCHAR)
2. robing_stats 테이블을 robeing_stats로 통합 필요 2. robeing_stats 테이블을 robeing_stats로 통합 필요
3. 일부 테이블의 소유자가 postgres로 되어있어 권한 조정 필요 3. 일부 테이블의 소유자가 postgres로 되어있어 권한 조정 필요
--- ---

View File

@ -119,7 +119,7 @@ sequenceDiagram
participant DB as PostgreSQL participant DB as PostgreSQL
participant SlackAPI as Slack API participant SlackAPI as Slack API
User->>Slack: /robing-login 명령 User->>Slack: /robeing-login 명령
Slack->>Auth: POST /auth/slack/command Slack->>Auth: POST /auth/slack/command
Note over Auth: user_id, team_id, response_url Note over Auth: user_id, team_id, response_url

View File

@ -42,7 +42,7 @@ graph TB
subgraph Database subgraph Database
PG[(PostgreSQL)] PG[(PostgreSQL)]
GT[gmail_tokens] GT[gmail_tokens]
RS[robing_stats] RS[robeing_stats]
end end
UI --> GW UI --> GW
@ -107,7 +107,7 @@ sequenceDiagram
participant Monitor as robeing-monitor participant Monitor as robeing-monitor
participant DB as PostgreSQL participant DB as PostgreSQL
Monitor->>DB: SELECT level FROM robing_stats<br/>WHERE robing_id = ? Monitor->>DB: SELECT level FROM robeing_stats<br/>WHERE robeing_id = ?
DB-->>Monitor: 현재 레벨 (예: 20) DB-->>Monitor: 현재 레벨 (예: 20)
Monitor->>Monitor: 아이템별 레벨 체크 Monitor->>Monitor: 아이템별 레벨 체크
@ -183,7 +183,7 @@ sequenceDiagram
Note over Monitor: Body: {robeing_id: "rb10508_micro"} Note over Monitor: Body: {robeing_id: "rb10508_micro"}
Monitor->>DB: 레벨 확인 Monitor->>DB: 레벨 확인
Note over DB: SELECT level FROM robing_stats<br/>WHERE robing_id = ? Note over DB: SELECT level FROM robeing_stats<br/>WHERE robeing_id = ?
DB-->>Monitor: level: 20 DB-->>Monitor: level: 20
Monitor->>Monitor: 레벨 요구사항 체크 Monitor->>Monitor: 레벨 요구사항 체크
@ -218,7 +218,7 @@ sequenceDiagram
Front->>Monitor: POST /api/items/gmail/{userId}/equip Front->>Monitor: POST /api/items/gmail/{userId}/equip
alt 레벨 부족 alt 레벨 부족
Monitor->>DB: SELECT level FROM robing_stats Monitor->>DB: SELECT level FROM robeing_stats
DB-->>Monitor: level: 3 DB-->>Monitor: level: 3
Monitor-->>Front: {error: "레벨 5 이상 필요"} Monitor-->>Front: {error: "레벨 5 이상 필요"}
else 토큰 없음 else 토큰 없음
@ -338,7 +338,7 @@ sequenceDiagram
participant Front as 프론트엔드 participant Front as 프론트엔드
RB->>DB: 경험치 획득 RB->>DB: 경험치 획득
DB->>DB: UPDATE robing_stats<br/>SET experience = experience + 100 DB->>DB: UPDATE robeing_stats<br/>SET experience = experience + 100
DB->>DB: 레벨 계산 DB->>DB: 레벨 계산
Note over DB: 레벨 4 → 5 상승! Note over DB: 레벨 4 → 5 상승!

View File

@ -33,20 +33,20 @@ class CostStructureAnalysis:
} }
} }
def calculate_unit_economics(self, active_robings: int): def calculate_unit_economics(self, active_robeings: int):
total_monthly_cost = sum( total_monthly_cost = sum(
sum(category.values()) sum(category.values())
for category in self.current_costs.values() for category in self.current_costs.values()
) )
cost_per_robing = total_monthly_cost / active_robings cost_per_robeing = total_monthly_cost / active_robeings
return { return {
"total_monthly_cost": total_monthly_cost, "total_monthly_cost": total_monthly_cost,
"cost_per_robing": cost_per_robing, "cost_per_robeing": cost_per_robeing,
"break_even_price": cost_per_robing * 1.3, # 30% 마진 "break_even_price": cost_per_robeing * 1.3, # 30% 마진
"target_price": 300_000, # 월 30만원 "target_price": 300_000, # 월 30만원
"required_robings_for_profit": total_monthly_cost / 230_000 # 실제 마진 "required_robeings_for_profit": total_monthly_cost / 230_000 # 실제 마진
} }
``` ```

View File

@ -96,19 +96,19 @@ class SaaSCoreFeatures:
"subscription_tiers": { "subscription_tiers": {
"starter": { "starter": {
"price": 300_000, # 월 30만원 "price": 300_000, # 월 30만원
"robings": 1, "robeings": 1,
"skills": ["basic"], "skills": ["basic"],
"support": "email" "support": "email"
}, },
"professional": { "professional": {
"price": 800_000, # 월 80만원 "price": 800_000, # 월 80만원
"robings": 3, "robeings": 3,
"skills": ["all"], "skills": ["all"],
"support": "priority" "support": "priority"
}, },
"enterprise": { "enterprise": {
"price": "custom", "price": "custom",
"robings": "unlimited", "robeings": "unlimited",
"skills": ["all + custom"], "skills": ["all + custom"],
"support": "dedicated" "support": "dedicated"
} }
@ -422,7 +422,7 @@ class OnboardingFlow {
] ]
}, },
{ {
id: "robing_creation", id: "robeing_creation",
component: "RobeingCreator", component: "RobeingCreator",
options: { options: {
name: "로빙 이름 설정", name: "로빙 이름 설정",
@ -502,7 +502,7 @@ class UserAnalytics:
""" """
dashboard_components = { dashboard_components = {
"usage_overview": { "usage_overview": {
"daily_active_robings": "Line chart", "daily_active_robeings": "Line chart",
"message_volume": "Area chart", "message_volume": "Area chart",
"skill_usage": "Heatmap", "skill_usage": "Heatmap",
"peak_hours": "Bar chart" "peak_hours": "Bar chart"
@ -516,7 +516,7 @@ class UserAnalytics:
}, },
"growth_insights": { "growth_insights": {
"robing_level_distribution": "Histogram", "robeing_level_distribution": "Histogram",
"skill_popularity": "Pie chart", "skill_popularity": "Pie chart",
"user_journey": "Funnel chart", "user_journey": "Funnel chart",
"cohort_analysis": "Retention table" "cohort_analysis": "Retention table"

View File

@ -89,7 +89,7 @@ RUN pip install --find-links /wheels --no-index torch sentence-transformers
# embedding_function 파라미터 제거 # embedding_function 파라미터 제거
self.conversations_collection = self.client.get_or_create_collection( self.conversations_collection = self.client.get_or_create_collection(
name="conversations", name="conversations",
metadata={"description": "Conversation memories", "robing_id": settings.ROBING_ID} metadata={"description": "Conversation memories", "robeing_id": settings.ROBEING_ID}
# embedding_function 제거 - 기본 임베딩 사용 # embedding_function 제거 - 기본 임베딩 사용
) )
``` ```

View File

@ -213,7 +213,7 @@ React 앱이 브라우저에서 실행되면:
```javascript ```javascript
// 동적 통신 구현 // 동적 통신 구현
const RobingGame = () => { const RobeingGame = () => {
useEffect(() => { useEffect(() => {
// WebSocket 연결 // WebSocket 연결
const ws = new WebSocket('wss://ro-being.com/ws/robbing/realtime'); const ws = new WebSocket('wss://ro-being.com/ws/robbing/realtime');

View File

@ -1,4 +1,4 @@
# 로빙(Robing) 경량 멀티테넌트 아키텍처 설계 # 로빙(Robeing) 경량 멀티테넌트 아키텍처 설계
## 1. 개요 ## 1. 개요
@ -174,7 +174,7 @@ CREATE TABLE skill_growth_logs (
### 4.2 초기화 및 상태 관리 ### 4.2 초기화 및 상태 관리
```python ```python
# app/services/robing_brain.py # app/services/robeing_brain.py
class RobeingBrain: class RobeingBrain:
def __init__(self, company_id: str): def __init__(self, company_id: str):
self.company_id = company_id self.company_id = company_id

View File

@ -16,7 +16,7 @@
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │ │ │ 공통 DB │ │
│ │ users, robings, stats │ │ │ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
@ -60,13 +60,13 @@
users: id, name, email, created_at users: id, name, email, created_at
-- 로빙 메타데이터 -- 로빙 메타데이터
robings: id, user_id, name, level, stats, container_id, status robeings: id, user_id, name, level, stats, container_id, status
-- 스킬 및 아이템 설정 -- 스킬 및 아이템 설정
skills: id, robing_id, skill_type, config, enabled skills: id, robeing_id, skill_type, config, enabled
-- 성능 통계 -- 성능 통계
performance: id, robing_id, date, tasks_completed, success_rate performance: id, robeing_id, date, tasks_completed, success_rate
``` ```
### 로빙 컨테이너 DB (개별) ### 로빙 컨테이너 DB (개별)

View File

@ -9,7 +9,7 @@
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │ │ │ 공통 DB │ │
│ │ users, robings, stats │ │ │ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
└─────────────────────────────────────┘ └─────────────────────────────────────┘

View File

@ -82,7 +82,7 @@ class EthicsEvaluator:
#### 2.1 RobeingBrain 업그레이드 #### 2.1 RobeingBrain 업그레이드
```python ```python
# app/services/robing_brain_v2.py # app/services/robeing_brain_v2.py
class RobeingBrainV2: class RobeingBrainV2:
def __init__(self): def __init__(self):
self.evaluator = InformationEvaluator() self.evaluator = InformationEvaluator()

View File

@ -279,7 +279,7 @@ processed = pipe(
#### 현재 적용 예시: RobeingBrain #### 현재 적용 예시: RobeingBrain
```python ```python
# /app/services/robing_brain.py # /app/services/robeing_brain.py
class RobeingBrain: class RobeingBrain:
"""순수 함수와 부작용 분리 오케스트레이터""" """순수 함수와 부작용 분리 오케스트레이터"""

View File

@ -16,7 +16,7 @@
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │ │ │ 공통 DB │ │
│ │ users, robings, stats │ │ │ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │ │ └─────────────────────────────┘ │
└─────────────────────────────────────┘ └─────────────────────────────────────┘
@ -60,13 +60,13 @@
users: id, name, email, created_at users: id, name, email, created_at
-- 로빙 메타데이터 -- 로빙 메타데이터
robings: id, user_id, name, level, stats, container_id, status robeings: id, user_id, name, level, stats, container_id, status
-- 스킬 및 아이템 설정 -- 스킬 및 아이템 설정
skills: id, robing_id, skill_type, config, enabled skills: id, robeing_id, skill_type, config, enabled
-- 성능 통계 -- 성능 통계
performance: id, robing_id, date, tasks_completed, success_rate performance: id, robeing_id, date, tasks_completed, success_rate
``` ```
### 로빙 컨테이너 DB (개별) ### 로빙 컨테이너 DB (개별)

View File

@ -23,7 +23,7 @@
#### Brain 처리 (think_functional) #### Brain 처리 (think_functional)
1. 입력 추출: message, user_id, context 1. 입력 추출: message, user_id, context
2. 의도 분석: "general_conversation" (하드코딩) 2. 의도 분석: "general_conversation" (하드코딩)
3. Identity 조회: user_name, robing_name 3. Identity 조회: user_name, robeing_name
4. 이름 감지: "내 이름은", "너를 ~라고 부를게" 4. 이름 감지: "내 이름은", "너를 ~라고 부를게"
5. 메모리 검색: ChromaDB query (100개 → 선택) 5. 메모리 검색: ChromaDB query (100개 → 선택)
6. Gemini API 응답 생성 6. Gemini API 응답 생성
@ -77,7 +77,7 @@
- 이중 경로로 인한 혼란 - 이중 경로로 인한 혼란
### 2.5 Identity 관리 미흡 ### 2.5 Identity 관리 미흡
- user_name, robing_name은 저장하지만 - user_name, robeing_name은 저장하지만
- 실제 사용자 계정과 연결 안 됨 - 실제 사용자 계정과 연결 안 됨
- OAuth 로그인과 Slack 사용자 매핑 없음 - OAuth 로그인과 Slack 사용자 매핑 없음
@ -91,7 +91,7 @@ CREATE TABLE slack_user_mapping (
slack_user_id VARCHAR(100) NOT NULL, -- U04KJHGLS slack_user_id VARCHAR(100) NOT NULL, -- U04KJHGLS
slack_workspace_id VARCHAR(100), -- T1234567 slack_workspace_id VARCHAR(100), -- T1234567
user_id UUID REFERENCES users(id), -- 우리 시스템 사용자 user_id UUID REFERENCES users(id), -- 우리 시스템 사용자
robing_id VARCHAR(100), -- rb10508_micro robeing_id VARCHAR(100), -- rb10508_micro
created_at TIMESTAMP, created_at TIMESTAMP,
UNIQUE(slack_user_id, slack_workspace_id) UNIQUE(slack_user_id, slack_workspace_id)
); );
@ -106,7 +106,7 @@ slack_user_id (U04KJHGLS) 추출
DB 조회: slack_user_mapping DB 조회: slack_user_mapping
user_id, robing_id 획득 user_id, robeing_id 획득
해당 로빙의 ChromaDB에 저장 해당 로빙의 ChromaDB에 저장
``` ```
@ -205,7 +205,7 @@ class SlackItem(APIItem):
3. **사용자-로빙 연결** 3. **사용자-로빙 연결**
- Auth 서버가 사용자와 로빙 매핑 - Auth 서버가 사용자와 로빙 매핑
- Slack user_id → system user_id → robing_id - Slack user_id → system user_id → robeing_id
- 로빙이 직접 해당 사용자 메시지 처리 - 로빙이 직접 해당 사용자 메시지 처리
4. **skill-slack의 역할** 4. **skill-slack의 역할**
@ -257,7 +257,7 @@ class SlackItem(APIItem):
- **옵션 A**: skill-slack에서 다중 로빙 지원 - **옵션 A**: skill-slack에서 다중 로빙 지원
- 환경변수로 rb8001, rb10508 둘 다 설정 - 환경변수로 rb8001, rb10508 둘 다 설정
- 사용자별로 어느 로빙 사용할지 결정 로직 - 사용자별로 어느 로빙 사용할지 결정 로직
- **옵션 B**: robing-gateway에서 중앙 라우팅 - **옵션 B**: robeing-gateway에서 중앙 라우팅
- Gateway가 사용자→로빙 매핑 관리 - Gateway가 사용자→로빙 매핑 관리
- skill-slack은 Gateway로만 전송 - skill-slack은 Gateway로만 전송
@ -276,7 +276,7 @@ class SlackItem(APIItem):
- Gateway에서 변환 vs 각 서비스에서 호환 - Gateway에서 변환 vs 각 서비스에서 호환
### 9.5 ChromaDB 사용자 분리 전략 ### 9.5 ChromaDB 사용자 분리 전략
- 컬렉션 명명 규칙: `{robing_id}_{system_user_id}_{type}` - 컬렉션 명명 규칙: `{robeing_id}_{system_user_id}_{type}`
- 기존 데이터 마이그레이션 계획 - 기존 데이터 마이그레이션 계획
- 백업 및 복구 정책 - 백업 및 복구 정책
@ -295,12 +295,12 @@ class SlackItem(APIItem):
- 3명의 사용자 등록됨 (happybell80, eagle0914, hhyong91) - 3명의 사용자 등록됨 (happybell80, eagle0914, hhyong91)
2. **workspaces 테이블** 2. **workspaces 테이블**
- 워크스페이스 정보 (robing_id, robing_port, robing_url 포함) - 워크스페이스 정보 (robeing_id, robeing_port, robeing_url 포함)
- 현재 1개 워크스페이스: ivada-robeing (rb10508_micro 사용) - 현재 1개 워크스페이스: ivada-robeing (rb10508_micro 사용)
3. **workspace_members 테이블** 3. **workspace_members 테이블**
- 사용자와 워크스페이스 연결 - 사용자와 워크스페이스 연결
- robing_id 필드로 각 멤버가 사용할 로빙 지정 가능 - robeing_id 필드로 각 멤버가 사용할 로빙 지정 가능
4. **slack_workspaces 테이블** 4. **slack_workspaces 테이블**
- Slack 워크스페이스 정보 (team_id, bot_token, bot_user_id 등) - Slack 워크스페이스 정보 (team_id, bot_token, bot_user_id 등)
@ -345,7 +345,7 @@ slack_user_mapping 조회
user_id 획득 user_id 획득
workspace_members에서 robing_id 확인 workspace_members에서 robeing_id 확인
해당 로빙으로 라우팅 해당 로빙으로 라우팅

View File

@ -4,7 +4,7 @@
작성자: 51123 서버, 51124 서버, 로컬 개발자 작성자: 51123 서버, 51124 서버, 로컬 개발자
상태: 아이디어 → 추가 검토 필요 상태: 아이디어 → 추가 검토 필요
우선순위: 🔴 Critical (보안 취약점) 우선순위: 🔴 Critical (보안 취약점)
관련: auth-server, robing services (rb8001, rb10408, rb10508), frontend 관련: auth-server, robeing services (rb8001, rb10408, rb10508), frontend
## 개요 ## 개요
@ -49,14 +49,14 @@ openssl rand -base64 32
``` ```
#### 로컬 개발자 작업 #### 로컬 개발자 작업
1. **robing-api.ts 수정** 1. **robeing-api.ts 수정**
```typescript ```typescript
export async function sendMessage( export async function sendMessage(
text: string, text: string,
token: string, token: string,
userId: string userId: string
): Promise<MessageResponse> { ): Promise<MessageResponse> {
const response = await fetch(`${ROBING_API_URL}/api/chat`, { const response = await fetch(`${ROBEING_API_URL}/api/chat`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@ -113,13 +113,13 @@ CREATE TABLE config_bundle (
``` ```
### 6.2 Redis 키 구조 ### 6.2 Redis 키 구조
- `robing:config:active:<scope>:<id>` → 현재 설정 묶음 - `robeing:config:active:<scope>:<id>` → 현재 설정 묶음
- `robing:config:version:<version>` → 버전별 설정 - `robeing:config:version:<version>` → 버전별 설정
### 6.3 Slack 명령 인터페이스 ### 6.3 Slack 명령 인터페이스
``` ```
/robing config add memory.blocked_terms "금지어" /robeing config add memory.blocked_terms "금지어"
/robing config set retrieval.top_k=10 --scope user:@kim /robeing config set retrieval.top_k=10 --scope user:@kim
``` ```
## 7. 안전장치 ## 7. 안전장치
@ -175,7 +175,7 @@ CREATE TABLE config_bundle (
| rb10408_test | app/core/config.py | 다중 LLM 지원 | | rb10408_test | app/core/config.py | 다중 LLM 지원 |
| rb10508_micro | app/config.py | 감정/베이지안 설정 | | rb10508_micro | app/config.py | 감정/베이지안 설정 |
| skill-embedding | config.py | Field() 사용, 상세 설명 | | skill-embedding | config.py | Field() 사용, 상세 설명 |
| robing-gateway | **없음** | os.getenv() 분산 사용 | | robeing-gateway | **없음** | os.getenv() 분산 사용 |
### 11.2 도구에서 존재로: 4가지 핵심 축 ### 11.2 도구에서 존재로: 4가지 핵심 축
@ -197,14 +197,14 @@ from pydantic import BaseModel, Field, ValidationError
class BaseRobeingSettings(BaseSettings): class BaseRobeingSettings(BaseSettings):
model_config = SettingsConfigDict( model_config = SettingsConfigDict(
env_file=(".env",), env_file=(".env",),
env_prefix="ROBING_", env_prefix="ROBEING_",
case_sensitive=False, case_sensitive=False,
extra="ignore", extra="ignore",
) )
# 정체성 (불변) # 정체성 (불변)
ROBING_ID: str ROBEING_ID: str
ROBING_VERSION: str = "1.0.0" ROBEING_VERSION: str = "1.0.0"
# 시스템 (검증된 범위) # 시스템 (검증된 범위)
MEMORY_LIMIT: int = Field(1000, ge=128, le=65536) MEMORY_LIMIT: int = Field(1000, ge=128, le=65536)
@ -400,7 +400,7 @@ async def config_history(limit: int = 10):
#### Phase 1: 즉시 적용 (1일) #### Phase 1: 즉시 적용 (1일)
1. 모든 config.py를 `app/core/config.py`로 통일 1. 모든 config.py를 `app/core/config.py`로 통일
2. robing-gateway에 config.py 생성 2. robeing-gateway에 config.py 생성
3. BaseRobeingSettings 상속 구조 도입 3. BaseRobeingSettings 상속 구조 도입
#### Phase 2: 동적 레이어 (1주) #### Phase 2: 동적 레이어 (1주)

View File

@ -40,7 +40,7 @@ Frontend → Gateway → 적절한 로빙
### 2. 초기 핸드셰이크 후 직접 연결 (하이브리드) ### 2. 초기 핸드셰이크 후 직접 연결 (하이브리드)
``` ```
Frontend → Gateway/api/robing/info (초기) Frontend → Gateway/api/robeing/info (초기)
→ 직접 로빙 연결 (이후) → 직접 로빙 연결 (이후)
``` ```
@ -131,6 +131,6 @@ async def universal_proxy(path: str, request: Request,
- [ ] 시크릿 관리 체계 개선 방안 검토 - [ ] 시크릿 관리 체계 개선 방안 검토
## 참고 자료 ## 참고 자료
- 250809_happybell80_robing-gateway구현.md - 250809_happybell80_robeing-gateway구현.md
- 250818_happybell80_GatewayJWT인증구현.md - 250818_happybell80_GatewayJWT인증구현.md
- 250818_happybell80_대화히스토리구현.md - 250818_happybell80_대화히스토리구현.md

View File

@ -1,4 +1,4 @@
# conversation_logs 및 robing_stats 테이블 활용 계획 # conversation_logs 및 robeing_stats 테이블 활용 계획
작성일: 2025년 8월 18일 작성일: 2025년 8월 18일
작성자: Claude (51123 서버) 작성자: Claude (51123 서버)
@ -10,8 +10,8 @@
```sql ```sql
-- main_db에 존재하지만 전혀 사용되지 않는 테이블들 -- main_db에 존재하지만 전혀 사용되지 않는 테이블들
conversation_logs: 0 records (0 KB) conversation_logs: 0 records (0 KB)
robing_stats: 0 records (0 KB) robeing_stats: 0 records (0 KB)
robing_settings: 0 records (0 KB) robeing_settings: 0 records (0 KB)
``` ```
### 1.2 코드 검사 결과 ### 1.2 코드 검사 결과
@ -31,7 +31,7 @@ robing_settings: 0 records (0 KB)
```sql ```sql
CREATE TABLE conversation_logs ( CREATE TABLE conversation_logs (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
robing_id VARCHAR(100), robeing_id VARCHAR(100),
user_id UUID, user_id UUID,
message TEXT, message TEXT,
response TEXT, response TEXT,
@ -56,7 +56,7 @@ class ConversationLogger:
async def log_conversation( async def log_conversation(
self, self,
robing_id: str, robeing_id: str,
user_id: str, user_id: str,
message: str, message: str,
response: str, response: str,
@ -66,11 +66,11 @@ class ConversationLogger:
async with asyncpg.connect(self.db_url) as conn: async with asyncpg.connect(self.db_url) as conn:
await conn.execute(""" await conn.execute("""
INSERT INTO conversation_logs INSERT INTO conversation_logs
(id, robing_id, user_id, message, response, created_at, metadata) (id, robeing_id, user_id, message, response, created_at, metadata)
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7)
""", """,
uuid.uuid4(), uuid.uuid4(),
robing_id, robeing_id,
user_id, user_id,
message, message,
response, response,
@ -91,7 +91,7 @@ async def handle_message(event: dict):
# 신규: 대화 로그 저장 # 신규: 대화 로그 저장
await conversation_logger.log_conversation( await conversation_logger.log_conversation(
robing_id=ROBING_ID, # rb8001, rb10508 등 robeing_id=ROBEING_ID, # rb8001, rb10508 등
user_id=user_id, user_id=user_id,
message=user_message, message=user_message,
response=response, response=response,
@ -111,13 +111,13 @@ async def handle_message(event: dict):
3. **품질 분석**: 응답 품질 모니터링 3. **품질 분석**: 응답 품질 모니터링
4. **사용 패턴 분석**: 자주 묻는 질문, 피크 시간대 등 4. **사용 패턴 분석**: 자주 묻는 질문, 피크 시간대 등
## 3. robing_stats 테이블 활용 방안 ## 3. robeing_stats 테이블 활용 방안
### 3.1 테이블 구조 (현재) ### 3.1 테이블 구조 (현재)
```sql ```sql
CREATE TABLE robing_stats ( CREATE TABLE robeing_stats (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
robing_id VARCHAR(100) UNIQUE, robeing_id VARCHAR(100) UNIQUE,
level INTEGER DEFAULT 1, level INTEGER DEFAULT 1,
experience INTEGER DEFAULT 0, experience INTEGER DEFAULT 0,
memory_stat INTEGER DEFAULT 10, memory_stat INTEGER DEFAULT 10,
@ -139,27 +139,27 @@ CREATE TABLE robing_stats (
```python ```python
# app/services/stats_manager.py (신규) # app/services/stats_manager.py (신규)
class StatsManager: class StatsManager:
def __init__(self, db_url: str, robing_id: str): def __init__(self, db_url: str, robeing_id: str):
self.db_url = db_url self.db_url = db_url
self.robing_id = robing_id self.robeing_id = robeing_id
async def initialize_stats(self): async def initialize_stats(self):
"""로빙 스탯 초기화""" """로빙 스탯 초기화"""
async with asyncpg.connect(self.db_url) as conn: async with asyncpg.connect(self.db_url) as conn:
await conn.execute(""" await conn.execute("""
INSERT INTO robing_stats INSERT INTO robeing_stats
(id, robing_id, created_at, updated_at) (id, robeing_id, created_at, updated_at)
VALUES ($1, $2, $3, $3) VALUES ($1, $2, $3, $3)
ON CONFLICT (robing_id) DO NOTHING ON CONFLICT (robeing_id) DO NOTHING
""", uuid.uuid4(), self.robing_id, datetime.now()) """, uuid.uuid4(), self.robeing_id, datetime.now())
async def add_experience(self, xp: int): async def add_experience(self, xp: int):
"""경험치 추가 및 레벨업 체크""" """경험치 추가 및 레벨업 체크"""
async with asyncpg.connect(self.db_url) as conn: async with asyncpg.connect(self.db_url) as conn:
# 현재 스탯 조회 # 현재 스탯 조회
stats = await conn.fetchrow( stats = await conn.fetchrow(
"SELECT * FROM robing_stats WHERE robing_id = $1", "SELECT * FROM robeing_stats WHERE robeing_id = $1",
self.robing_id self.robeing_id
) )
new_xp = stats['experience'] + xp new_xp = stats['experience'] + xp
@ -172,10 +172,10 @@ class StatsManager:
# 경험치와 레벨 업데이트 # 경험치와 레벨 업데이트
await conn.execute(""" await conn.execute("""
UPDATE robing_stats UPDATE robeing_stats
SET experience = $1, level = $2, updated_at = $3 SET experience = $1, level = $2, updated_at = $3
WHERE robing_id = $4 WHERE robeing_id = $4
""", new_xp, new_level, datetime.now(), self.robing_id) """, new_xp, new_level, datetime.now(), self.robeing_id)
def calculate_level(self, xp: int) -> int: def calculate_level(self, xp: int) -> int:
"""경험치로 레벨 계산""" """경험치로 레벨 계산"""
@ -195,10 +195,10 @@ async def on_task_success(task_type: str, difficulty: int):
# 성공 카운트 증가 # 성공 카운트 증가
await conn.execute(""" await conn.execute("""
UPDATE robing_stats UPDATE robeing_stats
SET successful_tasks = successful_tasks + 1 SET successful_tasks = successful_tasks + 1
WHERE robing_id = $1 WHERE robeing_id = $1
""", ROBING_ID) """, ROBEING_ID)
``` ```
### 3.3 스탯 활용 방안 ### 3.3 스탯 활용 방안
@ -207,13 +207,13 @@ async def on_task_success(task_type: str, difficulty: int):
3. **성장 시각화**: 프론트엔드에서 레벨/스탯 표시 3. **성장 시각화**: 프론트엔드에서 레벨/스탯 표시
4. **게이미피케이션**: 업적, 뱃지 시스템 4. **게이미피케이션**: 업적, 뱃지 시스템
## 4. robing_settings 테이블 활용 방안 ## 4. robeing_settings 테이블 활용 방안
### 4.1 테이블 구조 (현재) ### 4.1 테이블 구조 (현재)
```sql ```sql
CREATE TABLE robing_settings ( CREATE TABLE robeing_settings (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
robing_id VARCHAR(100) UNIQUE, robeing_id VARCHAR(100) UNIQUE,
user_preferences JSON, user_preferences JSON,
system_settings JSON, system_settings JSON,
skill_settings JSON, skill_settings JSON,
@ -228,9 +228,9 @@ CREATE TABLE robing_settings (
```python ```python
# app/services/settings_manager.py (신규) # app/services/settings_manager.py (신규)
class SettingsManager: class SettingsManager:
def __init__(self, db_url: str, robing_id: str): def __init__(self, db_url: str, robeing_id: str):
self.db_url = db_url self.db_url = db_url
self.robing_id = robing_id self.robeing_id = robeing_id
self._cache = {} self._cache = {}
async def get_setting(self, category: str, key: str, default=None): async def get_setting(self, category: str, key: str, default=None):
@ -248,8 +248,8 @@ class SettingsManager:
async with asyncpg.connect(self.db_url) as conn: async with asyncpg.connect(self.db_url) as conn:
# 현재 설정 조회 # 현재 설정 조회
current = await conn.fetchrow( current = await conn.fetchrow(
f"SELECT {settings_field} FROM robing_settings WHERE robing_id = $1", f"SELECT {settings_field} FROM robeing_settings WHERE robeing_id = $1",
self.robing_id self.robeing_id
) )
settings = current[settings_field] or {} settings = current[settings_field] or {}
@ -257,10 +257,10 @@ class SettingsManager:
# 업데이트 # 업데이트
await conn.execute(f""" await conn.execute(f"""
UPDATE robing_settings UPDATE robeing_settings
SET {settings_field} = $1, updated_at = $2 SET {settings_field} = $1, updated_at = $2
WHERE robing_id = $3 WHERE robeing_id = $3
""", settings, datetime.now(), self.robing_id) """, settings, datetime.now(), self.robeing_id)
# 캐시 갱신 # 캐시 갱신
self._cache[settings_field] = settings self._cache[settings_field] = settings
@ -306,7 +306,7 @@ skill_settings = {
- 모든 대화를 DB에 저장 - 모든 대화를 DB에 저장
- 메타데이터 포함 (채널, 시간 등) - 메타데이터 포함 (채널, 시간 등)
2. **robing_stats 초기화** 2. **robeing_stats 초기화**
- 각 로빙별 스탯 레코드 생성 - 각 로빙별 스탯 레코드 생성
- 대화 카운트 업데이트 - 대화 카운트 업데이트
@ -336,21 +336,21 @@ skill_settings = {
CREATE INDEX idx_conversation_user_time CREATE INDEX idx_conversation_user_time
ON conversation_logs(user_id, created_at DESC); ON conversation_logs(user_id, created_at DESC);
CREATE INDEX idx_conversation_robing_time CREATE INDEX idx_conversation_robeing_time
ON conversation_logs(robing_id, created_at DESC); ON conversation_logs(robeing_id, created_at DESC);
-- robing_stats 빠른 조회 -- robeing_stats 빠른 조회
CREATE INDEX idx_robing_stats_robing CREATE INDEX idx_robeing_stats_robeing
ON robing_stats(robing_id); ON robeing_stats(robeing_id);
``` ```
### 6.2 배치 처리 ### 6.2 배치 처리
- 대화 로그는 즉시 저장하되, 통계 업데이트는 배치로 처리 - 대화 로그는 즉시 저장하되, 통계 업데이트는 배치로 처리
- 5분마다 집계하여 robing_stats 업데이트 - 5분마다 집계하여 robeing_stats 업데이트
### 6.3 캐싱 ### 6.3 캐싱
- robing_settings는 메모리 캐시 유지 - robeing_settings는 메모리 캐시 유지
- robing_stats는 Redis 캐시 활용 - robeing_stats는 Redis 캐시 활용
## 7. 모니터링 지표 ## 7. 모니터링 지표
@ -362,8 +362,8 @@ FROM conversation_logs
GROUP BY DATE(created_at); GROUP BY DATE(created_at);
-- 로빙별 활동 -- 로빙별 활동
SELECT robing_id, total_conversations, level SELECT robeing_id, total_conversations, level
FROM robing_stats; FROM robeing_stats;
``` ```
### 7.2 성능 지표 ### 7.2 성능 지표

View File

@ -10,7 +10,7 @@
- **현상**: - **현상**:
- 종태님(U0925SXQFDK)을 "전희재님"으로 호칭 - 종태님(U0925SXQFDK)을 "전희재님"으로 호칭
- 모든 사용자 대화가 하나의 ChromaDB 컬렉션에 저장 - 모든 사용자 대화가 하나의 ChromaDB 컬렉션에 저장
- `robing_rb8001_memories` 단일 컬렉션 사용 - `robeing_rb8001_memories` 단일 컬렉션 사용
- **원인**: - **원인**:
- 슬랙 직접 연결 (Gateway/JWT 미사용) - 슬랙 직접 연결 (Gateway/JWT 미사용)
@ -70,11 +70,11 @@ Web → Gateway(JWT) → rb10508 → ChromaDB (사용자별?)
#### 1. ChromaDB 사용자별 분리 #### 1. ChromaDB 사용자별 분리
```python ```python
# 현재 (문제) # 현재 (문제)
collection_name = "robing_rb8001_memories" collection_name = "robeing_rb8001_memories"
# 개선안 # 개선안
collection_name = f"robing_rb8001_{user_id}_memories" collection_name = f"robeing_rb8001_{user_id}_memories"
# 예: robing_rb8001_U0925SXQFDK_memories # 예: robeing_rb8001_U0925SXQFDK_memories
``` ```
#### 2. 기존 데이터 처리 #### 2. 기존 데이터 처리

View File

@ -21,7 +21,7 @@
```sql ```sql
- user_id: UUID (FK to users.id) - user_id: UUID (FK to users.id)
- workspace_id: UUID - workspace_id: UUID
- robing_id: VARCHAR(100) # rb8001, rb10508 등 - robeing_id: VARCHAR(100) # rb8001, rb10508 등
- role: ENUM (owner, member, guest) - role: ENUM (owner, member, guest)
``` ```
@ -63,14 +63,14 @@ email_to_username = {
#### 현재 문제점 #### 현재 문제점
```typescript ```typescript
// robing-api.ts - 토큰 전송 안함! // robeing-api.ts - 토큰 전송 안함!
export async function sendMessage(text: string, userId: string = 'test_user') { export async function sendMessage(text: string, userId: string = 'test_user') {
// Authorization 헤더 없음 // Authorization 헤더 없음
// 모든 사용자가 'test_user'로 처리됨 // 모든 사용자가 'test_user'로 처리됨
} }
``` ```
### 4. Robing Gateway (포트 8100) ### 4. Robeing Gateway (포트 8100)
#### 현재 처리 방식 #### 현재 처리 방식
```python ```python
@ -79,12 +79,12 @@ x_user_id: Optional[str] = Header(None) # X-User-Id 헤더만 확인
# JWT 토큰 검증 없음! # JWT 토큰 검증 없음!
``` ```
### 5. Robing Services (51124 서버) ### 5. Robeing Services (51124 서버)
#### ChromaDB 컬렉션명 생성 #### ChromaDB 컬렉션명 생성
```python ```python
# rb10508_micro/app/core/memory/storage.py # rb10508_micro/app/core/memory/storage.py
collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{memory_type}" collection_name = f"{settings.ROBEING_ID}_{username if username else 'default'}_{memory_type}"
# 현재: rb10508_test_default_episodic (모든 사용자 공유!) # 현재: rb10508_test_default_episodic (모든 사용자 공유!)
# 문제: endpoints.py에서 username 전달하지 않음 # 문제: endpoints.py에서 username 전달하지 않음
@ -104,11 +104,11 @@ collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{
### 1. ~~인증 토큰 미전송~~ ✅ 해결됨 ### 1. ~~인증 토큰 미전송~~ ✅ 해결됨
- ~~Frontend가 API 호출 시 JWT 토큰을 전송하지 않음~~ - ~~Frontend가 API 호출 시 JWT 토큰을 전송하지 않음~~
- ~~`robing-api.ts`에 Authorization 헤더 없음~~ - ~~`robeing-api.ts`에 Authorization 헤더 없음~~
- **수정 완료**: robing-api.ts:40, chat-interface.tsx:275-279에서 토큰 전송 구현 - **수정 완료**: robeing-api.ts:40, chat-interface.tsx:275-279에서 토큰 전송 구현
### 2. 백엔드 토큰 미검증 ⚠️ ### 2. 백엔드 토큰 미검증 ⚠️
- Robing Gateway가 JWT 토큰을 검증하지 않음 - Robeing Gateway가 JWT 토큰을 검증하지 않음
- X-User-Id 헤더만 확인 (누구나 위조 가능) - X-User-Id 헤더만 확인 (누구나 위조 가능)
### 3. 사용자 식별 실패 및 채널별 메모리 분리 ⚠️ ### 3. 사용자 식별 실패 및 채널별 메모리 분리 ⚠️
@ -163,7 +163,7 @@ collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{
### 8. JWT Secret 불일치 🔑 ### 8. JWT Secret 불일치 🔑
- Auth Server: `ro-being-auth-jwt-secret-key-2024` - Auth Server: `ro-being-auth-jwt-secret-key-2024`
- Robing Services: `your-jwt-secret-key` (기본값) - Robeing Services: `your-jwt-secret-key` (기본값)
- 토큰 검증 시 실패할 수 있음 - 토큰 검증 시 실패할 수 있음
## 결정 필요 사항 ## 결정 필요 사항
@ -208,20 +208,20 @@ X-Username: happybell80 // 명확한 이름
### 4. ChromaDB 컬렉션 네이밍 ### 4. ChromaDB 컬렉션 네이밍
``` ```
옵션 A: {robing_id}_{username}_episodic 옵션 A: {robeing_id}_{username}_episodic
예: rb10508_happybell80_episodic 예: rb10508_happybell80_episodic
옵션 B: {robing_id}_{user_uuid}_episodic 옵션 B: {robeing_id}_{user_uuid}_episodic
예: rb10508_a1b2c3d4_episodic 예: rb10508_a1b2c3d4_episodic
옵션 C: {workspace_id}_{robing_id}_{username}_episodic 옵션 C: {workspace_id}_{robeing_id}_{username}_episodic
예: workspace1_rb10508_happybell80_episodic 예: workspace1_rb10508_happybell80_episodic
``` ```
### 5. 토큰 저장 키 통일 ### 5. 토큰 저장 키 통일
```javascript ```javascript
현재: 'auth_token' 현재: 'auth_token'
대안: 'jwt_token', 'access_token', 'robing_token' 대안: 'jwt_token', 'access_token', 'robeing_token'
``` ```
### 6. OAuth 권한 분리 전략 ### 6. OAuth 권한 분리 전략
@ -274,7 +274,7 @@ X-Username: happybell80 // 명확한 이름
#### 1. Auth 서버 JWT 활용 (권장) 🌟 #### 1. Auth 서버 JWT 활용 (권장) 🌟
``` ```
Frontend 로그인 → JWT(username 포함) → Gateway 검증 → Robing username 추출 Frontend 로그인 → JWT(username 포함) → Gateway 검증 → Robeing username 추출
→ Slack과 동일한 컬렉션 사용 (rb10508_test_happybell80_episodic) → Slack과 동일한 컬렉션 사용 (rb10508_test_happybell80_episodic)
``` ```
@ -307,10 +307,10 @@ email ↔ slack_id ↔ username
```sql ```sql
-- 1. users: 기존 유지 -- 1. users: 기존 유지
-- 2. slack_workspaces: 단순화 -- 2. slack_workspaces: 단순화
-- 3. user_robings: 사용자-로빙 직접 연결 -- 3. user_robeings: 사용자-로빙 직접 연결
CREATE TABLE user_robings ( CREATE TABLE user_robeings (
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), -- rb10508_micro 등 robeing_id VARCHAR(100), -- rb10508_micro 등
is_primary BOOLEAN is_primary BOOLEAN
); );
@ -319,15 +319,15 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100), -- U0925SXQFDK slack_user_id VARCHAR(100), -- U0925SXQFDK
slack_team_id VARCHAR(100), slack_team_id VARCHAR(100),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) -- Slack별 로빙 지정 가능 robeing_id VARCHAR(100) -- Slack별 로빙 지정 가능
); );
``` ```
### 개별 수정 사항 ### 개별 수정 사항
#### 1. ~~Frontend (robing-api.ts)~~ ✅ 완료 #### 1. ~~Frontend (robeing-api.ts)~~ ✅ 완료
```typescript ```typescript
// 수정 완료 - robing-api.ts:38-41 // 수정 완료 - robeing-api.ts:38-41
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
@ -345,14 +345,14 @@ if authorization:
username = payload.get("sub") # 또는 username username = payload.get("sub") # 또는 username
``` ```
#### 3. Robing Service (rb10508_micro) #### 3. Robeing Service (rb10508_micro)
```python ```python
# endpoints.py 수정 필요: # endpoints.py 수정 필요:
# 1. JWT 토큰에서 username 추출 # 1. JWT 토큰에서 username 추출
# 2. storage 함수 호출 시 username 전달 # 2. storage 함수 호출 시 username 전달
# storage.py는 이미 준비됨: # storage.py는 이미 준비됨:
collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{memory_type}" collection_name = f"{settings.ROBEING_ID}_{username if username else 'default'}_{memory_type}"
``` ```
## 단계별 구현 계획 ## 단계별 구현 계획

View File

@ -57,7 +57,7 @@
### 1단계: 서비스 분리 ### 1단계: 서비스 분리
#### robing-gateway (구 frontend-base 일부) #### robeing-gateway (구 frontend-base 일부)
- **위치**: 51123 서버 - **위치**: 51123 서버
- **포트**: 8000 (기존 유지) - **포트**: 8000 (기존 유지)
- **역할**: - **역할**:
@ -66,7 +66,7 @@
- 사용자-로빙 매핑 (메모리 캐시) - 사용자-로빙 매핑 (메모리 캐시)
- 캐시 관리: 로그인 시 추가, 로그아웃 시 제거 - 캐시 관리: 로그인 시 추가, 로그아웃 시 제거
#### robing-control (구 frontend-base 일부) #### robeing-control (구 frontend-base 일부)
- **위치**: 51123 서버 - **위치**: 51123 서버
- **포트**: 9023 - **포트**: 9023
- **역할**: - **역할**:
@ -76,7 +76,7 @@
- 24서버 상태는 API로 조회 - 24서버 상태는 API로 조회
- 향후 간단한 관제 기능 추가 가능 - 향후 간단한 관제 기능 추가 가능
#### robing-monitor (신규) #### robeing-monitor (신규)
- **위치**: 51124 서버 - **위치**: 51124 서버
- **포트**: 9024 - **포트**: 9024
- **역할**: - **역할**:
@ -94,7 +94,7 @@ sequenceDiagram
participant F as Frontend participant F as Frontend
participant G as Gateway participant G as Gateway
participant A as Auth participant A as Auth
participant R as Robing participant R as Robeing
U->>F: 로그인 클릭 U->>F: 로그인 클릭
F->>G: /api/auth/login F->>G: /api/auth/login
@ -128,7 +128,7 @@ sequenceDiagram
#### 단기 계획 #### 단기 계획
- auth-server PostgreSQL을 메인 DB로 사용 - auth-server PostgreSQL을 메인 DB로 사용
- user_id를 모든 서비스에서 통일 - user_id를 모든 서비스에서 통일
- user_id → robing_port 매핑 테이블 관리 - user_id → robeing_port 매핑 테이블 관리
#### 장기 계획 #### 장기 계획
- 로빙별 SQLite → PostgreSQL 마이그레이션 - 로빙별 SQLite → PostgreSQL 마이그레이션
@ -136,7 +136,7 @@ sequenceDiagram
### 4단계: API 설계 ### 4단계: API 설계
#### robing-gateway API #### robeing-gateway API
``` ```
POST /api/auth/login # 로그인 시작 POST /api/auth/login # 로그인 시작
GET /api/auth/callback # OAuth 콜백 GET /api/auth/callback # OAuth 콜백
@ -144,21 +144,21 @@ POST /api/auth/logout # 로그아웃
GET /api/auth/status # 인증 상태 GET /api/auth/status # 인증 상태
POST /api/chat # 채팅 (자동 라우팅) POST /api/chat # 채팅 (자동 라우팅)
GET /api/robing/info # 할당된 로빙 정보 GET /api/robeing/info # 할당된 로빙 정보
``` ```
#### robing-control API #### robeing-control API
``` ```
GET /admin # 대시보드 UI GET /admin # 대시보드 UI
GET /api/monitor/overview # 전체 시스템 상태 GET /api/monitor/overview # 전체 시스템 상태
GET /api/monitor/23 # 23서버 상태 GET /api/monitor/23 # 23서버 상태
GET /api/monitor/24 # 24서버 상태 (robing-monitor 호출) GET /api/monitor/24 # 24서버 상태 (robeing-monitor 호출)
POST /api/control/restart # 서비스 재시작 (향후) POST /api/control/restart # 서비스 재시작 (향후)
``` ```
#### robing-monitor API #### robeing-monitor API
``` ```
GET /api/status/robings # 로빙 상태 GET /api/status/robeings # 로빙 상태
GET /api/status/skills # 스킬 상태 GET /api/status/skills # 스킬 상태
GET /api/status/resources # 리소스 사용량 GET /api/status/resources # 리소스 사용량
GET /api/health # 헬스체크 GET /api/health # 헬스체크
@ -167,13 +167,13 @@ GET /api/health # 헬스체크
## 구현 우선순위 ## 구현 우선순위
### Phase 1 ### Phase 1
1. robing-gateway 기본 구조 생성 1. robeing-gateway 기본 구조 생성
2. 인증 흐름 연결 2. 인증 흐름 연결
3. 단순 라우팅 (모든 사용자 → rb10508_test) 3. 단순 라우팅 (모든 사용자 → rb10508_test)
### Phase 2 ### Phase 2
1. robing-monitor 구현 1. robeing-monitor 구현
2. robing-control 분리 2. robeing-control 분리
3. 모니터링 대시보드 연결 3. 모니터링 대시보드 연결
### Phase 3 ### Phase 3

View File

@ -21,8 +21,8 @@
- 실제로는 1개 workspace만 사용 중 - 실제로는 1개 workspace만 사용 중
2. **중복 데이터** 2. **중복 데이터**
- robing_id가 workspaces와 workspace_members 양쪽에 존재 - robeing_id가 workspaces와 workspace_members 양쪽에 존재
- robing_url도 중복 저장 - robeing_url도 중복 저장
3. **불명확한 개념** 3. **불명확한 개념**
- companies의 실제 필요성 불분명 - companies의 실제 필요성 불분명
@ -59,14 +59,14 @@ CREATE TABLE users (
); );
-- 사용자별 로빙 할당 -- 사용자별 로빙 할당
CREATE TABLE user_robings ( CREATE TABLE user_robeings (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL, -- rb10508_micro, rb8001 등 robeing_id VARCHAR(100) NOT NULL, -- rb10508_micro, rb8001 등
robing_port INTEGER, robeing_port INTEGER,
is_primary BOOLEAN DEFAULT false, is_primary BOOLEAN DEFAULT false,
created_at TIMESTAMP, created_at TIMESTAMP,
UNIQUE(user_id, robing_id) UNIQUE(user_id, robeing_id)
); );
-- Slack 사용자 매핑 -- Slack 사용자 매핑
@ -117,15 +117,15 @@ CREATE TABLE slack_workspaces (
); );
-- 사용자-로빙 연결 -- 사용자-로빙 연결
CREATE TABLE user_robings ( CREATE TABLE user_robeings (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL, robeing_id VARCHAR(100) NOT NULL,
robing_port INTEGER, robeing_port INTEGER,
robing_host VARCHAR(255) DEFAULT '192.168.219.52', robeing_host VARCHAR(255) DEFAULT '192.168.219.52',
is_primary BOOLEAN DEFAULT false, is_primary BOOLEAN DEFAULT false,
created_at TIMESTAMP, created_at TIMESTAMP,
UNIQUE(user_id, robing_id) UNIQUE(user_id, robeing_id)
); );
-- Slack-시스템 사용자 매핑 -- Slack-시스템 사용자 매핑
@ -134,7 +134,7 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_team_id VARCHAR(100) REFERENCES slack_workspaces(team_id), slack_team_id VARCHAR(100) REFERENCES slack_workspaces(team_id),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), -- 이 Slack 사용자가 대화할 로빙 robeing_id VARCHAR(100), -- 이 Slack 사용자가 대화할 로빙
created_at TIMESTAMP, created_at TIMESTAMP,
updated_at TIMESTAMP, updated_at TIMESTAMP,
UNIQUE(slack_user_id, slack_team_id) UNIQUE(slack_user_id, slack_team_id)
@ -159,8 +159,8 @@ CREATE TABLE users (
email VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(50) UNIQUE, username VARCHAR(50) UNIQUE,
name VARCHAR(255), name VARCHAR(255),
robing_id VARCHAR(100), -- 기본 로빙 robeing_id VARCHAR(100), -- 기본 로빙
robing_port INTEGER, robeing_port INTEGER,
slack_user_id VARCHAR(100), -- Slack ID (있으면) slack_user_id VARCHAR(100), -- Slack ID (있으면)
slack_team_id VARCHAR(100), -- Slack workspace (있으면) slack_team_id VARCHAR(100), -- Slack workspace (있으면)
oauth_provider VARCHAR(50), oauth_provider VARCHAR(50),
@ -203,18 +203,18 @@ CREATE TABLE backup_companies AS SELECT * FROM companies;
#### Phase 2: 새 테이블 생성 #### Phase 2: 새 테이블 생성
```sql ```sql
-- user_robings 생성 및 데이터 이전 -- user_robeings 생성 및 데이터 이전
CREATE TABLE user_robings AS CREATE TABLE user_robeings AS
SELECT SELECT
gen_random_uuid() as id, gen_random_uuid() as id,
user_id, user_id,
robing_id, robeing_id,
CASE robing_id CASE robeing_id
WHEN 'rb10508_micro' THEN 10508 WHEN 'rb10508_micro' THEN 10508
WHEN 'rb8001' THEN 8001 WHEN 'rb8001' THEN 8001
WHEN 'rb10408' THEN 10408 WHEN 'rb10408' THEN 10408
END as robing_port, END as robeing_port,
'192.168.219.52' as robing_host, '192.168.219.52' as robeing_host,
true as is_primary, true as is_primary,
joined_at as created_at joined_at as created_at
FROM workspace_members; FROM workspace_members;
@ -225,7 +225,7 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_team_id VARCHAR(100), slack_team_id VARCHAR(100),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), robeing_id VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(slack_user_id, slack_team_id) UNIQUE(slack_user_id, slack_team_id)
@ -249,7 +249,7 @@ DROP TABLE companies;
``` ```
OAuth 로그인 → users 테이블 조회/생성 OAuth 로그인 → users 테이블 조회/생성
user_robings에서 기본 로빙 확인 user_robeings에서 기본 로빙 확인
로빙 서비스 연결 로빙 서비스 연결
``` ```
@ -258,7 +258,7 @@ user_robings에서 기본 로빙 확인
``` ```
Slack 이벤트 수신 (slack_user_id 포함) Slack 이벤트 수신 (slack_user_id 포함)
slack_users 테이블에서 user_id, robing_id 조회 slack_users 테이블에서 user_id, robeing_id 조회
해당 로빙으로 메시지 라우팅 해당 로빙으로 메시지 라우팅
@ -271,7 +271,7 @@ ChromaDB에 user_id 기반 저장
- Slack 사용자 매핑 기능 구현 가능 - Slack 사용자 매핑 기능 구현 가능
- 기존 테이블 영향 없음 - 기존 테이블 영향 없음
2. **단계적 마이그레이션**: user_robings 테이블 2. **단계적 마이그레이션**: user_robeings 테이블
- workspace_members 데이터 이전 - workspace_members 데이터 이전
- 테스트 후 기존 테이블 제거 - 테스트 후 기존 테이블 제거
@ -287,13 +287,13 @@ ChromaDB에 user_id 기반 저장
SELECT * FROM workspace_members WHERE user_id = ? SELECT * FROM workspace_members WHERE user_id = ?
# 변경 # 변경
SELECT * FROM user_robings WHERE user_id = ? AND is_primary = true SELECT * FROM user_robeings WHERE user_id = ? AND is_primary = true
``` ```
### 5.2 Slack 사용자 조회 ### 5.2 Slack 사용자 조회
```python ```python
# 신규 # 신규
SELECT u.*, sr.robing_id SELECT u.*, sr.robeing_id
FROM slack_users sr FROM slack_users sr
JOIN users u ON sr.user_id = u.id JOIN users u ON sr.user_id = u.id
WHERE sr.slack_user_id = ? AND sr.slack_team_id = ? WHERE sr.slack_user_id = ? AND sr.slack_team_id = ?
@ -339,7 +339,7 @@ WHERE sr.slack_user_id = ? AND sr.slack_team_id = ?
- 명확한 관심사 분리 - 명확한 관심사 분리
2. **slack_users 테이블 설계** 2. **slack_users 테이블 설계**
- `robing_id` 필드 포함이 좋습니다 - `robeing_id` 필드 포함이 좋습니다
- Slack 사용자별로 다른 로빙 할당 가능 - Slack 사용자별로 다른 로빙 할당 가능
- UNIQUE 제약 조건 적절 - UNIQUE 제약 조건 적절
@ -350,21 +350,21 @@ WHERE sr.slack_user_id = ? AND sr.slack_team_id = ?
### 8.2 추가 제안사항 ### 8.2 추가 제안사항
#### user_robings 테이블에 추가 필드 #### user_robeings 테이블에 추가 필드
```sql ```sql
CREATE TABLE user_robings ( CREATE TABLE user_robeings (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL, robeing_id VARCHAR(100) NOT NULL,
robing_port INTEGER, robeing_port INTEGER,
robing_host VARCHAR(255) DEFAULT '192.168.219.52', robeing_host VARCHAR(255) DEFAULT '192.168.219.52',
is_primary BOOLEAN DEFAULT false, is_primary BOOLEAN DEFAULT false,
last_used_at TIMESTAMP, -- 마지막 사용 시간 last_used_at TIMESTAMP, -- 마지막 사용 시간
usage_count INTEGER DEFAULT 0, -- 사용 횟수 usage_count INTEGER DEFAULT 0, -- 사용 횟수
status VARCHAR(20) DEFAULT 'active', -- active/inactive/suspended status VARCHAR(20) DEFAULT 'active', -- active/inactive/suspended
settings JSON, -- 로빙별 개인 설정 settings JSON, -- 로빙별 개인 설정
created_at TIMESTAMP, created_at TIMESTAMP,
UNIQUE(user_id, robing_id) UNIQUE(user_id, robeing_id)
); );
``` ```
@ -375,7 +375,7 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_team_id VARCHAR(100), slack_team_id VARCHAR(100),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), robeing_id VARCHAR(100),
display_name VARCHAR(255), -- Slack 표시 이름 display_name VARCHAR(255), -- Slack 표시 이름
slack_timezone VARCHAR(50), -- 사용자 시간대 slack_timezone VARCHAR(50), -- 사용자 시간대
last_message_at TIMESTAMP, -- 마지막 메시지 시간 last_message_at TIMESTAMP, -- 마지막 메시지 시간
@ -393,25 +393,25 @@ CREATE INDEX idx_slack_users_lookup
ON slack_users(slack_user_id, slack_team_id); ON slack_users(slack_user_id, slack_team_id);
-- 주 로빙 빠른 조회 -- 주 로빙 빠른 조회
CREATE INDEX idx_user_robings_primary CREATE INDEX idx_user_robeings_primary
ON user_robings(user_id, is_primary); ON user_robeings(user_id, is_primary);
-- 활성 상태 필터링 -- 활성 상태 필터링
CREATE INDEX idx_user_robings_status CREATE INDEX idx_user_robeings_status
ON user_robings(status, user_id); ON user_robeings(status, user_id);
``` ```
### 8.3 주의사항 ### 8.3 주의사항
1. **데이터 일관성** 1. **데이터 일관성**
- `robing_id`가 여러 테이블에 분산 - `robeing_id`가 여러 테이블에 분산
- 중앙 관리 방법 필요 (enum 또는 별도 robings 테이블) - 중앙 관리 방법 필요 (enum 또는 별도 robeings 테이블)
2. **포트 하드코딩 문제** 2. **포트 하드코딩 문제**
```sql ```sql
-- 하드코딩 대신 설정 테이블 권장 -- 하드코딩 대신 설정 테이블 권장
CREATE TABLE robing_configs ( CREATE TABLE robeing_configs (
robing_id VARCHAR(100) PRIMARY KEY, robeing_id VARCHAR(100) PRIMARY KEY,
port INTEGER NOT NULL, port INTEGER NOT NULL,
host VARCHAR(255), host VARCHAR(255),
is_active BOOLEAN DEFAULT true is_active BOOLEAN DEFAULT true
@ -419,7 +419,7 @@ ON user_robings(status, user_id);
``` ```
3. **ChromaDB 컬렉션명 규칙** 3. **ChromaDB 컬렉션명 규칙**
- `{robing_id}_{user_id}_episodic` 형식 통일 - `{robeing_id}_{user_id}_episodic` 형식 통일
- slack_users의 user_id 사용 필수 - slack_users의 user_id 사용 필수
### 8.4 개선된 실행 순서 ### 8.4 개선된 실행 순서

View File

@ -49,14 +49,14 @@ async def get_system_user_id(slack_user_id: str) -> str:
**파일**: `rb10508_micro/app/core/memory/storage.py` **파일**: `rb10508_micro/app/core/memory/storage.py`
```python ```python
def get_user_collection_name(robing_id: str, user_id: str, memory_type: str) -> str: def get_user_collection_name(robeing_id: str, user_id: str, memory_type: str) -> str:
"""사용자별 컬렉션 이름 생성""" """사용자별 컬렉션 이름 생성"""
if user_id == "default_user": if user_id == "default_user":
# 매핑 없는 사용자는 공용 컬렉션 사용 # 매핑 없는 사용자는 공용 컬렉션 사용
return f"{robing_id}_{memory_type}" return f"{robeing_id}_{memory_type}"
else: else:
# 사용자별 전용 컬렉션 # 사용자별 전용 컬렉션
return f"{robing_id}_{user_id[:8]}_{memory_type}" return f"{robeing_id}_{user_id[:8]}_{memory_type}"
``` ```
### 2.3 Brain 모듈 수정 ### 2.3 Brain 모듈 수정
@ -80,7 +80,7 @@ async def think_functional(
# 사용자별 컬렉션 사용 # 사용자별 컬렉션 사용
collection_name = get_user_collection_name( collection_name = get_user_collection_name(
settings.ROBING_ID, settings.ROBEING_ID,
system_user_id, system_user_id,
"episodic" "episodic"
) )
@ -112,7 +112,7 @@ async def get_user_mapping(
sum.user_id, sum.user_id,
u.username, u.username,
u.email, u.email,
wm.robing_id wm.robeing_id
FROM slack_user_mapping sum FROM slack_user_mapping sum
JOIN users u ON sum.user_id = u.id JOIN users u ON sum.user_id = u.id
LEFT JOIN workspace_members wm ON u.id = wm.user_id LEFT JOIN workspace_members wm ON u.id = wm.user_id
@ -131,7 +131,7 @@ async def get_user_mapping(
"user_id": str(result["user_id"]), "user_id": str(result["user_id"]),
"username": result["username"], "username": result["username"],
"email": result["email"], "email": result["email"],
"robing_id": result["robing_id"] or "rb10508_micro" "robeing_id": result["robeing_id"] or "rb10508_micro"
} }
@router.post("/mapping") @router.post("/mapping")
@ -188,23 +188,23 @@ async def route_slack_message(
if resp.status_code == 200: if resp.status_code == 200:
mapping = resp.json() mapping = resp.json()
robing_id = mapping["robing_id"] robeing_id = mapping["robeing_id"]
user_id = mapping["user_id"] user_id = mapping["user_id"]
else: else:
# 매핑 없으면 기본 로빙 사용 # 매핑 없으면 기본 로빙 사용
robing_id = "rb10508_micro" robeing_id = "rb10508_micro"
user_id = "default_user" user_id = "default_user"
# 해당 로빙으로 전달 # 해당 로빙으로 전달
robing_urls = { robeing_urls = {
"rb8001": "http://192.168.219.52:8001", "rb8001": "http://192.168.219.52:8001",
"rb10508_micro": "http://192.168.219.52:10508", "rb10508_micro": "http://192.168.219.52:10508",
"rb10408": "http://192.168.219.52:10408" "rb10408": "http://192.168.219.52:10408"
} }
target_url = robing_urls.get(robing_id) target_url = robeing_urls.get(robeing_id)
if not target_url: if not target_url:
return {"error": "Unknown robing"} return {"error": "Unknown robeing"}
# X-System-User-Id 헤더 추가 # X-System-User-Id 헤더 추가
headers = {"X-System-User-Id": user_id} headers = {"X-System-User-Id": user_id}

View File

@ -183,7 +183,7 @@ Message: 이메일을 보내기 위해 다음 정보가 필요합니다...
"message": "종태님한테 회의 일정 메일 보내줘", "message": "종태님한테 회의 일정 메일 보내줘",
"user_id": "U091UNVE41M", "user_id": "U091UNVE41M",
"channel": "C123456", "channel": "C123456",
"robing_id": "rb8001" "robeing_id": "rb8001"
} }
``` ```

View File

@ -59,26 +59,26 @@ rb8002 → skill-email (callback URL 전달 또는 없음)
conversation_handler = ConversationHandler() conversation_handler = ConversationHandler()
# 변경: 로빙별 독립 handler # 변경: 로빙별 독립 handler
conversation_handlers = {} # robing_id별 핸들러 딕셔너리 conversation_handlers = {} # robeing_id별 핸들러 딕셔너리
@app.post("/process") @app.post("/process")
async def process_email_conversation(request: Request): async def process_email_conversation(request: Request):
data = await request.json() data = await request.json()
robing_id = data.get("robing_id", "default") robeing_id = data.get("robeing_id", "default")
llm_callback = data.get("llm_callback") llm_callback = data.get("llm_callback")
# 로빙별 핸들러 가져오기 또는 생성 # 로빙별 핸들러 가져오기 또는 생성
if robing_id not in conversation_handlers: if robeing_id not in conversation_handlers:
conversation_handlers[robing_id] = ConversationHandler( conversation_handlers[robeing_id] = ConversationHandler(
llm_callback_url=llm_callback llm_callback_url=llm_callback
) )
else: else:
# callback URL 업데이트 # callback URL 업데이트
handler = conversation_handlers[robing_id] handler = conversation_handlers[robeing_id]
if llm_callback and handler.llm_callback_url != llm_callback: if llm_callback and handler.llm_callback_url != llm_callback:
handler.llm_callback_url = llm_callback handler.llm_callback_url = llm_callback
handler = conversation_handlers[robing_id] handler = conversation_handlers[robeing_id]
result = await handler.process_message(...) result = await handler.process_message(...)
``` ```
@ -97,7 +97,7 @@ def __init__(self, llm_callback_url: Optional[str] = None):
**2. LLM 추출 로직** **2. LLM 추출 로직**
```python ```python
async def _extract_email_info(self, message: str, robing_id: str, async def _extract_email_info(self, message: str, robeing_id: str,
llm_hints: Optional[Dict] = None) -> Dict: llm_hints: Optional[Dict] = None) -> Dict:
"""이메일 정보 추출 - callback URL 또는 자체 파싱 사용""" """이메일 정보 추출 - callback URL 또는 자체 파싱 사용"""
@ -109,7 +109,7 @@ async def _extract_email_info(self, message: str, robing_id: str,
json={ json={
"message": message, "message": message,
"task": "extract_email_info", "task": "extract_email_info",
"robing_id": robing_id "robeing_id": robeing_id
} }
) )
@ -165,7 +165,7 @@ async def llm_extract_callback(request: Request):
data = await request.json() data = await request.json()
message = data.get("message", "") message = data.get("message", "")
task = data.get("task", "") task = data.get("task", "")
robing_id = data.get("robing_id", "") robeing_id = data.get("robeing_id", "")
if task == "extract_email_info": if task == "extract_email_info":
# rb8001의 LLM으로 이메일 정보 추출 # rb8001의 LLM으로 이메일 정보 추출
@ -244,7 +244,7 @@ async def process_email_request(self, message: str, user_id: str, channel: str =
"message": message, "message": message,
"user_id": user_id, "user_id": user_id,
"channel": channel or "", "channel": channel or "",
"robing_id": "rb8001", "robeing_id": "rb8001",
"llm_hints": llm_hints, "llm_hints": llm_hints,
"llm_callback": "http://localhost:8001/api/llm/extract" # ← Callback URL "llm_callback": "http://localhost:8001/api/llm/extract" # ← Callback URL
} }
@ -277,7 +277,7 @@ async def process_email_request(self, message: str, user_id: str, channel: str =
```bash ```bash
curl -X POST http://localhost:8001/api/llm/extract \ curl -X POST http://localhost:8001/api/llm/extract \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"message": "종태님한테 회의 메일 보내줘", "task": "extract_email_info", "robing_id": "rb8001"}' -d '{"message": "종태님한테 회의 메일 보내줘", "task": "extract_email_info", "robeing_id": "rb8001"}'
# 응답 # 응답
{ {
@ -357,7 +357,7 @@ rb8001:
### 8.2 skill-email 처리 ### 8.2 skill-email 처리
``` ```
skill-email: skill-email:
- robing_id로 handler 선택/생성 - robeing_id로 handler 선택/생성
- callback URL 설정 - callback URL 설정
- 이메일 정보 추출 필요 - 이메일 정보 추출 필요
@ -412,8 +412,8 @@ skill-email:
### 11.1 WebSocket 지원 ### 11.1 WebSocket 지원
```python ```python
# 실시간 양방향 통신 # 실시간 양방향 통신
@app.websocket("/ws/{robing_id}") @app.websocket("/ws/{robeing_id}")
async def websocket_endpoint(websocket: WebSocket, robing_id: str): async def websocket_endpoint(websocket: WebSocket, robeing_id: str):
# 지속적인 대화 세션 # 지속적인 대화 세션
``` ```
@ -429,7 +429,7 @@ elif task == "extract_contact_info":
### 11.3 로빙별 커스터마이징 ### 11.3 로빙별 커스터마이징
```python ```python
# 각 로빙의 특성에 맞는 LLM 프롬프트 # 각 로빙의 특성에 맞는 LLM 프롬프트
robing_prompts = { robeing_prompts = {
"rb8001": "정중하고 격식있게", "rb8001": "정중하고 격식있게",
"rb8002": "친근하고 캐주얼하게" "rb8002": "친근하고 캐주얼하게"
} }

View File

@ -166,7 +166,7 @@ async def _init_collections(self):
if settings.USE_CONVERSATION_CACHE: if settings.USE_CONVERSATION_CACHE:
self.conversation_cache = self.client.get_or_create_collection( self.conversation_cache = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_conversation_cache", name=f"{settings.ROBEING_ID}_conversation_cache",
metadata={ metadata={
"type": "conversation_cache", "type": "conversation_cache",
"version": "1.0", "version": "1.0",

View File

@ -14,7 +14,7 @@
## 아키텍처 구조 ## 아키텍처 구조
``` ```
robing-brain/ robeing-brain/
├── core/ # 존재적 삼각형 (120MB) ├── core/ # 존재적 삼각형 (120MB)
│ ├── memory/ # 기억 모듈 (50MB) │ ├── memory/ # 기억 모듈 (50MB)
│ │ ├── entropy.py # 정보엔트로피 계산 │ │ ├── entropy.py # 정보엔트로피 계산
@ -44,10 +44,10 @@ robing-brain/
## 1. 핵심 브레인 (내장 기능) ## 1. 핵심 브레인 (내장 기능)
```python ```python
class RobingBrain: class RobeingBrain:
"""경량화된 로빙 브레인 - 사고의 핵심만 포함""" """경량화된 로빙 브레인 - 사고의 핵심만 포함"""
def __init__(self, robing_id: str): def __init__(self, robeing_id: str):
# 존재적 삼각형 # 존재적 삼각형
self.memory = MemoryCore() # 50MB self.memory = MemoryCore() # 50MB
self.emotion = EmotionCore() # 35MB self.emotion = EmotionCore() # 35MB
@ -59,7 +59,7 @@ class RobingBrain:
self.items = ItemInventory() # 10MB self.items = ItemInventory() # 10MB
# 정체성 # 정체성
self.identity = Identity(robing_id) # 20MB self.identity = Identity(robeing_id) # 20MB
async def think(self, input_data: Dict) -> Dict: async def think(self, input_data: Dict) -> Dict:
"""핵심 사고 프로세스""" """핵심 사고 프로세스"""
@ -451,15 +451,15 @@ Response:
} }
# 스탯 조회 # 스탯 조회
GET /stats/{robing_id} GET /stats/{robeing_id}
# 스킬 확인 # 스킬 확인
GET /skills/{robing_id} GET /skills/{robeing_id}
# 아이템 장착 # 아이템 장착
POST /items/equip POST /items/equip
{ {
"robing_id": "rb10408", "robeing_id": "rb10408",
"item_id": "gmail_api" "item_id": "gmail_api"
} }
``` ```

View File

@ -115,7 +115,7 @@ git push -f origin main
- **Gitea 서버**: https://git.ro-being.com - **Gitea 서버**: https://git.ro-being.com
- **백업 정책**: `/home/admin/BACKUP_TO_NAS_POLICY.md` - **백업 정책**: `/home/admin/BACKUP_TO_NAS_POLICY.md`
- **아키텍처 문서**: `/home/admin/ROBING_ARCHITECTURE_PHILOSOPHY.md` - **아키텍처 문서**: `/home/admin/ROBEING_ARCHITECTURE_PHILOSOPHY.md`
--- ---
*이 문서는 Claude에 의해 자동 생성되었습니다.* *이 문서는 Claude에 의해 자동 생성되었습니다.*

View File

@ -24,8 +24,8 @@ sudo apt-get update
sudo apt-get install timescaledb-2-postgresql-15 sudo apt-get install timescaledb-2-postgresql-15
# 데이터베이스 생성 및 확장 활성화 # 데이터베이스 생성 및 확장 활성화
sudo -u postgres createdb robing_metrics sudo -u postgres createdb robeing_metrics
sudo -u postgres psql robing_metrics -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" sudo -u postgres psql robeing_metrics -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
``` ```
### 3. HDD 저장소 설정 ### 3. HDD 저장소 설정

View File

@ -22,7 +22,7 @@ cp -r /home/admin/github-migration/test_meta-skill /home/happybell80/my_project/
# 추가 설정 파일들 복사 # 추가 설정 파일들 복사
cp /home/admin/BACKUP_TO_NAS_POLICY.md /home/happybell80/my_project/ cp /home/admin/BACKUP_TO_NAS_POLICY.md /home/happybell80/my_project/
cp /home/admin/CLAUDE.md /home/happybell80/my_project/ cp /home/admin/CLAUDE.md /home/happybell80/my_project/
cp /home/admin/ROBING_ARCHITECTURE_PHILOSOPHY.md /home/happybell80/my_project/ cp /home/admin/ROBEING_ARCHITECTURE_PHILOSOPHY.md /home/happybell80/my_project/
``` ```
### 2. Git Remote 토큰 업데이트 ### 2. Git Remote 토큰 업데이트

View File

@ -82,19 +82,19 @@ SELECT time_bucket(INTERVAL %s, time) as time_bucket, ...
### 1. PostgreSQL에서 직접 테스트 ### 1. PostgreSQL에서 직접 테스트
```bash ```bash
sudo -u postgres psql robing_metrics -c "SELECT time_bucket('1 minute', NOW()) as test_bucket;" sudo -u postgres psql robeing_metrics -c "SELECT time_bucket('1 minute', NOW()) as test_bucket;"
# 결과: 정상 동작 확인 # 결과: 정상 동작 확인
``` ```
### 2. 데이터 존재 확인 ### 2. 데이터 존재 확인
```bash ```bash
sudo -u postgres psql robing_metrics -c "SELECT COUNT(*) FROM system_metrics;" sudo -u postgres psql robeing_metrics -c "SELECT COUNT(*) FROM system_metrics;"
# 결과: 336개 데이터 존재 # 결과: 336개 데이터 존재
``` ```
### 3. 최신 데이터 확인 ### 3. 최신 데이터 확인
```bash ```bash
sudo -u postgres psql robing_metrics -c "SELECT time, metric_type, value FROM system_metrics ORDER BY time DESC LIMIT 5;" sudo -u postgres psql robeing_metrics -c "SELECT time, metric_type, value FROM system_metrics ORDER BY time DESC LIMIT 5;"
# 결과: 1분 전까지 정상 수집됨 # 결과: 1분 전까지 정상 수집됨
``` ```

View File

@ -73,7 +73,7 @@ cp -r rb10508_test/app/stats rb8001/app/
cp -r rb10508_test/app/utils rb8001/app/ cp -r rb10508_test/app/utils rb8001/app/
# 3. 핵심 서비스 파일 복사 # 3. 핵심 서비스 파일 복사
cp rb10508_test/app/services/robing_brain.py rb8001/app/services/ cp rb10508_test/app/services/robeing_brain.py rb8001/app/services/
cp rb10508_test/app/services/chroma_service.py rb8001/app/services/ cp rb10508_test/app/services/chroma_service.py rb8001/app/services/
cp rb10508_test/app/services/gemini_service.py rb8001/app/services/ cp rb10508_test/app/services/gemini_service.py rb8001/app/services/
cp rb10508_test/app/services/slack_service.py rb8001/app/services/ cp rb10508_test/app/services/slack_service.py rb8001/app/services/
@ -88,7 +88,7 @@ rb8001/docker-compose.yml 수정:
- 컨테이너 이름: `robeing-8001` - 컨테이너 이름: `robeing-8001`
- 포트 매핑: `8001:8000` - 포트 매핑: `8001:8000`
- `network_mode: host` (서버 DB 접근용) - `network_mode: host` (서버 DB 접근용)
- 환경변수에 `ROBING_ID=8001` 추가 - 환경변수에 `ROBEING_ID=8001` 추가
- PostgreSQL 컨테이너 제거 (서버 DB 사용) - PostgreSQL 컨테이너 제거 (서버 DB 사용)
## 주요 교훈 ## 주요 교훈
@ -105,7 +105,7 @@ rb8001/docker-compose.yml 수정:
### 3. 서버 환경 이해 ### 3. 서버 환경 이해
- Docker는 애플리케이션만 (DB는 서버에 직접 설치) - Docker는 애플리케이션만 (DB는 서버에 직접 설치)
- `network_mode: host`로 서버 리소스 접근 - `network_mode: host`로 서버 리소스 접근
- 포트 충돌 주의 (8001은 ROBING_ID와 일치) - 포트 충돌 주의 (8001은 ROBEING_ID와 일치)
## 다음 단계 ## 다음 단계

View File

@ -10,7 +10,7 @@ rb8001과 rb10508_test 두 로빙 인스턴스가 동일한 DB를 공유하는
## 문제 상황 ## 문제 상황
### 초기 상태 ### 초기 상태
- 두 로빙이 동일한 PostgreSQL DB 사용 (robing_metrics) - 두 로빙이 동일한 PostgreSQL DB 사용 (robeing_metrics)
- ChromaDB 컬렉션명 충돌 (conversations, documents, insights) - ChromaDB 컬렉션명 충돌 (conversations, documents, insights)
- 로그 파일 구분 없음 - 로그 파일 구분 없음
@ -42,14 +42,14 @@ ln -s /mnt/hdd/robeing-logs/rb10508_test /home/admin/rb10508_test/logs
### 2. 코드 수정 ### 2. 코드 수정
#### config.py - ROBING_ID 기반 설정 #### config.py - ROBEING_ID 기반 설정
```python ```python
class Settings(BaseSettings): class Settings(BaseSettings):
# Robeing Configuration # Robeing Configuration
ROBING_ID: str = "rb8001" # 또는 "rb10508_test" ROBEING_ID: str = "rb8001" # 또는 "rb10508_test"
# Database Configuration # Database Configuration
DATABASE_URL: str = f"postgresql://postgres:postgres@localhost/{ROBING_ID}_db" DATABASE_URL: str = f"postgresql://postgres:postgres@localhost/{ROBEING_ID}_db"
``` ```
#### chroma_service.py - 기존 경로 유지 #### chroma_service.py - 기존 경로 유지
@ -61,14 +61,14 @@ self.client = chromadb.PersistentClient(
) )
``` ```
#### main.py - 로그 파일명에 ROBING_ID 포함 #### main.py - 로그 파일명에 ROBEING_ID 포함
```python ```python
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[ handlers=[
logging.StreamHandler(), logging.StreamHandler(),
logging.FileHandler(f"/code/logs/{settings.ROBING_ID}_app.log") logging.FileHandler(f"/code/logs/{settings.ROBEING_ID}_app.log")
] ]
) )
``` ```
@ -88,7 +88,7 @@ volumes:
## 핵심 설계 결정 ## 핵심 설계 결정
### 1. ChromaDB 분리 방식 ### 1. ChromaDB 분리 방식
- **초기 고려안**: 컬렉션명에 ROBING_ID 포함 (`rb8001_conversations`) - **초기 고려안**: 컬렉션명에 ROBEING_ID 포함 (`rb8001_conversations`)
- **최종 결정**: 디렉토리 분리로 해결 - **최종 결정**: 디렉토리 분리로 해결
- **이유**: 코드 수정 최소화, 기존 데이터 유지, 마이그레이션 불필요 - **이유**: 코드 수정 최소화, 기존 데이터 유지, 마이그레이션 불필요

View File

@ -35,7 +35,7 @@ location /rb10408/ {
- 사용자가 "어디로 보냈어?"라고 물어도 대답 못함 - 사용자가 "어디로 보냈어?"라고 물어도 대답 못함
**원인 분석**: **원인 분석**:
- robing_brain.py에서 이메일 내용만 생성하고 send_email 호출 안 함 - robeing_brain.py에서 이메일 내용만 생성하고 send_email 호출 안 함
- 의도 분석이 "써줘"를 email intent로 인식 못함 - 의도 분석이 "써줘"를 email intent로 인식 못함
**해결**: **해결**:
@ -77,7 +77,7 @@ location /rb10408/ {
**개선 방향 결정**: **개선 방향 결정**:
1. 가장 심플한 해결책: 메모리 딕셔너리 사용 1. 가장 심플한 해결책: 메모리 딕셔너리 사용
2. robing_brain.py에만 코드 추가 2. robeing_brain.py에만 코드 추가
3. Redis 같은 외부 의존성 추가 안 함 3. Redis 같은 외부 의존성 추가 안 함
**장단점 분석**: **장단점 분석**:

View File

@ -20,7 +20,7 @@ Python 프로젝트의 파일 간 의존성과 함수 호출 관계를 시각화
``` ```
2. **함수 필터링 일관성 문제** 2. **함수 필터링 일관성 문제**
- 증상: `AssertionError: non existent node 'app/services/robing_brain.py::__init__'` - 증상: `AssertionError: non existent node 'app/services/robeing_brain.py::__init__'`
- 원인: 첫 번째 패스에서만 `__init__` 함수 제외하고 두 번째 패스에서는 누락 - 원인: 첫 번째 패스에서만 `__init__` 함수 제외하고 두 번째 패스에서는 누락
- 해결: 모든 패스에서 일관되게 함수 필터링 적용 - 해결: 모든 패스에서 일관되게 함수 필터링 적용
```python ```python

View File

@ -24,7 +24,7 @@
└── current/ └── current/
├── config/ ├── config/
├── gitea/ ├── gitea/
└── robing/ └── robeing/
``` ```
## 오후 11시 10분 ## 오후 11시 10분

View File

@ -108,7 +108,7 @@ CREATE TABLE workspace_members (
workspace_id UUID REFERENCES workspaces(id), workspace_id UUID REFERENCES workspaces(id),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
role VARCHAR(50), role VARCHAR(50),
robing_id VARCHAR(100), -- 할당된 로빙 robeing_id VARCHAR(100), -- 할당된 로빙
... ...
); );
``` ```

View File

@ -50,10 +50,10 @@ async def test_message(message: dict):
```env ```env
VITE_AUTH_SERVER_URL=https://auth.ro-being.com VITE_AUTH_SERVER_URL=https://auth.ro-being.com
VITE_API_URL=http://localhost:8000 VITE_API_URL=http://localhost:8000
VITE_ROBING_API_URL=https://ro-being.com/rb10508 # 임시 하드코딩 VITE_ROBEING_API_URL=https://ro-being.com/rb10508 # 임시 하드코딩
``` ```
**2. 로빙 API 서비스 생성** (`src/services/robing-api.ts`): **2. 로빙 API 서비스 생성** (`src/services/robeing-api.ts`):
- `sendMessage()`: 메시지 전송 - `sendMessage()`: 메시지 전송
- `checkHealth()`: 연결 상태 확인 - `checkHealth()`: 연결 상태 확인
- 에러 처리 클래스 구현 - 에러 처리 클래스 구현
@ -115,7 +115,7 @@ git push origin main
### 프론트엔드 변경사항 푸시 ### 프론트엔드 변경사항 푸시
**커밋 내용**: **커밋 내용**:
- robing-api.ts 서비스 생성 - robeing-api.ts 서비스 생성
- ChatInterface 컴포넌트 API 통합 - ChatInterface 컴포넌트 API 통합
- Health Check 및 연결 상태 표시 - Health Check 및 연결 상태 표시
- 로딩 상태 및 에러 처리 - 로딩 상태 및 에러 처리
@ -139,38 +139,38 @@ git push origin main
**현재 상태 (개발용)**: **현재 상태 (개발용)**:
```javascript ```javascript
// .env.local // .env.local
VITE_ROBING_API_URL=https://ro-being.com/rb10508 // 하드코딩 VITE_ROBEING_API_URL=https://ro-being.com/rb10508 // 하드코딩
// src/services/robing-api.ts // src/services/robeing-api.ts
const ROBING_API_URL = import.meta.env.VITE_ROBING_API_URL || 'https://ro-being.com/rb10508'; const ROBEING_API_URL = import.meta.env.VITE_ROBEING_API_URL || 'https://ro-being.com/rb10508';
``` ```
**서비스용으로 변경해야 할 부분**: **서비스용으로 변경해야 할 부분**:
1. **환경변수 제거**: 1. **환경변수 제거**:
- `.env.local`에서 `VITE_ROBING_API_URL` 삭제 - `.env.local`에서 `VITE_ROBEING_API_URL` 삭제
- 하드코딩된 로빙 ID 제거 - 하드코딩된 로빙 ID 제거
2. **동적 로빙 할당 구현**: 2. **동적 로빙 할당 구현**:
```javascript ```javascript
// src/services/robing-api.ts 수정 // src/services/robeing-api.ts 수정
// 1. getUserRobing() 함수 구현 // 1. getUserRobeing() 함수 구현
export async function getUserRobing(userId: string): Promise<string> { export async function getUserRobeing(userId: string): Promise<string> {
const response = await fetch(`${AUTH_SERVER_URL}/api/users/${userId}/robing`); const response = await fetch(`${AUTH_SERVER_URL}/api/users/${userId}/robeing`);
const data = await response.json(); const data = await response.json();
return data.robing_url; // 예: "https://ro-being.com/rb8001" return data.robeing_url; // 예: "https://ro-being.com/rb8001"
} }
// 2. sendMessage() 수정 // 2. sendMessage() 수정
export async function sendMessage(text: string, userId: string) { export async function sendMessage(text: string, userId: string) {
const robingUrl = await getUserRobing(userId); // 동적으로 가져옴 const robeingUrl = await getUserRobeing(userId); // 동적으로 가져옴
const response = await fetch(`${robingUrl}/api/dev/message`, {...}); const response = await fetch(`${robeingUrl}/api/dev/message`, {...});
} }
``` ```
3. **auth-server DB 구축 필요**: 3. **auth-server DB 구축 필요**:
- users 테이블 - users 테이블
- workspace_members 테이블 (user_id, robing_id 매핑) - workspace_members 테이블 (user_id, robeing_id 매핑)
- 로빙 할당 API 엔드포인트 - 로빙 할당 API 엔드포인트
4. **ChatInterface 수정**: 4. **ChatInterface 수정**:

View File

@ -123,7 +123,7 @@ ls -la /mnt/51123logs/rb10508_test/
## 오전 12시 04분 - 중앙 로그 시스템 완전 실패 및 롤백 ## 오전 12시 04분 - 중앙 로그 시스템 완전 실패 및 롤백
### 시도했던 작업 ### 시도했던 작업
1. .env 파일에 LOG_ROOT=/mnt/51123logs, ROBING_NAME=rb10508_test 설정 1. .env 파일에 LOG_ROOT=/mnt/51123logs, ROBEING_NAME=rb10508_test 설정
2. 템플릿 시스템 동작 확인 (docker compose config에서 경로 정상 해석됨) 2. 템플릿 시스템 동작 확인 (docker compose config에서 경로 정상 해석됨)
3. 컨테이너 재시작 시도 3. 컨테이너 재시작 시도
@ -136,7 +136,7 @@ Error response from daemon: error while creating mount source path '/mnt/51123lo
Docker가 볼륨 마운트 시 상위 디렉토리를 새로 만들려고 했지만, 해당 경로는 이미 SSHFS 마운트 포인트로 존재해서 충돌 발생. Docker가 볼륨 마운트 시 상위 디렉토리를 새로 만들려고 했지만, 해당 경로는 이미 SSHFS 마운트 포인트로 존재해서 충돌 발생.
### 롤백 작업 ### 롤백 작업
1. .env 파일에서 LOG_ROOT, ROBING_NAME 환경변수 제거 1. .env 파일에서 LOG_ROOT, ROBEING_NAME 환경변수 제거
2. 기존 ./logs:/code/logs:rw 방식으로 복원 2. 기존 ./logs:/code/logs:rw 방식으로 복원
3. 컨테이너 정상 시작됨 3. 컨테이너 정상 시작됨

View File

@ -95,29 +95,29 @@ sudo chown -R 999:999 /mnt/hdd/logs/51124-server/
services: services:
app: app:
volumes: volumes:
- ${LOG_ROOT:-./logs}/${ROBING_NAME:-${COMPOSE_PROJECT_NAME}}:/code/logs:rw - ${LOG_ROOT:-./logs}/${ROBEING_NAME:-${COMPOSE_PROJECT_NAME}}:/code/logs:rw
``` ```
### 2. .env.template 생성 ### 2. .env.template 생성
```bash ```bash
# .env.template # .env.template
LOG_ROOT=/mnt/51123logs LOG_ROOT=/mnt/51123logs
ROBING_NAME=__ROBING__ ROBEING_NAME=__ROBEING__
``` ```
### 3. 배포 자동화 스크립트 ### 3. 배포 자동화 스크립트
```bash ```bash
#!/bin/bash #!/bin/bash
# deploy_robing.sh rb10508_test # deploy_robeing.sh rb10508_test
set -e set -e
ROBING=$1 ROBEING=$1
LOG_ROOT=/mnt/51123logs LOG_ROOT=/mnt/51123logs
sudo mkdir -p ${LOG_ROOT}/${ROBING} sudo mkdir -p ${LOG_ROOT}/${ROBEING}
sudo chown 999:docker ${LOG_ROOT}/${ROBING} sudo chown 999:docker ${LOG_ROOT}/${ROBEING}
cp .env.template .env cp .env.template .env
sed -i "s/__ROBING__/${ROBING}/" .env sed -i "s/__ROBEING__/${ROBEING}/" .env
docker compose up -d docker compose up -d
``` ```
@ -130,7 +130,7 @@ docker compose up -d
ssh admin@192.168.219.52 "sudo chown 999:docker /mnt/51123logs/${{ env.PROJECT }}" ssh admin@192.168.219.52 "sudo chown 999:docker /mnt/51123logs/${{ env.PROJECT }}"
- name: Deploy - name: Deploy
run: | run: |
sed -i "s/__ROBING__/${{ env.PROJECT }}/" .env sed -i "s/__ROBEING__/${{ env.PROJECT }}/" .env
docker compose up -d docker compose up -d
``` ```

View File

@ -8,7 +8,7 @@ ChromaDB에서 각 로빙의 메모리를 저장할 때, 컬렉션 이름 생성
```python ```python
# /home/heejae/rb10408_test/app/state/memory_manager.py # /home/heejae/rb10408_test/app/state/memory_manager.py
self.collection_name = f"robing_{robing_id}_memories" self.collection_name = f"robeing_{robeing_id}_memories"
``` ```
각 로빙은 고유한 ID를 가지고 있어 (`rb10408_test`, `rb10508_test` 등), 컬렉션 이름이 중복될 가능성이 없었습니다. 각 로빙은 고유한 ID를 가지고 있어 (`rb10408_test`, `rb10508_test` 등), 컬렉션 이름이 중복될 가능성이 없었습니다.
@ -18,7 +18,7 @@ self.collection_name = f"robing_{robing_id}_memories"
```python ```python
def generate_id(self, content: str, timestamp: str) -> str: def generate_id(self, content: str, timestamp: str) -> str:
"""고유 ID 생성""" """고유 ID 생성"""
unique_string = f"{self.robing_id}_{content}_{timestamp}" unique_string = f"{self.robeing_id}_{content}_{timestamp}"
return hashlib.md5(unique_string.encode()).hexdigest() return hashlib.md5(unique_string.encode()).hexdigest()
``` ```

View File

@ -19,7 +19,7 @@
### 1. Gemini API 모드로 전환 (권장) ### 1. Gemini API 모드로 전환 (권장)
```python ```python
# /home/heejae/robing-llm-service/.env # /home/heejae/robeing-llm-service/.env
GEMINI_USE_CLI=false # true -> false 변경 GEMINI_USE_CLI=false # true -> false 변경
``` ```
@ -35,7 +35,7 @@ LLM 서비스에는 필수 필드만 전송:
- `message` - `message`
- `user_id` - `user_id`
- `channel` - `channel`
- `robing_id` - `robeing_id`
### 3. 서비스 상태 확인 ### 3. 서비스 상태 확인
```bash ```bash
@ -47,12 +47,12 @@ curl http://localhost:8003/health
``` ```
## 기술적 세부사항 ## 기술적 세부사항
- LLM 서비스 위치: `/home/heejae/robing-llm-service/` - LLM 서비스 위치: `/home/heejae/robeing-llm-service/`
- 포트: 8003 - 포트: 8003
- 기본 모델: gemini-flash - 기본 모델: gemini-flash
- CLI 타임아웃: 15초 (느린 원인) - CLI 타임아웃: 15초 (느린 원인)
## 관련 파일 ## 관련 파일
- `/home/heejae/robing-llm-service/app/llm/gemini_handler.py` - `/home/heejae/robeing-llm-service/app/llm/gemini_handler.py`
- `/home/heejae/robing-llm-service/app/core/config.py` - `/home/heejae/robeing-llm-service/app/core/config.py`
- `/home/heejae/rb10408_test/app/router/router.py` - `/home/heejae/rb10408_test/app/router/router.py`

View File

@ -13,7 +13,7 @@ sshpass -p '19800508' ssh -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123
### 2. .env 파일 설정 ### 2. .env 파일 설정
```bash ```bash
# /home/heejae/robing-state-service/.env # /home/heejae/robeing-state-service/.env
DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db
SERVICE_NAME=state SERVICE_NAME=state
PORT=8002 PORT=8002
@ -44,5 +44,5 @@ psql postgresql://robeings:robeings@localhost:5433/main_db -c "SELECT 1"
systemd 서비스로 SSH 터널을 관리하거나, autossh 사용을 고려할 수 있습니다. systemd 서비스로 SSH 터널을 관리하거나, autossh 사용을 고려할 수 있습니다.
## 관련 파일 ## 관련 파일
- `/home/heejae/robing-state-service/.env` - `/home/heejae/robeing-state-service/.env`
- `/home/heejae/robing-state-service/app/core/config.py` - `/home/heejae/robeing-state-service/app/core/config.py`

View File

@ -58,7 +58,7 @@ State 서비스가 없어 메모리 저장 불가능 확인.
### 오후 7:00 - 서비스 의존성 확인 ### 오후 7:00 - 서비스 의존성 확인
실행 중인 컨테이너: 실행 중인 컨테이너:
- rb10408_test (라우터) - rb10408_test (라우터)
- robing_llm (AI 처리) - robeing_llm (AI 처리)
- skill-email - skill-email
- skill-slack - skill-slack
- skill-news - skill-news
@ -68,7 +68,7 @@ State 서비스가 없어 메모리 저장 불가능 확인.
## 발견된 문제점 ## 발견된 문제점
### 1. State 서비스 미실행 ### 1. State 서비스 미실행
- 코드는 `/home/heejae/robing-state-service/`에 존재 - 코드는 `/home/heejae/robeing-state-service/`에 존재
- 포트 8002 미사용 - 포트 8002 미사용
- 메모리 저장/검색 불가능 - 메모리 저장/검색 불가능
@ -111,7 +111,7 @@ State 서비스가 없어 메모리 저장 불가능 확인.
1. **즉시 조치**: State 서비스 실행 1. **즉시 조치**: State 서비스 실행
```bash ```bash
cd /home/heejae/robing-state-service cd /home/heejae/robeing-state-service
docker-compose up -d docker-compose up -d
``` ```

View File

@ -150,7 +150,7 @@ volumes:
2. **ChromaDB 컬렉션 생성 시 embedding_function 전달**: 2. **ChromaDB 컬렉션 생성 시 embedding_function 전달**:
```python ```python
self.episodic = self.client.get_or_create_collection( self.episodic = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_episodic", name=f"{settings.ROBEING_ID}_episodic",
metadata={"type": "episodic"}, metadata={"type": "episodic"},
embedding_function=self.embedding_function embedding_function=self.embedding_function
) )

View File

@ -224,7 +224,7 @@ async def get_user_lock(self, user_id: str) -> asyncio.Lock:
if settings.USE_CONVERSATION_CACHE: if settings.USE_CONVERSATION_CACHE:
try: try:
self.conversation_cache = self.client.get_or_create_collection( self.conversation_cache = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_conversation_cache", name=f"{settings.ROBEING_ID}_conversation_cache",
metadata={ metadata={
"type": "conversation_cache", "type": "conversation_cache",
"version": "1.0", "version": "1.0",

View File

@ -174,19 +174,19 @@ f"안녕하세요{greeting_name}! 오늘은 어떤 도움이 필요하신가요?
1. **memory.py에 identity 컬렉션 추가**: 1. **memory.py에 identity 컬렉션 추가**:
```python ```python
self.identity_collection = self.client.get_or_create_collection( self.identity_collection = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_identity", name=f"{settings.ROBEING_ID}_identity",
metadata={"type": "identity", "description": "User and robing names"} metadata={"type": "identity", "description": "User and robeing names"}
) )
``` ```
2. **store_identity() 메서드**: 2. **store_identity() 메서드**:
```python ```python
async def store_identity(self, user_id: str, user_name: str = None, robing_name: str = None): async def store_identity(self, user_id: str, user_name: str = None, robeing_name: str = None):
doc_id = f"identity:{user_id}" doc_id = f"identity:{user_id}"
self.identity_collection.upsert( self.identity_collection.upsert(
ids=[doc_id], ids=[doc_id],
documents=[f"user:{user_name},robing:{robing_name}"], documents=[f"user:{user_name},robeing:{robeing_name}"],
metadatas=[{"user_name": user_name, "robing_name": robing_name}] metadatas=[{"user_name": user_name, "robeing_name": robeing_name}]
) )
``` ```
@ -301,7 +301,7 @@ await self.memory.store_memory(
# 로빙 응답 (필수) # 로빙 응답 (필수)
await self.memory.store_memory( await self.memory.store_memory(
content=f"Robing: {final_response}", content=f"Robeing: {final_response}",
user_id=user_id, user_id=user_id,
memory_type="episodic", memory_type="episodic",
metadata={"role": "assistant"} metadata={"role": "assistant"}

View File

@ -41,7 +41,7 @@
**Before (OOP):** **Before (OOP):**
```python ```python
class RobingBrain: class RobeingBrain:
def __init__(self): def __init__(self):
self.memory = MemoryCore() self.memory = MemoryCore()
self.emotion = EmotionCore() self.emotion = EmotionCore()

View File

@ -1,4 +1,4 @@
# robing-gateway API 게이트웨이 구현 및 배포 # robeing-gateway API 게이트웨이 구현 및 배포
**날짜**: 2025-08-09 **날짜**: 2025-08-09
**작업자**: happybell80 & Claude & 23서버팀 **작업자**: happybell80 & Claude & 23서버팀
@ -15,7 +15,7 @@
**프로젝트 구조**: **프로젝트 구조**:
``` ```
robing-gateway/ robeing-gateway/
├── app/ ├── app/
│ ├── main.py # FastAPI 메인 앱 │ ├── main.py # FastAPI 메인 앱
│ ├── cache.py # 메모리 캐시 (30분 TTL) │ ├── cache.py # 메모리 캐시 (30분 TTL)
@ -39,7 +39,7 @@ robing-gateway/
**중요 변경사항 - 새 테이블 불필요**: **중요 변경사항 - 새 테이블 불필요**:
- 기존 main_db의 workspaces, workspace_members 테이블 활용 - 기존 main_db의 workspaces, workspace_members 테이블 활용
- workspace_members에서 user_id로 robing 정보 조회 - workspace_members에서 user_id로 robeing 정보 조회
- JOIN 쿼리로 사용자 → 워크스페이스 → 로빙 매핑 - JOIN 쿼리로 사용자 → 워크스페이스 → 로빙 매핑
## 오전 11시 00분 - 배포 문제 해결 ## 오전 11시 00분 - 배포 문제 해결
@ -107,9 +107,9 @@ SELECT COUNT(*) FROM workspaces; # 2 (테스트 데이터)
``` ```
**현재 상태**: **현재 상태**:
- robing-gateway 포트 8100에서 정상 실행 - robeing-gateway 포트 8100에서 정상 실행
- PostgreSQL main_db 연결 성공 - PostgreSQL main_db 연결 성공
- DEFAULT_ROBING_ID: rb10508_micro 설정 - DEFAULT_ROBEING_ID: rb10508_micro 설정
- 메모리 캐시 활성화 (30분 TTL) - 메모리 캐시 활성화 (30분 TTL)
## 시스템 아키텍처 ## 시스템 아키텍처
@ -145,7 +145,7 @@ fetch('http://localhost:8100/api/chat', {
```sql ```sql
-- 사용자를 워크스페이스에 할당 -- 사용자를 워크스페이스에 할당
INSERT INTO workspace_members ( INSERT INTO workspace_members (
id, user_id, workspace_id, role, robing_id, is_active id, user_id, workspace_id, role, robeing_id, is_active
) VALUES ( ) VALUES (
gen_random_uuid(), gen_random_uuid(),
'USER_UUID', -- 실제 사용자 ID 'USER_UUID', -- 실제 사용자 ID
@ -157,8 +157,8 @@ INSERT INTO workspace_members (
``` ```
### 3. 모니터링 구현 ### 3. 모니터링 구현
- robing-control 서비스 분리 (관리 대시보드) - robeing-control 서비스 분리 (관리 대시보드)
- robing-monitor 서비스 구현 (24서버 모니터링) - robeing-monitor 서비스 구현 (24서버 모니터링)
- 메트릭 수집 및 시각화 - 메트릭 수집 및 시각화
### 4. 인증 통합 ### 4. 인증 통합
@ -184,7 +184,7 @@ location ^~ /gateway/ {
**Frontend 수정**: **Frontend 수정**:
```javascript ```javascript
// robing-api.ts 변경사항 // robeing-api.ts 변경사항
// 1. 엔드포인트: /api/message → /api/chat // 1. 엔드포인트: /api/message → /api/chat
// 2. X-User-Id 헤더 추가 // 2. X-User-Id 헤더 추가
// 3. 요청 body: text → message 필드 // 3. 요청 body: text → message 필드
@ -278,7 +278,7 @@ INSERT INTO workspace_members (..., role, ...) VALUES
2. **Gateway 라우팅**: 2. **Gateway 라우팅**:
- X-User-Id 헤더로 사용자 식별 - X-User-Id 헤더로 사용자 식별
- DB 조회: workspace_members → robing_id 확인 - DB 조회: workspace_members → robeing_id 확인
- rb10508_micro (192.168.219.52:10508)로 라우팅 - rb10508_micro (192.168.219.52:10508)로 라우팅
3. **개인화된 응답**: 3. **개인화된 응답**:

View File

@ -57,7 +57,7 @@ async def healthz():
return {"status": "ok"} return {"status": "ok"}
# 3. Frontend - 호출 경로 수정 # 3. Frontend - 호출 경로 수정
const response = await fetch(`${ROBING_API_URL}/healthz`) const response = await fetch(`${ROBEING_API_URL}/healthz`)
# 4. Docker & Gitea Actions - 헬스체크 경로 통일 # 4. Docker & Gitea Actions - 헬스체크 경로 통일
test: ["CMD", "curl", "-f", "http://localhost:10508/healthz"] test: ["CMD", "curl", "-f", "http://localhost:10508/healthz"]
@ -66,7 +66,7 @@ test: ["CMD", "curl", "-f", "http://localhost:10508/healthz"]
### 문제 3: Frontend 빌드 시 환경변수 미적용 ### 문제 3: Frontend 빌드 시 환경변수 미적용
**증상**: **증상**:
- .env에 `VITE_ROBING_API_URL=https://ro-being.com/gateway` 설정 - .env에 `VITE_ROBEING_API_URL=https://ro-being.com/gateway` 설정
- 하지만 빌드된 JS는 기본값 `/rb10508` 사용 - 하지만 빌드된 JS는 기본값 `/rb10508` 사용
- 422 에러: `message` vs `text` 필드 불일치 - 422 에러: `message` vs `text` 필드 불일치
@ -74,14 +74,14 @@ test: ["CMD", "curl", "-f", "http://localhost:10508/healthz"]
```yaml ```yaml
# .gitea/workflows/deploy.yml (수정 전) # .gitea/workflows/deploy.yml (수정 전)
export VITE_API_URL=http://localhost:8001 export VITE_API_URL=http://localhost:8001
# VITE_ROBING_API_URL 누락! # VITE_ROBEING_API_URL 누락!
npm run build npm run build
``` ```
**해결**: **해결**:
```yaml ```yaml
export VITE_API_URL=http://localhost:8001 export VITE_API_URL=http://localhost:8001
export VITE_ROBING_API_URL=https://ro-being.com/gateway export VITE_ROBEING_API_URL=https://ro-being.com/gateway
npm run build npm run build
``` ```
@ -94,7 +94,7 @@ npm run build
**해결**: **해결**:
```python ```python
# database.py - users 테이블 JOIN으로 username 지원 # database.py - users 테이블 JOIN으로 username 지원
async def get_robing_info(username: str): async def get_robeing_info(username: str):
query = text(""" query = text("""
SELECT ... SELECT ...
FROM workspace_members wm FROM workspace_members wm

View File

@ -30,7 +30,7 @@ async def healthz():
**해결**: **해결**:
```python ```python
# robeing-gateway/app/database.py # robeing-gateway/app/database.py
async def get_robing_info(username: str): async def get_robeing_info(username: str):
"""username으로 직접 조회""" """username으로 직접 조회"""
query = text(""" query = text("""
SELECT ... SELECT ...
@ -143,7 +143,7 @@ class MessageRequest(BaseModel):
### 문제 5: Frontend 빌드 시 환경변수 미적용 ### 문제 5: Frontend 빌드 시 환경변수 미적용
**증상**: **증상**:
- .env 파일에 `VITE_ROBING_API_URL=https://ro-being.com/gateway` 설정됨 - .env 파일에 `VITE_ROBEING_API_URL=https://ro-being.com/gateway` 설정됨
- 하지만 빌드된 JS 파일에는 하드코딩 기본값 `/rb10508` 사용 - 하지만 빌드된 JS 파일에는 하드코딩 기본값 `/rb10508` 사용
- 결과: nginx가 Gateway 우회하여 직접 프록시 - 결과: nginx가 Gateway 우회하여 직접 프록시
@ -153,14 +153,14 @@ class MessageRequest(BaseModel):
- name: Build application - name: Build application
run: | run: |
export VITE_API_URL=http://localhost:8001 export VITE_API_URL=http://localhost:8001
# VITE_ROBING_API_URL 누락! # VITE_ROBEING_API_URL 누락!
npm run build npm run build
``` ```
**해결**: **해결**:
```yaml ```yaml
export VITE_API_URL=http://localhost:8001 export VITE_API_URL=http://localhost:8001
export VITE_ROBING_API_URL=https://ro-being.com/gateway # 추가 export VITE_ROBEING_API_URL=https://ro-being.com/gateway # 추가
npm run build npm run build
``` ```

View File

@ -38,7 +38,7 @@ const userId = user?.id || user?.email || 'default';
const response = await sendMessage(input, token || '', userId); const response = await sendMessage(input, token || '', userId);
``` ```
**2. robing-api.ts 수정** **2. robeing-api.ts 수정**
```typescript ```typescript
// 함수 시그니처 변경 (토큰 파라미터 추가) // 함수 시그니처 변경 (토큰 파라미터 추가)
export async function sendMessage( export async function sendMessage(
@ -90,7 +90,7 @@ if (redirectPath && redirectPath !== window.location.pathname) {
- https://ro-being.com/game 에서 확인 가능 - https://ro-being.com/game 에서 확인 가능
#### 다음 단계 #### 다음 단계
- Backend(robing-gateway)에서 JWT 토큰 검증 로직 추가 필요 - Backend(robeing-gateway)에서 JWT 토큰 검증 로직 추가 필요
- 51124 서버 담당자와 협의하여 검증 구현 - 51124 서버 담당자와 협의하여 검증 구현
- JWT_SECRET 환경변수 설정 및 공유 필요 - JWT_SECRET 환경변수 설정 및 공유 필요

View File

@ -2,10 +2,10 @@
## 오전 11시 31분 ## 오전 11시 31분
### 1. robing-gateway 헬스체크 경로 불일치 해결 ### 1. robeing-gateway 헬스체크 경로 불일치 해결
#### 문제 상황 #### 문제 상황
- robing-gateway가 3일 이상 unhealthy 상태 - robeing-gateway가 3일 이상 unhealthy 상태
- FailingStreak: 10,028회 실패 - FailingStreak: 10,028회 실패
- 서비스는 정상 작동하지만 헬스체크만 실패 - 서비스는 정상 작동하지만 헬스체크만 실패
@ -100,6 +100,6 @@ settings.MISTRAL_MODEL
--- ---
## 배포 결과 ## 배포 결과
- robing-gateway: Dockerfile 헬스체크 수정 후 푸시 - robeing-gateway: Dockerfile 헬스체크 수정 후 푸시
- rb10508_micro: 하드코딩 제거 후 푸시 - rb10508_micro: 하드코딩 제거 후 푸시
- 두 서비스 모두 Gitea Actions 통해 자동 배포 진행 중 - 두 서비스 모두 Gitea Actions 통해 자동 배포 진행 중

View File

@ -58,12 +58,12 @@ x_user_id: str = Depends(get_verified_user)
# 수정 전 - JWT_SECRET_KEY 없음 # 수정 전 - JWT_SECRET_KEY 없음
environment: environment:
- DATABASE_URL=${DATABASE_URL} - DATABASE_URL=${DATABASE_URL}
- DEFAULT_ROBING_HOST=${DEFAULT_ROBING_HOST} - DEFAULT_ROBEING_HOST=${DEFAULT_ROBEING_HOST}
# 수정 후 # 수정 후
environment: environment:
- DATABASE_URL=${DATABASE_URL} - DATABASE_URL=${DATABASE_URL}
- DEFAULT_ROBING_HOST=${DEFAULT_ROBING_HOST} - DEFAULT_ROBEING_HOST=${DEFAULT_ROBEING_HOST}
- JWT_SECRET_KEY=${JWT_SECRET_KEY} # 추가! - JWT_SECRET_KEY=${JWT_SECRET_KEY} # 추가!
``` ```
@ -86,7 +86,7 @@ environment:
**원인**: Frontend 환경변수가 잘못됨 **원인**: Frontend 환경변수가 잘못됨
```javascript ```javascript
// .env.local // .env.local
VITE_ROBING_API_URL=https://ro-being.com/rb10508 // 잘못됨 VITE_ROBEING_API_URL=https://ro-being.com/rb10508 // 잘못됨
``` ```
### 해결 과정 ### 해결 과정
@ -181,4 +181,4 @@ Frontend → Gateway → 로빙
- robeing-gateway/app/main.py - robeing-gateway/app/main.py
- robeing-gateway/docker-compose.yml - robeing-gateway/docker-compose.yml
- frontend-customer/.env.local - frontend-customer/.env.local
- frontend-customer/src/services/robing-api.ts - frontend-customer/src/services/robeing-api.ts

View File

@ -42,7 +42,7 @@ MAX_MESSAGES_IN_DOM: int = int(os.getenv("MAX_MESSAGES_IN_DOM", 200))
## 오후 1시 00분 - 프론트엔드 구현 ## 오후 1시 00분 - 프론트엔드 구현
### 1. robing-api.ts 확장 ### 1. robeing-api.ts 확장
- `getConfig()`: 백엔드 설정 가져오기 - `getConfig()`: 백엔드 설정 가져오기
- `getMessages()`: 페이지네이션 지원 메시지 조회 - `getMessages()`: 페이지네이션 지원 메시지 조회
@ -146,7 +146,7 @@ def resolve_username(user_id: str) -> str:
**2025년 8월 9일의 잘못된 결정이 문제의 시작**: **2025년 8월 9일의 잘못된 결정이 문제의 시작**:
```sql ```sql
-- 잘못된 예: 테스트용 UUID 하드코딩 (250809_happybell80_robing-gateway구현.md) -- 잘못된 예: 테스트용 UUID 하드코딩 (250809_happybell80_robeing-gateway구현.md)
INSERT INTO users (id, email, name) VALUES INSERT INTO users (id, email, name) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'goeun2dc@gmail.com', '김종태'), ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'goeun2dc@gmail.com', '김종태'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '0914eagle@gmail.com', '전희재'), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '0914eagle@gmail.com', '전희재'),
@ -249,7 +249,7 @@ async def get_chat_history(
```javascript ```javascript
// 변경 전: /api/messages // 변경 전: /api/messages
// 변경 후: /api/history // 변경 후: /api/history
const response = await fetch(`${ROBING_API_URL}/api/history?${params}`) const response = await fetch(`${ROBEING_API_URL}/api/history?${params}`)
``` ```
### 함수형 프로그래밍 원칙 준수 ### 함수형 프로그래밍 원칙 준수
@ -325,7 +325,7 @@ docker-compose up -d
3. **연결 확인** 3. **연결 확인**
```bash ```bash
docker logs robing-gateway --tail 50 docker logs robeing-gateway --tail 50
# "Database connection established successfully" 확인 # "Database connection established successfully" 확인
# "All required tables found in main_db" 확인 # "All required tables found in main_db" 확인
``` ```
@ -345,7 +345,7 @@ docker logs robing-gateway --tail 50
### 근본 원인 ### 근본 원인
```javascript ```javascript
// src/services/robing-api.ts // src/services/robeing-api.ts
const userId = localStorage.getItem('user_id') || 'default_user'; // 항상 default_user const userId = localStorage.getItem('user_id') || 'default_user'; // 항상 default_user
// src/contexts/auth-context.tsx // src/contexts/auth-context.tsx
@ -436,14 +436,14 @@ search_memories_fn=lambda query, user_id: search_memories(
### 문제 ### 문제
- ChromaDB에 role: "assistant"로 저장 - ChromaDB에 role: "assistant"로 저장
- 프론트엔드는 sender: "robing" 기대 - 프론트엔드는 sender: "robeing" 기대
- 모든 메시지가 sender: "user"로 표시 - 모든 메시지가 sender: "user"로 표시
### 해결 ### 해결
```python ```python
# endpoints.py 수정 # endpoints.py 수정
role = metadata.get('role', 'user') role = metadata.get('role', 'user')
sender = 'robing' if role == 'assistant' else 'user' sender = 'robeing' if role == 'assistant' else 'user'
messages.append({ messages.append({
"sender": sender, # role을 sender로 변환 "sender": sender, # role을 sender로 변환
}) })

View File

@ -45,9 +45,9 @@ metadata = {
# robeing-gateway/app/main.py에 추가 # robeing-gateway/app/main.py에 추가
@app.get("/api/{path:path}") @app.get("/api/{path:path}")
async def proxy_get(path: str, request: Request, x_user_id: str = Depends(get_verified_user)): async def proxy_get(path: str, request: Request, x_user_id: str = Depends(get_verified_user)):
"""Proxy GET requests to robing service""" """Proxy GET requests to robeing service"""
robing_info = user_cache.get(x_user_id) or get_default_robing_info() robeing_info = user_cache.get(x_user_id) or get_default_robeing_info()
base_url = robing_info['robing_url'].rstrip('/') if 'robing_url' in robing_info else f"http://{robing_info['host']}:{robing_info['port']}" base_url = robeing_info['robeing_url'].rstrip('/') if 'robeing_url' in robeing_info else f"http://{robeing_info['host']}:{robeing_info['port']}"
response = await http_client.get( response = await http_client.get(
f"{base_url}/api/{path}", f"{base_url}/api/{path}",
@ -65,7 +65,7 @@ async def proxy_get(path: str, request: Request, x_user_id: str = Depends(get_ve
#### 해결 방법 #### 해결 방법
```typescript ```typescript
// src/services/robing-api.ts 수정 // src/services/robeing-api.ts 수정
export async function getUserHistory(_userId: string, _token: string = '') { export async function getUserHistory(_userId: string, _token: string = '') {
// getMessages 재사용하여 중복 제거 // getMessages 재사용하여 중복 제거
const result = await getMessages(undefined, 1); const result = await getMessages(undefined, 1);

View File

@ -113,7 +113,7 @@ const logger = {
1. `auth-context.tsx` - 6개 console 문 제거 1. `auth-context.tsx` - 6개 console 문 제거
2. `login-dialog.tsx` - 2개 console 문 제거 (비밀번호 로그) 2. `login-dialog.tsx` - 2개 console 문 제거 (비밀번호 로그)
3. `chat-interface.tsx` - 5개 console 문 제거 3. `chat-interface.tsx` - 5개 console 문 제거
4. `robing-api.ts` - 1개 console 문 제거 4. `robeing-api.ts` - 1개 console 문 제거
5. `dos-terminal.tsx` - 1개 console 문 제거 5. `dos-terminal.tsx` - 1개 console 문 제거
6. `confetti.tsx` - 3개 console 문 제거 6. `confetti.tsx` - 3개 console 문 제거

View File

@ -101,7 +101,7 @@ cat > .env << 'ENVFILE'
# 개별 URL 변수 사용 # 개별 URL 변수 사용
BRAIN_SERVICE_URL=http://localhost:8001 BRAIN_SERVICE_URL=http://localhost:8001
SKILL_EMAIL_URL=http://localhost:8501 SKILL_EMAIL_URL=http://localhost:8501
# (ROBING_URLS, SKILL_URLS 형태 아님) # (ROBEING_URLS, SKILL_URLS 형태 아님)
``` ```
## 오전 10시 00분 ## 오전 10시 00분
@ -203,7 +203,7 @@ Gitea Actions 오류 시:
## 오전 10시 30분 - 프론트엔드 인벤토리 UI 개발 ## 오전 10시 30분 - 프론트엔드 인벤토리 UI 개발
### 개발 순서 ### 개발 순서
1. **API 클라이언트 확장** (`robing-api.ts`) 1. **API 클라이언트 확장** (`robeing-api.ts`)
- GmailCredentialItem 타입 정의 - GmailCredentialItem 타입 정의
- getGmailItems, equipGmailItem, unequipGmailItem, startGmailReauth, revokeGmailItem 함수 구현 - getGmailItems, equipGmailItem, unequipGmailItem, startGmailReauth, revokeGmailItem 함수 구현
@ -256,7 +256,7 @@ npm run build
### frontend-customer/.gitea/workflows/deploy.yml 수정 ### frontend-customer/.gitea/workflows/deploy.yml 수정
1. Line 12: `github.ref``gitea.ref` 변경 1. Line 12: `github.ref``gitea.ref` 변경
2. Line 63: `VITE_API_URL=http://localhost:8001``VITE_ROBING_API_URL=https://ro-being.com/gateway` 변경 2. Line 63: `VITE_API_URL=http://localhost:8001``VITE_ROBEING_API_URL=https://ro-being.com/gateway` 변경
## 오전 11시 10분 - 배포 완료 ## 오전 11시 10분 - 배포 완료

View File

@ -55,7 +55,7 @@ before_time = to_utc_aware(before)
- 서버 .env 파일에서 환경변수 읽도록 변경 - 서버 .env 파일에서 환경변수 읽도록 변경
```yaml ```yaml
# 기존 # 기존
export VITE_ROBING_API_URL=https://ro-being.com/rb10508 export VITE_ROBEING_API_URL=https://ro-being.com/rb10508
# 수정 # 수정
if [ -f /home/admin/frontend-customer/.env ]; then if [ -f /home/admin/frontend-customer/.env ]; then

View File

@ -43,26 +43,26 @@
### rb10508_micro 레벨 시스템 활성화 ### rb10508_micro 레벨 시스템 활성화
#### 문제 상황 #### 문제 상황
- `/api/stats/{robing_id}` 엔드포인트가 비활성화 상태 - `/api/stats/{robeing_id}` 엔드포인트가 비활성화 상태
- 빈 객체만 반환하여 프론트엔드에서 레벨 확인 불가 - 빈 객체만 반환하여 프론트엔드에서 레벨 확인 불가
#### 해결 방법 #### 해결 방법
1. **endpoints.py 수정** 1. **endpoints.py 수정**
```python ```python
@router.get("/stats/{robing_id}") @router.get("/stats/{robeing_id}")
async def get_stats(robing_id: str): async def get_stats(robeing_id: str):
# PostgreSQL 직접 연결 # PostgreSQL 직접 연결
conn = await asyncpg.connect(...) conn = await asyncpg.connect(...)
# robing_stats 테이블 조회 # robeing_stats 테이블 조회
query = """ query = """
SELECT level, experience, stat_points, SELECT level, experience, stat_points,
memory, compute, empathy, leadership, ethics memory, compute, empathy, leadership, ethics
FROM robing_stats FROM robeing_stats
WHERE robeing_id = $1 WHERE robeing_id = $1
""" """
row = await conn.fetchrow(query, robing_id) row = await conn.fetchrow(query, robeing_id)
``` ```
2. **DB 레벨 설정** 2. **DB 레벨 설정**

View File

@ -83,9 +83,9 @@ CREATE INDEX idx_conversations_robeing ON robeing.conversations(robeing_id);
### 스키마 vs 테이블 내 구분 ### 스키마 vs 테이블 내 구분
- 스키마별 분리: 삭제 쉬움 (`DROP SCHEMA rb10508 CASCADE`) - 스키마별 분리: 삭제 쉬움 (`DROP SCHEMA rb10508 CASCADE`)
- robing_id로 구분: 관리 단순 - robeing_id로 구분: 관리 단순
- 파티션 테이블: 성능 최적화 - 파티션 테이블: 성능 최적화
- 최종 결정: robing_id + 삭제 함수 방식 - 최종 결정: robeing_id + 삭제 함수 방식
### JSONB 활용 ### JSONB 활용
- 핵심 컬럼 + extra JSONB 필드로 유연성 확보 - 핵심 컬럼 + extra JSONB 필드로 유연성 확보

View File

@ -24,7 +24,7 @@
users 테이블 (UUID) users 테이블 (UUID)
workspace_members 테이블 workspace_members 테이블
(robing_id 할당) (robeing_id 할당)
로빙 컨테이너 로빙 컨테이너
(rb8001, rb10508_micro 등) (rb8001, rb10508_micro 등)
@ -35,8 +35,8 @@ workspace_members 테이블
#### 핵심 테이블 관계 #### 핵심 테이블 관계
- **users**: 사용자 UUID 저장 - **users**: 사용자 UUID 저장
- **workspace_members**: user_id → robing_id 매핑 - **workspace_members**: user_id → robeing_id 매핑
- **robing_stats**: 각 로빙의 레벨, 경험치, 스탯 - **robeing_stats**: 각 로빙의 레벨, 경험치, 스탯
#### 서비스 구조 #### 서비스 구조
- **51123 서버**: nginx, frontend-customer, auth-server - **51123 서버**: nginx, frontend-customer, auth-server
@ -52,8 +52,8 @@ workspace_members 테이블
#### 1. API 구조 설계 #### 1. API 구조 설계
```typescript ```typescript
// 필요한 API 엔드포인트 // 필요한 API 엔드포인트
1. GET /api/user/robing - 사용자에게 할당된 로빙 정보 1. GET /api/user/robeing - 사용자에게 할당된 로빙 정보
2. GET /api/stats/{robing_id} - 특정 로빙의 스탯 조회 2. GET /api/stats/{robeing_id} - 특정 로빙의 스탯 조회
``` ```
#### 2. 프론트엔드 수정 계획 #### 2. 프론트엔드 수정 계획
@ -67,17 +67,17 @@ workspace_members 테이블
### 구현 작업 ### 구현 작업
#### 1. robing-api.ts 수정 #### 1. robeing-api.ts 수정
```typescript ```typescript
// 새로운 함수 추가 // 새로운 함수 추가
export async function getUserRobing(): Promise<{ export async function getUserRobeing(): Promise<{
robing_id: string; robeing_id: string;
robing_url: string; robeing_url: string;
} | null> { } | null> {
// workspace_members에서 사용자의 로빙 조회 // workspace_members에서 사용자의 로빙 조회
} }
export async function getRobingStats(robingId: string): Promise<{ export async function getRobeingStats(robeingId: string): Promise<{
level: number; level: number;
experience: number; experience: number;
// ... // ...
@ -89,29 +89,29 @@ export async function getRobingStats(robingId: string): Promise<{
#### 2. game-layout.tsx 수정 #### 2. game-layout.tsx 수정
```typescript ```typescript
// 변경 전 // 변경 전
const [robingLevel, setRobingLevel] = useState<number>(1); const [robeingLevel, setRobeingLevel] = useState<number>(1);
// 하드코딩: LV.12 // 하드코딩: LV.12
// 변경 후 // 변경 후
const [userRobingId, setUserRobingId] = useState<string>('rb10508_micro'); const [userRobeingId, setUserRobeingId] = useState<string>('rb10508_micro');
// 동적 할당: LV.{robingLevel} // 동적 할당: LV.{robeingLevel}
// useEffect로 사용자별 로빙 정보 조회 // useEffect로 사용자별 로빙 정보 조회
useEffect(() => { useEffect(() => {
const fetchUserRobing = async () => { const fetchUserRobeing = async () => {
const robingInfo = await getUserRobing(); const robeingInfo = await getUserRobeing();
if (robingInfo) { if (robeingInfo) {
setUserRobingId(robingInfo.robing_id); setUserRobeingId(robeingInfo.robeing_id);
} }
}; };
fetchUserRobing(); fetchUserRobeing();
}, [user]); }, [user]);
``` ```
#### 3. rb10508_micro endpoints.py 수정 #### 3. rb10508_micro endpoints.py 수정
```python ```python
@router.get("/api/stats/{robing_id}") @router.get("/api/stats/{robeing_id}")
async def get_stats(robing_id: str): async def get_stats(robeing_id: str):
# PostgreSQL 직접 연결 # PostgreSQL 직접 연결
conn = await asyncpg.connect( conn = await asyncpg.connect(
host='192.168.219.45', host='192.168.219.45',
@ -121,14 +121,14 @@ async def get_stats(robing_id: str):
database='main_db' database='main_db'
) )
# robing_stats 테이블 조회 # robeing_stats 테이블 조회
query = """ query = """
SELECT level, experience, stat_points, SELECT level, experience, stat_points,
memory, compute, empathy, leadership, ethics memory, compute, empathy, leadership, ethics
FROM robing_stats FROM robeing_stats
WHERE robeing_id = $1 WHERE robeing_id = $1
""" """
row = await conn.fetchrow(query, robing_id) row = await conn.fetchrow(query, robeing_id)
``` ```
--- ---
@ -155,10 +155,10 @@ async def get_stats(robing_id: str):
#### 푸시된 저장소들 #### 푸시된 저장소들
1. **frontend-customer** 1. **frontend-customer**
- 사용자별 로빙 동적 할당 구현 - 사용자별 로빙 동적 할당 구현
- getUserRobing(), getRobingStats() 함수 추가 - getUserRobeing(), getRobeingStats() 함수 추가
2. **rb10508_micro** 2. **rb10508_micro**
- /api/stats/{robing_id} DB 조회 구현 - /api/stats/{robeing_id} DB 조회 구현
- PostgreSQL 직접 연결 로직 추가 - PostgreSQL 직접 연결 로직 추가
3. **DOCS** 3. **DOCS**
@ -177,7 +177,7 @@ async def get_stats(robing_id: str):
### 1. 시스템 구조 이해의 중요성 ### 1. 시스템 구조 이해의 중요성
- 단순한 레벨 표시 문제가 아닌 전체 아키텍처 이해 필요 - 단순한 레벨 표시 문제가 아닌 전체 아키텍처 이해 필요
- users → workspace_members → robing_stats 관계 파악 필수 - users → workspace_members → robeing_stats 관계 파악 필수
- 다중 로빙 체계와 사용자 매핑 구조 이해 - 다중 로빙 체계와 사용자 매핑 구조 이해
### 2. 하드코딩 제거의 필요성 ### 2. 하드코딩 제거의 필요성