DOCS/journey/troubleshooting/251024_happybell80_rb8001_llm_eventloop_coldmail_domain.md
Claude-51124 22557e7132 docs: 오래된 트러블슈팅 아카이브 및 구조 정리
- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동
- book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서)
- 빈 폴더 제거 (journey/assets/*)
2025-11-17 14:06:05 +09:00

3.9 KiB
Raw Permalink Blame History

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_contentrun_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