Skip to content

Conversation

@YHK0427
Copy link
Contributor

@YHK0427 YHK0427 commented Oct 23, 2025

AI 모듈의 finder, transform, xai를 연결하는 통합 파이프라인을 구축하고 뉴스 수집 방식을 개선함.

  1. 통합 파이프라인 구축
  • finder → transform → xai 순차 실행 파이프라인(libs/core/pipeline.py) 구현.
  • 부재중인 모듈(e.g., transform 모델)은 임시 데이터(mock)로 대체하고, 실제 코드는 주석 처리하여 스캐폴딩.
  1. 뉴스 수집 방식 리팩토링
  • requests와 BeautifulSoup 기반의 뉴스 수집 방식(news_processing_requests.py) 추가.
  • 기존 selenium 방식과 선택적 사용 가능.
  1. 버그 수정 및 안정성 향상
  • DB 쿼리: price_data 테이블을 참조하도록 스키마 오류 수정.
  • Import 경로: transform 모듈의 ModuleNotFoundError 해결.
  • URL 파싱: 표준 URL 형식으로 재조립하여 크롤링 안정성 개선.

테스트 방법
1 python libs/core/pipeline.py

  • pipeline.py에서 news_processing 또는 news_processing_requests를 선택적으로 import하여 테스트 가능.

향후 작업

  • 최종 데이터 처리 방식 추가 필요
    • 현재 파이프라인은 최종 XAI 보고서를 콘솔에 출력합니다. 이 결과물을 DB에 저장하거나 파일로 내보내는 등의 후처리 방식에 대한 논의 및 구현이 필요합니다.

Summary by CodeRabbit

  • 새로운 기능

    • 해외 주식 뉴스 자동 수집·텍스트 추출·LLM 요약(주간 요약) 기능 추가
  • 리팩토링

    • 파이프라인 흐름을 모듈화하여 단계별 실행(탐색 → 변환 → 설명가능성 리포트)으로 재구성
    • 로깅 대체 방식 도입으로 안정성 개선
  • 버그 수정

    • 시세 데이터 조회 쿼리 및 파라미터 사용 방식 조정
  • Chores

    • 요구 패키지 목록 정리 및 변경

- finder, transform, xai 모듈을 순차 실행하는 기본 파이프라인 구조를 구현합니다.

- DB 스키마 및 import 경로 등 발견된 오류를 수정합니다.

- 향후 실제 모듈 테스트를 위해 임시 데이터는 주석으로 남겨둡니다.
- 기존 selenium 기반 뉴스 수집 방식은 유지합니다.

- requests와 beautifulsoup를 사용하는 새로운 수집 방식(news_processing_requests.py)을 추가하여, import 구문 변경만으로 수집 방식을 선택할 수 있도록 개선합니다.
@YHK0427 YHK0427 requested a review from Kosw6 as a code owner October 23, 2025 21:19
@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

Walkthrough

파이프라인이 주간 발굴, 신호 변환, XAI 리포트의 모듈식 단계로 재구성되었고, 새로운 상위 오케스트레이터 run_pipeline이 단계들을 순차 실행하도록 추가되었습니다. OHLCV 데이터 조회 SQL과 뉴스 수집/요약 모듈, 요구 패키지 목록도 변경되었습니다.

Changes

