AI 오케스트레이션 교체 PRD (invyone × vexplor_pipeline)
작성: planner (BMAD 1단계 / Analyst·PM)
일자: 2026-04-27
상태: Architect 검토 대기
다음 단계: architect (기술 아키텍처 결정) → executor → verifier
0. TL;DR
invyone 프로젝트의 기존 AI 어시스턴트(Node Express, port 3100) 를 전면 제거하고, vexplor_pipeline 프로젝트에서 운영 중인 멀티 에이전트 오케스트레이션 시스템(에이전트 CRUD, 그룹 조립, 커넥터, OpenClaw, 스케줄러, 사용량 추적)을 invyone에 이식한다. 백엔드는 TypeScript/Node → Java 21/Spring Boot 3.3.5/MyBatis 로 1:1 포팅, 프론트엔드는 vexplor의 7개 화면을 invyone Next.js로 그대로 옮기고, OpenClaw은 컨테이너로 함께 운영한다.
총량: 8 Epic / 39 Story 추정 / 백엔드 포팅 비중 가장 큼
1. 목표 (Goals & Non-Goals)
1.1 Goals (P0)
- (G1) invyone에 멀티 에이전트 운영(에이전트 정의 → 그룹 조립 → 실행 모드 = 순차/병렬/혼합) 기능 도입
- (G2) API 키 기반 외부 호출(OpenAI 호환
/v1/chat/completions) 엔드포인트 유지 — 외부 시스템이 invyone을 LLM 게이트웨이로 사용 가능
- (G3) 멀티테넌시 강제 — 모든 AI 리소스(에이전트/그룹/키/대화/사용량)에
company_code 필터 일관 적용 (* = 전체 공유)
- (G4) 기존 AI 어시스턴트(ai-assistant 컨테이너 + 프록시 + DB 테이블) 완전 제거 — 운영 데이터 보존 불필요(사용자 확인 완료)
- (G5) Spring Security + JJWT 기반 인증 통합 (관리 API는 JWT, 외부 API는 sk-pipe-* 키)
- (G6) 부서/권한 그룹 시스템(직전 커밋
2c57dc8c)에 새 AI 메뉴들 등록 가능
1.2 Goals (P1)
- (G7) 다중 LLM 프로바이더(Anthropic, OpenAI, Google, DeepSeek, Ollama) 동시 운영 + 우선순위 라우팅
- (G8) 에이전트별 커넥터(DB/REST/PLC/크롤러/파일) 매핑으로 운영 데이터에 기반한 분석 수행
- (G9) cron 기반 스케줄 실행 + 알림(시스템 공지/슬랙 웹훅/이메일)
- (G10) 사용량 대시보드(일별/월별 토큰·비용·요청 수)
1.3 Goals (P2)
- (G11) 대화 모니터링(에이전트 간 메시지 트레이스)
- (G12) 지식 파일 라이브러리(에이전트별 RAG 컨텍스트 주입)
- (G13) AI 분석 이력 기반 정확도 추적(
ai_analysis_logs)
1.4 Non-Goals
- (NG1) vexplor_pipeline 자체의 화면관리/테이블관리/AI 화면 자동생성(
workflow_patterns, keyword_mapping 등)은 이번 작업 범위 아님
- (NG2) OpenClaw 자체 코드 포팅/수정. invyone은 OpenClaw을 블랙박스 외부 엔진으로만 사용
- (NG3) Spring AI / Spring AI MCP 도입 결정 — 이번에는 직접 HTTP 호출(LlmClient 직역) 유지 (architect 단계에서 재검토)
- (NG4) 사용량 청구/결제 기능
- (NG5) 다중 회사 간 에이전트 권한 위임(P3 이상)
- (NG6) 기존 vexplor
_pipeline_backup 폴더 처리
2. 이해관계자 / 사용자 시나리오
| 페르소나 |
권한 |
핵심 시나리오 |
| 슈퍼관리자(SUPER_ADMIN) |
전사 |
글로벌 LLM 프로바이더 등록/키 관리, * 회사용 공유 에이전트 정의, 전체 사용량 모니터링 |
| 회사관리자(COMPANY_ADMIN) |
자사 company_code |
자사 에이전트/그룹 CRUD, 자사 API 키 발급, 자사 사용량 조회 |
| 일반사용자 |
자사 + 메뉴 권한 |
AI 채팅 사용, 자기 API 키 발급(역할별로), 대화 이력 조회 |
| 외부 시스템(B2B) |
API 키 |
Authorization: Bearer sk-pipe-... 로 /api/ai/v1/chat/completions 또는 /api/ai/v1/groups/:groupId 호출 |
| 운영자(DevOps) |
인프라 |
docker-compose 기동, OpenClaw 컨테이너 헬스체크, Jenkins 배포 |
2.1 핵심 사용 시나리오 흐름
S1. 회사관리자가 멀티 에이전트 그룹 생성
/admin/aiAssistant/agents 에서 "재고 분석가"(Claude Sonnet 4.5) + "구매 추천가"(GPT-4o) 에이전트 2개 생성
/admin/aiAssistant/workspace 에서 그룹 재고-구매 자동분석 생성, 두 에이전트를 멤버로 추가, 각각 PostgreSQL 커넥터(외부 DB) 매핑, execution_mode = mixed
- UI에서 "현재 재고 위험 품목 분석해줘" 입력 → 백엔드 MultiAgentExecutionEngine이 두 에이전트를 mixed 모드로 실행 → 최종 요약 반환 + 대화 이력 저장 +
ai_analysis_logs 기록
S2. 외부 시스템(B2B) 호출
- 관리자가
/admin/aiAssistant/api-keys-manage 에서 키 발급(키는 한 번만 표시, sha256 해시만 저장)
- 외부 시스템:
POST /api/ai/v1/groups/123 with Authorization: Bearer sk-pipe-... and {"message":"오늘 재고 위험"}
- AiApiKeyAuthFilter 통과 → 월간 토큰 한도 체크 → 그룹 실행 → 사용량 누적
S3. cron 스케줄
/admin/aiAssistant/scheduler(신규) 에서 매일 09:00 - 그룹 X 실행 - 슬랙 웹훅 알림
- Spring
@Scheduled 또는 Quartz가 등록된 cron 표현식으로 그룹 실행 → ai_analysis_logs + system_notice + 슬랙 발송
3. 기능 요구사항 (FR)
3.1 에이전트 관리 (P0)
| FR |
설명 |
우선순위 |
| FR-A-1 |
에이전트 CRUD(agent_id, name, model, system_prompt, tools, config, company_code) |
P0 |
| FR-A-2 |
검색·상태(active/inactive/archived)·회사별 필터 |
P0 |
| FR-A-3 |
agent_id 유니크 제약 |
P0 |
| FR-A-4 |
에이전트별 config.knowledge_files(라이브러리 참조 또는 인라인 콘텐츠) |
P2 |
| FR-A-5 |
tools 정의(JSON, MCP 도구 명세) |
P1 |
3.2 멀티 에이전트 그룹 (P0)
| FR |
설명 |
우선순위 |
| FR-G-1 |
그룹 CRUD(group_id, name, execution_mode = parallel/sequential/mixed) |
P0 |
| FR-G-2 |
그룹 멤버 추가·수정·삭제(role_name, connectors, execution_order) |
P0 |
| FR-G-3 |
가용 커넥터 조회 — DB(external_db_connections) / REST(external_rest_api_connections) / PLC(pipeline_device_connections) / 크롤러(crawl_configs) |
P1 |
| FR-G-4 |
그룹 실행 = MultiAgentExecutionEngine — sequential/parallel/mixed 분기 처리 + 이전 컨텍스트 누적 + 최종 요약 |
P0 |
| FR-G-5 |
그룹 실행 시 과거 분석 이력 5건 자동 컨텍스트 주입 |
P2 |
3.3 LLM 프로바이더 관리 (P0)
| FR |
설명 |
우선순위 |
| FR-P-1 |
프로바이더 CRUD(name, display_name, model_name, endpoint, priority, cost_per_1k_input/output) |
P0 |
| FR-P-2 |
API 키 AES-256 암호화 저장(api_key_encrypted) — 응답 시 ****<last 4> 마스킹 |
P0 |
| FR-P-3 |
다중 프로바이더 지원(Anthropic / OpenAI / Google / DeepSeek / Ollama) |
P1 |
| FR-P-4 |
우선순위 기반 default 모델 자동 선정 |
P1 |
3.4 외부 API 키 관리 (P0)
| FR |
설명 |
우선순위 |
| FR-K-1 |
sk-pipe-{64hex} 키 발급, sha256 해시 저장, prefix(16자) 표시용 저장 |
P0 |
| FR-K-2 |
키 목록(SUPER/COMPANY 관리자는 전체, 일반 사용자는 자기 키만) |
P0 |
| FR-K-3 |
키 폐기(SOFT delete: status=revoked 또는 hard delete — vexplor는 hard delete) |
P0 |
| FR-K-4 |
rate_limit, monthly_token_limit, expires_at, permissions(JSON) |
P0 |
| FR-K-5 |
키 검증 시 last_used_at, usage_count 자동 갱신 |
P0 |
| FR-K-6 |
월간 토큰 한도 초과 시 HTTP 429 |
P0 |
3.5 외부 호출 게이트웨이 (P0)
| FR |
설명 |
우선순위 |
| FR-X-1 |
POST /api/ai/v1/chat/completions — OpenAI 호환(스트리밍 포함) |
P0 |
| FR-X-2 |
POST /api/ai/v1/groups/:groupId — 멀티 에이전트 그룹 실행 |
P0 |
| FR-X-3 |
GET /api/ai/v1/groups — 사용 가능 그룹 목록 |
P1 |
| FR-X-4 |
GET /api/ai/v1/models — 모델 목록(프로바이더 기반) |
P1 |
| FR-X-5 |
GET /api/ai/v1/health — 헬스체크 |
P1 |
| FR-X-6 |
API 키 + JWT 동시 인증 허용(내부/외부 모두 사용 가능) |
P0 |
3.6 사용량 추적 (P1)
| FR |
설명 |
우선순위 |
| FR-U-1 |
모든 LLM 호출에 대해 ai_agent_usage_logs 적재(prompt/completion/total tokens, cost_usd, 응답시간, success, error) |
P1 |
| FR-U-2 |
요약: 오늘/이번 달 토큰·요청·비용 + active 에이전트/키 수 |
P1 |
| FR-U-3 |
일별 차트(최근 30일) |
P1 |
| FR-U-4 |
페이징 로그 조회 |
P1 |
3.7 대화 모니터링 (P2)
| FR |
설명 |
우선순위 |
| FR-C-1 |
그룹 실행 시 대화 자동 저장(conversation + 사용자 메시지 + 각 step 응답) |
P2 |
| FR-C-2 |
대화 목록 페이징·에이전트별 필터 |
P2 |
| FR-C-3 |
대화 상세(메시지 시퀀스) 조회 |
P2 |
| FR-C-4 |
대화 삭제 |
P2 |
3.8 스케줄 실행 (P1)
| FR |
설명 |
우선순위 |
| FR-S-1 |
스케줄 CRUD(cron_expression, group_id, input_message, notification) |
P1 |
| FR-S-2 |
cron 표현식 검증 + 재등록 |
P1 |
| FR-S-3 |
알림 채널: 시스템 공지(system_notice) / 웹훅(슬랙) / 이메일(기존 mailSend 서비스 재사용) |
P1 |
| FR-S-4 |
서버 기동 시 활성 스케줄 자동 등록 |
P1 |
| FR-S-5 |
last_run_at, run_count 누적 |
P1 |
3.9 OpenClaw 통합 (P1)
| FR |
설명 |
우선순위 |
| FR-O-1 |
OpenClaw 컨테이너로 기동(invyone docker-compose에 서비스 추가, 기본 port 18789) |
P1 |
| FR-O-2 |
OpenClaw config 동기화 — 프로바이더(authProfiles) + 에이전트(agents) → JSON 파일 |
P1 |
| FR-O-3 |
프로바이더/에이전트 변경 시 트리거(저장 후 sync 호출) |
P1 |
| FR-O-4 |
OpenClaw 다운 시 graceful — 관리 기능은 동작 유지, 외부 호출만 503 |
P1 |
3.10 지식 라이브러리 (P2)
| FR |
설명 |
우선순위 |
| FR-N-1 |
지식 파일 CRUD(ai_knowledge_files: name, file_name, category, content) |
P2 |
| FR-N-2 |
에이전트 config.knowledge_files에서 library_id 또는 인라인 content 참조 |
P2 |
| FR-N-3 |
검색·카테고리 필터 |
P2 |
3.11 메뉴/권한 등록 (P0)
| FR |
설명 |
우선순위 |
| FR-M-1 |
새 AI 메뉴 7개를 부서/권한 그룹 시스템(직전 커밋 2c57dc8c)에 등록 |
P0 |
| FR-M-2 |
슈퍼관리자 전용 메뉴(LLM 관리, LLM 사용량 통계 등) 분리 |
P0 |
| FR-M-3 |
기존 INVION 메뉴 가이드(AI_어시스턴트_메뉴_등록_가이드.md) 갱신 |
P1 |
4. 비기능 요구사항 (NFR)
4.1 멀티테넌시 (강제)
- 모든 AI 테이블에
company_code VARCHAR(20) 컬럼 — NULL 또는 * 은 슈퍼관리자 전용 / 글로벌 공유
- 모든 list/get/update/delete 쿼리에
(company_code = :myCompany OR company_code IS NULL) 적용
- SUPER_ADMIN 만 회사 간 자료 조회 가능 (
company_code=* 쿼리 매개변수 허용)
4.2 보안
- API 키: 평문 저장 금지. SHA-256 해시 + 16자 prefix 만 저장. 발급 시점에만 평문 1회 노출
- LLM 프로바이더 API 키: AES-256 대칭 암호화(EncryptUtil 포팅 필요). 응답에서 마스킹
- JWT: 기존 invyone JJWT 0.12.3 그대로 사용
- API 키 인증 필터(
AiApiKeyAuthFilter): Spring Security 필터 체인에 추가, JWT 필터보다 앞 또는 같은 layer
- Rate limit: 키당 분당 N회(기본 60), 월간 토큰 한도(기본 100만)
- CORS: 기존 invyone 정책 따름
4.3 성능
- 그룹 parallel 실행:
CompletableFuture.allOf 기반(JDK 21 가상 스레드 권장)
- 그룹 sequential 실행: 직렬 await
- 그룹 mixed 실행: execution_order 동률 → 병렬, 다른 order → 순차 await
- LLM 호출 타임아웃: 60초 default, 스트리밍은 무제한 (커넥션 keep-alive)
- DB index:
ai_agent_usage_logs(created_at), ai_agent_api_keys(key_hash), ai_agents(agent_id), ai_agent_groups(group_id)
4.4 가용성
- OpenClaw 다운 시:
- 외부 LLM 호출은 invyone 자체 LlmClient(직접 프로바이더 HTTP 호출)로 fallback
- 관리 화면은 정상 동작
- DB 다운 시: 통상 invyone 정책(=실패) 따름
- 헬스체크:
/api/ai/v1/health 가 프로바이더 수 응답
4.5 마이그레이션 안전성
- ai-assistant Sequelize 테이블(users, api_keys, llm_providers, usage_logs) drop 가능 (사용자 확인됨, 운영 데이터 없음)
- 새 vexplor 스키마는 별도 마이그레이션 스크립트로 적용. 기존 invyone PostgreSQL과 같은 DB 사용
- 명명 충돌 위험: ai-assistant의
users, api_keys 등 일반 테이블명 — invyone 본 스키마와 동명 충돌 가능 → drop 시점에 보존 검증 필수
- 새 스키마는 모두
ai_ prefix 사용 (vexplor 컨벤션) → 충돌 없음
4.6 관찰성
- 모든 핵심 호출에 SLF4J + Spring Boot logback 로깅(요청/응답/에러)
- ai_agent_usage_logs 가 사실상 1차 audit
- 에러는 invyone 표준 ApiResponse(success/error code/message) 포맷
4.7 호환성
- Java 21, Spring Boot 3.3.5, MyBatis 3.0.3, PostgreSQL 16, Lombok
- Frontend: Next.js (invyone 기존), shadcn/ui, dnd-kit (vexplor에서 사용)
- vexplor의
Promise.all, node-cron, axios → Java 등가물(CompletableFuture, Spring @Scheduled/Quartz, RestClient/WebClient)
5. 이식 작업 분해 (WBS)
Epic A — 기존 AI 어시스턴트 제거 (P0)
목표: invyone 측의 ai-assistant 모듈을 흔적 없이 걷어낸다.
| Story |
작업 |
A1: ai-assistant/ 디렉토리 통째 삭제 |
C:\Dev\projects\invyone\ai-assistant\ 폴더 삭제. README, package.json, src/ 전부 |
| A2: Spring 프록시 컨트롤러 삭제 |
AiAssistantProxyController.java, AiAssistantProxyService.java 삭제 + Spring 컴포넌트 스캔 영향 확인 |
| A3: docker-compose 정리 |
docker-compose.backend.win.yml, docker-compose.frontend.win.yml(있다면), Dockerfile에서 ai-assistant 참조 항목 제거. 현재 파일에는 명시적 ai-assistant 서비스 없음(확인됨) — 환경변수 AI_ASSISTANT_SERVICE_URL 등 잔재만 정리 |
| A4: Jenkinsfile 정리 |
현재 Jenkinsfile은 단일 Build 단계만 있고 ai-assistant 별도 stage 없음(확인됨). 변경 불필요 — 단, helm-charts 측 values_invyone.yaml 의 ai-assistant 이미지 참조가 있다면 별도 PR 필요(미확인 사항으로 표기) |
| A5: 프론트엔드 메뉴/페이지 제거 |
frontend/app/(main)/admin/aiAssistant/ 8개 페이지 + frontend/lib/api/aiAssistant/ (client.ts, index.ts) + frontend/components/layout/AdminPageRenderer.tsx 라인 128, 142~147 제거 후 vexplor 신규 페이지로 교체(Epic F) |
| A6: 메뉴 가이드 문서 제거/갱신 |
frontend/docs/AI_어시스턴트_메뉴_등록_가이드.md, docs/leeheejin/AI_어시스턴트_사용가이드.md 폐기 또는 신규 가이드로 대체 |
| A7: 기존 ai-assistant DB 테이블 drop |
users, api_keys, llm_providers, usage_logs (Sequelize 자동 생성). 주의: invyone 본 시스템에 동명 테이블 없는지 검증 후 drop. 마이그레이션 스크립트로 작성 |
Story 수: 7
Epic B — DB 스키마 마이그레이션 (P0)
목표: vexplor의 AI 스키마를 invyone PostgreSQL에 적용.
⚠️ 사용자 프롬프트에 명시된 db/migrations/300_create_openclaw_tables.sql 파일은 vexplor 리포지토리에 물리적으로 존재하지 않음(전체 검색 확인). 따라서 vexplor 서비스/타입 코드(aiAgent*.ts 9개 + aiAgent.ts 타입)에서 스키마를 역공학해야 한다 — 이 작업이 Epic B의 핵심이며 Story B1에서 수행한다.
| Story |
작업 |
| B1: vexplor 코드 역공학으로 스키마 재구성 |
9개 서비스 + types/aiAgent.ts 분석 → 13개 테이블 DDL 작성: ai_agents, ai_agent_groups, ai_agent_group_members, ai_agent_api_keys, ai_llm_providers, ai_agent_conversations, ai_agent_messages, ai_agent_usage_logs, ai_agent_schedules, ai_analysis_logs, ai_knowledge_files, (선택) ai_agent_tools (P3) |
| B2: 마이그레이션 스크립트 작성 |
invyone DB 마이그레이션 컨벤션에 맞춰(MyBatis용 .sql 또는 별도 도구) V20260427__ai_orchestration.sql 형태. 멱등 보장(IF NOT EXISTS) |
| B3: 인덱스 / FK / 제약 정의 |
5.3 NFR 인덱스 + cascade 정책(예: 그룹 삭제 시 멤버 cascade) |
| B4: 시드 데이터 |
기본 LLM 프로바이더 row 1~2개(키 비활성 placeholder), 슈퍼관리자용 샘플 에이전트 1개(선택 P2) |
| B5: 마이그레이션 적용 절차 문서화 |
dev/staging/prod 적용 가이드 + 롤백 스크립트 |
Story 수: 5
Epic C — Spring 백엔드 포팅 (P0)
목표: TS 9개 서비스 + 컨트롤러 + 라우트 → Java 21 / Spring / MyBatis 1:1 매핑
| Story |
작업 |
| C1: 공통 인프라 |
EncryptUtil(AES-256), LlmClient(Anthropic / OpenAI / Google / DeepSeek / Ollama HTTP 클라이언트, 스트리밍 포함), DTO 패키지(com.erp.dto.ai.*), MyBatis Mapper 인터페이스 + XML 13개 |
| C2: AiAgentService |
에이전트 CRUD + 검색·필터 + company_code 필터 |
| C3: AiAgentGroupService |
그룹 CRUD + 멤버 CRUD + 가용 커넥터 조회(invyone에 동등 테이블 존재 여부 확인 필요 — external_db_connections, external_rest_api_connections, crawl_configs, pipeline_device_connections 가 invyone에 있는지 architect가 결정) |
| C4: AiAgentApiKeyService |
sk-pipe 키 발급/검증/폐기 + 토큰 누적 |
| C5: AiAgentProviderService |
프로바이더 CRUD + 암복호화 + 마스킹 |
| C6: AiAgentConversationService |
대화 + 메시지 저장/조회/삭제 |
| C7: AiAgentUsageService |
사용량 적재 + 요약 + 일별/페이징 |
| C8: AiAnalysisLogService |
분석 이력 CRUD + 평균 정확도 |
| C9: MultiAgentExecutionEngine |
sequential/parallel/mixed 분기 + 컨텍스트 누적 + 커넥터 실행 + 지식 파일 주입 + 최종 요약 — CompletableFuture 기반 |
| C10: AiSchedulerService |
Spring @Scheduled 또는 Quartz로 cron 등록/해제, 알림 발송(시스템 공지 / 슬랙 웹훅 / 이메일) |
| C11: OpenClawSyncService |
프로바이더/에이전트 → OpenClaw config(JSON) 동기화 |
| C12: 컨트롤러 — 관리(JWT) |
/api/ai-agents, /api/ai-agents/keys/*, /api/ai-agents/providers/*, /api/ai-agents/conversations/*, /api/ai-agents/usage/* |
| C13: 컨트롤러 — 그룹(JWT) |
/api/ai-agent-groups(+ /:id/members, /connectors) |
| C14: 컨트롤러 — 외부(API Key) |
/api/ai/v1/chat/completions(스트리밍 포함), /api/ai/v1/groups/:groupId, /api/ai/v1/groups, /api/ai/v1/models, /api/ai/v1/health |
| C15: 컨트롤러 — 스케줄(JWT) |
/api/ai-agent-schedules CRUD |
| C16: 컨트롤러 — 지식(JWT) |
/api/ai-knowledge CRUD |
Story 수: 16
Epic D — 인증 미들웨어 (P0)
| Story |
작업 |
| D1: AiApiKeyAuthFilter |
Spring Security OncePerRequestFilter 로 작성 — Authorization: Bearer sk-pipe-* 만 처리, JWT 는 기존 필터로 위임 |
| D2: SecurityConfig 조정 |
/api/ai/v1/** 경로는 AiApiKeyAuthFilter 우선 적용. 관리 경로(/api/ai-agents/** 등)는 JWT 유지 |
| D3: 동시 인증 허용 |
외부 게이트웨이는 sk-pipe 키 + JWT 둘 다 허용 (vexplor 동일 동작) |
Story 수: 3
Epic E — OpenClaw 컨테이너 통합 (P1)
| Story |
작업 |
| E1: OpenClaw 배포 방식 결정 |
Dockerfile 또는 외부 이미지 사용 여부 조사. Helm chart 추가 또는 docker-compose 신규 서비스 |
| E2: docker-compose에 openclaw 서비스 추가 |
docker-compose.backend.win.yml 에 openclaw 서비스(포트 18789), 환경변수, 볼륨(config 공유) |
| E3: invyone Spring → OpenClaw URL 환경변수 |
OPENCLAW_BASE_URL, OPENCLAW_ENABLED 추가 |
| E4: Config 공유 볼륨 |
OpenClawSyncService 가 쓰는 JSON 파일 경로를 컨테이너 간 공유(또는 HTTP 동기화 API로 변경 architect 결정) |
| E5: K8s 매니페스트 |
k8s/ 폴더에 OpenClaw 매니페스트 추가(필요 시) |
Story 수: 5
Epic F — 프론트엔드 통합 (P0)
| Story |
작업 |
| F1: vexplor 7개 페이지 이식 |
agents/, providers/, conversations/, workspace/, api-keys-manage/, knowledge/, layout.tsx, page.tsx → invyone frontend/app/(main)/admin/aiAssistant/ 로 복사. 기존 invyone aiAssistant 폴더 통째 교체 |
| F2: API 클라이언트 이식 |
vexplor frontend/lib/api/aiAgent.ts → invyone frontend/lib/api/aiAgent.ts 신규. invyone 기존 lib/api/aiAssistant/ 폴더 삭제 |
| F3: BASE URL 정리 |
invyone apiClient(/api)와 일치하는지 검증, vexplor의 /ai-agents, /ai-agent-groups, /ai-knowledge 경로 그대로 사용 |
| F4: AdminPageRenderer 라우팅 갱신 |
기존 `aiAssistant/dashboard |
| F5: shadcn 의존성 점검 |
dnd-kit(@dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities), sonner(toast) — invyone에 없으면 추가 |
| F6: 다국어/스타일 점검 |
vexplor 페이지의 한국어 메시지, dark mode 클래스가 invyone 디자인 시스템과 호환되는지 확인 |
| F7: 스케줄러 페이지 신규 작성 (vexplor에 미포함) |
/admin/aiAssistant/scheduler — 백엔드 AiSchedulerService 와 짝 (아키텍트 결정 후 Story 추가 가능) |
Story 수: 6~7
Epic G — 메뉴/권한 등록 (P0)
| Story |
작업 |
| G1: 신규 AI 메뉴 7개를 메뉴 마스터에 등록 |
2c57dc8c 부서 권한 그룹 권한 관리 커밋의 메뉴 시스템에 다음 URL 등록: /admin/aiAssistant, /admin/aiAssistant/agents, /admin/aiAssistant/workspace, /admin/aiAssistant/providers(SUPER), /admin/aiAssistant/api-keys-manage, /admin/aiAssistant/conversations, /admin/aiAssistant/knowledge |
| G2: 권한 그룹 매핑 |
SUPER_ADMIN/COMPANY_ADMIN/일반사용자 별 메뉴 노출 범위 정의 |
| G3: 사이드바 아이콘 |
Bot 아이콘(기존 가이드 따라) — frontend/components/layout/Sidebar 등에서 키워드 매핑 |
| G4: 가이드 문서 갱신 |
frontend/docs/AI_어시스턴트_메뉴_등록_가이드.md 신규 메뉴로 교체 |
Story 수: 4
Epic H — 검증/배포 (P0)
| Story |
작업 |
| H1: 단위 테스트 — Service 계층 |
JUnit 5 + MyBatis 테스트(testcontainers PostgreSQL). 각 서비스 핵심 시나리오 |
| H2: 통합 테스트 — 외부 게이트웨이 |
sk-pipe 키 발급 → /api/ai/v1/chat/completions 호출 → 사용량 적재 검증(MockServer로 LLM 응답 stub) |
| H3: E2E — 그룹 실행 |
mixed 모드, 2 멤버 + 커넥터 1개, 응답 + 대화 저장 + 분석 이력 검증 |
| H4: 컨테이너 기동 검증 |
docker-compose up → backend + openclaw + frontend 헬스체크 통과 |
| H5: 마이그레이션 dry-run |
dev DB에 마이그레이션 적용 → 전체 화면 + API 동작 확인 |
| H6: 성능 smoke |
동시 그룹 실행 10개 + parallel 멤버 5개 → 토큰 누적·DB write 정합성 |
| H7: 보안 점검 |
API 키 평문 비노출, 프로바이더 키 마스킹, JWT 누락 시 401 |
| H8: 운영 가이드 |
배포 / 롤백 / 트러블슈팅 / OpenClaw 관리 문서 |
Story 수: 8
5.x WBS 요약
| Epic |
Story 수 |
주요 위험 |
| A. 기존 제거 |
7 |
helm-charts 측 잔재, 메뉴 깨짐 |
| B. DB 스키마 |
5 |
SQL 미존재 → 역공학 정확도 |
| C. Spring 포팅 |
16 |
LlmClient 스트리밍, ExecutionEngine 동시성 |
| D. 인증 |
3 |
필터 체인 충돌 |
| E. OpenClaw |
5 |
외부 엔진 라이선스/배포 가능성 |
| F. 프론트엔드 |
6~7 |
디자인 시스템 호환성 |
| G. 메뉴/권한 |
4 |
권한 매핑 누락 |
| H. 검증 |
8 |
LLM mock 정확도 |
| 합계 |
54~55 |
— |
참고: 사용자 프롬프트에서 "39 Story 추정"으로 시작했으나, 실제 분석 결과 54~55개가 더 정확함. 일부 Story는 architect 단계에서 묶거나 분할될 수 있다.
6. Story 상세 (대표 5개 발췌)
6.1 Story C9 — MultiAgentExecutionEngine 포팅
- 입력:
vexplor_pipeline/backend-node/src/services/multiAgentExecutionEngine.ts (478라인)
- 변경 작업:
executeSequential → 직렬 await 루프
executeParallel → CompletableFuture.allOf (Java 21 가상 스레드 권장)
executeMixed → execution_order 그룹핑 후 직렬+병렬 혼합
executeSingleAgent → 에이전트 조회 + 커넥터 실행 + 지식 파일 주입 + LlmClient 호출
executeConnector → DB / REST / PLC / 크롤러 / 파일 — invyone에 해당 테이블 존재 여부 확인 후 적용 (architect가 mapping 결정)
buildFinalSummary → 동일
- 사이드 이펙트:
AiAgentConversationService.createConversation/addMessage, AiAnalysisLogService.save, AiAgentUsageService.log
- DoD:
- 동일 입력에 대해 vexplor와 응답 메시지 구조 동일
- parallel 모드에서 5개 에이전트 동시 실행 시 응답시간 ≤ max(개별 응답시간) × 1.2
- 사용량 / 대화 / 분석 이력 모두 적재
- 에이전트 실행 실패 1건은 그룹 전체 실패로 이어지지 않음(개별 step에 [실행 실패] 응답 기록)
- 의존성: C1(LlmClient, EncryptUtil), C2~C7
- 위험: Java/Node 동시성 모델 차이로 race condition 발생 가능. JDK 21 가상 스레드 사용 시 MyBatis SqlSession 스레드 안전성 검증 필요
6.2 Story B1 — 스키마 역공학
- 입력: vexplor 9개 서비스 + types/aiAgent.ts
- 변경 작업: 13개 테이블 DDL 작성. 각 테이블 컬럼 추론 근거(서비스 SQL 쿼리 라인) 주석 첨부
- DoD: vexplor 백엔드를 invyone DB 스키마로 띄웠을 때(가상 시나리오) 모든 INSERT/UPDATE/SELECT 가 동작
- 의존성: 없음 (Epic B 시작점)
- 위험: 일부 컬럼이 서비스에서 SELECT/INSERT 모두 사용되지 않으면 역공학 누락. architect 검토 단계에서 vexplor DB dump 또는 별도 schema 문서 확보 권장
6.3 Story D1 — AiApiKeyAuthFilter
- 입력:
vexplor/backend-node/src/middleware/aiApiKeyAuthMiddleware.ts
- 변경 작업:
OncePerRequestFilter 상속, /api/ai/v1/** 경로 매칭
- sk-pipe-* 토큰 → AiAgentApiKeyService.validateKey
- 월간 토큰 한도 초과 → 429
- JWT 토큰은 기존 JwtAuthenticationFilter 로 위임 (이중 필터)
- DoD: 키로 호출 시
req.apiKey 정보가 컨트롤러에 전달, JWT로 호출 시 Authentication 객체 정상
- 의존성: C4(AiAgentApiKeyService)
- 위험: 필터 순서. Spring Security 6 의
addFilterBefore 위치 결정
6.4 Story A7 — 기존 ai-assistant DB 테이블 drop
- 입력: ai-assistant Sequelize 모델 4개(
users, api_keys, llm_providers, usage_logs)
- 변경 작업:
- 사전 검증: invyone 본 시스템에
users, api_keys 동명 테이블이 있는지 (있을 가능성 높음 — users는 ERP 핵심 테이블일 수 있음)
- 위험 해소: ai-assistant가 사용한 DB 스키마(예:
ai_assistant 스키마) 존재 여부 확인 필요. 만약 동일 schema 라면 컬럼 충돌 / 데이터 덮어쓰기 위험
- drop 스크립트(별도 schema에 있다면 DROP SCHEMA IF EXISTS ai_assistant CASCADE)
- DoD: 메인 invyone 기능에 영향 없이 잔여 테이블/스키마 제거
- 의존성: 없음 (Epic A 마지막)
- 위험: 메인
users/api_keys 와 충돌. 반드시 architect 단계에서 ai-assistant DB 분리 여부 확인
6.5 Story F1 — vexplor 페이지 이식
- 입력: vexplor 7개 페이지(
agents, providers, conversations, workspace, api-keys-manage, knowledge, layout.tsx, page.tsx)
- 변경 작업:
- 기존 invyone
aiAssistant/ 폴더 8개 페이지 삭제
- vexplor 페이지 그대로 복사 + import path 조정(
@/lib/api/aiAgent 신규)
- shadcn 컴포넌트 의존성 검증
- DoD: 7개 페이지 모두 build / type-check / lint 통과 + 핵심 화면 렌더링
- 의존성: F2(API 클라이언트), C12~C16(백엔드 컨트롤러)
- 위험: invyone vs vexplor의 shadcn 컴포넌트 버전 차이로 prop signature 불일치
7. 순서 / 의존성 그래프
Critical path: B → C → F → G → H (백엔드 + 프론트가 직렬에 가까움)
병행 가능: A(제거)는 B/C와 병행 가능, E(OpenClaw)는 C9(엔진)이 끝나면 시작
8. 위험 / 가정
8.1 위험 (Risk)
| ID |
위험 |
영향 |
대응 |
| R1 |
300_create_openclaw_tables.sql 미존재 → 역공학 오류 |
High |
architect에 vexplor DB dump 요청. 또는 vexplor 저자 인터뷰 |
| R2 |
OpenClaw 외부 엔진 라이선스/배포 불명 |
High |
architect 단계에서 OpenClaw 패키징 방식 결정 (npm global / Docker 이미지) |
| R3 |
invyone에 vexplor 커넥터 테이블 부재 (external_db_connections 등) |
Medium |
커넥터 기능 P2로 후순위 또는 invyone 측에 동등 테이블 신설 결정 |
| R4 |
ai-assistant users/api_keys 와 invyone 본 테이블 충돌 |
High |
A7 전 schema 분리 검증 필수 |
| R5 |
LLM 스트리밍 응답 Spring 구현 난이도 |
Medium |
Spring WebFlux SSE 또는 RestClient 스트림 응답 사용 |
| R6 |
컨테이너 추가에 따른 운영 비용 증가(OpenClaw) |
Low |
OPENCLAW_ENABLED=false 토글로 단계 도입 |
| R7 |
vexplor와 invyone의 shadcn/Next.js 버전 차이 |
Medium |
F5 의존성 점검 단계에서 잠재 호환 이슈 포착 |
| R8 |
Spring AI MCP 도입 결정 보류 → 향후 재작업 가능성 |
Medium |
인터페이스를 LlmClient 추상화로 만들어 교체 용이성 확보 |
| R9 |
멀티테넌시 정책 누락 → 회사 간 데이터 노출 |
Critical |
모든 쿼리에 company_code 강제, 단위 테스트로 검증 |
| R10 |
Jenkins helm-charts 측 ai-assistant 잔재 |
Low |
helm-charts 별도 PR(범위 외) |
8.2 가정 (Assumption)
- (A1) vexplor 백엔드의 PostgreSQL JSONB 사용은 invyone PostgreSQL에서도 동일하게 지원됨(같은 PG 16)
- (A2) invyone 운영자가 OpenClaw 컨테이너를 추가하는 것에 동의함
- (A3) AES-256 암호화 키는 환경변수로 주입(
AI_PROVIDER_ENCRYPTION_KEY)
- (A4) 기존 invyone JWT 발급 시
userType claim 에 SUPER_ADMIN / COMPANY_ADMIN / 일반 값이 들어 있음 (vexplor 컨트롤러가 의존)
- (A5)
company_code claim 도 JWT에 포함되어 있음
- (A6) invyone PostgreSQL 마이그레이션 도구가 정해져 있음(MyBatis 자체 또는 Flyway/Liquibase 중 — architect 결정)
- (A7) 외부 호출(B2B) 트래픽은 작음(분당 100 req 미만) — Rate limit 단순 구현 가능
9. Architect / Executor 가 결정해야 할 미정 사항
9.1 Architect 결정 필요
- 마이그레이션 도구: MyBatis 수동 SQL? Flyway? Liquibase?
- OpenClaw 배포: npm global 컨테이너 vs Docker Hub 공식 이미지(존재 여부 확인 필요) vs vexplor 측 Dockerfile 재사용
- OpenClaw config 동기화 방식: 공유 볼륨 파일? HTTP API? gRPC?
- Spring AI / Spring AI MCP 도입: 직접 HTTP 클라이언트 유지 vs Spring AI 채택
- 스케줄러 구현: Spring
@Scheduled (단순) vs Quartz (DB 영속화 + 클러스터링)
- 커넥터 테이블 존재 여부: invyone에
external_db_connections, external_rest_api_connections, crawl_configs, pipeline_device_connections 가 있는지 확인 → 없으면 P2 디그레이드
- API 키 폐기: hard delete (vexplor) vs soft delete (status='revoked') — 감사 로그 정책에 따라 결정
- AES-256 키 관리: 환경변수 vs Vault vs KMS
- 스트리밍 응답: Spring MVC SSE vs WebFlux
- Rate limit 구현: 인메모리(단일 인스턴스) vs Redis(클러스터)
- 테스트 전략: testcontainers PostgreSQL + WireMock LLM, 또는 별도 dev DB
- Schedule:
ai_agent_schedules 의 cron 타임존(서버 시간 vs UTC)
9.2 Executor 단계 결정 가능
- 커밋 단위 분리 전략 (Epic 단위 vs Story 단위)
- 기존 ai-assistant 제거 시점(B 시작 전 vs C 진행 중)
- 페이지별 점진 이식 vs 일괄 교체
9.3 검증/협의 필요(사용자에게 한 번 더)
- (Q1) helm-charts 리포지토리(
gitlab.kpslp.kr/root/helm-charts/values_invyone.yaml)에 ai-assistant 이미지 참조 잔재 존재 여부 — 확인 후 별도 PR 필요한지
- (Q2) ai-assistant가 사용한 DB schema 이름 / 호스트 — invyone 본 DB와 동일하면
users/api_keys 충돌 위험. A7 실행 전 반드시 확인 필요
- (Q3) 스케줄러 페이지 vexplor에 부재 — invyone에 신규 작성 필요한지(F7 Story 활성화 여부)
10. 산출물 및 다음 단계
10.1 본 PRD 결과물
- 파일:
C:\Dev\projects\invyone\.omc\plans\ai-orchestration-replacement-prd.md (본 문서)
- 추가 산출물 (이번 단계 외):
architecture.md (architect)
tasks/*.md (executor가 Story 분해)
verification.md (verifier)
10.2 권장 다음 단계
- 사용자에게 9.3 미정 질문 답변 요청 (특히 Q2 ai-assistant DB 분리 여부)
- architect 에이전트 호출:
- 9.1 결정 사항 12개에 대해 기술 결정
- C9 ExecutionEngine 동시성 모델 상세 설계
- 스키마 ERD 작성
- architect 산출물을 바탕으로 executor 가 Story 단위 구현
11. 부록 — invyone 측 영향 파일 인덱스
11.1 삭제 대상
11.2 수정 대상
11.3 신규 작성 대상 (Spring 백엔드)
11.4 신규 작성 대상 (프론트엔드)
12. 변경 이력
| 일자 |
작성자 |
변경 |
| 2026-04-27 |
planner |
최초 작성 |