Update robeing terminology and main_db references
This commit is contained in:
parent
35c456571e
commit
b2f8ae637f
@ -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())
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
```
|
||||
@ -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 (개별)
|
||||
|
||||
@ -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')
|
||||
```
|
||||
|
||||
### 보안 강화
|
||||
|
||||
@ -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() # 임베딩은 공유 서비스 사용
|
||||
)
|
||||
# 결과: 기억은 각자, 임베딩 엔진은 공유
|
||||
|
||||
@ -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 등)
|
||||
|
||||
@ -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로 되어있어 권한 조정 필요
|
||||
|
||||
---
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 상승!
|
||||
|
||||
@ -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 # 실제 마진
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 제거 - 기본 임베딩 사용
|
||||
)
|
||||
```
|
||||
|
||||
@ -213,7 +213,7 @@ React 앱이 브라우저에서 실행되면:
|
||||
|
||||
```javascript
|
||||
// 동적 통신 구현
|
||||
const RobingGame = () => {
|
||||
const RobeingGame = () => {
|
||||
useEffect(() => {
|
||||
// WebSocket 연결
|
||||
const ws = new WebSocket('wss://ro-being.com/ws/robbing/realtime');
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (개별)
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
│ └─────────────────────────────┘ │
|
||||
│ ┌─────────────────────────────┐ │
|
||||
│ │ 공통 DB │ │
|
||||
│ │ users, robings, stats │ │
|
||||
│ │ users, robeings, stats │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -279,7 +279,7 @@ processed = pipe(
|
||||
|
||||
#### 현재 적용 예시: RobeingBrain
|
||||
```python
|
||||
# /app/services/robing_brain.py
|
||||
# /app/services/robeing_brain.py
|
||||
class RobeingBrain:
|
||||
"""순수 함수와 부작용 분리 오케스트레이터"""
|
||||
|
||||
|
||||
@ -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 (개별)
|
||||
|
||||
@ -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 확인
|
||||
↓
|
||||
해당 로빙으로 라우팅
|
||||
↓
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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주)
|
||||
|
||||
@ -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
|
||||
@ -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 성능 지표
|
||||
|
||||
@ -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. 기존 데이터 처리
|
||||
|
||||
@ -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}"
|
||||
```
|
||||
|
||||
## 단계별 구현 계획
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 개선된 실행 순서
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -183,7 +183,7 @@ Message: 이메일을 보내기 위해 다음 정보가 필요합니다...
|
||||
"message": "종태님한테 회의 일정 메일 보내줘",
|
||||
"user_id": "U091UNVE41M",
|
||||
"channel": "C123456",
|
||||
"robing_id": "rb8001"
|
||||
"robeing_id": "rb8001"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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": "친근하고 캐주얼하게"
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
@ -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에 의해 자동 생성되었습니다.*
|
||||
@ -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 저장소 설정
|
||||
|
||||
@ -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 토큰 업데이트
|
||||
|
||||
@ -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분 전까지 정상 수집됨
|
||||
```
|
||||
|
||||
|
||||
@ -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와 일치)
|
||||
|
||||
## 다음 단계
|
||||
|
||||
|
||||
@ -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`)
|
||||
- **최종 결정**: 디렉토리 분리로 해결
|
||||
- **이유**: 코드 수정 최소화, 기존 데이터 유지, 마이그레이션 불필요
|
||||
|
||||
|
||||
@ -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 같은 외부 의존성 추가 안 함
|
||||
|
||||
**장단점 분석**:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
└── current/
|
||||
├── config/
|
||||
├── gitea/
|
||||
└── robing/
|
||||
└── robeing/
|
||||
```
|
||||
|
||||
## 오후 11시 10분
|
||||
|
||||
@ -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), -- 할당된 로빙
|
||||
...
|
||||
);
|
||||
```
|
||||
|
||||
@ -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 수정**:
|
||||
|
||||
@ -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. 컨테이너 정상 시작됨
|
||||
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
|
||||
@ -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()
|
||||
```
|
||||
|
||||
|
||||
@ -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`
|
||||
@ -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`
|
||||
@ -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
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"}
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
**Before (OOP):**
|
||||
```python
|
||||
class RobingBrain:
|
||||
class RobeingBrain:
|
||||
def __init__(self):
|
||||
self.memory = MemoryCore()
|
||||
self.emotion = EmotionCore()
|
||||
|
||||
@ -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. **개인화된 응답**:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
|
||||
@ -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 환경변수 설정 및 공유 필요
|
||||
|
||||
|
||||
@ -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 통해 자동 배포 진행 중
|
||||
@ -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
|
||||
@ -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로 변환
|
||||
})
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 문 제거
|
||||
|
||||
|
||||
@ -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분 - 배포 완료
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 레벨 설정**
|
||||
|
||||
@ -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 필드로 유연성 확보
|
||||
|
||||
@ -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. 하드코딩 제거의 필요성
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user