2D 픽셀아트 AI 에이전트 사무실 시각화 기능 설계. MVP: StockAgent + MusicAgent, 텔레그램 양방향, Canvas 렌더링. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
445 lines
19 KiB
Markdown
445 lines
19 KiB
Markdown
# Agent Office - AI 에이전트 사무실 시각화 설계
|
||
|
||
## 개요
|
||
|
||
Lab 하위에 2D 픽셀아트 스타일의 가상 사무실을 구현하여, AI 에이전트들이 실시간으로 작업하는 모습을 게임처럼 시각화하고 상호작용하는 페이지.
|
||
|
||
### 핵심 컨셉
|
||
- **게임 같은 사무실**: 2D 픽셀아트 오픈 오피스에 에이전트 캐릭터들이 배치
|
||
- **실제 작업 수행**: 에이전트들이 기존 백엔드 서비스 API를 호출하여 실제 결과물 생성
|
||
- **직접 지시**: 에이전트 클릭 → 채팅/명령 패널로 지시, 승인 요청 시 알림 표시
|
||
- **텔레그램 양방향**: 알림 발송 + 인라인 버튼으로 승인/거절/수정
|
||
- **아이들 행동**: 장시간 명령 없으면 휴게실에서 커피, 졸기, 동료 잡담 등
|
||
|
||
### MVP 범위
|
||
- **에이전트 2개**: StockAgent (주식 뉴스/주가 알람), MusicAgent (작곡 파이프라인)
|
||
- **사무실**: 단일 오픈 오피스 (향후 방/층 확장 가능)
|
||
- **텔레그램**: 양방향 (알림 + 인라인 버튼 승인)
|
||
|
||
---
|
||
|
||
## 1. 아키텍처
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Frontend (React) │
|
||
│ │
|
||
│ ┌──────────────┐ ┌─────────────────────────┐ │
|
||
│ │ OfficeCanvas │ │ React Overlay │ │
|
||
│ │ (Canvas 2D) │ │ - ChatPanel │ │
|
||
│ │ - 타일맵 렌더 │ │ - AgentStatus │ │
|
||
│ │ - 스프라이트 │ │ - TaskHistory │ │
|
||
│ │ - 클릭 히트맵 │ │ - ApprovalDialog │ │
|
||
│ └──────────────┘ └─────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ useAgentManager (상태 + WebSocket) │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
└──────────────────┬──────────────────────────────┘
|
||
│ WebSocket + REST
|
||
┌──────────────────▼──────────────────────────────┐
|
||
│ Backend: agent-office (새 서비스, 포트 18900) │
|
||
│ │
|
||
│ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │
|
||
│ │ Scheduler │ │ Agent FSM │ │ Telegram Bot │ │
|
||
│ │(APScheduler)│ │ (상태머신) │ │ (양방향) │ │
|
||
│ └────────────┘ └────────────┘ └──────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ Service Proxy (기존 서비스 API 호출) │ │
|
||
│ │ stock-lab / music-lab 등 │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 핵심 결정
|
||
- **agent-office**: 새 백엔드 서비스 (포트 18900). 기존 서비스는 수정하지 않음
|
||
- **Service Proxy 패턴**: agent-office가 기존 서비스 API를 HTTP 호출
|
||
- **WebSocket**: 에이전트 상태 변화를 실시간 전달
|
||
- **Canvas + React 오버레이 하이브리드**: 게임 렌더링은 Canvas, UI 패널은 React DOM
|
||
|
||
---
|
||
|
||
## 2. 에이전트 상태 머신 (FSM)
|
||
|
||
### 상태 전이
|
||
|
||
```
|
||
┌──────┐ 스케줄/지시 ┌──────────┐ 완료 ┌──────────┐
|
||
│ idle │ ──────────────→ │ working │ ───────→ │ reporting│
|
||
└──┬───┘ └────┬─────┘ └────┬─────┘
|
||
│ │ 승인 필요 │
|
||
│ 장시간 idle ▼ │ 결과 전달 후
|
||
│ ┌───────────┐ │
|
||
▼ │ waiting │ │
|
||
┌──────┐ │ (승인대기) │ │
|
||
│ break│ └───────────┘ │
|
||
│ (휴식)│ │
|
||
└──┬───┘◄───────────────────────────────────────────┘
|
||
│ 새 작업 발생
|
||
└──────────→ idle
|
||
```
|
||
|
||
### 상태별 시각화
|
||
|
||
| 상태 | 캐릭터 행동 | 위치 | 오버레이 |
|
||
|------|------------|------|---------|
|
||
| `idle` | 모니터 보며 대기 애니메이션 | 자기 데스크 | 없음 |
|
||
| `working` | 타이핑 애니메이션, 모니터에 진행 표시 | 자기 데스크 | 작업명 말풍선 |
|
||
| `waiting` | 살짝 좌우 흔들림 | 자기 데스크 | `❗` 아이콘 (클릭 유도) |
|
||
| `reporting` | 결과물 들고 걸어감 | 데스크 → 회의 테이블 | 결과 요약 말풍선 |
|
||
| `break` | 커피 마시기/졸기/산책/잡담 | 휴게실/복도 | `☕`/`💤` 아이콘 |
|
||
|
||
### 아이들 행동 규칙
|
||
- idle 상태 5분 경과 → 50% 확률로 break 전환
|
||
- break 지속: 1~3분 랜덤 → idle 복귀
|
||
- break 중 에이전트끼리 근처에 있으면 잡담 애니메이션
|
||
- 새 작업 발생 시 즉시 break 종료 → idle → working
|
||
|
||
### 승인 흐름별 분류
|
||
|
||
| 에이전트 | 자동 실행 | 승인 필요 |
|
||
|---------|----------|----------|
|
||
| Stock | 뉴스 요약, 주가 알람 | - |
|
||
| Music | - | 작곡 (프롬프트 확인 후) |
|
||
| Lotto (향후) | 통계 분석, 추천번호 | 구매 관련 |
|
||
| Blog (향후) | - | 키워드 제시 후 글 생성 |
|
||
| Realestate (향후) | 공고 수집, 매칭 | - |
|
||
| Claude AI (향후) | - | 직접 지시 + 승인 |
|
||
|
||
---
|
||
|
||
## 3. 사무실 맵 & 렌더링
|
||
|
||
### 타일맵 구조 (MVP: 단일 오픈 오피스)
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||
│ │Stock│ │Music│ │Claude│ │ (빈) │ │
|
||
│ │Desk │ │Desk │ │Desk │ │향후용│ │
|
||
│ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||
│ │
|
||
│ ┌───────────┐ │
|
||
│ │ 회의 테이블 │ │
|
||
│ │ (보고구역) │ │
|
||
│ └───────────┘ │
|
||
│ │
|
||
│ ┌──────────┐ ┌─────────────────┐ │
|
||
│ │ 휴게실 │ │ CEO 데스크 (나) │ │
|
||
│ │ coffee │ │ │ │
|
||
│ └──────────┘ └─────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### 렌더링 계층 (아래→위)
|
||
1. **바닥 타일**: 카펫, 나무 바닥
|
||
2. **가구**: 데스크, 의자, 소파, 화분, 커피머신
|
||
3. **캐릭터**: 에이전트 스프라이트 (상태별 애니메이션)
|
||
4. **오버레이**: 말풍선, 상태 아이콘, 이름표
|
||
|
||
### 스프라이트 에셋
|
||
- 무료 픽셀아트 에셋팩 활용 (타일셋, 가구)
|
||
- 에이전트 캐릭터: 기본 인물 스프라이트 + 액세서리로 구분
|
||
- Stock: 넥타이 + 차트 모니터
|
||
- Music: 헤드폰 + 음표 이펙트
|
||
- Claude: 보라색 톤 + AI 아이콘
|
||
- 스프라이트시트: 4방향 × 4프레임 (idle, walk, work, break)
|
||
|
||
### Canvas 렌더링 엔진
|
||
- **게임 루프**: `requestAnimationFrame` 기반, 60fps 타겟
|
||
- **카메라**: 고정 뷰 (MVP), 향후 줌/팬 추가 가능
|
||
- **클릭 히트맵**: 캐릭터 바운딩 박스 체크 → 클릭 시 React 이벤트 발생
|
||
- **이동**: 웨이포인트 기반 lerp (데스크↔회의실↔휴게실)
|
||
|
||
---
|
||
|
||
## 4. 백엔드: agent-office 서비스
|
||
|
||
### 파일 구조
|
||
|
||
```
|
||
agent-office/
|
||
├── app/
|
||
│ ├── main.py # FastAPI + WebSocket + lifespan
|
||
│ ├── db.py # SQLite (agent_tasks, agent_logs, agent_config)
|
||
│ ├── config.py # 환경변수, 서비스 URL 설정
|
||
│ ├── scheduler.py # APScheduler 스케줄 관리
|
||
│ ├── telegram_bot.py # Telegram Bot API 양방향
|
||
│ ├── websocket_manager.py # WebSocket 연결 관리 + 브로드캐스트
|
||
│ ├── service_proxy.py # 기존 서비스 API 호출 래퍼
|
||
│ ├── agents/
|
||
│ │ ├── base.py # BaseAgent (FSM, 공통 로직)
|
||
│ │ ├── stock.py # StockAgent
|
||
│ │ └── music.py # MusicAgent
|
||
│ └── models.py # Pydantic 모델
|
||
├── Dockerfile
|
||
└── requirements.txt
|
||
```
|
||
|
||
### DB 테이블 (agent_office.db)
|
||
|
||
**agent_config**
|
||
| 컬럼 | 타입 | 설명 |
|
||
|------|------|------|
|
||
| agent_id | TEXT PK | 에이전트 식별자 (stock, music, ...) |
|
||
| display_name | TEXT | 표시명 ("주식 트레이더") |
|
||
| enabled | BOOLEAN | 활성 상태 |
|
||
| schedule_config | TEXT (JSON) | 스케줄 설정 |
|
||
| custom_config | TEXT (JSON) | 에이전트별 커스텀 설정 (감시 종목 등) |
|
||
| created_at | TEXT | 생성 시각 |
|
||
| updated_at | TEXT | 수정 시각 |
|
||
|
||
**agent_tasks**
|
||
| 컬럼 | 타입 | 설명 |
|
||
|------|------|------|
|
||
| id | TEXT PK (UUID) | 작업 ID |
|
||
| agent_id | TEXT FK | 에이전트 |
|
||
| task_type | TEXT | 작업 유형 (news_summary, price_alert, compose, ...) |
|
||
| status | TEXT | pending / approved / working / succeeded / failed |
|
||
| input_data | TEXT (JSON) | 입력 파라미터 |
|
||
| result_data | TEXT (JSON) | 결과 데이터 |
|
||
| requires_approval | BOOLEAN | 승인 필요 여부 |
|
||
| approved_at | TEXT | 승인 시각 |
|
||
| approved_via | TEXT | 승인 경로 (web / telegram) |
|
||
| created_at | TEXT | 생성 시각 |
|
||
| completed_at | TEXT | 완료 시각 |
|
||
|
||
**agent_logs**
|
||
| 컬럼 | 타입 | 설명 |
|
||
|------|------|------|
|
||
| id | INTEGER PK | 자동 증가 |
|
||
| agent_id | TEXT FK | 에이전트 |
|
||
| task_id | TEXT FK | 관련 작업 (nullable) |
|
||
| level | TEXT | info / warn / error |
|
||
| message | TEXT | 로그 메시지 |
|
||
| created_at | TEXT | 시각 |
|
||
|
||
**telegram_state**
|
||
| 컬럼 | 타입 | 설명 |
|
||
|------|------|------|
|
||
| callback_id | TEXT PK | 텔레그램 콜백 ID |
|
||
| task_id | TEXT FK | 매핑된 작업 |
|
||
| agent_id | TEXT FK | 매핑된 에이전트 |
|
||
| action | TEXT | approve / reject / modify |
|
||
| responded | BOOLEAN | 응답 완료 여부 |
|
||
| created_at | TEXT | 생성 시각 |
|
||
|
||
### BaseAgent 인터페이스
|
||
|
||
```python
|
||
class BaseAgent:
|
||
agent_id: str
|
||
state: str # idle, working, waiting, reporting, break
|
||
|
||
async def on_schedule(self) -> None:
|
||
"""스케줄러에 의해 호출. 자동 작업 실행."""
|
||
|
||
async def on_command(self, command: str, params: dict) -> dict:
|
||
"""사용자 직접 지시 처리."""
|
||
|
||
async def on_approval(self, task_id: str, approved: bool, feedback: str) -> None:
|
||
"""승인/거절 콜백."""
|
||
|
||
async def get_status(self) -> dict:
|
||
"""현재 상태 + 최근 작업 요약."""
|
||
```
|
||
|
||
### MVP 에이전트 상세
|
||
|
||
**StockAgent:**
|
||
- 스케줄: 매일 08:00 `on_schedule()` → `stock-lab GET /api/stock/news` 호출
|
||
- AI 요약: 뉴스 데이터를 Ollama(192.168.45.59)로 요약 생성
|
||
- 텔레그램 전송: 요약 결과를 포맷팅하여 발송 (자동, 승인 불필요)
|
||
- 주가 알람: `agent_config.custom_config`에 감시 종목/조건 저장, 주기적 체크
|
||
- 상태 전이: idle → working(뉴스 수집) → reporting(텔레그램 전송) → idle
|
||
|
||
**MusicAgent:**
|
||
- 트리거: 사용자 웹/텔레그램 지시 → `on_command()`
|
||
- 프롬프트 확인: 사용자 입력 프롬프트를 텔레그램으로 전송 + 인라인 버튼
|
||
- 승인 시: `music-lab POST /api/music/generate` 호출
|
||
- 상태 폴링: `music-lab GET /api/music/status/{task_id}` → 완료까지 반복
|
||
- 결과 알림: 생성된 음악 URL을 텔레그램 + 웹에 전달
|
||
- 상태 전이: idle → waiting(프롬프트 승인 대기) → working(생성 중) → reporting(결과 전달) → idle
|
||
|
||
---
|
||
|
||
## 5. 텔레그램 봇
|
||
|
||
### 구성
|
||
- **Telegram Bot API** + **Webhook 수신** (NAS에서)
|
||
- agent-office 서비스 내부에 통합 (별도 프로세스 아님)
|
||
- Nginx: `/api/agent-office/telegram/webhook` → `agent-office:8000`
|
||
|
||
### 환경변수
|
||
- `TELEGRAM_BOT_TOKEN`: Bot Father에서 발급
|
||
- `TELEGRAM_CHAT_ID`: 사용자 채팅 ID (1:1 봇)
|
||
- `TELEGRAM_WEBHOOK_URL`: Webhook 수신 URL (NAS 외부 접근 가능 URL)
|
||
|
||
### 메시지 포맷
|
||
|
||
**자동 알림 (뉴스 요약):**
|
||
```
|
||
📈 [주식 에이전트] 아침 뉴스 요약
|
||
━━━━━━━━━━━━━━━━
|
||
• 삼성전자: 반도체 수출 호조...
|
||
• 코스피: 외인 순매수 전환...
|
||
• 미국 CPI 발표 예정...
|
||
|
||
📊 관심종목 현황
|
||
삼성전자 82,500원 (+2.1%)
|
||
AAPL $185.20 (+1.2%)
|
||
```
|
||
|
||
**승인 요청 (작곡):**
|
||
```
|
||
🎵 [음악 에이전트] 작곡 요청
|
||
━━━━━━━━━━━━━━━━
|
||
프롬프트: "Lo-fi hip hop, rainy day, piano"
|
||
스타일: Chill, Ambient
|
||
모델: V5.5
|
||
|
||
[✅ 승인] [❌ 거절] [✏️ 수정]
|
||
```
|
||
|
||
**주가 알람:**
|
||
```
|
||
🚨 [주식 에이전트] 주가 알림
|
||
━━━━━━━━━━━━━━━━
|
||
삼성전자 82,500원
|
||
조건: 82,000원 이상 → 도달!
|
||
현재 등락: +2.1%
|
||
```
|
||
|
||
### 양방향 흐름
|
||
1. 에이전트 → `telegram_bot.send_message()` → 텔레그램
|
||
2. 사용자 → 인라인 버튼 클릭 or 텍스트 입력
|
||
3. 텔레그램 → Webhook POST → `telegram_bot.handle_webhook()`
|
||
4. `handle_webhook()` → `telegram_state` 조회 → 에이전트 `on_approval()` 호출
|
||
5. 에이전트 FSM 상태 전이 → WebSocket 브로드캐스트 → 프론트엔드 반영
|
||
|
||
---
|
||
|
||
## 6. 프론트엔드 구조
|
||
|
||
### 파일 구조
|
||
|
||
```
|
||
src/pages/agent-office/
|
||
├── AgentOffice.jsx # 메인 페이지 (Canvas + Overlay 컨테이너)
|
||
├── AgentOffice.css # 스타일
|
||
├── canvas/
|
||
│ ├── OfficeRenderer.js # Canvas 렌더링 엔진 (게임루프)
|
||
│ ├── SpriteSheet.js # 스프라이트시트 로더 + 프레임 애니메이션
|
||
│ ├── TileMap.js # 타일맵 데이터 + 렌더링
|
||
│ └── AgentSprite.js # 에이전트 캐릭터 (위치, 상태, 이동, 애니메이션)
|
||
├── components/
|
||
│ ├── ChatPanel.jsx # 에이전트 채팅/명령 패널
|
||
│ ├── AgentBubble.jsx # 말풍선/상태 아이콘 오버레이
|
||
│ ├── TaskHistory.jsx # 작업 이력 사이드패널
|
||
│ └── ApprovalDialog.jsx # 승인 요청 다이얼로그
|
||
├── hooks/
|
||
│ ├── useAgentManager.js # WebSocket + 에이전트 상태 관리
|
||
│ └── useOfficeCanvas.js # Canvas 초기화 + 이벤트 바인딩
|
||
└── assets/
|
||
├── tileset.png # 사무실 타일셋 (16x16 or 32x32)
|
||
├── agents.png # 에이전트 스프라이트시트
|
||
└── office-map.json # 타일맵 데이터
|
||
```
|
||
|
||
### WebSocket 프로토콜
|
||
|
||
**서버 → 클라이언트:**
|
||
```json
|
||
{"type": "agent_state", "agent": "stock", "state": "working", "detail": "뉴스 수집 중..."}
|
||
{"type": "agent_state", "agent": "music", "state": "waiting", "detail": "프롬프트 승인 대기", "task_id": "abc-123"}
|
||
{"type": "task_complete", "agent": "stock", "task_id": "...", "result": {"summary": "..."}}
|
||
{"type": "agent_move", "agent": "stock", "target": "break_room"}
|
||
```
|
||
|
||
**클라이언트 → 서버:**
|
||
```json
|
||
{"type": "command", "agent": "music", "action": "compose", "params": {"prompt": "...", "style": "..."}}
|
||
{"type": "approval", "agent": "music", "task_id": "abc-123", "approved": true}
|
||
{"type": "query", "agent": "stock", "action": "status"}
|
||
```
|
||
|
||
### ChatPanel 기능
|
||
- 에이전트별 채팅 히스토리 표시
|
||
- 텍스트 입력 + 빠른 액션 버튼
|
||
- 승인 대기 중인 작업 강조 표시
|
||
- 최근 작업 결과 인라인 표시
|
||
|
||
---
|
||
|
||
## 7. 인프라 변경
|
||
|
||
### Docker Compose 추가
|
||
|
||
```yaml
|
||
agent-office:
|
||
build: ./agent-office
|
||
container_name: agent-office
|
||
ports:
|
||
- "18900:8000"
|
||
volumes:
|
||
- ${RUNTIME_PATH}/data:/app/data
|
||
environment:
|
||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
||
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL}
|
||
- STOCK_LAB_URL=http://stock-lab:8000
|
||
- MUSIC_LAB_URL=http://music-lab:8000
|
||
depends_on:
|
||
- stock-lab
|
||
- music-lab
|
||
restart: unless-stopped
|
||
```
|
||
|
||
### Nginx 라우팅 추가
|
||
|
||
```nginx
|
||
location /api/agent-office/ {
|
||
proxy_pass http://agent-office:8000;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade"; # WebSocket 지원
|
||
}
|
||
```
|
||
|
||
### 라우팅 (React Router)
|
||
|
||
```javascript
|
||
// routes.jsx
|
||
{ path: 'agent-office', lazy: () => import('./pages/agent-office/AgentOffice') }
|
||
```
|
||
|
||
Lab 페이지(EffectLab.jsx)의 LAB_ITEMS에 Agent Office 항목 추가.
|
||
|
||
---
|
||
|
||
## 8. 향후 확장 (Phase 2+)
|
||
|
||
| 단계 | 내용 |
|
||
|------|------|
|
||
| Phase 2 | LottoAgent, BlogAgent, RealestateAgent 추가 |
|
||
| Phase 3 | Claude AI Agent (자연어 복합 지시) |
|
||
| Phase 4 | 방/층 확장 (부서별 공간 분리) |
|
||
| Phase 5 | 에이전트 간 협업 시각화 (회의 테이블에서 토론) |
|
||
| Phase 6 | 에이전트 커스텀 (이름, 외형, 성격 설정) |
|
||
|
||
---
|
||
|
||
## 9. 기술 스택 요약
|
||
|
||
| 레이어 | 기술 |
|
||
|--------|------|
|
||
| 사무실 렌더링 | HTML5 Canvas 2D (커스텀 엔진) |
|
||
| 프론트엔드 | React 18 + Vite |
|
||
| 실시간 통신 | WebSocket (FastAPI) |
|
||
| 백엔드 | FastAPI (Python 3.12) |
|
||
| DB | SQLite (agent_office.db) |
|
||
| 스케줄러 | APScheduler |
|
||
| 메시징 | Telegram Bot API (Webhook) |
|
||
| 서비스 연동 | HTTP Proxy (기존 서비스 API 호출) |
|