# 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 호출) |