6fdc48e5b6
PR-A policy 레이어를 재사용하여 classify_worker 에 tier triage 경로를 추가.
Legacy ai_summary / ai_domain / ai_suggestion 은 유지 (회귀 0), tldr/bullets/
detail/inconsistencies 는 별도 필드로 분리.
Migrations (156~160):
- 156 documents: ai_tldr, ai_bullets, ai_detail_summary, ai_inconsistencies,
ai_analysis_tier 5컬럼
- 157 process_stage 에 'deep_summary' ADD VALUE 단독 (Postgres 동일 트랜잭션
제약 회피)
- 158 processing_queue.payload JSONB (envelope 전달)
- 159 analyze_events 에 tier + suppressed_reason
- 160 suppressed_reason partial index
Models/ORM:
- Document: 5컬럼 Mapped 추가
- ProcessingQueue: deep_summary enum 확장 + payload 필드, enqueue_stage 에
payload 옵션
- AnalyzeEvent: PR-A shadow 6컬럼 + PR-B tier/suppressed_reason
Workers:
- classify_worker: 기존 legacy 경로 뒤에 _run_tier_triage 추가.
- _match_subject_domain(doc, text): source_channel + 본문 keywords + ai_domain
prefix 로 PR-A policy 의 subject_domain 이름 결정 (category 매칭 금지).
- R1 TriageOutput pydantic + JSON 깨짐 fallback (triage_json_invalid).
- R2 _check_backlog_guard(): 30분 window ratio > threshold OR pending 초과면
soft escalate suppress. hard escalate 는 통과.
- R3 _slice_text_ranges(): 260k 초과 시 head 120k + mid 20k + tail 120k 3조각.
- escalate 시 EscalationEnvelope 구성 + {envelope, subject_domain} payload 로
deep_summary enqueue.
- deep_summary_worker (신규): queue payload 에서 envelope + subject_domain 읽기 →
render_26b("p3c_deep_summary", subject_domain) + MLX 호출 (llm_gate Semaphore(1)
경유) → ai_detail_summary + ai_inconsistencies 저장 + ai_analysis_tier='deep'.
_filter_inconsistencies 로 허용 kind (version_drift / procedure_conflict /
source_conflict / missing_basis) 만 통과 — 구매/계약 kind drop.
- queue_consumer: workers dict 에 deep_summary 추가 + BATCH_SIZE=1. next_stages
는 건드리지 않음 — classify → embed/chunk 는 그대로, deep_summary 는 독립 체인.
Telemetry:
- record_analyze_event: subject_domain / risk_flags / escalation_reasons /
confidence / policy_version / shadow_would_route_to / tier / escalated_to_26b /
suppressed_reason 파라미터 확장. classify/deep worker 가 mode="summary_triage"
또는 "summary_deep" 로 기록.
API:
- DocumentResponse 에 ai_tldr / ai_bullets / ai_detail_summary /
ai_inconsistencies / ai_analysis_tier 5필드 노출.
Prompts:
- classify.txt 에 DEPRECATED 주석만 추가 (파일 유지 — rollback 경로 보존).
- PR-A 의 app/prompts/policy/p3a_short_summary.txt (4B) 와 p3c_deep_summary.txt
(26B) 를 그대로 사용. 내 소유의 summary_triage.txt / summary_deep.txt 는 중복
이라 별도 커밋에서 제거하지 않고 바로 생성 전 삭제.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
3.1 KiB
Python
69 lines
3.1 KiB
Python
"""프롬프트/모델 버전 상수 — telemetry 기록용 (Phase E.1)
|
|
|
|
목적: ask_events / analyze_events 에 prompt_version 과 model_name 을 기록해서
|
|
튜닝 전/후 비교와 실험 분기를 식별 가능하게 함.
|
|
|
|
규칙:
|
|
- 프롬프트 파일이 의미 있게 바뀌면 해당 상수 문자열을 bump (예: v1-400char → v2-600char)
|
|
- 하드코딩 금지. 파이프라인은 여기 상수만 참조.
|
|
- 모델명은 런타임 config(settings.ai.primary.model)에서 읽어서 resolve_primary_model() 사용.
|
|
|
|
E.3 배포 타임라인:
|
|
- v1-400char → 현재 (search_synthesis.txt 17행 "400 characters max")
|
|
- v2-600char → E.3 배포 시 bump (동일 파일 "600 characters max")
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
# ─── ask (/search/ask) 프롬프트 버전 ─────────────────────────
|
|
# synthesis_service.py 가 로드하는 app/prompts/search_synthesis.txt 기준
|
|
ASK_PROMPT_VERSION: str = "search_synthesis.v2-600char"
|
|
|
|
# ─── /analyze 프롬프트 버전 ──────────────────────────────────
|
|
# documents.py analyze 라우트가 로드하는 app/prompts/document_analyze.txt 기준
|
|
ANALYZE_PROMPT_VERSION: str = "document_analyze.v1"
|
|
|
|
# ─── PR-B B-1: summary tier 분할 task 이름 ─────────────────────
|
|
# classify_worker / deep_summary_worker 가 PR-A 정책 템플릿 + policy_version 해시
|
|
# 조합으로 analyze_events.prompt_version 을 기록한다. (예: "p3a_short_summary@abc123")
|
|
SUMMARY_TRIAGE_TASK: str = "p3a_short_summary" # 4B gemma Ollama
|
|
SUMMARY_DEEP_TASK: str = "p3c_deep_summary" # 26B MLX
|
|
|
|
|
|
def resolve_primary_model() -> str | None:
|
|
"""런타임 config에서 primary 모델명을 resolve.
|
|
|
|
settings.ai 가 미구성이면 None.
|
|
telemetry 기록은 None 허용 (측정 필드는 nullable).
|
|
"""
|
|
try:
|
|
from core.config import settings
|
|
|
|
if settings.ai and settings.ai.primary:
|
|
return settings.ai.primary.model
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
# ─── Policy-layer prompt version helper (PR-A) ──────────────────────
|
|
# domain_policy.yaml + 정책 template 의 결합 해시로 automatic version 산출.
|
|
# analyze_events.policy_version 컬럼에 기록되어 drift 추적.
|
|
#
|
|
# 기존 ASK_PROMPT_VERSION / ANALYZE_PROMPT_VERSION 상수는 그대로 유지 — PR-B 에서
|
|
# 정책 렌더된 프롬프트로 전환 시 compute_policy_version() 결과로 대체할지 병기할지 결정.
|
|
|
|
def compute_policy_version(
|
|
task: str, *, policy_path: str | None = None
|
|
) -> str:
|
|
"""sha256(yaml_bytes + template_bytes)[:12] — deterministic hash.
|
|
|
|
task: policy template 이름 (예: 'p3a_short_summary'). app/prompts/policy/ 하위.
|
|
policy_path: override (테스트용). None 이면 loader 기본값.
|
|
|
|
import 지연 — app.policy 는 아직 worker 경로에서 쓰지 않는다 (PR-A 런타임 격리).
|
|
"""
|
|
from policy.prompt_render import policy_version as _pv
|
|
|
|
return _pv(task, policy_path=policy_path)
|