From e2eec51e2071b75ae695f6f41e4e2b7466596467 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Sat, 4 Oct 2025 23:26:42 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EB=A1=9C=EB=B9=99=20=EC=95=84=ED=82=A4?= =?UTF-8?q?=ED=85=8D=EC=B2=98=20=EC=9B=90=EC=B9=99=20=EC=9C=84=EB=B0=98=20?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EB=B0=8F=20?= =?UTF-8?q?naverworks=5Fslack=20=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=9E=AC?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md: 원칙 위반 현황 및 개선 계획 추가 - 250930_naverworks_slack_03: 완료 문서로 전환 (완료 기능만 포함) - 250930_naverworks_slack_04: Lists API skill-slack 통합 실행 계획 (신규) - 250919_naverworks_slack_04 → 05: DB/스케줄러 관리 (순서 변경) - 300_architecture: rb8001 직접 Slack 호출 제거, skill-slack HTTP 호출로 변경 - CLAUDE.md: "문서 100줄 이하 유지" 규칙 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ...320_Slack_기반_인터페이스_설계.md | 35 ++-- 300_architecture/README.md | 2 +- .../sequences/companyx_news_flow.md | 4 +- .../로빙_아키텍쳐_설계_경량.md | 8 +- plans/250906_news_system_integration.md | 6 +- plans/250907_company_x_news_implementation.md | 2 +- .../250828_slack_integration_level3_plan.md | 9 +- ..._로빙_뇌_기관_원칙_위반_현황.md | 151 ++++++++++++++++++ ...ppybell80_Slack_메시징_운영_런북.md | 40 ++--- ...works_slack_05_db_scheduler_management.md} | 0 ...slack_03_cold_mail_detection_completed.md} | 79 +-------- ...ks_slack_04_lists_api_skill_integration.md | 107 +++++++++++++ 12 files changed, 318 insertions(+), 125 deletions(-) create mode 100644 troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md rename troubleshooting/{250919_naverworks_slack_04_db_scheduler_management.md => 250919_naverworks_slack_05_db_scheduler_management.md} (100%) rename troubleshooting/{250930_naverworks_slack_03_cold_mail_list.md => 250930_naverworks_slack_03_cold_mail_detection_completed.md} (69%) create mode 100644 troubleshooting/250930_naverworks_slack_04_lists_api_skill_integration.md diff --git a/300_architecture/320_Slack_기반_인터페이스_설계.md b/300_architecture/320_Slack_기반_인터페이스_설계.md index f933c15..06659db 100644 --- a/300_architecture/320_Slack_기반_인터페이스_설계.md +++ b/300_architecture/320_Slack_기반_인터페이스_설계.md @@ -40,7 +40,8 @@ sequenceDiagram RB->>Skill: 필요시 스킬 호출 Skill-->>RB: 처리 결과 - RB->>Slack: 응답 전송 + RB->>Skill: Slack 전송 요청 + Skill-->>Slack: chat.postMessage Slack-->>User: 메시지 표시 ``` @@ -185,22 +186,19 @@ async def process_event(event_data): # 2. 실제 처리 (백그라운드) response = await generate_response(event_data) - # 3. Slack API로 응답 전송 - await slack_client.chat_postMessage( - channel=event_data['channel'], - text=response - ) + # 3. skill-slack을 통해 응답 전송 + async with httpx.AsyncClient() as client: + await client.post( + "http://skill-slack:8502/api/v1/send", + json={"channel": event_data["channel"], "text": response} + ) ``` ### 5.2 타이핑 인디케이터 ```python async def show_typing(channel: str): - """처리 중임을 표시""" - await slack_client.chat_postEphemeral( - channel=channel, - user=user_id, - text="생각 중... 🤔" - ) + """처리 중임을 표시 (전송은 skill-slack 경유)""" + # 구현 시 skill-slack 지원 엔드포인트 사용 또는 안내 메시지를 /send로 전달 ``` --- @@ -243,11 +241,12 @@ ERROR_MESSAGES = { ```python @retry(max_attempts=3, delay=1) async def send_slack_message(channel: str, text: str): - """실패시 재시도하는 메시지 전송""" - return await slack_client.chat_postMessage( - channel=channel, - text=text - ) + """실패시 재시도하는 메시지 전송 (skill-slack 경유)""" + async with httpx.AsyncClient() as client: + return await client.post( + "http://skill-slack:8502/api/v1/send", + json={"channel": channel, "text": text} + ) ``` --- @@ -303,4 +302,4 @@ async def send_slack_message(channel: str, text: str): --- -**문서 끝** \ No newline at end of file +**문서 끝** diff --git a/300_architecture/README.md b/300_architecture/README.md index b8f10b2..31e93be 100644 --- a/300_architecture/README.md +++ b/300_architecture/README.md @@ -35,7 +35,7 @@ Part 2 [[../200_core_design/README|핵심 설계]]에서 구상한 로빙의 시 ## 핵심 기술 스택 ``` Frontend: Slack Interface (사용자와의 소통 창구) -Backend: FastAPI + Celery (로빙의 두뇌와 신경계) +Backend: FastAPI (로빙의 두뇌) + skill-* / Workers (신경계) Database: PostgreSQL (구조화된 기억) + ChromaDB (맥락적/연상적 기억) Container: Docker Compose (로빙들의 아파트 단지) Embedding: Separated Service (언어 의미 이해 전문가) diff --git a/300_architecture/sequences/companyx_news_flow.md b/300_architecture/sequences/companyx_news_flow.md index f516005..bd06353 100644 --- a/300_architecture/sequences/companyx_news_flow.md +++ b/300_architecture/sequences/companyx_news_flow.md @@ -3,8 +3,8 @@ ## 시나리오 1. rb8001 → skill-news POST /api/news/google/companyx-search (09:10 크론) 2. skill-news가 status="summarized" 기사만 반환 -3. rb8001이 Slack 채널에 뉴스 표시 (버튼 value에 article_id 포함) +3. rb8001이 skill-slack에 전송 요청 → Slack 채널에 뉴스 표시 (버튼 value에 article_id 포함) 4. 버튼 클릭 → Gateway → rb8001 /api/slack/interactive 5. rb8001 → skill-news GET /api/news/google/companyx/{article_id} 6. rb8001 → skill-publish POST /publish (전체 데이터 전송) -7. rb8001 → skill-news PATCH /api/news/google/companyx/{article_id} (status 업데이트) \ No newline at end of file +7. rb8001 → skill-news PATCH /api/news/google/companyx/{article_id} (status 업데이트) diff --git a/_archive/docs/architecture/로빙_아키텍쳐_설계_경량.md b/_archive/docs/architecture/로빙_아키텍쳐_설계_경량.md index 13dc016..e5f554a 100644 --- a/_archive/docs/architecture/로빙_아키텍쳐_설계_경량.md +++ b/_archive/docs/architecture/로빙_아키텍쳐_설계_경량.md @@ -48,7 +48,7 @@ 4. 적절한 스킬 서버로 라우팅 5. 스킬 서버에서 LLM 처리 및 결과 생성 6. 결과를 로빙 컨테이너로 반환 -7. 로빙이 Slack으로 응답 전송 +7. 로빙이 skill-slack에 전송 요청 → Slack 응답 8. 사용 기록을 State Service에 업데이트 ``` @@ -193,10 +193,10 @@ class RobeingBrain: self.company_id ) - # Slack 토큰 설정 + # Slack 토큰 설정 (직접 Slack SDK 사용 금지) self.slack_token = company_state["slack_tokens"]["bot_token"] self.slack_signing_secret = company_state["slack_tokens"]["signing_secret"] - self.slack_client = SlackClient(token=self.slack_token) + # Slack API 호출은 skill-slack HTTP API를 통해 수행 # 회사 설정 로드 self.settings = company_state["settings"] @@ -620,4 +620,4 @@ services: 5. **성능**: 캐싱과 배치 처리로 응답 속도 향상 6. **무상태**: 컨테이너 재시작에 영향 받지 않는 안정적 서비스 -이 설계를 통해 로빙은 수백 개의 회사를 효율적으로 서비스할 수 있는 확장 가능한 플랫폼으로 성장할 수 있습니다. \ No newline at end of file +이 설계를 통해 로빙은 수백 개의 회사를 효율적으로 서비스할 수 있는 확장 가능한 플랫폼으로 성장할 수 있습니다. diff --git a/plans/250906_news_system_integration.md b/plans/250906_news_system_integration.md index 1e1e306..1b48b99 100644 --- a/plans/250906_news_system_integration.md +++ b/plans/250906_news_system_integration.md @@ -60,10 +60,10 @@ rb8001 → POST skill-publish:8511/publish **검색식**: `"컴퍼니 엑스" OR "컴퍼니엑스" -광고 -채용 -구인` **환경변수**: COMPANY_X_NEWS_KEYWORDS (기본값 내장) -### 3.1 Slack Block Kit 메시지 (rb8001) ✅ +### 3.1 Slack Block Kit 메시지 (rb8001 생성, skill-slack 전송) ✅ **위치**: app/skills/news_posting_skill.py (생성됨) **함수**: -- `send_news_for_posting()`: 채널에 뉴스 전송 +- `send_news_for_posting()`: Block Kit 생성 후 skill-slack에 전송 요청 - `create_news_blocks()`: Block Kit 메시지 생성 - `process_news_batch()`: APScheduler에서 호출 ```python @@ -180,4 +180,4 @@ docker logs rb8001 | grep postMessage - [ ] 테스트: 실제 작동 확인 필요 - [ ] Slack App 설정: Interactive URL 등록 필요 - [ ] 환경변수 설정: .env 파일 업데이트 필요 -- [ ] 배포: Docker 재시작 후 검증 필요 \ No newline at end of file +- [ ] 배포: Docker 재시작 후 검증 필요 diff --git a/plans/250907_company_x_news_implementation.md b/plans/250907_company_x_news_implementation.md index b2aa9dc..d642eb2 100644 --- a/plans/250907_company_x_news_implementation.md +++ b/plans/250907_company_x_news_implementation.md @@ -7,7 +7,7 @@ ## 1. 구현 완료 사항 ### 1.1 Slack Interactive 시스템 -- news_posting_skill.py: Company-X 뉴스 채널 포스팅 기능 구현 +- news_posting_skill.py: Company-X 뉴스 채널 메시지 생성 및 skill-slack 전송 요청 기능 - Block Kit 메시지 형식 (버튼 포함) - 홈페이지 게시 / 건너뛰기 버튼 - skill-publish 연동 코드 diff --git a/plans/completed/250828_slack_integration_level3_plan.md b/plans/completed/250828_slack_integration_level3_plan.md index 02fc634..7d796a6 100644 --- a/plans/completed/250828_slack_integration_level3_plan.md +++ b/plans/completed/250828_slack_integration_level3_plan.md @@ -83,6 +83,7 @@ Frontend(Slack 아이템 획득) → auth-server → OAuth 2.0 → 봇 토큰 ### Phase 3: 대화 동기화 ``` Slack Event → rb8001 → DB → SSE/WebSocket → Frontend +전송: rb8001 → skill-slack → Slack ``` **구현**: @@ -96,11 +97,11 @@ Slack Event → rb8001 → DB → SSE/WebSocket → Frontend ### Phase 4: 명령 처리 ``` -Slack → @robeing 또는 /robeing → rb8001 → 응답 +Slack → @robeing 또는 /robeing → rb8001 → skill-slack → Slack 응답 ``` **구현**: -1. 멘션: `app_mention` 이벤트 → `chat.postMessage` 응답 +1. 멘션: `app_mention` 이벤트 → rb8001가 skill-slack `/send` 호출 → Slack 2. 슬래시 명령 (앱 설정 또는 Manifest API로 등록): - `/robeing help`, `/robeing status`, `/robeing level` - Request URL: `rb8001/slack/commands` @@ -155,7 +156,7 @@ ALTER TABLE conversation_log ADD COLUMN channel_type VARCHAR(20); ### Phase 2 테스트 - 워크스페이스 앱 설치 - 토큰 암호화 저장 확인 -- chat.postMessage로 헬스체크 +- skill-slack `/send`로 헬스체크 ### Phase 3 테스트 - 공개/비공개 채널, DM, 멀티DM 각각 송수신 @@ -182,4 +183,4 @@ ALTER TABLE conversation_log ADD COLUMN channel_type VARCHAR(20); ### 내부 문서 - [auth_login_sequences.md](../300_architecture/sequences/auth_login_sequences.md) - [tables.md](../300_architecture/database/tables.md) -- [250828_conversation_log_channel_구분_개선.md](../troubleshooting/250828_conversation_log_channel_구분_개선.md) \ No newline at end of file +- [250828_conversation_log_channel_구분_개선.md](../troubleshooting/250828_conversation_log_channel_구분_개선.md) diff --git a/troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md b/troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md new file mode 100644 index 0000000..6b2d8a9 --- /dev/null +++ b/troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md @@ -0,0 +1,151 @@ +# 로빙 아키텍처 원칙 위반 현황 및 개선 계획 + +## 작성일: 2025-10-04 23:01:59 +## 작성자: happybell80 +## 위치: `/home/happybell80/ivada_project/DOCS/troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md` +## 상태: 인지 완료, 점진적 개선 예정 + +--- + +## 문제 인식 + +skill-slack 리포지토리에 Slack Lists API 테스트 파일이 추가되면서, 현재 rb8001이 Slack API를 직접 호출하는 구조가 로빙 아키텍처 원칙에 위배됨을 재확인했다. + +## 로빙 아키텍처 핵심 원칙 + +### 출처: `/DOCS/300_architecture/360_로빙_컨테이너_경량화_전략.md:7` +> "컨테이너는 몸, 기억은 영혼 - 100개 로빙이 하나의 스킬 서비스를 공유한다" + +### 출처: `/DOCS/300_architecture/README.md:38` +> "Backend: FastAPI + Celery (로빙의 두뇌와 신경계)" + +### 원칙 정리 +- **로빙 컨테이너 (rb8001) = 두뇌(뇌)**: 판단과 라우팅만 수행 +- **스킬 서비스 (skill-*) = 운동기관/감각기관**: 실제 실행 담당 +- **DB (PostgreSQL/ChromaDB) = 기억/영혼**: 상태 저장 + +### 출처: `/DOCS/troubleshooting/250721_happybell80_이메일스킬HTTP분리및아키텍처전환.md:99-113` +> **교훈** +> 1. "빠른 개발"이 항상 빠르지 않음 +> 2. 아키텍처 원칙을 지키는 것이 장기적으로 이득 +> 3. 마이크로서비스는 처음부터 제대로 분리해야 함 +> +> **CLAUDE.md 업데이트** +> - 스킬 포트 범위: 8501-8599 +> - HTTP API 통신 원칙 명시 +> - 직접 import 금지 규칙 추가 + +--- + +## 현재 원칙 위반 현황 + +### 1. Slack API 직접 호출 위반 + +#### 250915_happybell80_Slack_메시징_운영_런북.md +- **20-36줄**: `rb8001/app/router/slack_handler.py`가 WebClient로 직접 `chat_postMessage` 호출 +- **38-45줄**: `rb8001/app/skills/dm_skill.py`가 직접 Slack SDK 사용 +- **47-59줄**: `rb8001/app/skills/news_posting_skill.py`가 직접 Slack SDK 사용 +- **176줄**: "이벤트/인터랙티브 응답: rb8001 slack_handler가 채널/스레드로 직접 전송" + +#### 250930_naverworks_slack_03_cold_mail_list.md +- **36-40줄**: `rb8001/app/services/slack_lists_client.py` - 직접 Slack Lists API 호출 (aiohttp 사용) +- **문제**: rb8001(뇌)이 직접 손을 움직이는 격 + +### 2. 스킬 직접 Import 위반 + +#### 250819_rb8001_gmail_integration_completed.md +- Gmail 스킬을 직접 import하는 구조 (HTTP API 사용 안함) + +#### 250914_happybell80_깡프로뉴스_용어추출_기능추가.md +- rb8001이 스킬 로직 직접 포함 + +#### 250909_ocr_skill_implementation_plan.md +- 스킬 직접 import 계획 + +### 3. 관련 위반 사례 + +#### 250903_slack_multi_workspace_token_issue.md +- rb8001이 여러 Slack 토큰 직접 관리 + +#### 250906_news_skill_publish_separation.md, 250907_company_x_news_implementation.md +- rb8001이 뉴스 게시 직접 수행 + +--- + +## 위반 패턴 분석 + +### 잘못된 흐름 (현재) +- rb8001(뇌) → 직접 Slack API 호출 +- rb8001(뇌) → 직접 Lists API 호출 +- rb8001(뇌) → 스킬 로직 import + +### 올바른 흐름 (목표) +- rb8001(뇌) → skill-slack(손)에 HTTP 요청 → Slack API 호출 +- rb8001(뇌) → skill-slack(손)에 HTTP 요청 → Lists API 호출 +- rb8001(뇌) → skill-*(기관)에 HTTP 요청 → 스킬 실행 + +--- + +## 개선 계획 + +### Phase 1: skill-slack Lists API 엔드포인트 추가 (우선) +1. skill-slack에 `/api/v1/lists/items` 엔드포인트 구현 + - POST: 리스트 아이템 생성 (slackLists.items.create) + - GET: 리스트 아이템 조회 (slackLists.items.list) + - PUT: 리스트 아이템 수정 (slackLists.items.update) +2. `tests/test_slack_lists.py`의 헬퍼 함수를 서비스 모듈로 이동 +3. rb8001의 `slack_lists_client.py` → skill-slack HTTP 호출로 변경 + +### Phase 2: rb8001 Slack 직접 호출 제거 +1. rb8001/app/router/slack_handler.py → skill-slack /api/v1/send 사용 +2. rb8001/app/skills/dm_skill.py → skill-slack HTTP 호출로 변경 +3. rb8001/app/skills/news_posting_skill.py → skill-slack HTTP 호출로 변경 +4. Slack SDK (slack_sdk) 의존성 제거 + +### Phase 3: 기타 스킬 분리 +1. Gmail 스킬 HTTP API로 완전 분리 +2. OCR 스킬 분리 시 HTTP API 기본 적용 +3. 뉴스 스킬 로직 분리 + +### Phase 4: 문서 업데이트 +1. 위반 사례 문서에 "개선 완료" 표시 +2. 아키텍처 원칙 문서 강화 +3. 신규 스킬 개발 시 체크리스트 추가 + +--- + +## 즉시 적용 가능한 원칙 + +### skill-slack 테스트 파일 활용 +1. `/home/happybell80/ivada_project/skill-slack/tests/test_slack_lists.py`의 헬퍼 함수: + - `list_all_items()` → GET 엔드포인트 + - `add_file_to_list()` → POST 엔드포인트 +2. 실제 컬럼 구조 확인 후 구현 (추측 금지) +3. rb8001은 HTTP로만 호출 + +### 개발 체크리스트 +1. 로빙이 외부 API를 직접 호출하는가? → 스킬로 분리 +2. 로빙이 스킬 로직을 포함하는가? → HTTP API로 분리 +3. 로빙이 스킬을 import하는가? → HTTP 클라이언트로 교체 + +--- + +## 교훈 + +1. **"빠른 개발"의 함정**: 직접 호출이 당장은 빠르지만, 나중에 더 큰 리팩토링 비용 발생 +2. **아키텍처 일관성**: 원칙을 지키면 100개 로빙 운영 시 리소스 절감 (뇌 = 512MB, 스킬 = 공유) +3. **문서의 중요성**: 원칙 문서가 있었지만 실제 코드에 반영 안됨 → 주기적 점검 필요 + +--- + +## 참고 문서 + +- `/DOCS/300_architecture/360_로빙_컨테이너_경량화_전략.md` - 핵심 원칙 +- `/DOCS/300_architecture/README.md` - 아키텍처 개요 +- `/DOCS/troubleshooting/250721_happybell80_이메일스킬HTTP분리및아키텍처전환.md` - 성공 사례 +- `/DOCS/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md` - 위반 현황 +- `/DOCS/troubleshooting/250930_naverworks_slack_03_cold_mail_list.md` - Lists API 위반 + +--- + +*"컨테이너는 몸, 기억은 영혼 - 뇌는 판단만, 손발은 스킬이 담당한다"* diff --git a/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md b/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md index d02c5c5..3463fc3 100644 --- a/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md +++ b/troubleshooting/250915_happybell80_Slack_메시징_운영_런북.md @@ -15,18 +15,18 @@ - `POST /api/slack/interactive` → `app.router.slack_handler.handle_interactive()` 위임 - `POST /api/dm/send`, `POST /api/dm/send-to-user`, `POST /api/dm/send-email-summary`, `POST /api/dm/send-daily-summary` → DM 스킬 호출 - 스케줄 - - APScheduler로 헤드라인/뉴스/DM 작업을 트리거, 실제 전송은 각 스킬에서 Slack API 호출 + - APScheduler로 헤드라인/뉴스/DM 작업을 트리거, 실제 전송은 skill-slack이 수행 - **파일: rb8001/app/router/slack_handler.py** - 핵심 함수 - `handle_slack_events(request)` → Slack Events 수신 처리 - - 토큰: Gateway가 전달한 `X-Slack-Bot-Token`로 `WebClient` 생성(팀별 토큰) + - 토큰: Gateway가 전달한 `X-Slack-Bot-Token`을 skill-slack 호출 시 전달 - 채널/스레드/DM 판단: - DM: `event.channel_type == "im"` → thread_ts 없음 - 스레드: `event.thread_ts` 유지 - 멘션(app_mention)인데 스레드가 아니면 원본 `ts`를 `thread_ts`로 사용해 스레드 시작 - - 포맷: `chat_postMessage(channel, text, thread_ts?, unfurl_links=False, unfurl_media=False)` - - I/O: Slack 이벤트(JSON) → 내부 라우터 `router.route_message()` → Slack 메시지 전송 + - 포맷: channel, text, thread_ts 등 메시지 페이로드 구성 + - I/O: Slack 이벤트(JSON) → 내부 라우터 `router.route_message()` → skill-slack `/send` 호출 - `process_slack_message_async(text, user_id, context)` → 실제 메시지 생성/전송 - `handle_interactive(request)` → 인터랙티브 액션 처리 - Slack 서명 검증 후 액션 분기 @@ -38,8 +38,8 @@ - **파일: rb8001/app/skills/dm_skill.py** - 토큰: settings.ROBEING_SLACK_BOT_TOKEN 또는 settings.SLACK_BOT_TOKEN - DM 전송 - - `send_daily_dm(custom_message?)` → `chat_postMessage(channel=슬랙유저ID, text)` - - `send_dm_to_user(slack_id, message)` → `chat_postMessage(...)` + - `send_daily_dm(custom_message?)` → skill-slack `/send` + - `send_dm_to_user(slack_id, message)` → skill-slack `/send` - `send_email_summary_dm()` → 이메일 요약 포함 DM, `unfurl_links=False` - `send_daily_summary_dm()` → 이메일/뉴스 합친 모닝 브리핑 DM, 전송 후 뉴스 ID 기록 - 형식: 모두 텍스트, 필요 시 링크 미리보기 비활성화 @@ -47,15 +47,15 @@ - **파일: rb8001/app/skills/news_posting_skill.py** - 토큰: COMPANYX_SLACK_BOT_TOKEN - 채널 메시지(블록) - - `send_news_for_posting(channel?)` → 블록 생성 후 `chat_postMessage(channel, blocks, text=fallback)` - - `add_status_to_message(channel, ts, blocks)` → `chat_update`로 블록 갱신 + - `send_news_for_posting(channel?)` → 블록 생성 후 skill-slack `/send` + - `add_status_to_message(channel, ts, blocks)` → skill-slack `/update` - 인터랙티브 진행 업데이트 - `send_slack_update(response_url, message)` → `response_url`로 JSON POST(원본 미교체) - **파일: rb8001/app/skills/startup_news_skill.py** - 토큰: COMPANYX_SLACK_BOT_TOKEN - 채널 메시지(텍스트) - - `WebClient.chat_postMessage(channel=env 채널, text=뉴스 요약)` + - skill-slack `/send`로 채널 전송 - **파일: rb8001/app/router/dm_endpoint.py** - 엔드포인트(내부용): /api/dm/send, /api/dm/send-to-user → dm_skill 호출 @@ -93,7 +93,7 @@ ## 토큰/식별자 - **게이트웨이**: Slack 요청에서 팀 ID를 추출해 slack_workspace.bot_token 조회 후 rb8001 호출 시 헤더 X-Slack-Bot-Token로 전달. Slack 사용자 ID→내부 UUID는 user.oauth_provider='slack' AND oauth_id=slack_user_id 매핑으로 조회해 X-User-Id로 전달. -- **rb8001**: Slack 이벤트 응답 시 게이트웨이가 전달한 X-Slack-Bot-Token으로 WebClient 생성. DM 전송은 ROBEING_SLACK_BOT_TOKEN 또는 SLACK_BOT_TOKEN 사용. Company-X 뉴스 전송은 COMPANYX_SLACK_BOT_TOKEN 사용. +- **rb8001**: Slack 이벤트 응답 시 게이트웨이가 전달한 X-Slack-Bot-Token을 포함해 skill-slack에 위임. DM/뉴스 전송 역시 skill-slack을 사용. - **skill-slack**: 자체 SLACK_BOT_TOKEN으로 WebClient 사용. ## 엔드포인트(I/O) @@ -103,10 +103,10 @@ - `POST /slack/interactive`: 요청 본문을 rb8001 `POST /api/slack/interactive`로 전달, 헤더 `X-Slack-*` 및 `X-User-Id` 전달. ### rb8001 -- `POST /api/slack/events`: Slack 이벤트 처리 후 `chat_postMessage`로 채널/스레드/DM에 응답. -- `POST /api/slack/interactive`: Slack 인터랙티브 처리, 후속 안내는 `response_url`로 전송. -- `POST /api/dm/send`: 모든 사용자 대상 DM 전송 트리거(dmskill). -- `POST /api/dm/send-to-user`: 특정 Slack 사용자 ID로 DM 전송(dmskill). +- `POST /api/slack/events`: Slack 이벤트 처리 후 skill-slack `/send` 호출로 채널/스레드/DM 응답. +- `POST /api/slack/interactive`: Slack 인터랙티브 처리, 후속 안내도 skill-slack을 통해 전송(필요 시 response_url 활용). +- `POST /api/dm/send`: 모든 사용자 대상 DM 전송 트리거(dmskill → skill-slack). +- `POST /api/dm/send-to-user`: 특정 Slack 사용자 ID로 DM 전송(dmskill → skill-slack). ### skill-slack - `POST /api/v1/messages/send`: `chat_postMessage(channel, text, blocks?)` 실행. @@ -114,10 +114,10 @@ ## 메시지 전송 경로(플로우) -- **이벤트**: Slack → 게이트웨이(/slack/events) → rb8001(/api/slack/events) → rb8001이 Slack에 chat_postMessage 전송. -- **인터랙티브**: Slack → 게이트웨이(/slack/interactive) → rb8001(/api/slack/interactive) → 처리 후 response_url로 에페메럴 메시지 전송. -- **정기/수동 DM**: rb8001 스케줄러 또는 API → dm_skill → chat_postMessage(channel=슬랙 유저 ID). -- **뉴스 채널 게시**: rb8001 스케줄러 또는 명령 → news_posting_skill → chat_postMessage(channel=채널 ID, blocks). +- **이벤트**: Slack → 게이트웨이(/slack/events) → rb8001(/api/slack/events) → rb8001 → skill-slack(`/send`) → Slack +- **인터랙티브**: Slack → 게이트웨이(/slack/interactive) → rb8001(/api/slack/interactive) → rb8001 → skill-slack(`/send|/update`) → Slack +- **정기/수동 DM**: rb8001 스케줄러 또는 API → dm_skill → skill-slack(`/send`) +- **뉴스 채널 게시**: rb8001 스케줄러 또는 명령 → news_posting_skill → skill-slack(`/send|/update`) ## 채널/스레드/DM 규칙 @@ -127,7 +127,7 @@ ## 메시지 형식/옵션 -- **텍스트**: chat_postMessage(channel, text, thread_ts?) 사용. +- **텍스트**: skill-slack `/send`(channel, text, thread_ts?) 사용. - **블록**: 뉴스 게시 시 Block Kit 블록 배열(blocks) 사용, 필요 시 여러 메시지로 분할. - **링크 미리보기**: 일부 전송 경로에서 unfurl_links=False, unfurl_media=False 사용. - **인터랙티브 업데이트**: response_url에 JSON POST로 에페메럴 메시지 전송(replace_original=False, delete_original=False). @@ -194,4 +194,4 @@ - Slack → Gateway(/slack/interactive) → rb8001 → 액션 처리(뉴스 게시 등) → response_url로 진행/완료 에페메럴 메시지 ### 스케줄링/DM/뉴스 -- APScheduler → rb8001 스킬 → chat_postMessage 또는 chat_update 사용해 DM/채널 전송 \ No newline at end of file +- APScheduler → rb8001 스킬 → chat_postMessage 또는 chat_update 사용해 DM/채널 전송 diff --git a/troubleshooting/250919_naverworks_slack_04_db_scheduler_management.md b/troubleshooting/250919_naverworks_slack_05_db_scheduler_management.md similarity index 100% rename from troubleshooting/250919_naverworks_slack_04_db_scheduler_management.md rename to troubleshooting/250919_naverworks_slack_05_db_scheduler_management.md diff --git a/troubleshooting/250930_naverworks_slack_03_cold_mail_list.md b/troubleshooting/250930_naverworks_slack_03_cold_mail_detection_completed.md similarity index 69% rename from troubleshooting/250930_naverworks_slack_03_cold_mail_list.md rename to troubleshooting/250930_naverworks_slack_03_cold_mail_detection_completed.md index 3d0cd64..f932c84 100644 --- a/troubleshooting/250930_naverworks_slack_03_cold_mail_list.md +++ b/troubleshooting/250930_naverworks_slack_03_cold_mail_detection_completed.md @@ -1,21 +1,15 @@ -# NAVER WORKS → Slack 연동 [3/3] 콜드메일 자동 감지 및 IR 파일 분석 +# NAVER WORKS → Slack 연동 [3/5] 콜드메일 자동 감지 및 IR 분석 시스템 ## 날짜: 2025-09-30 ## 작성자: Claude (로컬 개발자) -## 상태: 부분 완료 (Slack 파일 업로드 미구현) +## 위치: `/home/happybell80/ivada_project/DOCS/troubleshooting/250930_naverworks_slack_03_cold_mail_detection_completed.md` +## 상태: 완료 (참고용 문서) -## 목표 +## 완료된 기능 NAVER WORKS 메일 중 투자 제안(콜드메일) 수신 시: -1. 자동 감지 및 필터링 -2. Slack 전용 채널에 리스트 포스팅 (Lists API) -3. Slack 채널에 첨부파일 업로드 (files.upload API) -4. 첨부파일(IR 자료) skill-rag-file 자동 학습 -5. AI 분석 요약 생성 후 채널 공유 - -## 구현 상태 -- ✅ 1-2: 완료 (자동 감지, Lists API) -- ❌ 3: 미구현 (Slack 파일 업로드) -- ✅ 4-5: 완료 (RAG 학습, AI 요약) +1. 자동 감지 및 필터링 (Naive Bayes) +2. 첨부파일(IR 자료) skill-rag-file 자동 학습 +3. AI 분석 요약 생성 후 채널 공유 --- @@ -113,65 +107,6 @@ NAVER WORKS 메일 중 투자 제안(콜드메일) 수신 시: --- -## Slack 파일 업로드 구현 가이드 (미구현) - -### API 변경 사항 (2025) -- **files.upload 폐기**: 2025-11-12부터 사용 불가, 신규 앱 접근 차단 -- **신규 방식**: files.getUploadURLExternal + files.completeUploadExternal - -### 구현 순서 -1. **파일 업로드 URL 획득** - ```python - # files.getUploadURLExternal 호출 - response = client.files_getUploadURLExternal( - filename="ir_document.pdf", - length=file_size - ) - upload_url = response["upload_url"] - file_id = response["file_id"] - ``` - -2. **파일 데이터 업로드** - ```python - # POST binary data to upload_url - async with session.post(upload_url, data=file_binary) as resp: - # 성공 시 200 응답 - ``` - -3. **업로드 완료 처리** - ```python - # files.completeUploadExternal 호출 - client.files_completeUploadExternal( - files=[{"id": file_id, "title": "IR 문서"}], - channel_id=COLDMAIL_CHANNEL_ID # 선택: 채널 공유 - ) - ``` - -4. **Slack Lists에 파일 첨부** - ```python - # slackLists.items.create 호출 - fields = { - "company": {"text": company_name}, - "attachments": {"attachment": [file_id]} # 방법 1 - # 또는 - "files": {"reference": [{"file": {"file_id": file_id}}]} # 방법 2 - } - ``` - -### 필수 스코프 추가 -- 현재: `lists:write`, `lists:read`, `files:read` -- 추가 필요: `files:write` - -### 구현 위치 -- **skill-slack**: POST /api/v1/upload-file 엔드포인트 추가 -- **rb8001/coldmail_briefing.py**: 9번 단계에 파일 업로드 로직 추가 -- **rb8001/services/slack_lists_client.py**: create_coldmail_list_item()에 file_id 파라미터 추가 - -### 참고 문서 -- files.getUploadURLExternal: https://api.slack.com/methods/files.getUploadURLExternal -- files.completeUploadExternal: https://api.slack.com/methods/files.completeUploadExternal -- slackLists.items.create: https://docs.slack.dev/reference/methods/slackLists.items.create/ - --- ## 테스트 diff --git a/troubleshooting/250930_naverworks_slack_04_lists_api_skill_integration.md b/troubleshooting/250930_naverworks_slack_04_lists_api_skill_integration.md new file mode 100644 index 0000000..a90b298 --- /dev/null +++ b/troubleshooting/250930_naverworks_slack_04_lists_api_skill_integration.md @@ -0,0 +1,107 @@ +# NAVER WORKS → Slack 연동 [4/5] Slack Lists API skill-slack 통합 실행 계획 + +## 작성일: 2025-09-30, 수정: 2025-10-04 +## 작성자: Claude (로컬 개발자) +## 위치: `/home/happybell80/ivada_project/DOCS/troubleshooting/250930_naverworks_slack_04_lists_api_skill_integration.md` +## 상태: 실행 문서 (구현 예정) + +--- + +## 목표 + +rb8001의 Slack Lists API 오류 수정 및 원칙 위반 구조 개선 + +### 현재 문제 +1. **API 엔드포인트 오류**: rb8001/app/services/slack_lists_client.py + - 35줄: `lists.items.create` (잘못됨) → `slackLists.items.create` (정답) + - 84줄: `lists.items.update` (잘못됨) → `slackLists.items.update` (정답) + - 136-143줄: 필드 형식 오류 (column_id 기반 구조 필요) +2. **원칙 위반**: rb8001(뇌)이 직접 aiohttp로 Slack API 호출 + +### 목표 +skill-slack에 올바른 Lists 엔드포인트 추가 → rb8001은 HTTP로만 호출 + +## 테스트 파일 확인 완료 + +### skill-slack/tests/test_slack_lists.py +- 실제 동작하는 헬퍼 함수 포함: + - `list_all_items(list_id, slack_token)` - 리스트 아이템 조회 + - `add_file_to_list(list_id, file_id, slack_token, email?, phone?)` - 아이템 추가 +- pytest 테스트 클래스: TestSlackLists +- 스탠드얼론 실행 지원 + +### 실제 컬럼 구조 (test 리스트) +| Column ID | 타입 | 설명 | +|-----------|------|------| +| `Col00` | checkbox | 완료 체크박스 | +| `Col09JME3980M` | attachment | 파일 첨부 | +| `Col09JQUKBSE6` | email | 이메일 주소 | +| `Col09JQU6DF3L` | phone | 전화번호 | + +**주의**: 컬럼 ID는 리스트마다 다를 수 있음 - 하드코딩 금지 + +--- + +## Slack Lists API 스펙 + +### 리스트 조회 +- **엔드포인트**: `https://slack.com/api/slackLists.items.list` +- **메서드**: POST +- **요청**: `{"list_id": "...", "limit": 100}` +- **응답**: `{"ok": true, "items": [...]}` + +### 아이템 생성 +- **엔드포인트**: `https://slack.com/api/slackLists.items.create` +- **메서드**: POST +- **요청**: `{"list_id": "...", "initial_fields": [...]}` +- **응답**: `{"ok": true, "item": {"id": "..."}}` + +--- + +## 구현 가이드 + +### 1. skill-slack 폴더 구조 확인 필요 +- 실제 app/services/ 디렉토리 존재 여부 확인 +- 실제 app/api/endpoints/ 디렉토리 존재 여부 확인 +- 기존 messages.py 파일 구조 참고 + +### 2. 헬퍼 함수 이전 +- skill-slack/tests/test_slack_lists.py:137-222 (list_all_items, add_file_to_list) +- → skill-slack/app/services/slack_lists_service.py로 이동 +- requests 라이브러리 사용 (기존 테스트와 동일) + +### 3. API 엔드포인트 추가 +- skill-slack/app/api/endpoints/lists.py 생성 +- GET /api/v1/lists/{list_id}/items - 아이템 조회 +- POST /api/v1/lists/items - 아이템 생성 +- PUT /api/v1/lists/items/{item_id} - 아이템 수정 + +### 4. 토큰 처리 +- 기존 skill-slack 방식 따름 (요청별 토큰 전달 or 환경변수) +- 250919_skill_slack_deployment_plan.md:60-74 참조 + +### 5. rb8001 수정 +- rb8001/app/services/slack_lists_client.py 제거 +- rb8001/scheduler/jobs/coldmail_briefing.py:100-102 - skill-slack HTTP 호출로 변경 +- SKILL_SLACK_URL 환경변수 사용 + +--- + +## 참고 문서 + +- `/home/happybell80/ivada_project/skill-slack/tests/test_slack_lists.py` - 실제 동작 코드 +- `/DOCS/troubleshooting/250919_skill_slack_deployment_plan.md` - skill-slack 아키텍처 +- `/DOCS/troubleshooting/250930_naverworks_slack_03_cold_mail_list.md` - 콜드메일 시스템 +- `/DOCS/troubleshooting/20251004_happybell80_로빙_뇌_기관_원칙_위반_현황.md` - 원칙 위반 현황 + +## 주의사항 + +1. **추측 금지**: skill-slack 실제 폴더 구조 확인 후 작업 +2. **의사코드 금지**: 실제 동작하는 코드만 작성 +3. **하드코딩 금지**: 컬럼 ID는 동적으로 처리 +4. **Slack Lists 제약**: 유료 플랜에서만 사용 가능 +5. **파일 업로드**: files_upload_v2 사용 (files.upload는 deprecated) + +--- + +*"로빙(뇌)은 판단만, skill-slack(손)이 실행한다"*