From c6335c9a1ec7c0ab3b05d92dd6252f9859fd8bf3 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 27 Apr 2026 07:35:27 +0900 Subject: [PATCH] =?UTF-8?q?fix(classify):=20law=5Fmonitor=20skip=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EB=B3=B5=EC=9B=90=20+=20tier=5Fbackfill?= =?UTF-8?q?=20law=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-B refactor 과정에서 e88640d 의 process() 진입부 source_channel='law_monitor' skip 분기가 사라져 매일 07:00 신규 법령 분할마다 26B legacy classify(8s) + 26B legacy summarize(10s) + 4B triage(1.5s) 전부 호출되고 있었다. 법령 분리 PR (stateless-churning-raccoon) 의 명제: "법령은 외부 source-of-truth + immutable + 자동 재수집 → 다른 수명주기" 와 일치하도록 process() 진입부에 skip 분기 복원. 최소 필드 (ai_domain='법령', ai_tags=['법령'], importance='medium') 만 세팅 후 return. queue_consumer 의 NEXT_STAGES['classify']=['embed','chunk'] 가 자동 chain 하므로 검색 영향 0. 법령 도메인 AI 산출물 가치 분석: - ai_summary: 법령 해석 환각 위험 (ASME/안전 엔지니어 사고 책임 소지) - ai_tldr/bullets: 이미 title 이 같은 정보 노출 — redundant - ai_inconsistencies: 공식 정합 문서라 100% false positive → 비용 (월 ~14분 26B 점유) 대비 가치 음수, skip 합당. tier_backfill.py 도 함께 수정: - DOMAIN_PRIORITY 에서 ('law', source_channel='law_monitor') 항목 제거 - safety 필터에 source_channel != 'law_monitor' 추가 (기존 ai_domain LIKE 'Industrial_Safety%' 매칭 안에 backfill 기 처리한 법령 doc 들이 잡혀 들어가는 case 차단) - 사유: skip 처리될 doc 을 enqueue 하면 야간마다 enqueue→skip→NULL→ enqueue 무한 루프 Co-Authored-By: Claude Opus 4.7 (1M context) --- app/workers/classify_worker.py | 18 ++++++++++++++++++ app/workers/tier_backfill.py | 11 +++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/workers/classify_worker.py b/app/workers/classify_worker.py index 723adc6..644e0c7 100644 --- a/app/workers/classify_worker.py +++ b/app/workers/classify_worker.py @@ -309,10 +309,28 @@ async def process(document_id: int, session: AsyncSession) -> None: 1) Legacy: classify() → ai_domain/document_type/ai_tags/ai_confidence/ai_suggestion 2) Legacy: summarize() → ai_summary 3) PR-B B-1: summary_triage (4B) → ai_tldr/ai_bullets/ai_analysis_tier='triage' + + 예외 — source_channel='law_monitor': + 법령은 외부 source-of-truth (law.go.kr) 보유 + immutable + 자동 재수집. + AI 분류는 무가치 + 본문 해석 환각 위험. 26B legacy + 4B triage 전부 skip. + 최소 필드만 세팅 후 return → queue_consumer 가 embed/chunk 자동 chain. + 참고: feedback_category_vs_ai_domain_axis.md, plan stateless-churning-raccoon.md. """ doc = await session.get(Document, document_id) if not doc: raise ValueError(f"문서 ID {document_id}를 찾을 수 없음") + + if doc.source_channel == "law_monitor": + if not doc.ai_domain: + doc.ai_domain = "법령" + if not doc.ai_tags: + doc.ai_tags = ["법령"] + if not doc.importance: + doc.importance = "medium" + await session.commit() + logger.info(f"doc {document_id}: law_monitor → classify skip") + return + if not doc.extracted_text: raise ValueError(f"문서 ID {document_id}: extracted_text가 비어있음") diff --git a/app/workers/tier_backfill.py b/app/workers/tier_backfill.py index 38d9686..3da1493 100644 --- a/app/workers/tier_backfill.py +++ b/app/workers/tier_backfill.py @@ -4,8 +4,11 @@ plan: ~/.claude/plans/swirling-swimming-liskov.md — 백필 장기 운영. 매 30분마다 트리거되어 (KST 00:00~06:00 시간대에만 실제 enqueue): 1. 우선순위 도메인별 NULL 문서 25건씩 classify 큐 재투입 - 2. 우선순위: safety > law > manual - (drive_sync / memo / news 는 별도 판단 — 본 스케줄러 제외) + 2. 우선순위: safety > manual + (drive_sync / memo / news / law_monitor 는 본 스케줄러 제외) + - news/memo: 분야 확정, classify 무가치 (legacy 결정) + - law_monitor: classify_worker 가 진입 시 skip 처리 (plan stateless-churning-raccoon.md). + backfill 에서 enqueue 해도 skip 만 반복되므로 시작부터 제외. 3. classify 큐가 이미 많으면 스킵 (MLX 부하 보호) 사유: @@ -43,9 +46,9 @@ BATCH_SIZE = 25 QUEUE_SKIP_THRESHOLD = 40 # 우선순위 도메인 (첫 번째가 후보 먼저 소진) +# law_monitor 제외: classify_worker 가 진입 시 skip — backfill 무한 루프 방지. DOMAIN_PRIORITY: list[tuple[str, str]] = [ - ("safety", "ai_domain LIKE 'Industrial_Safety%'"), - ("law", "source_channel = 'law_monitor'"), + ("safety", "ai_domain LIKE 'Industrial_Safety%' AND source_channel != 'law_monitor'"), ("manual", "source_channel = 'manual'"), ]