# 콜드메일 IR 자동 분석 시나리오 **날짜**: 2025-10-14 **작성자**: happybell80 **관련 파일**: `rb8001/app/scheduler/jobs/coldmail_briefing.py` --- ## 최종 목표 시나리오 ### 평일 오전 9시 5분 자동 실행 1. NAVER WORKS 메일 중 콜드메일 감지 (Naive Bayes) 2. IR 첨부파일(PDF) 다운로드 3. skill-rag-file에 업로드 → 51123 HDD 영구 저장 4. AI 분석 실행: - 페이지별 요약 (RAG 기반 핵심 내용 추출) - 기업 평가 (사업분야, 재무, 기술 우위) - 베이지안 밸류에이션 및 신뢰도 계산 5. Slack Lists에 정리된 보고서 첨부: - 회사명, 이메일, IR 파일 - 밸류에이션, 신뢰도, 핵심 평가 6. Slack 채널에 요약 메시지 + 피드백 버튼 --- ## 구현 완료 (2025-10-14) - coldmail_briefing.py:78-302: 전체 플로우 - naverworks_file_processor.py:94-135: PDF → skill-rag-file 업로드 - ir_analyzer.py:86-168: RAG 6회 쿼리 → LLM 요약 - startup_valuation.py:63-173: 베이지안 VC Method - coldmail_briefing.py:191-221,241,246: Slack Lists 파일 첨부 - coldmail_briefing.py:262-285: Slack 피드백 버튼 - coldmail_filter.py:29-76: Naive Bayes 학습 --- ## 분석 결과 예시 굿베이션 (IT 인테리어 플랫폼): 27페이지 → 14 chunks, 밸류에이션 100억원 (30-300억원), 신뢰도 90% --- ## 구현 계획 ### 우선순위 1: Slack Lists 파일 첨부 (✅ 완료) - 상세: 251014_slack_lists_file_attachment.md - skill-rag-file/app/api/download.py: GET /api/download/{document_id} - skill-slack/app/api/endpoints/files.py: POST /files/upload (X-API-Key) - coldmail_briefing.py:191-221, 241, 246: document_id → file_id 변환 ### 우선순위 2: LLM JSON 파싱 에러 - ir_analyzer.py:155 다음 (156 전): llm_response 전처리 - cleaned = llm_response.strip() - if cleaned.startswith("```json"): cleaned = cleaned[7:] - if cleaned.startswith("```"): cleaned = cleaned[3:] - if cleaned.endswith("```"): cleaned = cleaned[:-3] - cleaned = cleaned.strip() - ir_analyzer.py:156: parsed = json.loads(cleaned) ### 우선순위 3: Slack 메시지에 IR 분석 결과 추가 - coldmail_briefing.py:144 다음: processed_results = [] 추가 - coldmail_briefing.py:188 다음: processed_results.append({...}) 추가 - company_name, ir_metrics.get("revenue"), valuation_result.median, valuation_result.confidence 포함 - coldmail_briefing.py:291 교체: - 기존: f"콜드메일 {processed_count}건 처리 완료 (...)" - 변경: summary_lines = [f"콜드메일 {processed_count}건 처리 완료"] - for item in processed_results: summary_lines.append(f"- {item['company']}: {item['median']}억원 (신뢰도 {int(item['confidence']*100)}%)") - summary_text = "\\n".join(summary_lines) ### 우선순위 4: LangGraph 워크플로우 - requirements.txt: langgraph 추가 - app/workflows/__init__.py: 빈 파일 생성 - app/workflows/coldmail_workflow.py 생성: - from typing import TypedDict; from langgraph.graph import StateGraph, END - class ColdmailState(TypedDict): emails: list, coldmail_candidates: list, processed_results: list, session: object - async def fetch_emails_node(state): coldmail_briefing.py:93-121 로직 이동 - async def filter_coldmail_node(state): coldmail_briefing.py:123-141 로직 이동 - async def process_email_node(state): coldmail_briefing.py:145-287 for 루프 내용 이동 - async def send_summary_node(state): coldmail_briefing.py:289-300 로직 이동 - graph = StateGraph(ColdmailState) - graph.add_node("fetch", fetch_emails_node), add_node("filter", filter_coldmail_node), add_node("process", process_email_node), add_node("send", send_summary_node) - graph.add_edge("fetch", "filter"), add_edge("filter", "process"), add_edge("process", "send"), add_edge("send", END) - workflow = graph.compile() - coldmail_briefing.py:78-302: _run_coldmail_briefing() 전체 교체 - from app.workflows.coldmail_workflow import workflow - initial_state = {"emails": [], "coldmail_candidates": [], "processed_results": [], "session": session} - result = await workflow.ainvoke(initial_state) --- ## 교훈 - 전체 시나리오 문서 먼저 작성 후 세부 구현 - LLM 응답은 항상 전처리 (마크다운 블록 제거)