03_rag: - companyx_grounding_pipeline.md: 코드 SSOT 섹션 추가, 진입 조건 3단계(IC→마커 폴백) 정확히 기술, 환경변수 참조로 IP 하드코딩 제거 - companyx_incremental_indexing_workflow.md: frontmatter 표준 적용 (type, last_updated) - rag_upload_indexing_pipeline.md: 코드 SSOT·재인덱싱·업로드 경로별 진입점 테이블 추가, 환경변수 참조 04_scheduler: - scheduled_daily_briefing.md: n8n cron 전제 제거, APScheduler DB 기반 + LangGraph 워크플로우 기준 재작성 - scheduled_healthcheck_alert.md: n8n cron 전제 제거, /health 엔드포인트 + SKILL.md registry 기반 재작성 - scheduled_rag_reindex_retry.md: 현행 코드에 해당 잡 없음 → _archive 이동 05_admin: - diary_reflection_pipeline.md: n8n 전제 제거, APScheduler + diary_generator.py 기준 재작성, /api/diary/generate(존재하지 않는 엔드포인트) 제거 Refs: DOCS#8 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
63 lines
2.9 KiB
Markdown
63 lines
2.9 KiB
Markdown
---
|
|
tags: [workflow, scheduler, news, headlines, slack]
|
|
type: workflow
|
|
last_updated: 2026-04-06
|
|
---
|
|
|
|
# scheduled_daily_briefing 워크플로우
|
|
|
|
## 목적
|
|
DB에 등록된 스케줄에 따라 네이버 + 동남아 스타트업 헤드라인을 수집하여 Slack 채널에 자동 게시한다.
|
|
|
|
## 아키텍처
|
|
- **스케줄러**: APScheduler (DB 기반). `db_loader.py`가 `scheduler_jobs` 테이블에서 `job_type=daily_headlines` 잡을 로드하여 등록.
|
|
- **실행 래퍼**: `app/scheduler/jobs/daily_headlines.py` → `_run_headlines_with_logging(channel_id)`.
|
|
- **실제 워크플로우**: `app/services/workflows/headlines_workflow.py` — LangGraph StateGraph로 구성.
|
|
- **n8n 미사용**. systemd로 rb8001이 직접 실행.
|
|
|
|
## 코드 SSOT
|
|
- `rb8001/app/scheduler/jobs/daily_headlines.py` — 스케줄러 잡 래퍼
|
|
- `rb8001/app/services/skills/startup_news_skill.py` — `run_headlines_job()` 진입점
|
|
- `rb8001/app/services/workflows/headlines_workflow.py` — LangGraph 워크플로우 본체
|
|
- `rb8001/app/scheduler/db_loader.py` — DB 잡 로더 (`JOB_TYPE_MAP["daily_headlines"]`)
|
|
|
|
## 흐름
|
|
|
|
```
|
|
APScheduler cron trigger
|
|
→ db_loader가 등록한 guarded_job (schedule_policy 평가)
|
|
→ _run_headlines_with_logging(channel_id)
|
|
→ run_headlines_job(channel_id)
|
|
→ run_headlines_workflow(channel_id) [LangGraph]
|
|
→ fetch_naver_node (SkillCommands.fetch_naver_headlines, fmt=slack)
|
|
→ fetch_sea_node (SkillCommands.fetch_sea_headlines, fmt=json)
|
|
→ format_node (네이버 텍스트에 동남아 섹션 삽입)
|
|
→ send_node (Slack chat.postMessage)
|
|
```
|
|
|
|
## 스케줄 정책
|
|
- cron 표현식은 `scheduler_jobs` DB 테이블에서 관리한다 (예: `10 9 * * 1-5`).
|
|
- `schedule_policy` 필드에 `workday` 정책이 설정되어 있으면 공휴일/주말을 건너뛴다 (`evaluate_schedule_policy()`).
|
|
- `channel_id`는 `config.channel_id` DB 필드에서 전달받는다.
|
|
|
|
## 주요 노드 (LangGraph)
|
|
|
|
| 노드 | 함수 | 설명 |
|
|
|------|------|------|
|
|
| fetch_naver | `fetch_naver_node()` | `SkillCommands.fetch_naver_headlines(fmt="slack")` 호출 |
|
|
| fetch_sea | `fetch_sea_node()` | `SkillCommands.fetch_sea_headlines(fmt="json")` 호출 |
|
|
| format | `format_node()` | 네이버 헤드라인 텍스트 끝에 동남아 섹션 삽입 |
|
|
| send | `send_node()` | Slack `chat.postMessage` 전송 |
|
|
|
|
## Slack 봇 토큰 해소
|
|
- `_get_slack_bot_token_for_channel(channel_id)` → DB에서 채널별 workspace bot_token 조회.
|
|
- 실패 시 `settings.COMPANYX_SLACK_BOT_TOKEN` → `settings.SLACK_BOT_TOKEN` 순서로 폴백.
|
|
|
|
## 실패 분기
|
|
- 네이버 또는 동남아 API 호출 실패 시 `errors` 리스트에 기록하고 빈 텍스트로 진행.
|
|
- 보낼 텍스트가 비어 있으면 Slack 전송을 스킵하고 `message_ts=None`으로 종료.
|
|
- Slack 전송 실패 시 `errors`에 기록.
|
|
|
|
## 관련 문서
|
|
- [skill_news_briefing_request](../02_skills/skill_news_briefing_request.md)
|