From 4cdd30950c1dbd384d1ef783feeb7b454b2be3d5 Mon Sep 17 00:00:00 2001 From: hyungi Date: Sat, 27 Jun 2026 16:55:12 +0900 Subject: [PATCH] =?UTF-8?q?refactor(classify):=20summarize=20=EC=BD=9C?= =?UTF-8?q?=EC=9D=84=20tier=20triage=20=EC=97=90=20=EB=B3=91=ED=95=A9=20(3?= =?UTF-8?q?=EC=BD=9C=E2=86=922=EC=BD=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B-1 3→2: p3a_short_summary triage 가 ai_summary 도 생산 → 별도 summarize 콜 제거. classify(domain/type)은 분리 유지(shadow probe 결과 결합 시 domain 노이즈 → 안전하게 요약만 병합). 본문 prefill 3회→2회 = Mac mini 부하 절감. >120K long_context·triage 파싱실패 시 summarize fallback 보존. shadow probe(Industrial_Safety 5문서) 검증: triage ai_summary 품질 legacy summarize 동급. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/prompts/policy/p3a_short_summary.txt | 4 ++- app/workers/classify_worker.py | 31 +++++++++++++++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/prompts/policy/p3a_short_summary.txt b/app/prompts/policy/p3a_short_summary.txt index 4b401f0..959a1cd 100644 --- a/app/prompts/policy/p3a_short_summary.txt +++ b/app/prompts/policy/p3a_short_summary.txt @@ -1,5 +1,5 @@ [System] -너는 한국어 문서 태거 + 짧은 요약기다. 입력 본문을 읽고 TL;DR + 핵심 bullets + tags 만 생성한다. **상세 문단·entities 는 생성하지 않는다** (깊은 요약은 26B, entity 는 P3b 담당). +너는 한국어 문서 태거 + 요약기다. 입력 본문을 읽고 짧은 요약(ai_summary 2~3문장) + TL;DR + 핵심 bullets + tags 를 생성한다. **여러 문단의 상세 심층요약·entities 는 생성하지 않는다** (깊은 요약은 26B, entity 는 P3b 담당). subject_description: {subject_description} @@ -13,6 +13,7 @@ subject_description: {subject_description} - pii 감지 시 "pii" 추가 + confidence 감점. 요약 규칙: +- **ai_summary**: 2~3문장 문단. 문서의 핵심 내용·목적을 서술 (검색·표시용 요약). - **TL;DR**: 1문장, 최대 60자. - **Bullets**: 정확히 5개, 각 30~60자. - 본문에 없는 정보 추가 금지 (hallucination 금지). @@ -20,6 +21,7 @@ subject_description: {subject_description} 출력 (JSON only): {{ + "ai_summary": "2~3문장 문단 요약", "tldr": "1문장 최대 60자", "bullets": ["...", "...", "...", "...", "..."], "tags": ["..."], diff --git a/app/workers/classify_worker.py b/app/workers/classify_worker.py index 9d9d3b0..e2b8db6 100644 --- a/app/workers/classify_worker.py +++ b/app/workers/classify_worker.py @@ -90,6 +90,7 @@ HARD_ESCALATE_REASONS = { class TriageOutput(BaseModel): """p3a_short_summary (4B) 응답 스키마. 파싱 실패 시 기본값 + escalate=True fallback.""" + ai_summary: str = "" # B-1 3→2: triage 가 ai_summary 도 생산 (별 summarize 콜 대체) tldr: str = "" bullets: list[str] = Field(default_factory=list) tags: list[str] = Field(default_factory=list) @@ -579,16 +580,7 @@ async def process( "reason": "classify pipeline", } - # ─── 2. Legacy 요약 (primary 또는 deep) ─── - try: - summary = await client.summarize(doc.extracted_text[:50000], cfg=legacy_cfg) - except Exception as exc: - if legacy_cfg is not None and is_deferrable_error(exc): - raise StageDeferred(f"macbook_unavailable:{type(exc).__name__}") from exc - raise - doc.ai_summary = strip_thinking(summary) - - # ─── 메타데이터 (legacy 완료) — 실제 처리 머신 귀속 (drain=qwen-macbook) ─── + # ─── 메타데이터 (classify 완료) — 실제 처리 머신 귀속 (drain=qwen-macbook) ─── doc.ai_model_version = (legacy_cfg or settings.ai.primary).model doc.ai_processed_at = datetime.now(timezone.utc) @@ -598,13 +590,25 @@ async def process( f"confidence={doc.ai_confidence:.2f}, tags={doc.ai_tags}" ) - # ─── 3. PR-B B-1 — tier triage (4B, 실패는 legacy 결과 보존) ─── + # ─── 2+3 통합 (B-1 3→2): tier triage 가 tldr/bullets/tier + ai_summary 생산. + # 기존 별도 summarize 콜 제거 → 본문 prefill 1회 절감 (Mac mini 부하). 실패는 fallback. try: await _run_tier_triage(client, doc, session, use_deep=use_deep) except StageDeferred: raise # 보류는 실패가 아님 — drain/consumer 가 attempts 미소모 처리 except Exception as exc: - logger.exception(f"[triage] id={document_id} 전체 실패 — legacy 유지: {exc}") + logger.exception(f"[triage] id={document_id} 전체 실패: {exc}") + + # ─── ai_summary fallback: triage 가 못 채운 경우만 summarize ─── + # (>120K long_context 는 triage 가 LLM skip, 또는 triage 파싱실패). 정상 경로는 미발동. + if not doc.ai_summary: + try: + summary = await client.summarize(doc.extracted_text[:50000], cfg=legacy_cfg) + doc.ai_summary = strip_thinking(summary) + except Exception as exc: + if legacy_cfg is not None and is_deferrable_error(exc): + raise StageDeferred(f"macbook_unavailable:{type(exc).__name__}") from exc + logger.warning(f"[summary-fallback] id={document_id}: {exc}") finally: await client.close() @@ -774,6 +778,9 @@ async def _apply_triage_result( if not parse_error: doc.ai_tldr = (triage_out.tldr or "").strip() or None doc.ai_bullets = triage_out.bullets or [] + # B-1 3→2: triage 가 ai_summary 도 생산(summarize 콜 대체). 비면 process() 가 fallback. + if triage_out.ai_summary.strip(): + doc.ai_summary = triage_out.ai_summary.strip() # Memo Intake Upgrade PR-2B — event kind hint (4B 가 출력했을 때만) # 허용 enum 외 값이면 무시 (DB enum 제약). AI worker 는 events row 직접 생성 X. valid_kinds = {"note", "task", "calendar_event", "activity_log", "reference"}