docs: archive headlines langgraph plan and add troubleshooting

This commit is contained in:
Claude-51124 2026-02-05 17:25:03 +09:00
parent 2f24fee7bb
commit 1125f42f08
3 changed files with 58 additions and 19 deletions

View File

@ -14,6 +14,7 @@
5. **동남아 스타트업 뉴스 아침브리핑 (260129)** - 완료 → `plans/archive/260129_동남아_스타트업_뉴스_아침브리핑.md` 5. **동남아 스타트업 뉴스 아침브리핑 (260129)** - 완료 → `plans/archive/260129_동남아_스타트업_뉴스_아침브리핑.md`
6. **의도 런타임 하이브리드 (251023)** - 전체 완료 (260203) → `plans/archive/251023_happybell80_의도_런타임_하이브리드_임베딩_베이지안_동적학습.md` 6. **의도 런타임 하이브리드 (251023)** - 전체 완료 (260203) → `plans/archive/251023_happybell80_의도_런타임_하이브리드_임베딩_베이지안_동적학습.md`
7. **감정 시스템 Phase 13 (250808)** - 전체 완료 (260204) → `plans/archive/250808_감정시스템_현실적용_5단계_로드맵.md` 7. **감정 시스템 Phase 13 (250808)** - 전체 완료 (260204) → `plans/archive/250808_감정시스템_현실적용_5단계_로드맵.md`
8. **뉴스 브리핑 LangGraph 전환 (260202)** - 완료 (260205) → `plans/archive/260202_뉴스브리핑_LangGraph_전환.md`
--- ---
@ -75,4 +76,3 @@
- `book/300_architecture/315_테스트_원칙.md` - `book/300_architecture/315_테스트_원칙.md`
- `troubleshooting/251204_admin_dashboard_business_integration.md` (완료) - `troubleshooting/251204_admin_dashboard_business_integration.md` (완료)
- `troubleshooting/251110_claude_gemini_file_search_coldmail_integration.md` (완료) - `troubleshooting/251110_claude_gemini_file_search_coldmail_integration.md` (완료)

View File

