--- type: workflow tags: [workflow, message-flow, rb8001, intent, skill, executor] last_updated: 2026-04-03 upper_principle: - /home/admin/0_VALUE/20_Governance/coding-principles.md - /home/admin/0_VALUE/20_Governance/work-system-principle.md --- # message_flow_v2 워크플로우 ## 목적 사용자 발화가 로빙(rb8001)에 도착해서 응답으로 돌아가기까지의 전체 흐름. SKILL.md 기반 동적 라우팅(M0~M5 완료) 현행 기준. v1(message_service.py 1409줄, decide_skill_sequence, route_with_tools, tool_registry)은 전부 삭제됨. ## 흐름 ``` 사용자 발화 (Slack/웹) → [수신] message_endpoint — 채널 정규화, user_id 추출 → [컨텍스트] 대화이력(DB 24h) + 감정상태 + 파일첨부 조립 → [이해] IntentClassifier — SKILL.md 본문을 LLM에 주입, IntentResult(skill, action, slots) 반환 → [실행] executor — registry에서 URL 해소, external=HTTP POST / internal=직접 호출 → [응답] 결과 조립 → 채널 어댑터 → 사용자 ``` ## 단계별 상세 ### 1. 수신 | 항목 | 내용 | |------|------| | 진입점 | `app/router/message_endpoint.py` | | 입력 | message, user_id, channel, thread_ts, file_inputs | | 책임 | 채널별 차이를 정규화하여 내부 형식으로 변환 | ### 2. 컨텍스트 조립 | 항목 | 내용 | |------|------| | 대화이력 | DB에서 최근 24h 대화 로드 (최근 5건) | | 감정상태 | EmotionEngine 현재 상태 | | 파일 | 첨부파일 요약 (있는 경우) | | 현재시각 | Asia/Seoul 기준 주입 (요일 포함) | ### 3. 이해 (IntentClassifier) | 항목 | 내용 | |------|------| | 코드 | `app/services/brain/intent_classifier.py` | | SSOT | `DOCS/skills/*/SKILL.md` — 스킬 목록과 트리거 조건 | | 입력 | 사용자 메시지 + 컨텍스트 | | 출력 | `IntentResult(skill, action, endpoint, slots, confidence, reasoning)` | | 방식 | SKILL.md 본문을 시스템 프롬프트에 주입 → LLM이 판단 → Pydantic 검증 | | 실패 시 | 에러 raise. 폴백 없음 | ### 4. 실행 (executor) | 항목 | 내용 | |------|------| | 코드 | `app/services/skills/executor.py` | | URL 해소 | `registry.py`가 SKILL.md의 `runtime.service_url_env`를 읽어 환경변수에서 URL 해소 | | external_http | `httpx.AsyncClient`로 스킬 서비스에 POST | | internal_python | 내부 handler 함수 직접 호출 | | 미등록 스킬 | `{"success": false, "error": "미등록"}` 반환 | ### 5. 응답 | 항목 | 내용 | |------|------| | 결과 조립 | executor 결과를 사용자 메시지로 변환 | | 채널 전달 | Slack reply / 웹 응답 | ## 스킬 참여 예시 | 사용자 발화 | skill | action | 실행 | |------------|-------|--------|------| | "내일 2시 미팅 잡아줘" | skill-calendar | create_event | POST SKILL_CALENDAR_URL/api/events | | "메일 확인해줘" | skill-email | fetch | POST SKILL_EMAIL_URL/api/emails | | "로빙아 안녕" | general_chat | chat | 내부 LLM 호출 | | "삼성전자 뉴스 검색" | web-search | web_search | 내부 웹검색 호출 | ## 코드에 분기문 없음 - 스킬 추가 시 `DOCS/skills/{name}/SKILL.md` 1개 + `runtime.env` URL 1개만 추가 - if/elif 매핑표, IntentType→skill 변환 계층 없음 - 근거: coding-principles §질문별 특례 하드코딩 금지, §식별자 변환 계층 금지 ## M0~M5 완료로 삭제된 v1 코드 | 삭제 대상 | 줄 수 | 이유 | |-----------|-------|------| | message_service.py | 1409 | v2(137줄)로 완전 대체 | | decision_engine.decide_skill_sequence + create_execution_plan + route_with_tools | 571 | v2에서 미사용 | | tool_registry.py | 775 | route_with_tools 전용, import 0건 | | brain_service.py | 158 | import 0건 | | v1 전용 테스트 5파일 | ~1100 | 삭제된 코드 테스트 | ## 아직 남아있는 v1 잔재 (의존 살아있음) | 코드 | 이유 | |------|------| | DecisionEngine.classify(), analyze_intent_async() | slack_handler 파일 컨텍스트 주입, intent_classifier 폴백 | | IntentType enum | slack_handler, slack/message_service 분기 | | intent_graph, semantic_classifier, skill_doc_loader | router 초기화 체인 | 이들은 Slack 경로가 v2로 완전 전환되면 삭제 가능. ## 관련 문서 - [slack_basic_dialogue.md](./slack_basic_dialogue.md) — v1 (n8n + decision_engine 경로, 레거시) - [work-system-principle.md](/home/admin/0_VALUE/20_Governance/work-system-principle.md) — 스킬과 워크플로우 구분 - [coding-principles.md](/home/admin/0_VALUE/20_Governance/coding-principles.md) — 입력→판단→출력 3단계 분리 - [로빙_smoke_test_워크플로우.md](/mnt/nas/workspace/shared-editing/workflows/로빙_smoke_test_워크플로우.md) — 배포 후 검증 절차