응집군 / 파일(s) 변경 요약
파이프라인 재구성
AI/libs/core/pipeline.py
기존 일일/주간 스케줄·영속성 로직 제거. run_weekly_finder() -> List[str], run_signal_transform(tickers, config) -> pd.DataFrame, run_xai_report(decision_log) -> List[str], run_pipeline() (무인자) 추가. 외부 엔트리포인트(finder.main.run_finder, transform.main.run_transform, xai.run_xai) 사용.
데이터 접근 계층
AI/libs/utils/data/fetch_ohlcv.py
SQL 쿼리 대상 테이블을 stock_prices에서 public.price_data로 변경; WHERE 절에서 interval 필터 제거 및 파라미터 바인딩 축소.
뉴스 처리 모듈 추가
AI/libs/utils/news_processing_requests.py
신규 모듈: 네이버 해외 뉴스 크롤링, 본문 추출, 텍스트 정제, LLM 요약 생성 및 일별/주간 집계. 공개 API: get_weekly_news_summary(days, llm_client). 다수 헬퍼 함수와 예외 처리 포함.
로깅 폴백 및 임포트 정리
AI/transform/modules/main.py
외부 io._log 의존 주석처리 및 _log = print 폴백 삽입; TensorFlow import 순서 일부 조정(무의미한 재배치).
프로젝트 의존성(추가)
AI/requirements.txt
새/업데이트된 요구 패키지 목록에 pandas, psycopg2-binary, langchain-community, tqdm, selenium, webdriver-manager, numpy, scikit-learn, tensorflow, yfinance, groq, requests, beautifulsoup4 등 포함.
XAI 의존성 축소
AI/xai/requirements.txt
pandas, yfinance, numpy, groq 항목 삭제.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Pipeline
    participant Finder
    participant Transform
    participant OHLCV
    participant XAI
    participant Config

    Client->>Pipeline: run_pipeline()
    Pipeline->>Config: Load configs/config.json (optional)
    Config-->>Pipeline: config dict or {}

    rect rgb(220, 235, 255)
    Note over Pipeline,Finder: Phase 1 — Weekly Finder
    Pipeline->>Finder: run_weekly_finder()
    Finder-->>Pipeline: List[tickers]
    end

    rect rgb(220, 235, 255)
    Note over Pipeline,Transform: Phase 2 — Signal Transform
    Pipeline->>Transform: run_signal_transform(tickers, config)
    loop per ticker
        Transform->>OHLCV: fetch_ohlcv(ticker)
        OHLCV-->>Transform: DataFrame
    end
    Transform-->>Pipeline: decision_log (DataFrame)
    end

    rect rgb(220, 235, 255)
    Note over Pipeline,XAI: Phase 3 — XAI Report
    Pipeline->>XAI: run_xai_report(decision_log)
    XAI-->>Pipeline: List[reports] or error entries
    end

    Pipeline-->>Client: reports or None
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 분

주의할 파일/영역:

  • AI/libs/core/pipeline.py: 새 제어 흐름, 에러 처리, 외부 엔트리포인트 호출 방식 검증 필요
  • AI/libs/utils/news_processing_requests.py: 크롤링 셀렉터, 네트워크 예외, LLM 프롬프트·출력 안정성 점검
  • AI/libs/utils/data/fetch_ohlcv.py: SQL 변경(테이블명·파라미터)로 인한 데이터 적합성 및 성능 확인
  • 의존성 변경(AI/requirements.txt, AI/xai/requirements.txt): 런타임에서 필요 패키지 누락 여부 재확인

Possibly related PRs

Suggested reviewers

  • Kosw6

Poem

🐰 토끼가 말하네,
주간 발굴로 씨앗 뿌리고,
신호는 잎으로, 리포트는 꽃으로,
뉴스 바람 타고 주간을 엮네 —
모듈식 밭에 새봄이 왔구나! 🌱✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title Check ✅ Passed 제목 "[AI] SISC2-35 [FIX] AI 파트 통합 파이프라인"은 PR의 주요 변경사항인 AI 모듈들(finder → transform → xai)을 연결하는 통합 파이프라인 구축을 직접적으로 반영합니다. 핵심 내용인 "AI 파트 통합 파이프라인"은 간결하고 명확하며, 팀원들이 커밋 히스토리를 훑어볼 때 PR의 주요 목적을 쉽게 이해할 수 있습니다. 태그([AI], [FIX])와 티켓 참조(SISC2-35)는 프로젝트의 일반적인 관례이며, 뉴스 처리 리팩토링이나 버그 수정 같은 세부사항이 포함되지 않은 것은 PR 가이드라인에서 예상하는 수준입니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC2-35-AI-김영헌-AI-파트-통합-파이프라인

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
AI/transform/modules/main.py (1)

163-170: 컬럼 소문자화 이전에 타임스탬프 컬럼 탐지 → 대소문자 혼용 입력 시 실패 위험.

현재 ts_col을 찾은 뒤에 컬럼을 소문자화합니다. 입력 DF에 Date 등 대문자 컬럼이 오면 ts_col is None로 예외가 납니다. 소문자화 → 컬럼 탐지 순서로 교체하세요.