@ -2,7 +2,7 @@
**날짜**: 2026-02-02 **날짜**: 2026-02-02
**작성자**: Claude **작성자**: Claude
**관련 파일**: `rb8001/app/services/skills/startup_news_skill.py`, `skill_news/app/services/sea_news_collector.py` **관련 파일**: `rb8001/app/services/skills/startup_news_skill.py`, `rb8001/app/services/workflows/headlines_workflow.py`, `rb8001/tests/test_headlines_workflow.py`
**원칙 참조** (구현 전 필수 확인): **원칙 참조** (구현 전 필수 확인):
- `311_백엔드_구조_원칙.md`: LangGraph 워크플로우 (섹션 5), 계층 분리 - `311_백엔드_구조_원칙.md`: LangGraph 워크플로우 (섹션 5), 계층 분리
- `312_문서_작성_원칙.md`: 핵심만 간결, 파일명:줄번호 - `312_문서_작성_원칙.md`: 핵심만 간결, 파일명:줄번호
@ -25,36 +25,49 @@
| 필드 | 타입 | 설명 | | 필드 | 타입 | 설명 |
|------|------|------| |------|------|------|
| channel_id | str | Slack 채널 ID | | channel_id | str | Slack 채널 ID |
| naver_items | List[Dict] | 깡프로 헤드라인 | | naver_items | List[Dict] | 깡프로 헤드라인(json) |
| sea_items | List[Dict] | 동남아 뉴스 | | naver_slack_text | str | 깡프로 슬랙 포맷 텍스트 |
| terms | Optional[List[str]] | 추출된 용어 | | sea_text | str | 동남아 섹션 텍스트 (없으면 "") |
| terms | List[str] | 추출된 용어 (없으면 []) |
| formatted_text | str | 최종 Slack 텍스트 | | formatted_text | str | 최종 Slack 텍스트 |
| message_ts | Optional[str] | 전송된 메시지 ts | | message_ts | Optional[str] | 전송된 메시지 ts |
| errors | List[str] | 각 단계 에러 | | errors | List[str] | 각 단계 에러 |
| extract_terms | bool | HEADLINES_EXTRACT_TERMS 결과 |
### 2.2 노드 구성 ### 2.2 노드 구성 (입출력/에러 규칙 고정)
1. **fetch_naver_node**: skill-news API 호출 → `naver_items` 반환 1. **fetch_naver_node**: `SkillCommands.fetch_naver_headlines(fmt="json")``naver_items` (실패 시 `errors` 추가, `naver_items=[]`)
2. **fetch_sea_node**: skill-news SEA API 호출 → `sea_items` 반환 (실패 시 빈 리스트, 에러 기록) 2. **fetch_sea_node**: `SkillCommands.fetch_sea_headlines(fmt="json")``sea_text` (실패 시 `errors` 추가, `sea_text=""`)
3. **extract_terms_node**: Gemini로 용어 추출 → `terms` 반환 (환경변수 체크) 3. **extract_terms_node**: `HEADLINES_EXTRACT_TERMS=true` & `naver_items` 존재 시 `GeminiHandler.extract_keywords()``terms` (실패 시 `errors` 추가, `terms=[]`)
4. **format_node**: 깡프로 + 동남아 + 용어 섹션 조합 → `formatted_text` 반환 4. **format_node**: `fetch_naver_headlines(fmt="slack")``naver_slack_text` 확보 후
5. **send_node**: Slack 전송 → `message_ts` 반환 및 로그 기록 - `_insert_sea_section_into_headlines_text()``sea_text` 삽입
- 기존 용어 삽입 규칙(명언 앞 삽입) 유지 → `formatted_text`
5. **send_node**: `WebClient(...).chat_postMessage()``message_ts` (실패 시 `errors` 추가)
### 2.3 라우팅 ### 2.3 워크플로우 인터페이스
- `create_workflow(checkpointer=None) -> CompiledGraph`
- `async run_headlines_workflow(channel_id: str, date: Optional[str] = None) -> Dict`
- 반환: `{"success": bool, "message_ts": Optional[str], "errors": List[str]}`
### 2.4 라우팅
``` ```
START → fetch_naver → fetch_sea → extract_terms → format → send → END START → fetch_naver → fetch_sea → extract_terms → format → send → END
``` ```
- `fetch_sea` 실패 시에도 계속 진행 (graceful degradation) - `fetch_sea` 실패 시에도 계속 진행 (graceful degradation)
- `extract_terms` 환경변수 false면 skip - `extract_terms` 환경변수 false면 skip (terms 유지)
- 각 노드 실패는 `errors` 리스트에 기록 - 각 노드 실패는 `errors` 리스트에 기록, 워크플로우 중단 금지
### 2.4 체크포인터 ### 2.5 체크포인터
- **AsyncSqliteSaver** 사용 (경량 워크플로우, 서버 재시작 시 재개 불필요) - **AsyncSqliteSaver** 사용 (경량 워크플로우, 서버 재시작 시 재개 불필요)
- DB 경로: `/code/checkpoints/headlines_workflow.db` - DB 경로: `settings.LANGGRAPH_STATE_DIR` + `headlines_graph.sqlite`
- thread_id: `f"headlines_{channel_id}_{date}"` - thread_id: `f"headlines:{channel_id}:{date}"` (date=Asia/Seoul `YYYY-MM-DD`)
### 2.6 로깅 기준
- 노드 시작/완료 시 항목 수/문자 수 로그
- 오류는 `errors` 누적 + logger.warning/error
--- ---
@ -62,11 +75,12 @@ START → fetch_naver → fetch_sea → extract_terms → format → send → EN
### Phase 1: 워크플로우 파일 생성 ### Phase 1: 워크플로우 파일 생성
- **파일**: `rb8001/app/services/workflows/headlines_workflow.py` (신규) - **파일**: `rb8001/app/services/workflows/headlines_workflow.py` (신규)
- **내용**: HeadlinesState, 5개 노드, StateGraph 정의 - **내용**: HeadlinesState, 5개 노드, StateGraph 정의, `create_workflow()`/`run_headlines_workflow()` 제공
### Phase 2: startup_news_skill.py 리팩토링 ### Phase 2: startup_news_skill.py 리팩토링
- **기존**: `run_headlines_job()` 함수 → **변경**: LangGraph 워크플로우 호출 - **기존**: `run_headlines_job()` 함수 → **변경**: LangGraph 워크플로우 호출
- **로그**: 각 노드별 실행 로그 + 최종 message_ts 로그 - **로그**: 각 노드별 실행 로그 + 최종 message_ts 로그
- **시그니처 유지**: `run_headlines_job(channel_id)` 유지 (스케줄러 변경 최소화)
### Phase 3: 테스트 (TDD 진행, 315_테스트_원칙.md) ### Phase 3: 테스트 (TDD 진행, 315_테스트_원칙.md)
- **파일**: `rb8001/tests/test_headlines_workflow.py` (신규) - **파일**: `rb8001/tests/test_headlines_workflow.py` (신규)
@ -74,7 +88,8 @@ START → fetch_naver → fetch_sea → extract_terms → format → send → EN
- 정상 플로우 (깡프로 + 동남아 + 용어) - 정상 플로우 (깡프로 + 동남아 + 용어)
- 동남아 실패 시 graceful degradation - 동남아 실패 시 graceful degradation
- 용어 추출 skip - 용어 추출 skip
- message_ts 로그 확인 - Slack 실패 시 errors 기록
- message_ts 반환 확인 (성공 케이스)
### Phase 4: 배포 및 검증 ### Phase 4: 배포 및 검증
- **배포**: `git push origin main` (rb8001) → Gitea Actions 자동 배포 - **배포**: `git push origin main` (rb8001) → Gitea Actions 자동 배포

View File

@ -0,0 +1,24 @@
# 뉴스 브리핑 LangGraph 전환 완료
**날짜**: 2026-02-05
**작성자**: happybell80
**관련 파일**: `rb8001/app/services/skills/startup_news_skill.py:1-15`, `rb8001/app/services/workflows/headlines_workflow.py:1-265`, `rb8001/tests/test_headlines_workflow.py:1-164`
---
## 문제 상황
- 뉴스 브리핑이 다단계 처리인데 일반 함수로만 구현되어 추적성과 복구성이 부족함
- 단계별 실패 지점을 분리 기록하기 어려움
## 해결 방안
- LangGraph 워크플로우로 수집→동남아 삽입→용어 추출→포맷→전송을 단계화
- 체크포인터 기반 thread_id로 실행 상태 추적
- TDD로 정상/SEA 실패/용어 스킵/Slack 실패 시나리오 검증
## 구현 완료
- 커밋: `91fb6ad` (2026-02-05)
- 배포 확인: `docker ps`에서 rb8001 컨테이너 재기동 및 healthy 확인
## 교훈
- 다단계 처리 로직은 LangGraph로 분리해야 추적성과 복구성이 확보됨
- 워크플로우 전환 시 기존 출력 포맷을 유지하는 테스트가 필수