refactor(classify): summarize 콜을 tier triage 에 병합 (3콜→2콜)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
[System]
|
[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}
|
subject_description: {subject_description}
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ subject_description: {subject_description}
|
|||||||
- pii 감지 시 "pii" 추가 + confidence 감점.
|
- pii 감지 시 "pii" 추가 + confidence 감점.
|
||||||
|
|
||||||
요약 규칙:
|
요약 규칙:
|
||||||
|
- **ai_summary**: 2~3문장 문단. 문서의 핵심 내용·목적을 서술 (검색·표시용 요약).
|
||||||
- **TL;DR**: 1문장, 최대 60자.
|
- **TL;DR**: 1문장, 최대 60자.
|
||||||
- **Bullets**: 정확히 5개, 각 30~60자.
|
- **Bullets**: 정확히 5개, 각 30~60자.
|
||||||
- 본문에 없는 정보 추가 금지 (hallucination 금지).
|
- 본문에 없는 정보 추가 금지 (hallucination 금지).
|
||||||
@@ -20,6 +21,7 @@ subject_description: {subject_description}
|
|||||||
|
|
||||||
출력 (JSON only):
|
출력 (JSON only):
|
||||||
{{
|
{{
|
||||||
|
"ai_summary": "2~3문장 문단 요약",
|
||||||
"tldr": "1문장 최대 60자",
|
"tldr": "1문장 최대 60자",
|
||||||
"bullets": ["...", "...", "...", "...", "..."],
|
"bullets": ["...", "...", "...", "...", "..."],
|
||||||
"tags": ["..."],
|
"tags": ["..."],
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ HARD_ESCALATE_REASONS = {
|
|||||||
class TriageOutput(BaseModel):
|
class TriageOutput(BaseModel):
|
||||||
"""p3a_short_summary (4B) 응답 스키마. 파싱 실패 시 기본값 + escalate=True fallback."""
|
"""p3a_short_summary (4B) 응답 스키마. 파싱 실패 시 기본값 + escalate=True fallback."""
|
||||||
|
|
||||||
|
ai_summary: str = "" # B-1 3→2: triage 가 ai_summary 도 생산 (별 summarize 콜 대체)
|
||||||
tldr: str = ""
|
tldr: str = ""
|
||||||
bullets: list[str] = Field(default_factory=list)
|
bullets: list[str] = Field(default_factory=list)
|
||||||
tags: list[str] = Field(default_factory=list)
|
tags: list[str] = Field(default_factory=list)
|
||||||
@@ -579,16 +580,7 @@ async def process(
|
|||||||
"reason": "classify pipeline",
|
"reason": "classify pipeline",
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─── 2. Legacy 요약 (primary 또는 deep) ───
|
# ─── 메타데이터 (classify 완료) — 실제 처리 머신 귀속 (drain=qwen-macbook) ───
|
||||||
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) ───
|
|
||||||
doc.ai_model_version = (legacy_cfg or settings.ai.primary).model
|
doc.ai_model_version = (legacy_cfg or settings.ai.primary).model
|
||||||
doc.ai_processed_at = datetime.now(timezone.utc)
|
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}"
|
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:
|
try:
|
||||||
await _run_tier_triage(client, doc, session, use_deep=use_deep)
|
await _run_tier_triage(client, doc, session, use_deep=use_deep)
|
||||||
except StageDeferred:
|
except StageDeferred:
|
||||||
raise # 보류는 실패가 아님 — drain/consumer 가 attempts 미소모 처리
|
raise # 보류는 실패가 아님 — drain/consumer 가 attempts 미소모 처리
|
||||||
except Exception as exc:
|
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:
|
finally:
|
||||||
await client.close()
|
await client.close()
|
||||||
@@ -774,6 +778,9 @@ async def _apply_triage_result(
|
|||||||
if not parse_error:
|
if not parse_error:
|
||||||
doc.ai_tldr = (triage_out.tldr or "").strip() or None
|
doc.ai_tldr = (triage_out.tldr or "").strip() or None
|
||||||
doc.ai_bullets = triage_out.bullets or []
|
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 가 출력했을 때만)
|
# Memo Intake Upgrade PR-2B — event kind hint (4B 가 출력했을 때만)
|
||||||
# 허용 enum 외 값이면 무시 (DB enum 제약). AI worker 는 events row 직접 생성 X.
|
# 허용 enum 외 값이면 무시 (DB enum 제약). AI worker 는 events row 직접 생성 X.
|
||||||
valid_kinds = {"note", "task", "calendar_event", "activity_log", "reference"}
|
valid_kinds = {"note", "task", "calendar_event", "activity_log", "reference"}
|
||||||
|
|||||||
Reference in New Issue
Block a user