-    df = raw_data.copy()
-    ts_col = "ts_local" if "ts_local" in df.columns else ("date" if "date" in df.columns else None)
-    if ts_col is None:
-        raise ValueError("raw_data에 'ts_local' 또는 'date' 컬럼이 필요합니다.")
-    df[ts_col] = pd.to_datetime(df[ts_col])
-    df = df.rename(columns={c: c.lower() for c in df.columns})
+    df = raw_data.copy()
+    # 1) 우선 컬럼명을 소문자화
+    df = df.rename(columns={c: c.lower() for c in df.columns})
+    # 2) 그 후 타임스탬프 컬럼 탐지
+    ts_col = "ts_local" if "ts_local" in df.columns else ("date" if "date" in df.columns else None)
+    if ts_col is None:
+        raise ValueError("raw_data에 'ts_local' 또는 'date' 컬럼이 필요합니다.")
+    df[ts_col] = pd.to_datetime(df[ts_col])
🧹 Nitpick comments (10)
AI/libs/utils/data/fetch_ohlcv.py (2)

17-23: interval 인자/설명 불일치(더 이상 사용 안 함).

SQL에서 인터벌 필터가 제거되었는데 함수 시그니처와 Docstring에는 여전히 남아 있습니다. 혼선을 줄이기 위해 Docstring에 “deprecated/미사용” 표기 또는 인자 제거를 제안합니다(제거 시 상위 호출부 영향 확인 필요).

-    interval: str = "1d",
+    interval: str = "1d",  # deprecated, unused
@@
-        interval (str): 데이터 간격 ('1d' 등)
+        interval (str): (deprecated) 현재 무시됩니다.

Also applies to: 27-36


37-51: DB 커넥션 누수 방지 및 예외 안전성 향상(컨텍스트 매니저).

with 문을 사용해 커넥션을 자동 종료하면 예외 시에도 안전합니다.

