From 419cac7a2847b02c054c73defd7c274a6f82b0ac Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Fri, 24 Oct 2025 14:37:31 +0900 Subject: [PATCH] =?UTF-8?q?docs(troubleshooting):=20rb8001=20LLM=20event-l?= =?UTF-8?q?oop=20errors=20+=20coldmail=20domain=20parsing=20fix=20(co.kr)?= =?UTF-8?q?=20=E2=80=94=20findings,=20fixes,=20validation,=20lessons=20(KS?= =?UTF-8?q?T=202025-10-24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...80_rb8001_llm_eventloop_coldmail_domain.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 troubleshooting/251024_happybell80_rb8001_llm_eventloop_coldmail_domain.md diff --git a/troubleshooting/251024_happybell80_rb8001_llm_eventloop_coldmail_domain.md b/troubleshooting/251024_happybell80_rb8001_llm_eventloop_coldmail_domain.md new file mode 100644 index 0000000..dfe2d25 --- /dev/null +++ b/troubleshooting/251024_happybell80_rb8001_llm_eventloop_coldmail_domain.md @@ -0,0 +1,60 @@ +# rb8001 운영 트러블슈팅: LLM 이벤트 루프 오류 및 콜드메일 도메인 파싱 개선 (KST 2025-10-24) + +## 상황 요약 +- 8001(rb8001) 최근 로그에서 LLM 호출 실패 다수 확인: “Event loop is closed”, “coroutine … was never awaited”. +- Slack 콜드메일 피드백 흐름에서 리스트 생성 시 회사설명에 ‘CO’가 들어간 사례 발생(발신자: kmkim@koreage.co.kr, 제목: “해요코리아 IR 자료 송부의 건.”). + +## 영향 범위 +- LLM 경로: Slack 대화 처리 중 간헐적 실패 → 사용자에게 폴백 메시지 노출(“죄송합니다. 요청을 처리할 수 없습니다.”). +- 콜드메일: IR 지표가 N/A일 때 도메인 폴백이 ‘CO’로 잘못 표기되어 리스트 정보 품질 저하. + +## 증거(근거 로그/지표) +- Docker(rb8001): ERROR “Gemini chat error: Event loop is closed”, RuntimeWarning “coroutine '..._invoke' was never awaited”. +- OpenSearch(dataprepper-static, UTC): 최근 6시간 내 rb8001에서 동일 에러 6건 확인. +- Health 체크: http://localhost:8001/health 200 OK, 관련 스킬(8505/8511/8515) healthy. + +## 원인 분석 +1) LLM 비동기 처리 버그(비동기/이벤트 루프 경계 문제) + - 비동기 API 호출 중 이벤트 루프 종료/부재 상황 발생 시 예외 처리 미흡 → 연쇄 실패. + - 일부 경로에서 await 누락으로 코루틴 미대기 경고 발생. + +2) 도메인 폴백 파싱 오류 + - ‘koreage.co.kr’에서 2레벨 추출 로직(parts[-2])이 ‘CO’를 회사명으로 오인 추출. + +## 조치 사항(코드 수정) +1) LLM 안정화 및 로깅 개선 + - GeminiHandler: async 실패(“Event loop is closed”, “no running event loop”, “was never awaited” 포함) 시 동기 `generate_content`를 `run_in_executor`로 폴백하여 안전 재시도. + - 파일: `rb8001/app/llm/gemini_handler.py` + - 주요 변경: async → sync executor 폴백, `logger.exception`로 스택트레이스 기록. + - Router/LLMService 로깅 강화: `logger.exception` 사용으로 예외 스택 기록. + - 파일: `rb8001/app/router/router.py`, `rb8001/app/llm/llm_service.py` + +2) 콜드메일 회사명 파싱 정확도 개선 + - `tldextract` 도입으로 eTLD+1 기준 파싱: `koreage.co.kr` → domain=`koreage` 확보. + - 폴백 휴리스틱: 국가코드+세컨드레벨(co|com|or|ne|ac|go|gov|edu)+ccTLD 패턴 시 `parts[-3]` 사용. + - 파일: `rb8001/app/services/coldmail_processor.py` + - 의존성 추가: `tldextract>=5.1.2` (파일: `rb8001/requirements.txt`) + +## 배포 및 검증 +- Gitea 푸시 후 서버(51124)에서 `docker compose up -d --build`로 재기동. +- 헬스 확인: `curl http://localhost:8001/health` 200 OK, 관련 스킬 포트도 200 OK. +- 회귀 확인: + - 최근 1분 로그 기준 “Event loop is closed/never awaited” 미발견. + - 컨테이너 내 파싱 테스트: `tldextract.extract('koreage.co.kr')` → `domain='koreage', suffix='co.kr'` 정상. + +## 커밋 참조 +- fix(llm): executor 폴백 + 예외 로깅 강화 — `eb27d1b` → 정리 `59baa69` +- fix(coldmail): 도메인 파싱에 tldextract 적용 — `9629253` + +## 후속 조치(제안) +- Slack 경로 실제 사용자 시나리오 재현 테스트(의도 UNKNOWN 빈도/LLM 실패율 모니터링). +- OpenSearch 대시보드/쿼리 저장: “Event loop is closed”, “never awaited” 알람 룰 추가(UTC↔KST 변환 유의). +- 9024(robeing-monitor) 헬스 엔드포인트 정합성 확인(404 응답 시 문서화 또는 구현). + +## 교훈(📌 Lessons Learned) +- 비동기 호출은 이벤트 루프 수명/스레드 경계, await 일관성 확보가 핵심이다(폴백 경로 필수). +- 로깅은 스택트레이스까지 남겨야 원인 추적이 가능하다. +- 이메일 도메인 파싱은 공공 접미사 목록(eTLD) 기반 라이브러리 사용으로 오인식 방지. + +— 작성: happybell80, KST 2025-10-24 +