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] # 현재 장착 아이템
storage: List[Item] # 보관 중인 아이템
def can_equip(self, item: Item, robing_stats: Stats) -> bool:
def can_equip(self, item: Item, robeing_stats: Stats) -> bool:
"""아이템 장착 가능 여부 확인"""
return (
robing_stats.level >= item.level_req and
all(robing_stats[stat] >= req for stat, req in item.stat_req.items())
robeing_stats.level >= item.level_req and
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
async def chat(self, message: str, ...):
# Memory search
memory_manager = MemoryManager(self.robing_name)
memory_manager = MemoryManager(self.robeing_name)
memories = await memory_manager.search_memories(
query=message,
n_results=3
@ -286,7 +286,7 @@ docker compose up -d
```bash
# 로그 확인
docker logs rb8001 -f
docker logs robing_state -f
docker logs robeing_state -f
# 컨테이너 재시작
docker compose restart

View File

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

View File

@ -34,7 +34,7 @@
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │
│ │ users, robings, stats │ │
│ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
@ -78,13 +78,13 @@
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 (개별)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ graph TB
subgraph Database
PG[(PostgreSQL)]
GT[gmail_tokens]
RS[robing_stats]
RS[robeing_stats]
end
UI --> GW
@ -107,7 +107,7 @@ sequenceDiagram
participant Monitor as robeing-monitor
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)
Monitor->>Monitor: 아이템별 레벨 체크
@ -183,7 +183,7 @@ sequenceDiagram
Note over Monitor: Body: {robeing_id: "rb10508_micro"}
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
Monitor->>Monitor: 레벨 요구사항 체크
@ -218,7 +218,7 @@ sequenceDiagram
Front->>Monitor: POST /api/items/gmail/{userId}/equip
alt 레벨 부족
Monitor->>DB: SELECT level FROM robing_stats
Monitor->>DB: SELECT level FROM robeing_stats
DB-->>Monitor: level: 3
Monitor-->>Front: {error: "레벨 5 이상 필요"}
else 토큰 없음
@ -338,7 +338,7 @@ sequenceDiagram
participant Front as 프론트엔드
RB->>DB: 경험치 획득
DB->>DB: UPDATE robing_stats<br/>SET experience = experience + 100
DB->>DB: UPDATE robeing_stats<br/>SET experience = experience + 100
DB->>DB: 레벨 계산
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(
sum(category.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 {
"total_monthly_cost": total_monthly_cost,
"cost_per_robing": cost_per_robing,
"break_even_price": cost_per_robing * 1.3, # 30% 마진
"cost_per_robeing": cost_per_robeing,
"break_even_price": cost_per_robeing * 1.3, # 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": {
"starter": {
"price": 300_000, # 월 30만원
"robings": 1,
"robeings": 1,
"skills": ["basic"],
"support": "email"
},
"professional": {
"price": 800_000, # 월 80만원
"robings": 3,
"robeings": 3,
"skills": ["all"],
"support": "priority"
},
"enterprise": {
"price": "custom",
"robings": "unlimited",
"robeings": "unlimited",
"skills": ["all + custom"],
"support": "dedicated"
}
@ -422,7 +422,7 @@ class OnboardingFlow {
]
},
{
id: "robing_creation",
id: "robeing_creation",
component: "RobeingCreator",
options: {
name: "로빙 이름 설정",
@ -502,7 +502,7 @@ class UserAnalytics:
"""
dashboard_components = {
"usage_overview": {
"daily_active_robings": "Line chart",
"daily_active_robeings": "Line chart",
"message_volume": "Area chart",
"skill_usage": "Heatmap",
"peak_hours": "Bar chart"
@ -516,7 +516,7 @@ class UserAnalytics:
},
"growth_insights": {
"robing_level_distribution": "Histogram",
"robeing_level_distribution": "Histogram",
"skill_popularity": "Pie chart",
"user_journey": "Funnel chart",
"cohort_analysis": "Retention table"

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │
│ │ users, robings, stats │ │
│ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
@ -60,13 +60,13 @@
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 (개별)

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ 공통 DB │ │
│ │ users, robings, stats │ │
│ │ users, robeings, stats │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
@ -60,13 +60,13 @@
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 (개별)

View File

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

View File

@ -4,7 +4,7 @@
작성자: 51123 서버, 51124 서버, 로컬 개발자
상태: 아이디어 → 추가 검토 필요
우선순위: 🔴 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
export async function sendMessage(
text: string,
token: string,
userId: string
): Promise<MessageResponse> {
const response = await fetch(`${ROBING_API_URL}/api/chat`, {
const response = await fetch(`${ROBEING_API_URL}/api/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

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

View File

@ -40,7 +40,7 @@ Frontend → Gateway → 적절한 로빙
### 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_대화히스토리구현.md

View File

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

View File

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

View File

@ -21,7 +21,7 @@
```sql
- user_id: UUID (FK to users.id)
- workspace_id: UUID
- robing_id: VARCHAR(100) # rb8001, rb10508 등
- robeing_id: VARCHAR(100) # rb8001, rb10508 등
- role: ENUM (owner, member, guest)
```
@ -63,14 +63,14 @@ email_to_username = {
#### 현재 문제점
```typescript
// robing-api.ts - 토큰 전송 안함!
// robeing-api.ts - 토큰 전송 안함!
export async function sendMessage(text: string, userId: string = 'test_user') {
// Authorization 헤더 없음
// 모든 사용자가 'test_user'로 처리됨
}
```
### 4. Robing Gateway (포트 8100)
### 4. Robeing Gateway (포트 8100)
#### 현재 처리 방식
```python
@ -79,12 +79,12 @@ x_user_id: Optional[str] = Header(None) # X-User-Id 헤더만 확인
# JWT 토큰 검증 없음!
```
### 5. Robing Services (51124 서버)
### 5. Robeing Services (51124 서버)
#### ChromaDB 컬렉션명 생성
```python
# 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 (모든 사용자 공유!)
# 문제: endpoints.py에서 username 전달하지 않음
@ -104,11 +104,11 @@ collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{
### 1. ~~인증 토큰 미전송~~ ✅ 해결됨
- ~~Frontend가 API 호출 시 JWT 토큰을 전송하지 않음~~
- ~~`robing-api.ts`에 Authorization 헤더 없음~~
- **수정 완료**: robing-api.ts:40, chat-interface.tsx:275-279에서 토큰 전송 구현
- ~~`robeing-api.ts`에 Authorization 헤더 없음~~
- **수정 완료**: robeing-api.ts:40, chat-interface.tsx:275-279에서 토큰 전송 구현
### 2. 백엔드 토큰 미검증 ⚠️
- Robing Gateway가 JWT 토큰을 검증하지 않음
- Robeing Gateway가 JWT 토큰을 검증하지 않음
- X-User-Id 헤더만 확인 (누구나 위조 가능)
### 3. 사용자 식별 실패 및 채널별 메모리 분리 ⚠️
@ -163,7 +163,7 @@ collection_name = f"{settings.ROBING_ID}_{username if username else 'default'}_{
### 8. JWT Secret 불일치 🔑
- 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 컬렉션 네이밍
```
옵션 A: {robing_id}_{username}_episodic
옵션 A: {robeing_id}_{username}_episodic
예: rb10508_happybell80_episodic
옵션 B: {robing_id}_{user_uuid}_episodic
옵션 B: {robeing_id}_{user_uuid}_episodic
예: rb10508_a1b2c3d4_episodic
옵션 C: {workspace_id}_{robing_id}_{username}_episodic
옵션 C: {workspace_id}_{robeing_id}_{username}_episodic
예: workspace1_rb10508_happybell80_episodic
```
### 5. 토큰 저장 키 통일
```javascript
현재: 'auth_token'
대안: 'jwt_token', 'access_token', 'robing_token'
대안: 'jwt_token', 'access_token', 'robeing_token'
```
### 6. OAuth 권한 분리 전략
@ -274,7 +274,7 @@ X-Username: happybell80 // 명확한 이름
#### 1. Auth 서버 JWT 활용 (권장) 🌟
```
Frontend 로그인 → JWT(username 포함) → Gateway 검증 → Robing username 추출
Frontend 로그인 → JWT(username 포함) → Gateway 검증 → Robeing username 추출
→ Slack과 동일한 컬렉션 사용 (rb10508_test_happybell80_episodic)
```
@ -307,10 +307,10 @@ email ↔ slack_id ↔ username
```sql
-- 1. users: 기존 유지
-- 2. slack_workspaces: 단순화
-- 3. user_robings: 사용자-로빙 직접 연결
CREATE TABLE user_robings (
-- 3. user_robeings: 사용자-로빙 직접 연결
CREATE TABLE user_robeings (
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), -- rb10508_micro 등
robeing_id VARCHAR(100), -- rb10508_micro 등
is_primary BOOLEAN
);
@ -319,15 +319,15 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100), -- U0925SXQFDK
slack_team_id VARCHAR(100),
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
// 수정 완료 - robing-api.ts:38-41
// 수정 완료 - robeing-api.ts:38-41
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
@ -345,14 +345,14 @@ if authorization:
username = payload.get("sub") # 또는 username
```
#### 3. Robing Service (rb10508_micro)
#### 3. Robeing Service (rb10508_micro)
```python
# endpoints.py 수정 필요:
# 1. JWT 토큰에서 username 추출
# 2. storage 함수 호출 시 username 전달
# 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단계: 서비스 분리
#### robing-gateway (구 frontend-base 일부)
#### robeing-gateway (구 frontend-base 일부)
- **위치**: 51123 서버
- **포트**: 8000 (기존 유지)
- **역할**:
@ -66,7 +66,7 @@
- 사용자-로빙 매핑 (메모리 캐시)
- 캐시 관리: 로그인 시 추가, 로그아웃 시 제거
#### robing-control (구 frontend-base 일부)
#### robeing-control (구 frontend-base 일부)
- **위치**: 51123 서버
- **포트**: 9023
- **역할**:
@ -76,7 +76,7 @@
- 24서버 상태는 API로 조회
- 향후 간단한 관제 기능 추가 가능
#### robing-monitor (신규)
#### robeing-monitor (신규)
- **위치**: 51124 서버
- **포트**: 9024
- **역할**:
@ -94,7 +94,7 @@ sequenceDiagram
participant F as Frontend
participant G as Gateway
participant A as Auth
participant R as Robing
participant R as Robeing
U->>F: 로그인 클릭
F->>G: /api/auth/login
@ -128,7 +128,7 @@ sequenceDiagram
#### 단기 계획
- auth-server PostgreSQL을 메인 DB로 사용
- user_id를 모든 서비스에서 통일
- user_id → robing_port 매핑 테이블 관리
- user_id → robeing_port 매핑 테이블 관리
#### 장기 계획
- 로빙별 SQLite → PostgreSQL 마이그레이션
@ -136,7 +136,7 @@ sequenceDiagram
### 4단계: API 설계
#### robing-gateway API
#### robeing-gateway API
```
POST /api/auth/login # 로그인 시작
GET /api/auth/callback # OAuth 콜백
@ -144,21 +144,21 @@ POST /api/auth/logout # 로그아웃
GET /api/auth/status # 인증 상태
POST /api/chat # 채팅 (자동 라우팅)
GET /api/robing/info # 할당된 로빙 정보
GET /api/robeing/info # 할당된 로빙 정보
```
#### robing-control API
#### robeing-control API
```
GET /admin # 대시보드 UI
GET /api/monitor/overview # 전체 시스템 상태
GET /api/monitor/23 # 23서버 상태
GET /api/monitor/24 # 24서버 상태 (robing-monitor 호출)
GET /api/monitor/24 # 24서버 상태 (robeing-monitor 호출)
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/resources # 리소스 사용량
GET /api/health # 헬스체크
@ -167,13 +167,13 @@ GET /api/health # 헬스체크
## 구현 우선순위
### Phase 1
1. robing-gateway 기본 구조 생성
1. robeing-gateway 기본 구조 생성
2. 인증 흐름 연결
3. 단순 라우팅 (모든 사용자 → rb10508_test)
### Phase 2
1. robing-monitor 구현
2. robing-control 분리
1. robeing-monitor 구현
2. robeing-control 분리
3. 모니터링 대시보드 연결
### Phase 3

View File

@ -21,8 +21,8 @@
- 실제로는 1개 workspace만 사용 중
2. **중복 데이터**
- robing_id가 workspaces와 workspace_members 양쪽에 존재
- robing_url도 중복 저장
- robeing_id가 workspaces와 workspace_members 양쪽에 존재
- robeing_url도 중복 저장
3. **불명확한 개념**
- companies의 실제 필요성 불분명
@ -59,14 +59,14 @@ CREATE TABLE users (
);
-- 사용자별 로빙 할당
CREATE TABLE user_robings (
CREATE TABLE user_robeings (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL, -- rb10508_micro, rb8001 등
robing_port INTEGER,
robeing_id VARCHAR(100) NOT NULL, -- rb10508_micro, rb8001 등
robeing_port INTEGER,
is_primary BOOLEAN DEFAULT false,
created_at TIMESTAMP,
UNIQUE(user_id, robing_id)
UNIQUE(user_id, robeing_id)
);
-- Slack 사용자 매핑
@ -117,15 +117,15 @@ CREATE TABLE slack_workspaces (
);
-- 사용자-로빙 연결
CREATE TABLE user_robings (
CREATE TABLE user_robeings (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL,
robing_port INTEGER,
robing_host VARCHAR(255) DEFAULT '192.168.219.52',
robeing_id VARCHAR(100) NOT NULL,
robeing_port INTEGER,
robeing_host VARCHAR(255) DEFAULT '192.168.219.52',
is_primary BOOLEAN DEFAULT false,
created_at TIMESTAMP,
UNIQUE(user_id, robing_id)
UNIQUE(user_id, robeing_id)
);
-- Slack-시스템 사용자 매핑
@ -134,7 +134,7 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100) NOT NULL,
slack_team_id VARCHAR(100) REFERENCES slack_workspaces(team_id),
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100), -- 이 Slack 사용자가 대화할 로빙
robeing_id VARCHAR(100), -- 이 Slack 사용자가 대화할 로빙
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(slack_user_id, slack_team_id)
@ -159,8 +159,8 @@ CREATE TABLE users (
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(50) UNIQUE,
name VARCHAR(255),
robing_id VARCHAR(100), -- 기본 로빙
robing_port INTEGER,
robeing_id VARCHAR(100), -- 기본 로빙
robeing_port INTEGER,
slack_user_id VARCHAR(100), -- Slack ID (있으면)
slack_team_id VARCHAR(100), -- Slack workspace (있으면)
oauth_provider VARCHAR(50),
@ -203,18 +203,18 @@ CREATE TABLE backup_companies AS SELECT * FROM companies;
#### Phase 2: 새 테이블 생성
```sql
-- user_robings 생성 및 데이터 이전
CREATE TABLE user_robings AS
-- user_robeings 생성 및 데이터 이전
CREATE TABLE user_robeings AS
SELECT
gen_random_uuid() as id,
user_id,
robing_id,
CASE robing_id
robeing_id,
CASE robeing_id
WHEN 'rb10508_micro' THEN 10508
WHEN 'rb8001' THEN 8001
WHEN 'rb10408' THEN 10408
END as robing_port,
'192.168.219.52' as robing_host,
END as robeing_port,
'192.168.219.52' as robeing_host,
true as is_primary,
joined_at as created_at
FROM workspace_members;
@ -225,7 +225,7 @@ CREATE TABLE slack_users (
slack_user_id VARCHAR(100) NOT NULL,
slack_team_id VARCHAR(100),
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100),
robeing_id VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(slack_user_id, slack_team_id)
@ -249,7 +249,7 @@ DROP TABLE companies;
```
OAuth 로그인 → users 테이블 조회/생성
user_robings에서 기본 로빙 확인
user_robeings에서 기본 로빙 확인
로빙 서비스 연결
```
@ -258,7 +258,7 @@ user_robings에서 기본 로빙 확인
```
Slack 이벤트 수신 (slack_user_id 포함)
slack_users 테이블에서 user_id, robing_id 조회
slack_users 테이블에서 user_id, robeing_id 조회
해당 로빙으로 메시지 라우팅
@ -271,7 +271,7 @@ ChromaDB에 user_id 기반 저장
- Slack 사용자 매핑 기능 구현 가능
- 기존 테이블 영향 없음
2. **단계적 마이그레이션**: user_robings 테이블
2. **단계적 마이그레이션**: user_robeings 테이블
- workspace_members 데이터 이전
- 테스트 후 기존 테이블 제거
@ -287,13 +287,13 @@ ChromaDB에 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 사용자 조회
```python
# 신규
SELECT u.*, sr.robing_id
SELECT u.*, sr.robeing_id
FROM slack_users sr
JOIN users u ON sr.user_id = u.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 테이블 설계**
- `robing_id` 필드 포함이 좋습니다
- `robeing_id` 필드 포함이 좋습니다
- Slack 사용자별로 다른 로빙 할당 가능
- UNIQUE 제약 조건 적절
@ -350,21 +350,21 @@ WHERE sr.slack_user_id = ? AND sr.slack_team_id = ?
### 8.2 추가 제안사항
#### user_robings 테이블에 추가 필드
#### user_robeings 테이블에 추가 필드
```sql
CREATE TABLE user_robings (
CREATE TABLE user_robeings (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100) NOT NULL,
robing_port INTEGER,
robing_host VARCHAR(255) DEFAULT '192.168.219.52',
robeing_id VARCHAR(100) NOT NULL,
robeing_port INTEGER,
robeing_host VARCHAR(255) DEFAULT '192.168.219.52',
is_primary BOOLEAN DEFAULT false,
last_used_at TIMESTAMP, -- 마지막 사용 시간
usage_count INTEGER DEFAULT 0, -- 사용 횟수
status VARCHAR(20) DEFAULT 'active', -- active/inactive/suspended
settings JSON, -- 로빙별 개인 설정
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_team_id VARCHAR(100),
user_id UUID REFERENCES users(id),
robing_id VARCHAR(100),
robeing_id VARCHAR(100),
display_name VARCHAR(255), -- Slack 표시 이름
slack_timezone VARCHAR(50), -- 사용자 시간대
last_message_at TIMESTAMP, -- 마지막 메시지 시간
@ -393,25 +393,25 @@ CREATE INDEX idx_slack_users_lookup
ON slack_users(slack_user_id, slack_team_id);
-- 주 로빙 빠른 조회
CREATE INDEX idx_user_robings_primary
ON user_robings(user_id, is_primary);
CREATE INDEX idx_user_robeings_primary
ON user_robeings(user_id, is_primary);
-- 활성 상태 필터링
CREATE INDEX idx_user_robings_status
ON user_robings(status, user_id);
CREATE INDEX idx_user_robeings_status
ON user_robeings(status, user_id);
```
### 8.3 주의사항
1. **데이터 일관성**
- `robing_id`가 여러 테이블에 분산
- 중앙 관리 방법 필요 (enum 또는 별도 robings 테이블)
- `robeing_id`가 여러 테이블에 분산
- 중앙 관리 방법 필요 (enum 또는 별도 robeings 테이블)
2. **포트 하드코딩 문제**
```sql
-- 하드코딩 대신 설정 테이블 권장
CREATE TABLE robing_configs (
robing_id VARCHAR(100) PRIMARY KEY,
CREATE TABLE robeing_configs (
robeing_id VARCHAR(100) PRIMARY KEY,
port INTEGER NOT NULL,
host VARCHAR(255),
is_active BOOLEAN DEFAULT true
@ -419,7 +419,7 @@ ON user_robings(status, user_id);
```
3. **ChromaDB 컬렉션명 규칙**
- `{robing_id}_{user_id}_episodic` 형식 통일
- `{robeing_id}_{user_id}_episodic` 형식 통일
- slack_users의 user_id 사용 필수
### 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`
```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":
# 매핑 없는 사용자는 공용 컬렉션 사용
return f"{robing_id}_{memory_type}"
return f"{robeing_id}_{memory_type}"
else:
# 사용자별 전용 컬렉션
return f"{robing_id}_{user_id[:8]}_{memory_type}"
return f"{robeing_id}_{user_id[:8]}_{memory_type}"
```
### 2.3 Brain 모듈 수정
@ -80,7 +80,7 @@ async def think_functional(
# 사용자별 컬렉션 사용
collection_name = get_user_collection_name(
settings.ROBING_ID,
settings.ROBEING_ID,
system_user_id,
"episodic"
)
@ -112,7 +112,7 @@ async def get_user_mapping(
sum.user_id,
u.username,
u.email,
wm.robing_id
wm.robeing_id
FROM slack_user_mapping sum
JOIN users u ON sum.user_id = u.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"]),
"username": result["username"],
"email": result["email"],
"robing_id": result["robing_id"] or "rb10508_micro"
"robeing_id": result["robeing_id"] or "rb10508_micro"
}
@router.post("/mapping")
@ -188,23 +188,23 @@ async def route_slack_message(
if resp.status_code == 200:
mapping = resp.json()
robing_id = mapping["robing_id"]
robeing_id = mapping["robeing_id"]
user_id = mapping["user_id"]
else:
# 매핑 없으면 기본 로빙 사용
robing_id = "rb10508_micro"
robeing_id = "rb10508_micro"
user_id = "default_user"
# 해당 로빙으로 전달
robing_urls = {
robeing_urls = {
"rb8001": "http://192.168.219.52:8001",
"rb10508_micro": "http://192.168.219.52:10508",
"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:
return {"error": "Unknown robing"}
return {"error": "Unknown robeing"}
# X-System-User-Id 헤더 추가
headers = {"X-System-User-Id": user_id}

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
## 아키텍처 구조
```
robing-brain/
robeing-brain/
├── core/ # 존재적 삼각형 (120MB)
│ ├── memory/ # 기억 모듈 (50MB)
│ │ ├── entropy.py # 정보엔트로피 계산
@ -44,10 +44,10 @@ robing-brain/
## 1. 핵심 브레인 (내장 기능)
```python
class RobingBrain:
class RobeingBrain:
"""경량화된 로빙 브레인 - 사고의 핵심만 포함"""
def __init__(self, robing_id: str):
def __init__(self, robeing_id: str):
# 존재적 삼각형
self.memory = MemoryCore() # 50MB
self.emotion = EmotionCore() # 35MB
@ -59,7 +59,7 @@ class RobingBrain:
self.items = ItemInventory() # 10MB
# 정체성
self.identity = Identity(robing_id) # 20MB
self.identity = Identity(robeing_id) # 20MB
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
{
"robing_id": "rb10408",
"robeing_id": "rb10408",
"item_id": "gmail_api"
}
```

View File

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

View File

@ -24,8 +24,8 @@ sudo apt-get update
sudo apt-get install timescaledb-2-postgresql-15
# 데이터베이스 생성 및 확장 활성화
sudo -u postgres createdb robing_metrics
sudo -u postgres psql robing_metrics -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
sudo -u postgres createdb robeing_metrics
sudo -u postgres psql robeing_metrics -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"
```
### 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/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 토큰 업데이트

View File

@ -82,19 +82,19 @@ SELECT time_bucket(INTERVAL %s, time) as time_bucket, ...
### 1. PostgreSQL에서 직접 테스트
```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. 데이터 존재 확인
```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개 데이터 존재
```
### 3. 최신 데이터 확인
```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분 전까지 정상 수집됨
```

View File

@ -73,7 +73,7 @@ cp -r rb10508_test/app/stats rb8001/app/
cp -r rb10508_test/app/utils rb8001/app/
# 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/gemini_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`
- 포트 매핑: `8001:8000`
- `network_mode: host` (서버 DB 접근용)
- 환경변수에 `ROBING_ID=8001` 추가
- 환경변수에 `ROBEING_ID=8001` 추가
- PostgreSQL 컨테이너 제거 (서버 DB 사용)
## 주요 교훈
@ -105,7 +105,7 @@ rb8001/docker-compose.yml 수정:
### 3. 서버 환경 이해
- Docker는 애플리케이션만 (DB는 서버에 직접 설치)
- `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)
- 로그 파일 구분 없음
@ -42,14 +42,14 @@ ln -s /mnt/hdd/robeing-logs/rb10508_test /home/admin/rb10508_test/logs
### 2. 코드 수정
#### config.py - ROBING_ID 기반 설정
#### config.py - ROBEING_ID 기반 설정
```python
class Settings(BaseSettings):
# Robeing Configuration
ROBING_ID: str = "rb8001" # 또는 "rb10508_test"
ROBEING_ID: str = "rb8001" # 또는 "rb10508_test"
# 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 - 기존 경로 유지
@ -61,14 +61,14 @@ self.client = chromadb.PersistentClient(
)
```
#### main.py - 로그 파일명에 ROBING_ID 포함
#### main.py - 로그 파일명에 ROBEING_ID 포함
```python
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
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 분리 방식
- **초기 고려안**: 컬렉션명에 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로 인식 못함
**해결**:
@ -77,7 +77,7 @@ location /rb10408/ {
**개선 방향 결정**:
1. 가장 심플한 해결책: 메모리 딕셔너리 사용
2. robing_brain.py에만 코드 추가
2. robeing_brain.py에만 코드 추가
3. Redis 같은 외부 의존성 추가 안 함
**장단점 분석**:

View File

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

View File

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

View File

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

View File

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

View File

@ -123,7 +123,7 @@ ls -la /mnt/51123logs/rb10508_test/
## 오전 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에서 경로 정상 해석됨)
3. 컨테이너 재시작 시도
@ -136,7 +136,7 @@ Error response from daemon: error while creating mount source path '/mnt/51123lo
Docker가 볼륨 마운트 시 상위 디렉토리를 새로 만들려고 했지만, 해당 경로는 이미 SSHFS 마운트 포인트로 존재해서 충돌 발생.
### 롤백 작업
1. .env 파일에서 LOG_ROOT, ROBING_NAME 환경변수 제거
1. .env 파일에서 LOG_ROOT, ROBEING_NAME 환경변수 제거
2. 기존 ./logs:/code/logs:rw 방식으로 복원
3. 컨테이너 정상 시작됨

View File

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

View File

@ -8,7 +8,7 @@ ChromaDB에서 각 로빙의 메모리를 저장할 때, 컬렉션 이름 생성
```python
# /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` 등), 컬렉션 이름이 중복될 가능성이 없었습니다.
@ -18,7 +18,7 @@ self.collection_name = f"robing_{robing_id}_memories"
```python
def generate_id(self, content: str, timestamp: str) -> str:
"""고유 ID 생성"""
unique_string = f"{self.robing_id}_{content}_{timestamp}"
unique_string = f"{self.robeing_id}_{content}_{timestamp}"
return hashlib.md5(unique_string.encode()).hexdigest()
```

View File

@ -19,7 +19,7 @@
### 1. Gemini API 모드로 전환 (권장)
```python
# /home/heejae/robing-llm-service/.env
# /home/heejae/robeing-llm-service/.env
GEMINI_USE_CLI=false # true -> false 변경
```
@ -35,7 +35,7 @@ LLM 서비스에는 필수 필드만 전송:
- `message`
- `user_id`
- `channel`
- `robing_id`
- `robeing_id`
### 3. 서비스 상태 확인
```bash
@ -47,12 +47,12 @@ curl http://localhost:8003/health
```
## 기술적 세부사항
- LLM 서비스 위치: `/home/heejae/robing-llm-service/`
- LLM 서비스 위치: `/home/heejae/robeing-llm-service/`
- 포트: 8003
- 기본 모델: gemini-flash
- CLI 타임아웃: 15초 (느린 원인)
## 관련 파일
- `/home/heejae/robing-llm-service/app/llm/gemini_handler.py`
- `/home/heejae/robing-llm-service/app/core/config.py`
- `/home/heejae/robeing-llm-service/app/llm/gemini_handler.py`
- `/home/heejae/robeing-llm-service/app/core/config.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 파일 설정
```bash
# /home/heejae/robing-state-service/.env
# /home/heejae/robeing-state-service/.env
DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db
SERVICE_NAME=state
PORT=8002
@ -44,5 +44,5 @@ psql postgresql://robeings:robeings@localhost:5433/main_db -c "SELECT 1"
systemd 서비스로 SSH 터널을 관리하거나, autossh 사용을 고려할 수 있습니다.
## 관련 파일
- `/home/heejae/robing-state-service/.env`
- `/home/heejae/robing-state-service/app/core/config.py`
- `/home/heejae/robeing-state-service/.env`
- `/home/heejae/robeing-state-service/app/core/config.py`

View File

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

View File

@ -150,7 +150,7 @@ volumes:
2. **ChromaDB 컬렉션 생성 시 embedding_function 전달**:
```python
self.episodic = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_episodic",
name=f"{settings.ROBEING_ID}_episodic",
metadata={"type": "episodic"},
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:
try:
self.conversation_cache = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_conversation_cache",
name=f"{settings.ROBEING_ID}_conversation_cache",
metadata={
"type": "conversation_cache",
"version": "1.0",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,12 +58,12 @@ x_user_id: str = Depends(get_verified_user)
# 수정 전 - JWT_SECRET_KEY 없음
environment:
- DATABASE_URL=${DATABASE_URL}
- DEFAULT_ROBING_HOST=${DEFAULT_ROBING_HOST}
- DEFAULT_ROBEING_HOST=${DEFAULT_ROBEING_HOST}
# 수정 후
environment:
- DATABASE_URL=${DATABASE_URL}
- DEFAULT_ROBING_HOST=${DEFAULT_ROBING_HOST}
- DEFAULT_ROBEING_HOST=${DEFAULT_ROBEING_HOST}
- JWT_SECRET_KEY=${JWT_SECRET_KEY} # 추가!
```
@ -86,7 +86,7 @@ environment:
**원인**: Frontend 환경변수가 잘못됨
```javascript
// .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/docker-compose.yml
- 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. robing-api.ts 확장
### 1. robeing-api.ts 확장
- `getConfig()`: 백엔드 설정 가져오기
- `getMessages()`: 페이지네이션 지원 메시지 조회
@ -146,7 +146,7 @@ def resolve_username(user_id: str) -> str:
**2025년 8월 9일의 잘못된 결정이 문제의 시작**:
```sql
-- 잘못된 예: 테스트용 UUID 하드코딩 (250809_happybell80_robing-gateway구현.md)
-- 잘못된 예: 테스트용 UUID 하드코딩 (250809_happybell80_robeing-gateway구현.md)
INSERT INTO users (id, email, name) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid, 'goeun2dc@gmail.com', '김종태'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '0914eagle@gmail.com', '전희재'),
@ -249,7 +249,7 @@ async def get_chat_history(
```javascript
// 변경 전: /api/messages
// 변경 후: /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. **연결 확인**
```bash
docker logs robing-gateway --tail 50
docker logs robeing-gateway --tail 50
# "Database connection established successfully" 확인
# "All required tables found in main_db" 확인
```
@ -345,7 +345,7 @@ docker logs robing-gateway --tail 50
### 근본 원인
```javascript
// src/services/robing-api.ts
// src/services/robeing-api.ts
const userId = localStorage.getItem('user_id') || 'default_user'; // 항상 default_user
// src/contexts/auth-context.tsx
@ -436,14 +436,14 @@ search_memories_fn=lambda query, user_id: search_memories(
### 문제
- ChromaDB에 role: "assistant"로 저장
- 프론트엔드는 sender: "robing" 기대
- 프론트엔드는 sender: "robeing" 기대
- 모든 메시지가 sender: "user"로 표시
### 해결
```python
# endpoints.py 수정
role = metadata.get('role', 'user')
sender = 'robing' if role == 'assistant' else 'user'
sender = 'robeing' if role == 'assistant' else 'user'
messages.append({
"sender": sender, # role을 sender로 변환
})

View File

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

View File

@ -113,7 +113,7 @@ const logger = {
1. `auth-context.tsx` - 6개 console 문 제거
2. `login-dialog.tsx` - 2개 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 문 제거
6. `confetti.tsx` - 3개 console 문 제거

View File

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

View File

@ -55,7 +55,7 @@ before_time = to_utc_aware(before)
- 서버 .env 파일에서 환경변수 읽도록 변경
```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

View File

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

View File

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

View File

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