-    conn = get_db_conn(config)
-
-    query = """
+    query = """
         SELECT date, open, high, low, close, volume
         FROM public.price_data
         WHERE ticker = %s
           AND date BETWEEN %s AND %s
         ORDER BY date;
     """
 
-    # 파라미터 바인딩 (%s) 사용 → SQL injection 방지
-    df = pd.read_sql(query, conn, params=(ticker, start, end))
-
-    conn.close()
-    return df
+    # 파라미터 바인딩 (%s) 사용 → SQL injection 방지
+    with get_db_conn(config) as conn:
+        df = pd.read_sql(query, conn, params=(ticker, start, end))
+        return df
AI/libs/core/pipeline.py (3)

9-11: sys.path 주입 중복 방지.

동일 경로가 여러 번 추가되지 않도록 가드하세요.

-project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-sys.path.append(project_root)
+project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+if project_root not in sys.path:
+    sys.path.append(project_root)

24-28: 불필요한 f-string 제거(F541).

플레이스홀더 없는 f-string은 일반 문자열로 변경하세요. Ruff F541.

-    print("--- [PIPELINE-STEP 1] Finder 모듈 실행 시작 ---")
+    print("--- [PIPELINE-STEP 1] Finder 모듈 실행 시작 ---")
@@
-    print(f"--- [PIPELINE-STEP 1] Finder 모듈 실행 완료 ---")
+    print("--- [PIPELINE-STEP 1] Finder 모듈 실행 완료 ---")

86-114: XAI 단계: f-string(F541) 및 광범위 예외(BLE001).

  • 플레이스홀더 없는 f-string을 일반 문자열로 교체하세요. Ruff F541.
  • 광범위 except Exception은 가능한 한 구체화가 바람직합니다. 당장 축소가 어렵다면 TODO로 명시해 추후 run_xai가 던질 수 있는 예외군으로 좁히는 것을 권장합니다.
-    print("--- [PIPELINE-STEP 3] XAI 리포트 생성 시작 ---")
+    print("--- [PIPELINE-STEP 3] XAI 리포트 생성 시작 ---")
@@
-    print(f"--- [PIPELINE-STEP 3] XAI 리포트 생성 완료 ---")
+    print("--- [PIPELINE-STEP 3] XAI 리포트 생성 완료 ---")

추가로, except Exception as e: 구문 위에 다음과 같은 주석을 남겨 향후 축소 계획을 표시하는 것을 권장합니다.

# TODO: run_xai에서 발생 가능한 예외군으로 예외 범위 축소(ValueError, TimeoutError 등)
AI/libs/utils/news_processing_requests.py (4)

63-67: wdate 선택자 None 가드 추가.

요약 영역에 span.wdate가 없는 경우 AttributeError가 발생할 수 있습니다. 안전 가드를 추가하세요.

-                    if i < len(summaries):
-                        time_list.append(summaries[i].find('span', class_='wdate').text.strip())
+                    if i < len(summaries):
+                        w = summaries[i].find('span', class_='wdate')
+                        time_list.append(w.text.strip() if w else '')
                     else:
                         time_list.append('')

154-155: 불리언 비교 스타일(E712).

== False 대신 notnull()/isnull()을 사용하세요. 현재 표현식은 가독성과 정확성이 떨어집니다.

-    valid_df = href_df[href_df['content'].isnull()==False].reset_index(drop=True)
+    valid_df = href_df[href_df['content'].notnull()].reset_index(drop=True)

17-29: 타입 힌트/사용 불일치: target_date는 문자열(YYYYMMDD)로 사용됩니다.

함수는 datetime을 받도록 선언했지만 실제로는 문자열을 전달/사용합니다. 타입 일치로 혼란을 줄이세요.

-def _news_href_crawl(target_date: datetime) -> pd.DataFrame:
+def _news_href_crawl(target_date: str) -> pd.DataFrame:
@@
-        "date": target_date
+        "date": target_date

143-161: 타입 힌트/사용 불일치: _collect_and_summarize_news의 target_date.

여기도 동일하게 문자열을 사용합니다. 시그니처/Docstring을 정합화하세요.

-def _collect_and_summarize_news(target_date: datetime, llm_client) -> pd.DataFrame:
+def _collect_and_summarize_news(target_date: str, llm_client) -> pd.DataFrame:
@@
-    지정된 날짜의 해외증시 뉴스를 수집하고 LLM으로 요약하는 함수
+    지정된 날짜(YYYYMMDD)의 해외증시 뉴스를 수집하고 LLM으로 요약하는 함수
AI/transform/modules/main.py (1)

145-154: 미사용 매개변수 pred_h, interval 정리 권장 확인됨.

코드 검증 결과, 두 매개변수 모두 함수 내에서 실제로 사용되지 않습니다:

  • pred_h: 호출부(AI/libs/core/pipeline.py:54)에서 pred_h=1로 전달되지만, 함수 본체에서 참조 없음
  • interval: 기본값 "1d"로 정의되어 있으나 함수 로직 어디서도 사용되지 않음

즉시 제거하거나, 향후 사용을 대비한 의도 표시로 _ = (pred_h, interval) 추가를 권장합니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f287e4 and f01dbcc.

📒 Files selected for processing (6)
  • AI/libs/core/pipeline.py (1 hunks)
  • AI/libs/utils/data/fetch_ohlcv.py (1 hunks)
  • AI/libs/utils/news_processing_requests.py (1 hunks)
  • AI/requirements.txt (1 hunks)
  • AI/transform/modules/main.py (1 hunks)
  • AI/xai/requirements.txt (0 hunks)
💤 Files with no reviewable changes (1)
  • AI/xai/requirements.txt
🧰 Additional context used
🧬 Code graph analysis (1)
AI/libs/core/pipeline.py (4)
AI/finder/main.py (1)
  • run_finder (14-44)
AI/transform/modules/main.py (1)
  • run_transform (145-272)
AI/libs/utils/data/fetch_ohlcv.py (1)
  • fetch_ohlcv (17-51)
AI/xai/run_xai.py (1)
  • run_xai (29-33)
🪛 Ruff (0.14.1)
AI/libs/utils/news_processing_requests.py

37-37: Probable use of requests call without timeout

(S113)


79-79: Probable use of requests call without timeout

(S113)


154-154: Avoid equality comparisons to False; use not href_df['content'].isnull(): for false checks

Replace with not href_df['content'].isnull()

(E712)


183-183: Do not catch blind exception: Exception

(BLE001)


211-211: Do not catch blind exception: Exception

(BLE001)

AI/libs/core/pipeline.py

27-27: f-string without any placeholders

Remove extraneous f prefix

(F541)


79-79: f-string without any placeholders

Remove extraneous f prefix

(F541)


89-89: Avoid specifying long messages outside the exception class

(TRY003)


109-109: Do not catch blind exception: Exception

(BLE001)


113-113: f-string without any placeholders

Remove extraneous f prefix

(F541)

Comment on lines +30 to +61
def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame:
"""
종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그)를 반환합니다.
"""
print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")

# --- 실제 Transform 모듈 호출 ---
end_date = datetime.now()
start_date = end_date - timedelta(days=600)
all_ohlcv_df = []
for ticker in tickers:
ohlcv_df = fetch_ohlcv(
ticker=ticker,
start=start_date.strftime('%Y-%m-%d'),
end=end_date.strftime('%Y-%m-%d'),
config=config
)
ohlcv_df['ticker'] = ticker
all_ohlcv_df.append(ohlcv_df)
if not all_ohlcv_df:
print("OHLCV 데이터를 가져오지 못했습니다.")
return pd.DataFrame()
raw_data = pd.concat(all_ohlcv_df, ignore_index=True)
finder_df = pd.DataFrame(tickers, columns=['ticker'])
transform_result = run_transform(
finder_df=finder_df,
seq_len=60,
pred_h=1,
raw_data=raw_data,
config=config
)
logs_df: pd.DataFrame = tr["logs"] # (종목,날짜,매매여부,가격,비중,피쳐...,확률...)

# Transform 로그 저장 (Parquet)
out_dir = os.path.join(config["storage"]["out_dir"], "transform")
os.makedirs(out_dir, exist_ok=True)
log_path = os.path.join(out_dir, f"logs_{run_date}.parquet")
logs_df.to_parquet(log_path, index=False)

# XAI 리포트 생성 + 저장 (JSON per ticker)
xai_out_dir = os.path.join(config["storage"]["out_dir"], "xai", run_date)
os.makedirs(xai_out_dir, exist_ok=True)

xai_reports = run_xai(logs_df)
for ticker, report in xai_reports.items():
with open(os.path.join(xai_out_dir, f"{ticker}.json"), "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)

_log(f"[DAILY] Transform 로그 + XAI 저장 완료 ({run_date})")

# ==============================================
# 메인 파이프라인
# ==============================================
def run_pipeline(config: dict) -> bool:
run_date = dt.datetime.now(dt.timezone(dt.timedelta(hours=9))).strftime("%Y-%m-%d")

logs_df = transform_result.get("logs", pd.DataFrame())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

DB 설정 누락 시 즉시 크래시(핵심 경로 가드 필요).

config=None인 상태에서 fetch_ohlcv 호출 시 TypeError: 'NoneType' is not subscriptable로 파이프라인이 중단됩니다. 설정 부재 시 조기 반환하도록 가드하세요. 이는 데모/테스트 실행 안정성에 중요합니다.

-def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame:
+def run_signal_transform(tickers: List[str], config: Dict | None) -> pd.DataFrame:
@@
-    print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
+    print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
+    if not config or "db" not in config:
+        print("[WARN] DB 설정이 없어 Transform 단계를 건너뜁니다.")
+        return pd.DataFrame()
@@
-    for ticker in tickers:
-        ohlcv_df = fetch_ohlcv(
-            ticker=ticker,
-            start=start_date.strftime('%Y-%m-%d'),
-            end=end_date.strftime('%Y-%m-%d'),
-            config=config
-        )
-        ohlcv_df['ticker'] = ticker
-        all_ohlcv_df.append(ohlcv_df)
+    for ticker in tickers:
+        try:
+            ohlcv_df = fetch_ohlcv(
+                ticker=ticker,
+                start=start_date.strftime('%Y-%m-%d'),
+                end=end_date.strftime('%Y-%m-%d'),
+                config=config,
+            )
+            if ohlcv_df.empty:
+                continue
+            ohlcv_df['ticker'] = ticker
+            all_ohlcv_df.append(ohlcv_df)
+        except Exception as e:
+            print(f"[WARN] {ticker} OHLCV 조회 실패: {e}")
+            continue
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def run_signal_transform(tickers: List[str], config: Dict) -> pd.DataFrame:
"""
종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그) 반환합니다.
"""
print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
# --- 실제 Transform 모듈 호출 ---
end_date = datetime.now()
start_date = end_date - timedelta(days=600)
all_ohlcv_df = []
for ticker in tickers:
ohlcv_df = fetch_ohlcv(
ticker=ticker,
start=start_date.strftime('%Y-%m-%d'),
end=end_date.strftime('%Y-%m-%d'),
config=config
)
ohlcv_df['ticker'] = ticker
all_ohlcv_df.append(ohlcv_df)
if not all_ohlcv_df:
print("OHLCV 데이터를 가져오지 못했습니다.")
return pd.DataFrame()
raw_data = pd.concat(all_ohlcv_df, ignore_index=True)
finder_df = pd.DataFrame(tickers, columns=['ticker'])
transform_result = run_transform(
finder_df=finder_df,
seq_len=60,
pred_h=1,
raw_data=raw_data,
config=config
)
logs_df: pd.DataFrame = tr["logs"] # (종목,날짜,매매여부,가격,비중,피쳐...,확률...)
# Transform 로그 저장 (Parquet)
out_dir = os.path.join(config["storage"]["out_dir"], "transform")
os.makedirs(out_dir, exist_ok=True)
log_path = os.path.join(out_dir, f"logs_{run_date}.parquet")
logs_df.to_parquet(log_path, index=False)
# XAI 리포트 생성 + 저장 (JSON per ticker)
xai_out_dir = os.path.join(config["storage"]["out_dir"], "xai", run_date)
os.makedirs(xai_out_dir, exist_ok=True)
xai_reports = run_xai(logs_df)
for ticker, report in xai_reports.items():
with open(os.path.join(xai_out_dir, f"{ticker}.json"), "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
_log(f"[DAILY] Transform 로그 + XAI 저장 완료 ({run_date})")
# ==============================================
# 메인 파이프라인
# ==============================================
def run_pipeline(config: dict) -> bool:
run_date = dt.datetime.now(dt.timezone(dt.timedelta(hours=9))).strftime("%Y-%m-%d")
logs_df = transform_result.get("logs", pd.DataFrame())
def run_signal_transform(tickers: List[str], config: Dict | None) -> pd.DataFrame:
"""
종목 리스트를 받아 Transform 모듈을 실행하고, 신호(결정 로그) 반환합니다.
"""
print("--- [PIPELINE-STEP 2] Transform 모듈 실행 시작 ---")
if not config or "db" not in config:
print("[WARN] DB 설정이 없어 Transform 단계를 건너뜁니다.")
return pd.DataFrame()
# --- 실제 Transform 모듈 호출 ---
end_date = datetime.now()
start_date = end_date - timedelta(days=600)
all_ohlcv_df = []
for ticker in tickers:
try:
ohlcv_df = fetch_ohlcv(
ticker=ticker,
start=start_date.strftime('%Y-%m-%d'),
end=end_date.strftime('%Y-%m-%d'),
config=config,
)
if ohlcv_df.empty:
continue
ohlcv_df['ticker'] = ticker
all_ohlcv_df.append(ohlcv_df)
except Exception as e:
print(f"[WARN] {ticker} OHLCV 조회 실패: {e}")
continue
if not all_ohlcv_df:
print("OHLCV 데이터를 가져오지 못했습니다.")
return pd.DataFrame()
raw_data = pd.concat(all_ohlcv_df, ignore_index=True)
finder_df = pd.DataFrame(tickers, columns=['ticker'])
transform_result = run_transform(
finder_df=finder_df,
seq_len=60,
pred_h=1,
raw_data=raw_data,
config=config
)
logs_df = transform_result.get("logs", pd.DataFrame())
🤖 Prompt for AI Agents
In AI/libs/core/pipeline.py around lines 30 to 61, the function
run_signal_transform assumes config is present and passes it to fetch_ohlcv,
causing a TypeError when config is None; add an early guard at the top of the
function that checks for a missing or invalid config (e.g., if config is None or
required keys like DB/settings are absent), log/print a clear message about the
missing configuration, and immediately return an empty pd.DataFrame to avoid
calling fetch_ohlcv with None; this prevents crashes for demo/test runs and
ensures the pipeline fails gracefully.

Comment on lines +37 to +40
response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'})
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

네트워크 호출 타임아웃 누락(S113).

외부 호출에 타임아웃이 없어 무기한 블로킹 위험이 있습니다. 합리적인 타임아웃을 지정하세요.

-        response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'})
+        response = requests.get(
+            base_url,
+            params=params,
+            headers={'User-Agent': 'Mozilla/5.0'},
+            timeout=10,
+        )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = requests.get(base_url, params=params, headers={'User-Agent': 'Mozilla/5.0'})
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
response = requests.get(
base_url,
params=params,
headers={'User-Agent': 'Mozilla/5.0'},
timeout=10,
)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
🧰 Tools
🪛 Ruff (0.14.1)

37-37: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
In AI/libs/utils/news_processing_requests.py around lines 37-40, the
requests.get call lacks a timeout which can block indefinitely; add a reasonable
timeout to the call (for example timeout=10 or a connect/read tuple like
timeout=(5,10)) and update the call to requests.get(base_url, params=params,
headers=..., timeout=...) ; also wrap the network call in a try/except to catch
requests.Timeout and requests.RequestException and handle/log or re-raise
appropriately so callers won't hang indefinitely.

Comment on lines +79 to +82
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

네트워크 호출 타임아웃 누락(S113).

본문 수집 요청에도 동일하게 타임아웃을 지정하세요.

-            response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
+            response = requests.get(
+                url,
+                headers={'User-Agent': 'Mozilla/5.0'},
+                timeout=10,
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
response = requests.get(
url,
headers={'User-Agent': 'Mozilla/5.0'},
timeout=10,
)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
🧰 Tools
🪛 Ruff (0.14.1)

79-79: Probable use of requests call without timeout

(S113)

🤖 Prompt for AI Agents
In AI/libs/utils/news_processing_requests.py around lines 79 to 82, the
requests.get call lacks a timeout which can hang the process; update the call to
include a timeout parameter (e.g., timeout=10 or use a configured TIMEOUT
constant) so requests.get(url, headers=..., timeout=TIMEOUT). Ensure the chosen
timeout value is reasonable for your environment and keep
response.raise_for_status() and parsing logic unchanged.

Comment on lines +1 to +11
pandas
psycopg2-binary
langchain-community
tqdm
selenium
webdriver-manager
numpy
scikit-learn
tensorflow
yfinance
groq
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

뉴스 수집 모듈 필수 의존성 누락(런타임 실패 가능).

AI/libs/utils/news_processing_requests.py에서 requests, bs4(beautifulsoup4)를 사용하지만 본 파일에 미기재입니다. 현재 상태로는 파이프라인 1단계(Finder) 실행 시 ImportError/ModuleNotFoundError가 납니다. 최소한 아래 패키지를 추가하세요. 또한 버전 고정은 추후 constraints.txt로 관리 권장.

 pandas
 psycopg2-binary
 langchain-community
 tqdm
 selenium
 webdriver-manager
 numpy
 scikit-learn
 tensorflow
 yfinance
 groq
+requests
+beautifulsoup4
+# HTML parser 가속/안정성 개선용 (선택)
+lxml

다음 스크립트로 의존성-사용처 미스매치 여부를 점검해 주세요.


🏁 Script executed:

#!/bin/bash
echo "[imports needing deps]"
rg -nP --type=python -C1 '(^\s*import requests\b)|(^\s*from\s+bs4\s+import\s+BeautifulSoup\b)' AI | sed 's/^/  /'
echo
echo "[requirements]"
nl -ba AI/requirements.txt
echo
echo "[check presence]"
grep -Eq '^\s*requests(\b|[=<>~])' AI/requirements.txt || echo "MISSING: requests"
grep -Eq '^\s*beautifulsoup4(\b|[=<>~])' AI/requirements.txt || echo "MISSING: beautifulsoup4"

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Find the file and check its imports
echo "=== Checking for news_processing_requests.py ==="
find . -name "news_processing_requests.py" -type f 2>/dev/null | head -5
echo
echo "=== Content of AI/libs/utils/news_processing_requests.py ==="
if [ -f "AI/libs/utils/news_processing_requests.py" ]; then
  head -50 AI/libs/utils/news_processing_requests.py
else
  echo "File not found"
fi
echo
echo "=== Current AI/requirements.txt ==="
cat AI/requirements.txt
echo
echo "=== Verification: Search for requests/bs4 imports ==="
grep -n "import requests\|from bs4" AI/libs/utils/news_processing_requests.py 2>/dev/null || echo "No matches found"

Length of output: 334



---

🏁 Script executed:

```shell
#!/bin/bash
# Use corrected paths based on find output
echo "=== Content of news_processing_requests.py ==="
cat ./git/AI/libs/utils/news_processing_requests.py
echo
echo "=== Current AI/requirements.txt ==="
cat ./git/AI/requirements.txt
echo
echo "=== Search for requests/bs4 imports ==="
grep -n "import requests\|from bs4" ./git/AI/libs/utils/news_processing_requests.py

Length of output: 343


뉴스 수집 모듈 필수 의존성 누락 - 즉시 추가 필요.

AI/requirements.txt에서 requestsbeautifulsoup4가 누락되어 있습니다. 런타임에 ImportError/ModuleNotFoundError가 발생합니다. 다음 패키지를 추가하세요.

 pandas
 psycopg2-binary
 langchain-community
 tqdm
 selenium
 webdriver-manager
 numpy
 scikit-learn
 tensorflow
 yfinance
 groq
+requests
+beautifulsoup4
+lxml
  • requests: HTTP 요청 라이브러리
  • beautifulsoup4: HTML 파싱 라이브러리 (bs4 모듈 제공)
  • lxml: 선택사항, HTML 파서 성능 개선용
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pandas
psycopg2-binary
langchain-community
tqdm
selenium
webdriver-manager
numpy
scikit-learn
tensorflow
yfinance
groq
pandas
psycopg2-binary
langchain-community
tqdm
selenium
webdriver-manager
numpy
scikit-learn
tensorflow
yfinance
groq
requests
beautifulsoup4
lxml
🤖 Prompt for AI Agents
In AI/requirements.txt lines 1-11, the news-collection module is missing
HTTP/HTML parsing deps; add requests and beautifulsoup4 to the requirements (and
optionally lxml for improved parsing) so imports don't raise
ImportError/ModuleNotFoundError at runtime; append these packages to the file
(or insert in alphabetical order) and run your dependency installation/test to
confirm the news scraper imports successfully.

requests
beautifulsoup4
누락 되어 있어서 수정
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
AI/requirements.txt (2)

1-13: 버전 고정 없음 — 재현성 및 안정성 저하 가능.

모든 의존성이 버전 지정자 없이 나열되어 있어, 패키지 설치 시점에 따라 다른 버전이 설치될 수 있습니다. 특히 주요 버전 업그레이드 시 예기치 않은 동작 변경이나 호환성 문제가 발생할 수 있습니다.

추천: 각 패키지에 정확한 버전을 고정하거나, 향후 constraints.txt를 도입하여 명시적으로 관리하세요.

-pandas
-psycopg2-binary
-langchain-community
+pandas==2.1.4
+psycopg2-binary==2.9.9
+langchain-community==0.2.5
 tqdm
 selenium
 webdriver-manager
 numpy
 scikit-learn
 tensorflow
 yfinance
 groq
 requests
 beautifulsoup4

12-13: 선택사항: HTML 파싱 성능 개선용 lxml 추가 검토.

이전 리뷰에서 제안한 lxml은 BeautifulSoup의 HTML 파서로 사용되어 파싱 속도 및 안정성을 향상시킵니다. 뉴스 크롤링 성능이 중요하다면 추가를 고려하세요.

 requests
 beautifulsoup4
+lxml
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f01dbcc and e5b0575.

📒 Files selected for processing (1)
  • AI/requirements.txt (1 hunks)
🔇 Additional comments (1)
AI/requirements.txt (1)

12-13: ✅ 이전 리뷰의 필수 의존성 누락 문제 해결됨.

이전 리뷰에서 지적된 requestsbeautifulsoup4가 이제 추가되었습니다. 뉴스 수집 모듈의 런타임 ImportError 문제가 해결되었습니다.

@twq110 twq110 changed the title SISC2-35-AI-김영헌-AI-파트-통합-파이프라인 [AI] SISC2-35-[FIX]-AI-파트-통합-파이프라인 Oct 27, 2025
@twq110 twq110 changed the title [AI] SISC2-35-[FIX]-AI-파트-통합-파이프라인 [AI]-SISC2-35-[FIX]-AI-파트-통합-파이프라인 Oct 27, 2025
@twq110 twq110 changed the title [AI]-SISC2-35-[FIX]-AI-파트-통합-파이프라인 SISC2-35-AI-파트-통합-파이프라인 Oct 27, 2025
@twq110 twq110 changed the title SISC2-35-AI-파트-통합-파이프라인 [AI] SISC2-35 [FIX] AI-파트-통합-파이프라인 Oct 27, 2025
@twq110 twq110 changed the title [AI] SISC2-35 [FIX] AI-파트-통합-파이프라인 [AI] SISC2-35 [FIX] AI 파트 통합 파이프라인 Oct 27, 2025
@twq110
Copy link
Contributor

twq110 commented Oct 27, 2025

고생했습니당

@twq110 twq110 merged commit 1a77d95 into main Oct 27, 2025
2 checks passed
@twq110 twq110 deleted the SISC2-35-AI-김영헌-AI-파트-통합-파이프라인 branch October 27, 2025 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants